diff --git a/.jvmopts b/.jvmopts index 17d2012..f95486c 100644 --- a/.jvmopts +++ b/.jvmopts @@ -1,4 +1,5 @@ -XX:+UseG1GC -XX:+UseStringDeduplication --Xmx8G +-Xms8g +-Xmx28g -Xss4M \ No newline at end of file diff --git a/.scalafmt.conf b/.scalafmt.conf index 897b746..3f110b2 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,18 +1,20 @@ -version = 2.7.5 - +version = 3.5.9 maxColumn = 120 -align.arrowEnumeratorGenerator = true -align.openParenCallSite = true -align.preset=more -continuationIndent.defnSite = 4 +style = defaultWithAlign danglingParentheses.preset = true -includeCurlyBraceInSelectChains = false -newlines.alwaysBeforeTopLevelStatements = true -newlines.penalizeSingleSelectMultiArgList = false +indentOperator.preset = "spray" +align.arrowEnumeratorGenerator = true +align.openParenCallSite = false +align.openParenDefnSite = false +align.tokens = [{code = "="}, {code = "<-"}, {code = ":="}, {code = "->"}, {code = "%"}, {code = "%%"}] +continuationIndent.callSite = 2 +continuationIndent.defnSite = 2 optIn.breakChainOnFirstMethodDot = true -optIn.configStyleArguments = true + +spaces { + inImportCurlyBraces = true +} + project.git = true -rewrite.rules = [ RedundantBraces, RedundantParens, PreferCurlyFors] -runner.optimizer.forceConfigStyleOnOffset = 120 -runner.optimizer.forceConfigStyleMinArgCount = 3 \ No newline at end of file +runner.dialect = scala213 \ No newline at end of file diff --git a/README.md b/README.md index 9378d4a..8c5f3be 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A few aspects differentiate Thylacine from other Bayesian inference frameworks: * Framework and design largely de-coupled from Bayesian graphical representations of the problem (more details in the FAQ below) * Designed to be multi-threaded from the ground up using a high-performance Software Transactional Memory (STM) implementation to facilitate efficient concurrency across the differing computational profiles for sampling, integration, visualisation, etc. * Analytic gradient calculations can be specified on a component level that enables automatic differentiation to be used in gradient calculations. This is essential if aiming to perform high-dimension inference using gradient information. -* A growing list of advanced algorithms/concepts have already been implementated: +* A growing list of advanced algorithms/concepts have already been implemented: * Gaussian analytic posteriors * Optimisation * Hooke & Jeeves diff --git a/build.sbt b/build.sbt index 8c90cd5..a54a37e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,9 +1,9 @@ -ThisBuild / baseVersion := "0.5.0-SNAPSHOT" +ThisBuild / baseVersion := "0.6.0" -ThisBuild / organization := "ai.entrolution" -ThisBuild / organizationName := "Greg von Nessi" +ThisBuild / organization := "ai.entrolution" +ThisBuild / organizationName := "Greg von Nessi" ThisBuild / publishGithubUser := "gvonness" -ThisBuild / publishFullName := "Greg von Nessi" +ThisBuild / publishFullName := "Greg von Nessi" ThisBuild / homepage := Some(url("https://github.com/gvonness/thylacine")) ThisBuild / scmInfo := Some( @@ -11,15 +11,15 @@ ThisBuild / scmInfo := Some( ) ThisBuild / startYear := Some(2020) -ThisBuild / endYear := Some(2022) +ThisBuild / endYear := Some(2022) ThisBuild / spiewakCiReleaseSnapshots := false -ThisBuild / spiewakMainBranches := Seq("main") +ThisBuild / spiewakMainBranches := Seq("main") ThisBuild / sonatypeCredentialHost := "s01.oss.sonatype.org" -ThisBuild / sonatypeRepository := "https://s01.oss.sonatype.org/service/local" +ThisBuild / sonatypeRepository := "https://s01.oss.sonatype.org/service/local" -scalaVersion := DependencyVersions.scala2p13Version +scalaVersion := DependencyVersions.scala2p13Version ThisBuild / crossScalaVersions := Seq(DependencyVersions.scala2p13Version) Global / idePackagePrefix := Some("ai.entrolution") diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0ef607e..565ee56 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,9 +1,9 @@ -import sbt._ +import sbt.* object DependencyVersions { val scala2p13Version = "2.13.11" - val bengalStmVersion = "0.9.2" + val bengalStmVersion = "0.9.3" val bigMathVersion = "2.3.0" val breezeVersion = "2.1.0" val catsEffectVersion = "3.4.8" @@ -13,7 +13,7 @@ object DependencyVersions { } object Dependencies { - import DependencyVersions._ + import DependencyVersions.* private val bengalStm: ModuleID = "ai.entrolution" %% "bengal-stm" % bengalStmVersion diff --git a/project/build.properties b/project/build.properties index 22af262..875b706 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.9.2 diff --git a/src/main/scala/thylacine/config/ConjugateGradientConfig.scala b/src/main/scala/thylacine/config/ConjugateGradientConfig.scala index e7d2908..198c624 100644 --- a/src/main/scala/thylacine/config/ConjugateGradientConfig.scala +++ b/src/main/scala/thylacine/config/ConjugateGradientConfig.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.config case class ConjugateGradientConfig( - convergenceThreshold: Double, - goldenSectionTolerance: Double, - lineProbeExpansionFactor: Double, - numberOfResultsToRetain: Int + convergenceThreshold: Double, + goldenSectionTolerance: Double, + lineProbeExpansionFactor: Double, + numberOfResultsToRetain: Int ) diff --git a/src/main/scala/thylacine/config/CoordinateSlideConfig.scala b/src/main/scala/thylacine/config/CoordinateSlideConfig.scala index 3047d72..2e5c780 100644 --- a/src/main/scala/thylacine/config/CoordinateSlideConfig.scala +++ b/src/main/scala/thylacine/config/CoordinateSlideConfig.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.config case class CoordinateSlideConfig( - convergenceThreshold: Double, - goldenSectionTolerance: Double, - lineProbeExpansionFactor: Double, - numberOfPriorSamplesToSetScale: Option[Int] + convergenceThreshold: Double, + goldenSectionTolerance: Double, + lineProbeExpansionFactor: Double, + numberOfPriorSamplesToSetScale: Option[Int] ) diff --git a/src/main/scala/thylacine/config/HmcmcConfig.scala b/src/main/scala/thylacine/config/HmcmcConfig.scala index 2ec159e..689ae48 100644 --- a/src/main/scala/thylacine/config/HmcmcConfig.scala +++ b/src/main/scala/thylacine/config/HmcmcConfig.scala @@ -18,9 +18,9 @@ package ai.entrolution package thylacine.config case class HmcmcConfig( - stepsBetweenSamples: Int, - stepsInDynamicsSimulation: Int, - warmupStepCount: Int, - dynamicsSimulationStepSize: Double, - sampleParallelism: Option[Int] + stepsBetweenSamples: Int, + stepsInDynamicsSimulation: Int, + warmupStepCount: Int, + dynamicsSimulationStepSize: Double, + sampleParallelism: Option[Int] ) diff --git a/src/main/scala/thylacine/config/HookeAndJeevesConfig.scala b/src/main/scala/thylacine/config/HookeAndJeevesConfig.scala index 8ed64f9..95b8224 100644 --- a/src/main/scala/thylacine/config/HookeAndJeevesConfig.scala +++ b/src/main/scala/thylacine/config/HookeAndJeevesConfig.scala @@ -18,6 +18,6 @@ package ai.entrolution package thylacine.config case class HookeAndJeevesConfig( - convergenceThreshold: Double, - numberOfPriorSamplesToSetScale: Option[Int] + convergenceThreshold: Double, + numberOfPriorSamplesToSetScale: Option[Int] ) diff --git a/src/main/scala/thylacine/config/LeapfrogMcmcConfig.scala b/src/main/scala/thylacine/config/LeapfrogMcmcConfig.scala index 0ca8005..a5facdc 100644 --- a/src/main/scala/thylacine/config/LeapfrogMcmcConfig.scala +++ b/src/main/scala/thylacine/config/LeapfrogMcmcConfig.scala @@ -18,7 +18,7 @@ package ai.entrolution package thylacine.config case class LeapfrogMcmcConfig( - stepsBetweenSamples: Int, - warmupStepCount: Int, - samplePoolSize: Int + stepsBetweenSamples: Int, + warmupStepCount: Int, + samplePoolSize: Int ) diff --git a/src/main/scala/thylacine/config/MdsConfig.scala b/src/main/scala/thylacine/config/MdsConfig.scala index e044d72..8c3fc89 100644 --- a/src/main/scala/thylacine/config/MdsConfig.scala +++ b/src/main/scala/thylacine/config/MdsConfig.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.config case class MdsConfig( - convergenceThreshold: Double, - expansionMultiplier: Double, - contractionMultiplier: Double, - numberOfPriorSamplesToSetStartingPoint: Option[Int] + convergenceThreshold: Double, + expansionMultiplier: Double, + contractionMultiplier: Double, + numberOfPriorSamplesToSetStartingPoint: Option[Int] ) diff --git a/src/main/scala/thylacine/config/SlqConfig.scala b/src/main/scala/thylacine/config/SlqConfig.scala index f851542..085ad44 100644 --- a/src/main/scala/thylacine/config/SlqConfig.scala +++ b/src/main/scala/thylacine/config/SlqConfig.scala @@ -18,11 +18,11 @@ package ai.entrolution package thylacine.config case class SlqConfig( - poolSize: Int, - abscissaNumber: Int, - domainScalingIncrement: Double, - targetAcceptanceProbability: Double, - sampleParallelism: Int, - maxIterationCount: Int, - minIterationCount: Int + poolSize: Int, + abscissaNumber: Int, + domainScalingIncrement: Double, + targetAcceptanceProbability: Double, + sampleParallelism: Int, + maxIterationCount: Int, + minIterationCount: Int ) diff --git a/src/main/scala/thylacine/model/components/forwardmodel/ForwardModel.scala b/src/main/scala/thylacine/model/components/forwardmodel/ForwardModel.scala index 3527a03..cb1abaf 100644 --- a/src/main/scala/thylacine/model/components/forwardmodel/ForwardModel.scala +++ b/src/main/scala/thylacine/model/components/forwardmodel/ForwardModel.scala @@ -28,7 +28,7 @@ private[thylacine] trait ForwardModel[F[_]] extends GenericMapping with CanValid // Note that input validation should be done within // the surrounding likelihood private[thylacine] def evalAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): F[VectorContainer] private[thylacine] final def evalAt(input: Map[String, Vector[Double]]): F[Vector[Double]] = @@ -37,7 +37,7 @@ private[thylacine] trait ForwardModel[F[_]] extends GenericMapping with CanValid // Note that input validation should be done within // the surrounding likelihood private[thylacine] def jacobianAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): F[IndexedMatrixCollection] private[thylacine] final def jacobianAt(input: Map[String, Vector[Double]]): F[Map[String, Vector[Vector[Double]]]] = diff --git a/src/main/scala/thylacine/model/components/forwardmodel/InMemoryMemoizedForwardModel.scala b/src/main/scala/thylacine/model/components/forwardmodel/InMemoryMemoizedForwardModel.scala index 9905a2c..f2da0d4 100644 --- a/src/main/scala/thylacine/model/components/forwardmodel/InMemoryMemoizedForwardModel.scala +++ b/src/main/scala/thylacine/model/components/forwardmodel/InMemoryMemoizedForwardModel.scala @@ -20,7 +20,7 @@ package thylacine.model.components.forwardmodel import thylacine.model.core.StmImplicits import thylacine.model.core.computation.CachedComputation import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.{IndexedMatrixCollection, VectorContainer} +import thylacine.model.core.values.{ IndexedMatrixCollection, VectorContainer } private[thylacine] trait InMemoryMemoizedForwardModel[F[_]] extends ForwardModel[F] { this: StmImplicits[F] => @@ -29,12 +29,12 @@ private[thylacine] trait InMemoryMemoizedForwardModel[F[_]] extends ForwardModel protected val jacobianCache: CachedComputation[F, IndexedMatrixCollection] private[thylacine] override final def evalAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[VectorContainer] = evalCache.performComputation(input) private[thylacine] override def jacobianAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[IndexedMatrixCollection] = jacobianCache.performComputation(input) } diff --git a/src/main/scala/thylacine/model/components/forwardmodel/LinearForwardModel.scala b/src/main/scala/thylacine/model/components/forwardmodel/LinearForwardModel.scala index 41ee114..79f8e1b 100644 --- a/src/main/scala/thylacine/model/components/forwardmodel/LinearForwardModel.scala +++ b/src/main/scala/thylacine/model/components/forwardmodel/LinearForwardModel.scala @@ -24,7 +24,7 @@ import thylacine.model.core.computation.CachedComputation import thylacine.model.core.values._ import thylacine.model.core.values.modelparameters.ModelParameterContext -import breeze.linalg.{DenseMatrix, DenseVector} +import breeze.linalg.{ DenseMatrix, DenseVector } import cats.effect.kernel.Async import cats.syntax.all._ @@ -33,13 +33,13 @@ import scala.annotation.unused // A linear forward model may work across more than // one model parameter generator case class LinearForwardModel[F[_]: STM: Async]( - protected override val evalCache: CachedComputation[F, VectorContainer], - protected override val jacobianCache: CachedComputation[F, IndexedMatrixCollection], - private[thylacine] val transform: IndexedMatrixCollection, - private[thylacine] val vectorOffset: Option[VectorContainer], - override val domainDimension: Int, - override val rangeDimension: Int, - private[thylacine] override val validated: Boolean = false + protected override val evalCache: CachedComputation[F, VectorContainer], + protected override val jacobianCache: CachedComputation[F, IndexedMatrixCollection], + private[thylacine] val transform: IndexedMatrixCollection, + private[thylacine] val vectorOffset: Option[VectorContainer], + override val domainDimension: Int, + override val rangeDimension: Int, + private[thylacine] override val validated: Boolean = false ) extends StmImplicits[F] with InMemoryMemoizedForwardModel[F] { if (!validated) { @@ -54,9 +54,9 @@ case class LinearForwardModel[F[_]: STM: Async]( this } else { this.copy( - transform = transform.getValidated, + transform = transform.getValidated, vectorOffset = vectorOffset.map(_.getValidated), - validated = true + validated = true ) } @@ -66,7 +66,7 @@ case class LinearForwardModel[F[_]: STM: Async]( transform private[thylacine] override def jacobianAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): F[IndexedMatrixCollection] = Async[F].pure(getJacobian) } @@ -74,9 +74,9 @@ case class LinearForwardModel[F[_]: STM: Async]( object LinearForwardModel { def of[F[_]: STM: Async]( - transform: IndexedMatrixCollection, - vectorOffset: Option[VectorContainer], - evalCacheDepth: Option[Int] + transform: IndexedMatrixCollection, + vectorOffset: Option[VectorContainer], + evalCacheDepth: Option[Int] ): F[LinearForwardModel[F]] = { val rangeDimension: Int = transform.index.head._2.rowTotalNumber @@ -109,7 +109,7 @@ object LinearForwardModel { } def transformedEval( - input: IndexedVectorCollection + input: IndexedVectorCollection ): VectorContainer = VectorContainer(applyOffset(rawMatrixTransform * rawMappings.modelParameterCollectionToRawVector(input))) @@ -125,37 +125,37 @@ object LinearForwardModel { @unused private[thylacine] def of[F[_]: STM: Async]( - identifier: ModelParameterIdentifier, - values: Vector[Vector[Double]], - evalCacheDepth: Option[Int] + identifier: ModelParameterIdentifier, + values: Vector[Vector[Double]], + evalCacheDepth: Option[Int] ): F[LinearForwardModel[F]] = of[F]( - transform = IndexedMatrixCollection(identifier, values), - vectorOffset = None, + transform = IndexedMatrixCollection(identifier, values), + vectorOffset = None, evalCacheDepth = evalCacheDepth ) def of[F[_]: STM: Async]( - label: String, - values: Vector[Vector[Double]], - evalCacheDepth: Option[Int] + label: String, + values: Vector[Vector[Double]], + evalCacheDepth: Option[Int] ): F[LinearForwardModel[F]] = of[F]( - transform = IndexedMatrixCollection(ModelParameterIdentifier(label), values), - vectorOffset = None, + transform = IndexedMatrixCollection(ModelParameterIdentifier(label), values), + vectorOffset = None, evalCacheDepth = evalCacheDepth ) @unused def identityOf[F[_]: STM: Async]( - label: String, - dimension: Int, - evalCacheDepth: Option[Int] + label: String, + dimension: Int, + evalCacheDepth: Option[Int] ): F[LinearForwardModel[F]] = of[F]( transform = IndexedMatrixCollection .squareIdentity(ModelParameterIdentifier(label), dimension), - vectorOffset = None, + vectorOffset = None, evalCacheDepth = evalCacheDepth ) } diff --git a/src/main/scala/thylacine/model/components/forwardmodel/NonLinearForwardModel.scala b/src/main/scala/thylacine/model/components/forwardmodel/NonLinearForwardModel.scala index 16ce455..a672078 100644 --- a/src/main/scala/thylacine/model/components/forwardmodel/NonLinearForwardModel.scala +++ b/src/main/scala/thylacine/model/components/forwardmodel/NonLinearForwardModel.scala @@ -19,18 +19,18 @@ package thylacine.model.components.forwardmodel import bengal.stm.STM import thylacine.model.core.StmImplicits -import thylacine.model.core.computation.{CachedComputation, FiniteDifferenceJacobian} -import thylacine.model.core.values.{IndexedMatrixCollection, IndexedVectorCollection, VectorContainer} +import thylacine.model.core.computation.{ CachedComputation, FiniteDifferenceJacobian } +import thylacine.model.core.values.{ IndexedMatrixCollection, IndexedVectorCollection, VectorContainer } import cats.effect.kernel.Async import cats.syntax.all._ case class NonLinearForwardModel[F[_]: STM: Async]( - protected override val evalCache: CachedComputation[F, VectorContainer], - protected override val jacobianCache: CachedComputation[F, IndexedMatrixCollection], - private val domainDimensions: Map[String, Int], - override val rangeDimension: Int, - private[thylacine] override val validated: Boolean = false + protected override val evalCache: CachedComputation[F, VectorContainer], + protected override val jacobianCache: CachedComputation[F, IndexedMatrixCollection], + private val domainDimensions: Map[String, Int], + override val rangeDimension: Int, + private[thylacine] override val validated: Boolean = false ) extends StmImplicits[F] with InMemoryMemoizedForwardModel[F] { @@ -42,12 +42,12 @@ case class NonLinearForwardModel[F[_]: STM: Async]( object NonLinearForwardModel { def of[F[_]: STM: Async]( - evaluation: Map[String, Vector[Double]] => Vector[Double], - differential: Double, - domainDimensions: Map[String, Int], - rangeDimension: Int, - evalCacheDepth: Option[Int], - jacobianCacheDepth: Option[Int] + evaluation: Map[String, Vector[Double]] => Vector[Double], + differential: Double, + domainDimensions: Map[String, Int], + rangeDimension: Int, + evalCacheDepth: Option[Int], + jacobianCacheDepth: Option[Int] ): F[NonLinearForwardModel[F]] = { def transformedEval(input: IndexedVectorCollection): VectorContainer = VectorContainer(evaluation(input.index.map(i => i._1.value -> i._2.scalaVector))) @@ -62,14 +62,14 @@ object NonLinearForwardModel { } def of[F[_]: STM: Async]( - evaluation: Map[String, Vector[Double]] => Vector[Double], - jacobian: Map[String, Vector[Double]] => Map[String, Vector[ - Vector[Double] - ]], - domainDimensions: Map[String, Int], - rangeDimension: Int, - evalCacheDepth: Option[Int], - jacobianCacheDepth: Option[Int] + evaluation: Map[String, Vector[Double]] => Vector[Double], + jacobian: Map[String, Vector[Double]] => Map[String, Vector[ + Vector[Double] + ]], + domainDimensions: Map[String, Int], + rangeDimension: Int, + evalCacheDepth: Option[Int], + jacobianCacheDepth: Option[Int] ): F[NonLinearForwardModel[F]] = { def transformedEval(input: IndexedVectorCollection): VectorContainer = diff --git a/src/main/scala/thylacine/model/components/likelihood/CauchyLikelihood.scala b/src/main/scala/thylacine/model/components/likelihood/CauchyLikelihood.scala index ca1d7b6..4840710 100644 --- a/src/main/scala/thylacine/model/components/likelihood/CauchyLikelihood.scala +++ b/src/main/scala/thylacine/model/components/likelihood/CauchyLikelihood.scala @@ -26,12 +26,13 @@ import thylacine.model.distributions.CauchyDistribution import cats.effect.kernel.Async import java.util.UUID +import scala.annotation.unused case class CauchyLikelihood[F[_]: Async]( - private[thylacine] override val posteriorTermIdentifier: TermIdentifier, - private[thylacine] val observations: RecordedData, - private[thylacine] override val forwardModel: ForwardModel[F], - private[thylacine] override val validated: Boolean = false + private[thylacine] override val posteriorTermIdentifier: TermIdentifier, + private[thylacine] val observations: RecordedData, + private[thylacine] override val forwardModel: ForwardModel[F], + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Likelihood[F, ForwardModel[F], CauchyDistribution] { if (!validated) { @@ -50,17 +51,18 @@ case class CauchyLikelihood[F[_]: Async]( } +@unused object CauchyLikelihood { def apply[F[_]: Async]( - forwardModel: ForwardModel[F], - measurements: Vector[Double], - uncertainties: Vector[Double] + forwardModel: ForwardModel[F], + measurements: Vector[Double], + uncertainties: Vector[Double] ): CauchyLikelihood[F] = CauchyLikelihood( posteriorTermIdentifier = TermIdentifier(UUID.randomUUID().toString), observations = RecordedData( - values = VectorContainer(measurements), + values = VectorContainer(measurements), symmetricConfidenceIntervals = VectorContainer(uncertainties) ), forwardModel = forwardModel diff --git a/src/main/scala/thylacine/model/components/likelihood/GaussianLikelihood.scala b/src/main/scala/thylacine/model/components/likelihood/GaussianLikelihood.scala index b7c0b13..ba7a8bd 100644 --- a/src/main/scala/thylacine/model/components/likelihood/GaussianLikelihood.scala +++ b/src/main/scala/thylacine/model/components/likelihood/GaussianLikelihood.scala @@ -28,10 +28,10 @@ import cats.effect.kernel.Async import java.util.UUID case class GaussianLikelihood[F[_]: Async, T <: ForwardModel[F]]( - private[thylacine] override val posteriorTermIdentifier: TermIdentifier, - private[thylacine] val observations: RecordedData, - private[thylacine] override val forwardModel: T, - private[thylacine] override val validated: Boolean = false + private[thylacine] override val posteriorTermIdentifier: TermIdentifier, + private[thylacine] val observations: RecordedData, + private[thylacine] override val forwardModel: T, + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Likelihood[F, T, GaussianDistribution] { if (!validated) { @@ -53,14 +53,14 @@ case class GaussianLikelihood[F[_]: Async, T <: ForwardModel[F]]( object GaussianLikelihood { def from[F[_]: Async, T <: ForwardModel[F]]( - forwardModel: T, - measurements: Vector[Double], - uncertainties: Vector[Double] + forwardModel: T, + measurements: Vector[Double], + uncertainties: Vector[Double] ): GaussianLikelihood[F, T] = GaussianLikelihood( posteriorTermIdentifier = TermIdentifier(UUID.randomUUID().toString), observations = RecordedData( - values = VectorContainer(measurements), + values = VectorContainer(measurements), symmetricConfidenceIntervals = VectorContainer(uncertainties) ), forwardModel = forwardModel diff --git a/src/main/scala/thylacine/model/components/likelihood/GaussianLinearLikelihood.scala b/src/main/scala/thylacine/model/components/likelihood/GaussianLinearLikelihood.scala index f17b012..1aff5b0 100644 --- a/src/main/scala/thylacine/model/components/likelihood/GaussianLinearLikelihood.scala +++ b/src/main/scala/thylacine/model/components/likelihood/GaussianLinearLikelihood.scala @@ -35,10 +35,10 @@ import scala.annotation.unused // Thus, it gets a dedicated case class that is leveraged to do an analytic check in the posterior // construction. case class GaussianLinearLikelihood[F[_]: Async]( - private[thylacine] override val posteriorTermIdentifier: TermIdentifier, - private[thylacine] val observations: RecordedData, - private[thylacine] override val forwardModel: LinearForwardModel[F], - private[thylacine] override val validated: Boolean = false + private[thylacine] override val posteriorTermIdentifier: TermIdentifier, + private[thylacine] val observations: RecordedData, + private[thylacine] override val forwardModel: LinearForwardModel[F], + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Likelihood[F, LinearForwardModel[F], GaussianDistribution] { if (!validated) { @@ -61,23 +61,23 @@ object GaussianLinearLikelihood { @unused def of[F[_]: STM: Async]( - coefficients: Vector[Vector[Double]], - measurements: Vector[Double], - uncertainties: Vector[Double], - prior: Prior[F, _], - evalCacheDepth: Option[Int] + coefficients: Vector[Vector[Double]], + measurements: Vector[Double], + uncertainties: Vector[Double], + prior: Prior[F, _], + evalCacheDepth: Option[Int] ): F[GaussianLinearLikelihood[F]] = for { linearForwardModel <- LinearForwardModel .of[F]( - identifier = prior.identifier, - values = coefficients, + identifier = prior.identifier, + values = coefficients, evalCacheDepth = evalCacheDepth ) } yield GaussianLinearLikelihood( posteriorTermIdentifier = TermIdentifier(UUID.randomUUID().toString), observations = RecordedData( - values = VectorContainer(measurements), + values = VectorContainer(measurements), symmetricConfidenceIntervals = VectorContainer(uncertainties) ), forwardModel = linearForwardModel diff --git a/src/main/scala/thylacine/model/components/likelihood/Likelihood.scala b/src/main/scala/thylacine/model/components/likelihood/Likelihood.scala index f7c5bf7..b573775 100644 --- a/src/main/scala/thylacine/model/components/likelihood/Likelihood.scala +++ b/src/main/scala/thylacine/model/components/likelihood/Likelihood.scala @@ -22,7 +22,7 @@ import thylacine.model.components.posterior.PosteriorTerm import thylacine.model.core._ import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection import thylacine.model.core.values.modelparameters.ModelParameterPdf -import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} +import thylacine.model.core.values.{ IndexedVectorCollection, VectorContainer } import thylacine.model.distributions.Distribution import cats.effect.kernel.Async @@ -41,7 +41,7 @@ private[thylacine] trait Likelihood[F[_], +FM <: ForwardModel[F], +D <: Distribu forwardModel.domainDimension private[thylacine] override final def logPdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] = for { mappedVec <- forwardModel.evalAt(input) @@ -49,16 +49,18 @@ private[thylacine] trait Likelihood[F[_], +FM <: ForwardModel[F], +D <: Distribu } yield res private[thylacine] override final def logPdfGradientAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[ModelParameterCollection] = for { mappedVec <- forwardModel.evalAt(input) forwardJac <- forwardModel.jacobianAt(input) measGrad <- Async[F].delay(observationDistribution.logPdfGradientAt(mappedVec)) - } yield forwardJac.index.toList.map { fj => - IndexedVectorCollection( - fj._1, - VectorContainer((measGrad.rawVector.t * fj._2.rawMatrix).t) - ) - }.reduce(_ rawMergeWith _) + } yield forwardJac.index.toList + .map { fj => + IndexedVectorCollection( + fj._1, + VectorContainer((measGrad.rawVector.t * fj._2.rawMatrix).t) + ) + } + .reduce(_ rawMergeWith _) } diff --git a/src/main/scala/thylacine/model/components/likelihood/UniformLikelihood.scala b/src/main/scala/thylacine/model/components/likelihood/UniformLikelihood.scala index e2175cd..3c328d5 100644 --- a/src/main/scala/thylacine/model/components/likelihood/UniformLikelihood.scala +++ b/src/main/scala/thylacine/model/components/likelihood/UniformLikelihood.scala @@ -27,13 +27,14 @@ import thylacine.model.distributions.UniformDistribution import cats.effect.kernel.Async import java.util.UUID +import scala.annotation.unused case class UniformLikelihood[F[_]: Async, T <: ForwardModel[F]]( - private[thylacine] override val posteriorTermIdentifier: TermIdentifier, - private[thylacine] val upperBounds: VectorContainer, - private[thylacine] val lowerBounds: VectorContainer, - private[thylacine] override val forwardModel: T, - private[thylacine] override val validated: Boolean = false + private[thylacine] override val posteriorTermIdentifier: TermIdentifier, + private[thylacine] val upperBounds: VectorContainer, + private[thylacine] val lowerBounds: VectorContainer, + private[thylacine] override val forwardModel: T, + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Likelihood[F, T, UniformDistribution] { if (!validated) { @@ -47,7 +48,7 @@ case class UniformLikelihood[F[_]: Async, T <: ForwardModel[F]]( this.copy( lowerBounds = lowerBounds.getValidated, upperBounds = upperBounds.getValidated, - validated = true + validated = true ) } @@ -56,17 +57,18 @@ case class UniformLikelihood[F[_]: Async, T <: ForwardModel[F]]( } +@unused object UniformLikelihood { def apply[F[_]: Async, T <: ForwardModel[F]]( - forwardModel: T, - upperBounds: Vector[Double], - lowerBounds: Vector[Double] + forwardModel: T, + upperBounds: Vector[Double], + lowerBounds: Vector[Double] ): UniformLikelihood[F, T] = UniformLikelihood( posteriorTermIdentifier = TermIdentifier(UUID.randomUUID().toString), - upperBounds = VectorContainer(upperBounds), - lowerBounds = VectorContainer(lowerBounds), - forwardModel = forwardModel + upperBounds = VectorContainer(upperBounds), + lowerBounds = VectorContainer(lowerBounds), + forwardModel = forwardModel ) } diff --git a/src/main/scala/thylacine/model/components/posterior/ConjugateGradientOptimisedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/ConjugateGradientOptimisedPosterior.scala index 503ea11..6b96aa9 100644 --- a/src/main/scala/thylacine/model/components/posterior/ConjugateGradientOptimisedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/ConjugateGradientOptimisedPosterior.scala @@ -26,11 +26,11 @@ import thylacine.model.optimization.gradientdescent.ConjugateGradientEngine import cats.effect.kernel.Async case class ConjugateGradientOptimisedPosterior[F[_]: Async]( - private[thylacine] val gradientDescentConfig: ConjugateGradientConfig, - protected override val newMaximumCallback: Double => F[Unit], - protected override val isConvergedCallback: Unit => F[Unit], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]] + private[thylacine] val gradientDescentConfig: ConjugateGradientConfig, + protected override val newMaximumCallback: Double => F[Unit], + protected override val isConvergedCallback: Unit => F[Unit], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]] ) extends AsyncImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with ConjugateGradientEngine[F] { @@ -51,16 +51,16 @@ case class ConjugateGradientOptimisedPosterior[F[_]: Async]( object ConjugateGradientOptimisedPosterior { def from[F[_]: Async]( - conjugateGradientConfig: ConjugateGradientConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - newMaximumCallback: Double => F[Unit], - isConvergedCallback: Unit => F[Unit] + conjugateGradientConfig: ConjugateGradientConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + newMaximumCallback: Double => F[Unit], + isConvergedCallback: Unit => F[Unit] ): ConjugateGradientOptimisedPosterior[F] = ConjugateGradientOptimisedPosterior( gradientDescentConfig = conjugateGradientConfig, - newMaximumCallback = newMaximumCallback, - isConvergedCallback = isConvergedCallback, - priors = posterior.priors, - likelihoods = posterior.likelihoods + newMaximumCallback = newMaximumCallback, + isConvergedCallback = isConvergedCallback, + priors = posterior.priors, + likelihoods = posterior.likelihoods ) } diff --git a/src/main/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosterior.scala index daa1ce5..5c41efb 100644 --- a/src/main/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosterior.scala @@ -30,14 +30,14 @@ import cats.effect.kernel.Async import cats.syntax.all._ case class CoordinateSlideOptimisedPosterior[F[_]: STM: Async]( - private[thylacine] val coordinateSlideConfig: CoordinateSlideConfig, - protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - protected override val isConvergedCallback: Unit => F[Unit], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - protected override val currentBest: TxnVar[F, (Double, Vector[Double])], - protected override val currentScale: TxnVar[F, Double], - protected override val isConverged: TxnVar[F, Boolean] + private[thylacine] val coordinateSlideConfig: CoordinateSlideConfig, + protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + protected override val isConvergedCallback: Unit => F[Unit], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + protected override val currentBest: TxnVar[F, (Double, Vector[Double])], + protected override val currentScale: TxnVar[F, Double], + protected override val isConverged: TxnVar[F, Boolean] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with CoordinateSlideEngine[F] { @@ -58,23 +58,23 @@ case class CoordinateSlideOptimisedPosterior[F[_]: STM: Async]( object CoordinateSlideOptimisedPosterior { def of[F[_]: STM: Async]( - coordinateSlideConfig: CoordinateSlideConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - isConvergedCallback: Unit => F[Unit] + coordinateSlideConfig: CoordinateSlideConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + isConvergedCallback: Unit => F[Unit] ): F[CoordinateSlideOptimisedPosterior[F]] = for { currentBest <- TxnVar.of((0d, Vector[Double]())) currentScale <- TxnVar.of(0d) isConverged <- TxnVar.of(false) } yield CoordinateSlideOptimisedPosterior( - coordinateSlideConfig = coordinateSlideConfig, + coordinateSlideConfig = coordinateSlideConfig, iterationUpdateCallback = iterationUpdateCallback, - isConvergedCallback = isConvergedCallback, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - currentBest = currentBest, - currentScale = currentScale, - isConverged = isConverged + isConvergedCallback = isConvergedCallback, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + currentBest = currentBest, + currentScale = currentScale, + isConverged = isConverged ) } diff --git a/src/main/scala/thylacine/model/components/posterior/GaussianAnalyticPosterior.scala b/src/main/scala/thylacine/model/components/posterior/GaussianAnalyticPosterior.scala index 756c012..3256688 100644 --- a/src/main/scala/thylacine/model/components/posterior/GaussianAnalyticPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/GaussianAnalyticPosterior.scala @@ -23,7 +23,7 @@ import thylacine.model.components.prior._ import thylacine.model.core.GenericIdentifier._ import thylacine.model.core._ import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.{MatrixContainer, VectorContainer} +import thylacine.model.core.values.{ MatrixContainer, VectorContainer } import thylacine.model.sampling.ModelParameterSampler import breeze.linalg._ @@ -33,12 +33,12 @@ import cats.syntax.all._ import org.apache.commons.math3.random.MersenneTwister import scala.annotation.unused -import scala.{Vector => ScalaVector} +import scala.{ Vector => ScalaVector } case class GaussianAnalyticPosterior[F[_]: Async]( - private[thylacine] override val priors: Set[GaussianPrior[F]], - private[thylacine] override val likelihoods: Set[GaussianLinearLikelihood[F]], - private[thylacine] override val validated: Boolean + private[thylacine] override val priors: Set[GaussianPrior[F]], + private[thylacine] override val likelihoods: Set[GaussianLinearLikelihood[F]], + private[thylacine] override val validated: Boolean ) extends AsyncImplicits[F] with Posterior[F, GaussianPrior[F], GaussianLinearLikelihood[F]] with ModelParameterSampler[F] @@ -62,9 +62,9 @@ case class GaussianAnalyticPosterior[F[_]: Async]( this } else { GaussianAnalyticPosterior( - priors = priors.map(_.getValidated), + priors = priors.map(_.getValidated), likelihoods = likelihoods.map(_.getValidated), - validated = true + validated = true ) } @@ -73,7 +73,8 @@ case class GaussianAnalyticPosterior[F[_]: Async]( lazy val entropy: Double = rawDistribution.entropy - lazy val priorEntropies = priors.map(_.entropy) + @unused + lazy val priorEntropies: Set[Double] = priors.map(_.entropy) private lazy val rawDistribution: MultivariateGaussian = { val priorsAdded: AnalyticPosteriorAccumulation[F] = @@ -99,7 +100,7 @@ case class GaussianAnalyticPosterior[F[_]: Async]( rawDistribution.covariance.toArray.toVector private[thylacine] override def logPdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] = Async[F].delay(rawDistribution.logPdf(modelParameterCollectionToRawVector(input))) @@ -116,8 +117,8 @@ case class GaussianAnalyticPosterior[F[_]: Async]( object GaussianAnalyticPosterior { def apply[F[_]: Async]( - priors: Set[GaussianPrior[F]], - likelihoods: Set[GaussianLinearLikelihood[F]] + priors: Set[GaussianPrior[F]], + likelihoods: Set[GaussianLinearLikelihood[F]] ): GaussianAnalyticPosterior[F] = GaussianAnalyticPosterior(priors = priors, likelihoods = likelihoods, validated = false) @@ -126,12 +127,12 @@ object GaussianAnalyticPosterior { // Multivariate Gaussian distribution that represents the // Posterior distribution private[thylacine] case class AnalyticPosteriorAccumulation[F[_]: Async]( - priorMean: Option[VectorContainer] = None, - priorCovariance: Option[MatrixContainer] = None, - data: Option[VectorContainer] = None, - likelihoodCovariance: Option[MatrixContainer] = None, - likelihoodTransformations: Option[MatrixContainer] = None, - orderedParameterIdentifiersWithDimension: ScalaVector[(ModelParameterIdentifier, Int)] + priorMean: Option[VectorContainer] = None, + priorCovariance: Option[MatrixContainer] = None, + data: Option[VectorContainer] = None, + likelihoodCovariance: Option[MatrixContainer] = None, + likelihoodTransformations: Option[MatrixContainer] = None, + orderedParameterIdentifiersWithDimension: ScalaVector[(ModelParameterIdentifier, Int)] ) { private[thylacine] lazy val gRawDistribution: MultivariateGaussian = @@ -165,7 +166,7 @@ object GaussianAnalyticPosterior { ) private[thylacine] def add( - prior: GaussianPrior[F] + prior: GaussianPrior[F] ): AnalyticPosteriorAccumulation[F] = { val incomingPriorMean = prior.priorData.data val incomingPriorCovariance = prior.priorData.covariance @@ -185,16 +186,18 @@ object GaussianAnalyticPosterior { } private[thylacine] def add( - likelihood: GaussianLinearLikelihood[F] + likelihood: GaussianLinearLikelihood[F] ): AnalyticPosteriorAccumulation[F] = { val incomingData = likelihood.observations.data val incomingDataCovariance = likelihood.observations.covariance - val incomingTransformationMatrix = orderedParameterIdentifiersWithDimension.map { id => - likelihood.forwardModel.getJacobian.index.getOrElse( - id._1, - MatrixContainer.zeros(likelihood.forwardModel.rangeDimension, id._2) - ) - }.reduce(_ columnMergeWith _) + val incomingTransformationMatrix = orderedParameterIdentifiersWithDimension + .map { id => + likelihood.forwardModel.getJacobian.index.getOrElse( + id._1, + MatrixContainer.zeros(likelihood.forwardModel.rangeDimension, id._2) + ) + } + .reduce(_ columnMergeWith _) this.copy( data = Some( diff --git a/src/main/scala/thylacine/model/components/posterior/HmcmcSampledPosterior.scala b/src/main/scala/thylacine/model/components/posterior/HmcmcSampledPosterior.scala index cffd659..847b692 100644 --- a/src/main/scala/thylacine/model/components/posterior/HmcmcSampledPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/HmcmcSampledPosterior.scala @@ -31,21 +31,22 @@ import thylacine.model.sampling.hmcmc.HmcmcEngine import cats.effect.kernel.Async import cats.syntax.all._ +import scala.annotation.unused import scala.collection.immutable.Queue case class HmcmcSampledPosterior[F[_]: STM: Async]( - private[thylacine] val hmcmcConfig: HmcmcConfig, - protected override val hamiltonianDifferentialUpdateCallback: Double => F[Unit], - protected override val sampleProcessedCallback: HmcmcTelemetryUpdate => F[Unit], - private[thylacine] val seed: Map[String, Vector[Double]], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - protected override val currentMcmcPositions: TxnVar[F, Queue[ModelParameterCollection]], - protected override val burnInComplete: TxnVar[F, Boolean], - protected override val workTokenPool: TxnVar[F, Int], - protected override val numberOfSamplesRemaining: TxnVar[F, Int], - protected override val jumpAcceptances: TxnVar[F, Int], - protected override val jumpAttempts: TxnVar[F, Int] + private[thylacine] val hmcmcConfig: HmcmcConfig, + protected override val hamiltonianDifferentialUpdateCallback: Double => F[Unit], + protected override val sampleProcessedCallback: HmcmcTelemetryUpdate => F[Unit], + private[thylacine] val seed: Map[String, Vector[Double]], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + protected override val currentMcmcPositions: TxnVar[F, Queue[ModelParameterCollection]], + protected override val burnInComplete: TxnVar[F, Boolean], + protected override val workTokenPool: TxnVar[F, Int], + protected override val numberOfSamplesRemaining: TxnVar[F, Int], + protected override val jumpAcceptances: TxnVar[F, Int], + protected override val jumpAttempts: TxnVar[F, Int] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with HmcmcEngine[F] { @@ -73,14 +74,16 @@ case class HmcmcSampledPosterior[F[_]: STM: Async]( Async[F].delay(IndexedVectorCollection(seed)) } +@unused object HmcmcSampledPosterior { + @unused def of[F[_]: STM: Async]( - hmcmcConfig: HmcmcConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - hamiltonianDifferentialUpdateCallback: Double => F[Unit], - sampleProcessedCallback: HmcmcTelemetryUpdate => F[Unit], - seed: Map[String, Vector[Double]] + hmcmcConfig: HmcmcConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + hamiltonianDifferentialUpdateCallback: Double => F[Unit], + sampleProcessedCallback: HmcmcTelemetryUpdate => F[Unit], + seed: Map[String, Vector[Double]] ): F[HmcmcSampledPosterior[F]] = for { currentMcmcPositions <- TxnVar.of(Queue[ModelParameterCollection]()) @@ -91,18 +94,18 @@ object HmcmcSampledPosterior { jumpAttempts <- TxnVar.of(0) posterior <- Async[F].delay { HmcmcSampledPosterior( - hmcmcConfig = hmcmcConfig, + hmcmcConfig = hmcmcConfig, hamiltonianDifferentialUpdateCallback = hamiltonianDifferentialUpdateCallback, - sampleProcessedCallback = sampleProcessedCallback, - seed = seed, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - currentMcmcPositions = currentMcmcPositions, - burnInComplete = burnInComplete, - workTokenPool = workTokenPool, - jumpAcceptances = jumpAcceptances, - jumpAttempts = jumpAttempts, - numberOfSamplesRemaining = numberOfSamplesRemaining + sampleProcessedCallback = sampleProcessedCallback, + seed = seed, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + currentMcmcPositions = currentMcmcPositions, + burnInComplete = burnInComplete, + workTokenPool = workTokenPool, + jumpAcceptances = jumpAcceptances, + jumpAttempts = jumpAttempts, + numberOfSamplesRemaining = numberOfSamplesRemaining ) } _ <- posterior.launchInitialisation diff --git a/src/main/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosterior.scala index 4cfae7b..a6dd9d8 100644 --- a/src/main/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosterior.scala @@ -30,14 +30,14 @@ import cats.effect.kernel.Async import cats.syntax.all._ case class HookeAndJeevesOptimisedPosterior[F[_]: STM: Async]( - private[thylacine] val hookeAndJeevesConfig: HookeAndJeevesConfig, - protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - protected override val isConvergedCallback: Unit => F[Unit], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - protected override val currentBest: TxnVar[F, (Double, Vector[Double])], - protected override val currentScale: TxnVar[F, Double], - protected override val isConverged: TxnVar[F, Boolean] + private[thylacine] val hookeAndJeevesConfig: HookeAndJeevesConfig, + protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + protected override val isConvergedCallback: Unit => F[Unit], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + protected override val currentBest: TxnVar[F, (Double, Vector[Double])], + protected override val currentScale: TxnVar[F, Double], + protected override val isConverged: TxnVar[F, Boolean] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with HookeAndJeevesEngine[F] { @@ -53,23 +53,23 @@ case class HookeAndJeevesOptimisedPosterior[F[_]: STM: Async]( object HookeAndJeevesOptimisedPosterior { def of[F[_]: STM: Async]( - hookeAndJeevesConfig: HookeAndJeevesConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - isConvergedCallback: Unit => F[Unit] + hookeAndJeevesConfig: HookeAndJeevesConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + isConvergedCallback: Unit => F[Unit] ): F[HookeAndJeevesOptimisedPosterior[F]] = for { currentBest <- TxnVar.of((0d, Vector[Double]())) currentScale <- TxnVar.of(0d) isConverged <- TxnVar.of(false) } yield HookeAndJeevesOptimisedPosterior( - hookeAndJeevesConfig = hookeAndJeevesConfig, + hookeAndJeevesConfig = hookeAndJeevesConfig, iterationUpdateCallback = iterationUpdateCallback, - isConvergedCallback = isConvergedCallback, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - currentBest = currentBest, - currentScale = currentScale, - isConverged = isConverged + isConvergedCallback = isConvergedCallback, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + currentBest = currentBest, + currentScale = currentScale, + isConverged = isConverged ) } diff --git a/src/main/scala/thylacine/model/components/posterior/LeapfrogMcmcSampledPosterior.scala b/src/main/scala/thylacine/model/components/posterior/LeapfrogMcmcSampledPosterior.scala index 3c9044e..0bb55da 100644 --- a/src/main/scala/thylacine/model/components/posterior/LeapfrogMcmcSampledPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/LeapfrogMcmcSampledPosterior.scala @@ -31,22 +31,23 @@ import thylacine.model.sampling.leapfrog.LeapfrogMcmcEngine import cats.effect.kernel.Async import cats.syntax.all._ +import scala.annotation.unused import scala.collection.immutable.Queue case class LeapfrogMcmcSampledPosterior[F[_]: STM: Async]( - private[thylacine] val leapfrogMcmcConfig: LeapfrogMcmcConfig, - protected val distanceCalculation: (Vector[Double], Vector[Double]) => Double, - protected val sampleRequestSetCallback: Int => F[Unit], - protected val sampleRequestUpdateCallback: Int => F[Unit], - private[thylacine] val seed: Map[String, Vector[Double]], - private[thylacine] val priors: Set[Prior[F, _]], - private[thylacine] val likelihoods: Set[Likelihood[F, _, _]], - protected val sampleRequests: TxnVar[F, Queue[SampleRequest[F]]], - protected val burnInComplete: TxnVar[F, Boolean], - protected val interSampleDistanceCalculationResults: TxnVar[F, Vector[Vector[Double]]], - protected val sampleLogPdfs: TxnVar[F, Vector[Double]], - protected val samplePool: TxnVar[F, Vector[Vector[Double]]], - protected val currentChainTallies: TxnVar[F, Vector[Int]] + private[thylacine] val leapfrogMcmcConfig: LeapfrogMcmcConfig, + protected val distanceCalculation: (Vector[Double], Vector[Double]) => Double, + protected val sampleRequestSetCallback: Int => F[Unit], + protected val sampleRequestUpdateCallback: Int => F[Unit], + private[thylacine] val seed: Map[String, Vector[Double]], + private[thylacine] val priors: Set[Prior[F, _]], + private[thylacine] val likelihoods: Set[Likelihood[F, _, _]], + protected val sampleRequests: TxnVar[F, Queue[SampleRequest[F]]], + protected val burnInComplete: TxnVar[F, Boolean], + protected val interSampleDistanceCalculationResults: TxnVar[F, Vector[Vector[Double]]], + protected val sampleLogPdfs: TxnVar[F, Vector[Double]], + protected val samplePool: TxnVar[F, Vector[Vector[Double]]], + protected val currentChainTallies: TxnVar[F, Vector[Int]] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with LeapfrogMcmcEngine[F] { @@ -64,15 +65,17 @@ case class LeapfrogMcmcSampledPosterior[F[_]: STM: Async]( Async[F].delay(IndexedVectorCollection(seed)) } +@unused object LeapfrogMcmcSampledPosterior { + @unused def of[F[_]: STM: Async]( - leapfrogMcmcConfig: LeapfrogMcmcConfig, - distanceCalculation: (Vector[Double], Vector[Double]) => Double, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - sampleRequestSetCallback: Int => F[Unit], - sampleRequestUpdateCallback: Int => F[Unit], - seed: Map[String, Vector[Double]] + leapfrogMcmcConfig: LeapfrogMcmcConfig, + distanceCalculation: (Vector[Double], Vector[Double]) => Double, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + sampleRequestSetCallback: Int => F[Unit], + sampleRequestUpdateCallback: Int => F[Unit], + seed: Map[String, Vector[Double]] ): F[LeapfrogMcmcSampledPosterior[F]] = for { sampleRequests <- TxnVar.of(Queue[SampleRequest[F]]()) @@ -83,19 +86,19 @@ object LeapfrogMcmcSampledPosterior { currentChainTallies <- TxnVar.of(Vector[Int]()) posterior <- Async[F].delay { LeapfrogMcmcSampledPosterior( - leapfrogMcmcConfig = leapfrogMcmcConfig, - distanceCalculation = distanceCalculation, - sampleRequestSetCallback = sampleRequestSetCallback, - sampleRequestUpdateCallback = sampleRequestUpdateCallback, - seed = seed, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - sampleRequests = sampleRequests, - burnInComplete = burnInComplete, + leapfrogMcmcConfig = leapfrogMcmcConfig, + distanceCalculation = distanceCalculation, + sampleRequestSetCallback = sampleRequestSetCallback, + sampleRequestUpdateCallback = sampleRequestUpdateCallback, + seed = seed, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + sampleRequests = sampleRequests, + burnInComplete = burnInComplete, interSampleDistanceCalculationResults = interSampleDistanceCalculationResults, - sampleLogPdfs = sampleLogPdfs, - samplePool = samplePool, - currentChainTallies = currentChainTallies + sampleLogPdfs = sampleLogPdfs, + samplePool = samplePool, + currentChainTallies = currentChainTallies ) } _ <- posterior.launchInitialisation diff --git a/src/main/scala/thylacine/model/components/posterior/MdsOptimisedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/MdsOptimisedPosterior.scala index af6411b..ff2bd65 100644 --- a/src/main/scala/thylacine/model/components/posterior/MdsOptimisedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/MdsOptimisedPosterior.scala @@ -24,7 +24,7 @@ import thylacine.model.components.likelihood.Likelihood import thylacine.model.components.prior.Prior import thylacine.model.core.StmImplicits import thylacine.model.core.telemetry.OptimisationTelemetryUpdate -import thylacine.model.optimization.mds.{MdsEngine, ModelParameterSimplex} +import thylacine.model.optimization.mds.{ MdsEngine, ModelParameterSimplex } import cats.effect.kernel.Async import cats.syntax.all._ @@ -32,14 +32,14 @@ import cats.syntax.all._ import scala.annotation.unused case class MdsOptimisedPosterior[F[_]: STM: Async]( - private[thylacine] val mdsConfig: MdsConfig, - protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - protected override val isConvergedCallback: Unit => F[Unit], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - protected override val currentBest: TxnVar[F, (Int, Double)], - protected override val currentSimplex: TxnVar[F, ModelParameterSimplex], - protected override val isConverged: TxnVar[F, Boolean] + private[thylacine] val mdsConfig: MdsConfig, + protected override val iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + protected override val isConvergedCallback: Unit => F[Unit], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + protected override val currentBest: TxnVar[F, (Int, Double)], + protected override val currentSimplex: TxnVar[F, ModelParameterSimplex], + protected override val isConverged: TxnVar[F, Boolean] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with MdsEngine[F] { @@ -61,10 +61,10 @@ object MdsOptimisedPosterior { @unused def of[F[_]: STM: Async]( - mdsConfig: MdsConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], - isConvergedCallback: Unit => F[Unit] + mdsConfig: MdsConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + iterationUpdateCallback: OptimisationTelemetryUpdate => F[Unit], + isConvergedCallback: Unit => F[Unit] ): F[MdsOptimisedPosterior[F]] = for { currentBest <- TxnVar.of((0, Double.NegativeInfinity)) @@ -74,13 +74,13 @@ object MdsOptimisedPosterior { ) isConverged <- TxnVar.of(false) } yield MdsOptimisedPosterior( - mdsConfig = mdsConfig, + mdsConfig = mdsConfig, iterationUpdateCallback = iterationUpdateCallback, - isConvergedCallback = isConvergedCallback, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - currentBest = currentBest, - currentSimplex = currentSimplex, - isConverged = isConverged + isConvergedCallback = isConvergedCallback, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + currentBest = currentBest, + currentSimplex = currentSimplex, + isConverged = isConverged ) } diff --git a/src/main/scala/thylacine/model/components/posterior/Posterior.scala b/src/main/scala/thylacine/model/components/posterior/Posterior.scala index ce8420b..e9fbf32 100644 --- a/src/main/scala/thylacine/model/components/posterior/Posterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/Posterior.scala @@ -22,7 +22,7 @@ import thylacine.model.components.prior._ import thylacine.model.core.GenericIdentifier._ import thylacine.model.core._ import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.modelparameters.{ModelParameterPdf, ModelParameterContext} +import thylacine.model.core.values.modelparameters.{ ModelParameterPdf, ModelParameterContext } import cats.syntax.all._ @@ -38,7 +38,7 @@ private[thylacine] trait Posterior[F[_], P <: Prior[F, _], L <: Likelihood[F, _, priors.toVector.map(_.domainDimension).sum private[thylacine] override final lazy val orderedParameterIdentifiersWithDimension - : Vector[(ModelParameterIdentifier, Int)] = + : Vector[(ModelParameterIdentifier, Int)] = priors.toVector .sortBy(_.posteriorTermIdentifier) .map(i => @@ -48,7 +48,7 @@ private[thylacine] trait Posterior[F[_], P <: Prior[F, _], L <: Likelihood[F, _, ) private[thylacine] override final def logPdfGradientAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[ModelParameterCollection] = for { priorSum <- @@ -65,7 +65,7 @@ private[thylacine] trait Posterior[F[_], P <: Prior[F, _], L <: Likelihood[F, _, priors.toVector.traverse(_.sampleModelParameters).map(_.reduce(_ rawMergeWith _)) private[thylacine] override def logPdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] = for { priorEvaluations <- priors.toList.traverse(_.logPdfAt(input)) diff --git a/src/main/scala/thylacine/model/components/posterior/SlqIntegratedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/SlqIntegratedPosterior.scala index 73ea487..1ed455d 100644 --- a/src/main/scala/thylacine/model/components/posterior/SlqIntegratedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/SlqIntegratedPosterior.scala @@ -18,7 +18,7 @@ package ai.entrolution package thylacine.model.components.posterior import bengal.stm.STM -import bengal.stm.model.{TxnVar, TxnVarMap} +import bengal.stm.model.{ TxnVar, TxnVarMap } import thylacine.config.SlqConfig import thylacine.model.components.likelihood.Likelihood import thylacine.model.components.prior.Prior @@ -34,23 +34,23 @@ import cats.syntax.all._ import scala.annotation.unused case class SlqIntegratedPosterior[F[_]: STM: Async]( - private[thylacine] val slqConfig: SlqConfig, - protected override val slqTelemetryUpdateCallback: SlqTelemetryUpdate => F[Unit], - protected override val domainRebuildStartCallback: Unit => F[Unit], - protected override val domainRebuildFinishCallback: Unit => F[Unit], - protected override val seeds: Set[ModelParameterCollection], - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - protected override val sampleDomain: TxnVar[F, PointInCubeCollection], - protected override val samplePool: TxnVarMap[F, Double, ModelParameterCollection], - protected override val samplePoolMinimumLogPdf: TxnVar[F, Double], - protected override val logPdfResults: TxnVar[F, Vector[(Double, ModelParameterCollection)]], - protected override val sampleDomainScalingState: TxnVar[F, QuadratureDomainTelemetry], - protected override val workTokenPool: TxnVar[F, Int], - protected override val abscissas: TxnVar[F, QuadratureAbscissaCollection], - protected override val quadratureIntegrations: TxnVar[F, QuadratureIntegrator], - protected override val samplingSimulation: TxnVar[F, SamplingSimulation], - protected override val isConverged: TxnVar[F, Boolean] + private[thylacine] val slqConfig: SlqConfig, + protected override val slqTelemetryUpdateCallback: SlqTelemetryUpdate => F[Unit], + protected override val domainRebuildStartCallback: Unit => F[Unit], + protected override val domainRebuildFinishCallback: Unit => F[Unit], + protected override val seeds: Set[ModelParameterCollection], + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + protected override val sampleDomain: TxnVar[F, PointInCubeCollection], + protected override val samplePool: TxnVarMap[F, Double, ModelParameterCollection], + protected override val samplePoolMinimumLogPdf: TxnVar[F, Double], + protected override val logPdfResults: TxnVar[F, Vector[(Double, ModelParameterCollection)]], + protected override val sampleDomainScalingState: TxnVar[F, QuadratureDomainTelemetry], + protected override val workTokenPool: TxnVar[F, Int], + protected override val abscissas: TxnVar[F, QuadratureAbscissaCollection], + protected override val quadratureIntegrations: TxnVar[F, QuadratureIntegrator], + protected override val samplingSimulation: TxnVar[F, SamplingSimulation], + protected override val isConverged: TxnVar[F, Boolean] ) extends StmImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with SlqEngine[F] { @@ -80,15 +80,17 @@ case class SlqIntegratedPosterior[F[_]: STM: Async]( } yield () } +@unused object SlqIntegratedPosterior { + @unused def of[F[_]: STM: Async]( - slqConfig: SlqConfig, - posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], - slqTelemetryUpdateCallback: SlqTelemetryUpdate => F[Unit], - domainRebuildStartCallback: Unit => F[Unit], - domainRebuildFinishCallback: Unit => F[Unit], - seedsSpec: F[Set[Map[String, Vector[Double]]]] + slqConfig: SlqConfig, + posterior: Posterior[F, Prior[F, _], Likelihood[F, _, _]], + slqTelemetryUpdateCallback: SlqTelemetryUpdate => F[Unit], + domainRebuildStartCallback: Unit => F[Unit], + domainRebuildFinishCallback: Unit => F[Unit], + seedsSpec: F[Set[Map[String, Vector[Double]]]] ): F[SlqIntegratedPosterior[F]] = for { sampleDomain <- TxnVar.of(PointInCubeCollection.empty) @@ -103,23 +105,23 @@ object SlqIntegratedPosterior { isConverged <- TxnVar.of(false) seeds <- seedsSpec.map(seeds => seeds.map(IndexedVectorCollection(_))) } yield SlqIntegratedPosterior( - slqConfig = slqConfig, - slqTelemetryUpdateCallback = slqTelemetryUpdateCallback, - domainRebuildStartCallback = domainRebuildStartCallback, + slqConfig = slqConfig, + slqTelemetryUpdateCallback = slqTelemetryUpdateCallback, + domainRebuildStartCallback = domainRebuildStartCallback, domainRebuildFinishCallback = domainRebuildFinishCallback, - seeds = seeds, - priors = posterior.priors, - likelihoods = posterior.likelihoods, - sampleDomain = sampleDomain, - samplePool = samplePool, - samplePoolMinimumLogPdf = samplePoolMinimumLogPdf, - logPdfResults = logPdfResults, - sampleDomainScalingState = sampleDomainScalingState, - workTokenPool = workTokenPool, - abscissas = abscissas, - quadratureIntegrations = quadratureIntegrations, - samplingSimulation = samplingSimulation, - isConverged = isConverged + seeds = seeds, + priors = posterior.priors, + likelihoods = posterior.likelihoods, + sampleDomain = sampleDomain, + samplePool = samplePool, + samplePoolMinimumLogPdf = samplePoolMinimumLogPdf, + logPdfResults = logPdfResults, + sampleDomainScalingState = sampleDomainScalingState, + workTokenPool = workTokenPool, + abscissas = abscissas, + quadratureIntegrations = quadratureIntegrations, + samplingSimulation = samplingSimulation, + isConverged = isConverged ) } diff --git a/src/main/scala/thylacine/model/components/posterior/UnnormalisedPosterior.scala b/src/main/scala/thylacine/model/components/posterior/UnnormalisedPosterior.scala index 1627f95..4c9e0d4 100644 --- a/src/main/scala/thylacine/model/components/posterior/UnnormalisedPosterior.scala +++ b/src/main/scala/thylacine/model/components/posterior/UnnormalisedPosterior.scala @@ -20,15 +20,15 @@ package thylacine.model.components.posterior import thylacine.model.components.forwardmodel.ForwardModel import thylacine.model.components.likelihood._ import thylacine.model.components.prior._ -import thylacine.model.core.{AsyncImplicits, CanValidate} +import thylacine.model.core.{ AsyncImplicits, CanValidate } import thylacine.model.distributions.Distribution import cats.effect.kernel.Async case class UnnormalisedPosterior[F[_]: Async]( - private[thylacine] override val priors: Set[Prior[F, _]], - private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], - private[thylacine] override val validated: Boolean = false + private[thylacine] override val priors: Set[Prior[F, _]], + private[thylacine] override val likelihoods: Set[Likelihood[F, _, _]], + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Posterior[F, Prior[F, _], Likelihood[F, _, _]] with CanValidate[UnnormalisedPosterior[F]] { @@ -51,9 +51,9 @@ case class UnnormalisedPosterior[F[_]: Async]( this } else { UnnormalisedPosterior( - priors = priors.map(_.getValidated.asInstanceOf[Prior[F, Distribution]]), + priors = priors.map(_.getValidated.asInstanceOf[Prior[F, Distribution]]), likelihoods = likelihoods.map(_.getValidated.asInstanceOf[Likelihood[F, ForwardModel[F], Distribution]]), - validated = true + validated = true ) } } diff --git a/src/main/scala/thylacine/model/components/prior/CauchyPrior.scala b/src/main/scala/thylacine/model/components/prior/CauchyPrior.scala index 0ce454d..d91aca7 100644 --- a/src/main/scala/thylacine/model/components/prior/CauchyPrior.scala +++ b/src/main/scala/thylacine/model/components/prior/CauchyPrior.scala @@ -24,10 +24,12 @@ import thylacine.model.distributions.CauchyDistribution import cats.effect.kernel.Async +import scala.annotation.unused + case class CauchyPrior[F[_]: Async]( - private[thylacine] override val identifier: ModelParameterIdentifier, - private[thylacine] val priorData: RecordedData, - private[thylacine] override val validated: Boolean = false + private[thylacine] override val identifier: ModelParameterIdentifier, + private[thylacine] val priorData: RecordedData, + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Prior[F, CauchyDistribution] { @@ -42,18 +44,19 @@ case class CauchyPrior[F[_]: Async]( Async[F].delay(priorDistribution.getRawSample) } +@unused object CauchyPrior { def apply[F[_]: Async]( - label: String, - values: Vector[Double], - confidenceIntervals: Vector[Double] + label: String, + values: Vector[Double], + confidenceIntervals: Vector[Double] ): CauchyPrior[F] = { assert(values.size == confidenceIntervals.size) CauchyPrior( identifier = ModelParameterIdentifier(label), priorData = RecordedData( - values = VectorContainer(values), + values = VectorContainer(values), symmetricConfidenceIntervals = VectorContainer(confidenceIntervals) ) ) diff --git a/src/main/scala/thylacine/model/components/prior/GaussianPrior.scala b/src/main/scala/thylacine/model/components/prior/GaussianPrior.scala index c6af3c6..3239206 100644 --- a/src/main/scala/thylacine/model/components/prior/GaussianPrior.scala +++ b/src/main/scala/thylacine/model/components/prior/GaussianPrior.scala @@ -19,7 +19,7 @@ package thylacine.model.components.prior import thylacine.model.core.GenericIdentifier._ import thylacine.model.core._ -import thylacine.model.core.values.{MatrixContainer, VectorContainer} +import thylacine.model.core.values.{ MatrixContainer, VectorContainer } import thylacine.model.distributions.GaussianDistribution import breeze.stats.distributions.MultivariateGaussian @@ -28,9 +28,9 @@ import cats.effect.kernel.Async import scala.annotation.unused case class GaussianPrior[F[_]: Async]( - private[thylacine] override val identifier: ModelParameterIdentifier, - private[thylacine] val priorData: RecordedData, - private[thylacine] override val validated: Boolean = false + private[thylacine] override val identifier: ModelParameterIdentifier, + private[thylacine] val priorData: RecordedData, + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Prior[F, GaussianDistribution] { @@ -54,21 +54,21 @@ case class GaussianPrior[F[_]: Async]( private[thylacine] lazy val covariance: Vector[Double] = rawDistribution.covariance.toArray.toVector - lazy val entropy = rawDistribution.entropy + lazy val entropy: Double = rawDistribution.entropy } object GaussianPrior { def fromConfidenceIntervals[F[_]: Async]( - label: String, - values: Vector[Double], - confidenceIntervals: Vector[Double] + label: String, + values: Vector[Double], + confidenceIntervals: Vector[Double] ): GaussianPrior[F] = { assert(values.size == confidenceIntervals.size) GaussianPrior( identifier = ModelParameterIdentifier(label), priorData = RecordedData( - values = VectorContainer(values), + values = VectorContainer(values), symmetricConfidenceIntervals = VectorContainer(confidenceIntervals) ) ) @@ -76,9 +76,9 @@ object GaussianPrior { @unused def fromCovarianceMatrix[F[_]: Async]( - label: String, - values: Vector[Double], - covarianceMatrix: Vector[Vector[Double]] + label: String, + values: Vector[Double], + covarianceMatrix: Vector[Vector[Double]] ): GaussianPrior[F] = { val covarianceContainer = MatrixContainer(covarianceMatrix) val valueContainer = VectorContainer(values) @@ -86,7 +86,7 @@ object GaussianPrior { GaussianPrior( identifier = ModelParameterIdentifier(label), priorData = RecordedData( - data = valueContainer, + data = valueContainer, covariance = covarianceContainer ) ) diff --git a/src/main/scala/thylacine/model/components/prior/Prior.scala b/src/main/scala/thylacine/model/components/prior/Prior.scala index fbdcf09..83212b3 100644 --- a/src/main/scala/thylacine/model/components/prior/Prior.scala +++ b/src/main/scala/thylacine/model/components/prior/Prior.scala @@ -20,9 +20,9 @@ package thylacine.model.components.prior import thylacine.model.components.posterior.PosteriorTerm import thylacine.model.core.GenericIdentifier._ import thylacine.model.core._ -import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} +import thylacine.model.core.values.{ IndexedVectorCollection, VectorContainer } import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.modelparameters.{ModelParameterGenerator, ModelParameterPdf} +import thylacine.model.core.values.modelparameters.{ ModelParameterGenerator, ModelParameterPdf } import thylacine.model.distributions.Distribution import thylacine.model.sampling.ModelParameterSampler @@ -49,17 +49,18 @@ private[thylacine] trait Prior[F[_], +D <: Distribution] override final val generatorDimension: Int = priorDistribution.domainDimension override final def logPdfAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): F[Double] = Async[F].delay(priorDistribution.logPdfAt(input.retrieveIndex(identifier))) override final def logPdfGradientAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): F[ModelParameterCollection] = Async[F].delay { - IndexedVectorCollection(identifier, - priorDistribution - .logPdfGradientAt(input.retrieveIndex(identifier)) + IndexedVectorCollection( + identifier, + priorDistribution + .logPdfGradientAt(input.retrieveIndex(identifier)) ) } diff --git a/src/main/scala/thylacine/model/components/prior/UniformPrior.scala b/src/main/scala/thylacine/model/components/prior/UniformPrior.scala index 2afdbf8..4a06c04 100644 --- a/src/main/scala/thylacine/model/components/prior/UniformPrior.scala +++ b/src/main/scala/thylacine/model/components/prior/UniformPrior.scala @@ -20,17 +20,17 @@ package thylacine.model.components.prior import thylacine.model.core.AsyncImplicits import thylacine.model.core.GenericIdentifier._ import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} +import thylacine.model.core.values.{ IndexedVectorCollection, VectorContainer } import thylacine.model.distributions import thylacine.model.distributions.UniformDistribution import cats.effect.kernel.Async case class UniformPrior[F[_]: Async]( - private[thylacine] override val identifier: ModelParameterIdentifier, - private[thylacine] val maxBounds: Vector[Double], - private[thylacine] val minBounds: Vector[Double], - private[thylacine] override val validated: Boolean = false + private[thylacine] override val identifier: ModelParameterIdentifier, + private[thylacine] val maxBounds: Vector[Double], + private[thylacine] val minBounds: Vector[Double], + private[thylacine] override val validated: Boolean = false ) extends AsyncImplicits[F] with Prior[F, UniformDistribution] { @@ -44,7 +44,7 @@ case class UniformPrior[F[_]: Async]( else this.copy(validated = true) private[thylacine] final override def pdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] = Async[F].delay { if (priorDistribution.insideBounds(input.retrieveIndex(identifier))) { @@ -55,7 +55,7 @@ case class UniformPrior[F[_]: Async]( } private[thylacine] final override def pdfGradientAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[ModelParameterCollection] = Async[F].delay(IndexedVectorCollection(identifier, priorDistribution.zeroVector)) @@ -66,13 +66,13 @@ case class UniformPrior[F[_]: Async]( object UniformPrior { def fromBounds[F[_]: Async]( - label: String, - maxBounds: Vector[Double], - minBounds: Vector[Double] + label: String, + maxBounds: Vector[Double], + minBounds: Vector[Double] ): UniformPrior[F] = UniformPrior( identifier = ModelParameterIdentifier(label), - maxBounds = maxBounds, - minBounds = minBounds + maxBounds = maxBounds, + minBounds = minBounds ) } diff --git a/src/main/scala/thylacine/model/core/CanValidate.scala b/src/main/scala/thylacine/model/core/CanValidate.scala index feadfd6..c164157 100644 --- a/src/main/scala/thylacine/model/core/CanValidate.scala +++ b/src/main/scala/thylacine/model/core/CanValidate.scala @@ -17,18 +17,12 @@ package ai.entrolution package thylacine.model.core -/** We use the type system to ensure the right structure - * of the Bayesian Graph components. but do not use it in - * validating the mathematics of the underpinning - * computations. Doing so would make the typing model - * significantly more complicated to get some relative - * simple sanity checks in place. +/** We use the type system to ensure the right structure of the Bayesian Graph components. but do not use it in + * validating the mathematics of the underpinning computations. Doing so would make the typing model significantly more + * complicated to get some relative simple sanity checks in place. * - * Instead we create a flexible validation framework that - * will be enacted at the creation of any Bayesian graphical - * network. Obviously, this won't get us compile time - * guarantees, but violations will fail the model quickly - * at runtime + * Instead we create a flexible validation framework that will be enacted at the creation of any Bayesian graphical + * network. Obviously, this won't get us compile time guarantees, but violations will fail the model quickly at runtime */ private[thylacine] trait CanValidate[+T <: CanValidate[_]] { private[thylacine] def validated: Boolean diff --git a/src/main/scala/thylacine/model/core/GenericIdentifier.scala b/src/main/scala/thylacine/model/core/GenericIdentifier.scala index 75c6856..84a52f3 100644 --- a/src/main/scala/thylacine/model/core/GenericIdentifier.scala +++ b/src/main/scala/thylacine/model/core/GenericIdentifier.scala @@ -24,7 +24,7 @@ private[thylacine] sealed trait GenericIdentifier { private[thylacine] object GenericIdentifier { private[thylacine] case class ModelParameterIdentifier(value: String) extends GenericIdentifier - private[thylacine] case class TermIdentifier(value: String) extends GenericIdentifier + private[thylacine] case class TermIdentifier(value: String) extends GenericIdentifier private[thylacine] implicit def orderByValue[T <: GenericIdentifier]: Ordering[T] = Ordering.fromLessThan((i, j) => (i.value compareTo j.value) < 0) diff --git a/src/main/scala/thylacine/model/core/RecordedData.scala b/src/main/scala/thylacine/model/core/RecordedData.scala index 49ef3ee..2104c9f 100644 --- a/src/main/scala/thylacine/model/core/RecordedData.scala +++ b/src/main/scala/thylacine/model/core/RecordedData.scala @@ -17,12 +17,12 @@ package ai.entrolution package thylacine.model.core -import thylacine.model.core.values.{MatrixContainer, VectorContainer} +import thylacine.model.core.values.{ MatrixContainer, VectorContainer } private[thylacine] case class RecordedData( - data: VectorContainer, - covariance: MatrixContainer, - validated: Boolean = false + data: VectorContainer, + covariance: MatrixContainer, + validated: Boolean = false ) extends CanValidate[RecordedData] { if (!validated) { assert(covariance.rowTotalNumber == covariance.columnTotalNumber) @@ -46,8 +46,8 @@ private[thylacine] case class RecordedData( private[thylacine] object RecordedData { private[thylacine] def apply( - values: VectorContainer, - symmetricConfidenceIntervals: VectorContainer + values: VectorContainer, + symmetricConfidenceIntervals: VectorContainer ): RecordedData = { val validatedValues: VectorContainer = values.getValidated val validatedConfidenceIntervals: VectorContainer = @@ -59,10 +59,10 @@ private[thylacine] object RecordedData { RecordedData( data = validatedValues, covariance = MatrixContainer( - values = validatedConfidenceIntervals.values.map(i => (i._1, i._1) -> Math.pow(i._2 / 2, 2)), - rowTotalNumber = values.dimension, + values = validatedConfidenceIntervals.values.map(i => (i._1, i._1) -> Math.pow(i._2 / 2, 2)), + rowTotalNumber = values.dimension, columnTotalNumber = values.dimension, - validated = true + validated = true ), validated = true ) diff --git a/src/main/scala/thylacine/model/core/computation/CachedComputation.scala b/src/main/scala/thylacine/model/core/computation/CachedComputation.scala index ce79be3..0c11ae9 100644 --- a/src/main/scala/thylacine/model/core/computation/CachedComputation.scala +++ b/src/main/scala/thylacine/model/core/computation/CachedComputation.scala @@ -30,8 +30,8 @@ import cats.syntax.all._ import scala.annotation.unused case class CachedComputation[F[_]: STM: Async, T]( - computation: ModelParameterCollection => T, - cacheDepth: Option[Int] = None + computation: ModelParameterCollection => T, + cacheDepth: Option[Int] = None )(scalarClock: TxnVar[F, Int], computationCache: TxnVarMap[F, Int, ComputationResult[T]]) extends ComputationWrapper[F, T] { @@ -45,7 +45,7 @@ case class CachedComputation[F[_]: STM: Async, T]( } yield innerResult).commit private def retrieveKeyFromComputationStore( - key: Int + key: Int ): F[Option[T]] = computationCache .get(key) @@ -53,8 +53,8 @@ case class CachedComputation[F[_]: STM: Async, T]( .commit private def updateAccessTimeForKeyInComputationStore( - key: Int, - time: Int + key: Int, + time: Int ): F[Unit] = { def updateAccessTime(input: ComputationResult[T]): ComputationResult[T] = if (time > input.lastAccessed) { @@ -72,8 +72,8 @@ case class CachedComputation[F[_]: STM: Async, T]( } private def upsertResultIntoComputationStore( - key: Int, - newResult: ComputationResult[T] + key: Int, + newResult: ComputationResult[T] ): F[Unit] = { def updateResult(input: ComputationResult[T]): ComputationResult[T] = if (newResult.lastAccessed > input.lastAccessed) { @@ -91,19 +91,23 @@ case class CachedComputation[F[_]: STM: Async, T]( } private val cleanComputationStore: F[Unit] = - computationCache.modify { ec => - if (ec.size > cacheDepth.getOrElse(0)) { - ec.toSeq - .sortBy(_._2.lastAccessed) - .takeRight(cacheDepth.getOrElse(0)) - .toMap - } else { - ec + computationCache + .modify { ec => + if (ec.size > cacheDepth.getOrElse(0)) { + ec.toSeq + .sortBy(_._2.lastAccessed) + .takeRight(cacheDepth.getOrElse(0)) + .toMap + } else { + ec + } } - }.commit.start.void + .commit + .start + .void private def retrieveComputationFromStoreFor( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Option[T]] = for { time <- tickAndGet @@ -129,7 +133,7 @@ case class CachedComputation[F[_]: STM: Async, T]( } yield () private[thylacine] override def performComputation( - input: ModelParameterCollection + input: ModelParameterCollection ): F[T] = if (cacheDepth.exists(_ > 0)) { for { @@ -152,17 +156,17 @@ object CachedComputation { @unused def of[F[_]: STM: Async, T]( - computation: ModelParameterCollection => T, - cacheDepth: Option[Int] = None + computation: ModelParameterCollection => T, + cacheDepth: Option[Int] = None ): F[CachedComputation[F, T]] = for { scalarClock <- TxnVar.of(0) computationCache <- TxnVarMap.of(Map[Int, ComputationResult[T]]()) } yield CachedComputation( computation = computation, - cacheDepth = cacheDepth + cacheDepth = cacheDepth )( - scalarClock = scalarClock, + scalarClock = scalarClock, computationCache = computationCache ) diff --git a/src/main/scala/thylacine/model/core/computation/FiniteDifferenceJacobian.scala b/src/main/scala/thylacine/model/core/computation/FiniteDifferenceJacobian.scala index 6a91cfe..eaea358 100644 --- a/src/main/scala/thylacine/model/core/computation/FiniteDifferenceJacobian.scala +++ b/src/main/scala/thylacine/model/core/computation/FiniteDifferenceJacobian.scala @@ -22,13 +22,13 @@ import thylacine.model.core.values._ import scala.annotation.unused private[thylacine] case class FiniteDifferenceJacobian( - private val evalAt: IndexedVectorCollection => VectorContainer, - differential: Double + private val evalAt: IndexedVectorCollection => VectorContainer, + differential: Double ) { @unused private[thylacine] def finiteDifferenceJacobianAt( - input: IndexedVectorCollection + input: IndexedVectorCollection ): IndexedMatrixCollection = { val currentEvaluation = evalAt(input) val newMatrixCollectionMapping = diff --git a/src/main/scala/thylacine/model/core/telemetry/HmcmcTelemetryUpdate.scala b/src/main/scala/thylacine/model/core/telemetry/HmcmcTelemetryUpdate.scala index 5b432fd..75c00fa 100644 --- a/src/main/scala/thylacine/model/core/telemetry/HmcmcTelemetryUpdate.scala +++ b/src/main/scala/thylacine/model/core/telemetry/HmcmcTelemetryUpdate.scala @@ -18,9 +18,9 @@ package ai.entrolution package thylacine.model.core.telemetry case class HmcmcTelemetryUpdate( - samplesRemaining: Int, - jumpAttempts: Int, - jumpAcceptances: Int + samplesRemaining: Int, + jumpAttempts: Int, + jumpAcceptances: Int ) extends TelemetryReport { override lazy val logMessage: String = diff --git a/src/main/scala/thylacine/model/core/telemetry/OptimisationTelemetryUpdate.scala b/src/main/scala/thylacine/model/core/telemetry/OptimisationTelemetryUpdate.scala index 1528bda..f337d8b 100644 --- a/src/main/scala/thylacine/model/core/telemetry/OptimisationTelemetryUpdate.scala +++ b/src/main/scala/thylacine/model/core/telemetry/OptimisationTelemetryUpdate.scala @@ -17,7 +17,8 @@ package ai.entrolution package thylacine.model.core.telemetry -case class OptimisationTelemetryUpdate(maxLogPdf: Double, currentScale: Double, prefix: String) extends TelemetryReport { +case class OptimisationTelemetryUpdate(maxLogPdf: Double, currentScale: Double, prefix: String) + extends TelemetryReport { override lazy val logMessage = s"$prefix optimisation :: Max ln(pdf) - $maxLogPdf // Current scale - $currentScale" diff --git a/src/main/scala/thylacine/model/core/telemetry/SlqTelemetryUpdate.scala b/src/main/scala/thylacine/model/core/telemetry/SlqTelemetryUpdate.scala index 19562a7..4d6a1ce 100644 --- a/src/main/scala/thylacine/model/core/telemetry/SlqTelemetryUpdate.scala +++ b/src/main/scala/thylacine/model/core/telemetry/SlqTelemetryUpdate.scala @@ -18,14 +18,14 @@ package ai.entrolution package thylacine.model.core.telemetry case class SlqTelemetryUpdate( - negEntropyAvg: Double, - logPdf: Double, - samplePoolMinimumLogPdf: Double, - domainVolumeScaling: Double, - acceptancesSinceDomainRebuild: Int, - samplePoolSize: Int, - domainCubeCount: Int, - iterationCount: Int + negEntropyAvg: Double, + logPdf: Double, + samplePoolMinimumLogPdf: Double, + domainVolumeScaling: Double, + acceptancesSinceDomainRebuild: Int, + samplePoolSize: Int, + domainCubeCount: Int, + iterationCount: Int ) extends TelemetryReport { override lazy val logMessage: String = diff --git a/src/main/scala/thylacine/model/core/values/IndexedCollection.scala b/src/main/scala/thylacine/model/core/values/IndexedCollection.scala index bac57be..515d181 100644 --- a/src/main/scala/thylacine/model/core/values/IndexedCollection.scala +++ b/src/main/scala/thylacine/model/core/values/IndexedCollection.scala @@ -23,14 +23,10 @@ private[thylacine] trait IndexedCollection[T <: Container] { private[thylacine] def index: Map[ModelParameterIdentifier, T] private[thylacine] def retrieveIndex(identifier: ModelParameterIdentifier): T = - index.getOrElse(identifier, - throw new RuntimeException( - s"Identifier $identifier not found in indexed collection: $index" - ) + index.getOrElse( + identifier, + throw new RuntimeException( + s"Identifier $identifier not found in indexed collection: $index" + ) ) - - private[thylacine] def getSortedValues: List[T] = - index.toList - .sortBy(_._1) - .map(_._2) } diff --git a/src/main/scala/thylacine/model/core/values/IndexedMatrixCollection.scala b/src/main/scala/thylacine/model/core/values/IndexedMatrixCollection.scala index 364d7f9..3ba75f8 100644 --- a/src/main/scala/thylacine/model/core/values/IndexedMatrixCollection.scala +++ b/src/main/scala/thylacine/model/core/values/IndexedMatrixCollection.scala @@ -21,8 +21,8 @@ import thylacine.model.core.CanValidate import thylacine.model.core.GenericIdentifier.ModelParameterIdentifier private[thylacine] case class IndexedMatrixCollection( - index: Map[ModelParameterIdentifier, MatrixContainer], - validated: Boolean = false + index: Map[ModelParameterIdentifier, MatrixContainer], + validated: Boolean = false ) extends IndexedCollection[MatrixContainer] with CanValidate[IndexedMatrixCollection] { @@ -40,7 +40,7 @@ private[thylacine] case class IndexedMatrixCollection( index.map(i => i._1.value -> i._2.genericScalaRepresentation) private[thylacine] def rawMergeWith( - other: IndexedMatrixCollection + other: IndexedMatrixCollection ): IndexedMatrixCollection = IndexedMatrixCollection(index ++ other.index).getValidated } @@ -48,23 +48,23 @@ private[thylacine] case class IndexedMatrixCollection( private[thylacine] object IndexedMatrixCollection { private[thylacine] def apply( - identifier: ModelParameterIdentifier, - values: Vector[Vector[Double]] + identifier: ModelParameterIdentifier, + values: Vector[Vector[Double]] ): IndexedMatrixCollection = IndexedMatrixCollection( index = Map(identifier -> MatrixContainer(values)) ) private[thylacine] def apply( - labelledValues: Map[String, Vector[Vector[Double]]] + labelledValues: Map[String, Vector[Vector[Double]]] ): IndexedMatrixCollection = IndexedMatrixCollection( index = labelledValues.map(lvs => ModelParameterIdentifier(lvs._1) -> MatrixContainer(lvs._2)) ) private[thylacine] def squareIdentity( - identifier: ModelParameterIdentifier, - dimension: Int + identifier: ModelParameterIdentifier, + dimension: Int ): IndexedMatrixCollection = IndexedMatrixCollection( index = Map(identifier -> MatrixContainer.squareIdentity(dimension)) diff --git a/src/main/scala/thylacine/model/core/values/IndexedVectorCollection.scala b/src/main/scala/thylacine/model/core/values/IndexedVectorCollection.scala index d177643..adf5ce9 100644 --- a/src/main/scala/thylacine/model/core/values/IndexedVectorCollection.scala +++ b/src/main/scala/thylacine/model/core/values/IndexedVectorCollection.scala @@ -18,11 +18,11 @@ package ai.entrolution package thylacine.model.core.values import thylacine.model.core.GenericIdentifier._ -import thylacine.model.core.{CanValidate, GenericIdentifier} +import thylacine.model.core.{ CanValidate, GenericIdentifier } private[thylacine] case class IndexedVectorCollection( - index: Map[ModelParameterIdentifier, VectorContainer], - validated: Boolean = false + index: Map[ModelParameterIdentifier, VectorContainer], + validated: Boolean = false ) extends IndexedCollection[VectorContainer] with CanValidate[IndexedVectorCollection] { @@ -51,7 +51,7 @@ private[thylacine] case class IndexedVectorCollection( // Low-level API // ------------- private[thylacine] def rawMergeWith( - other: IndexedVectorCollection + other: IndexedVectorCollection ): IndexedVectorCollection = { val keyIntersection = index.keySet.intersect(other.index.keySet) if (keyIntersection.nonEmpty) { @@ -64,7 +64,7 @@ private[thylacine] case class IndexedVectorCollection( } private[thylacine] def rawSumWith( - other: IndexedVectorCollection + other: IndexedVectorCollection ): IndexedVectorCollection = { val keySet = getValidated.index.keySet ++ other.getValidated.index.keySet @@ -86,29 +86,28 @@ private[thylacine] case class IndexedVectorCollection( } private[thylacine] def rawScalarMultiplyWith( - input: Double + input: Double ): IndexedVectorCollection = this.copy( index = index.view.mapValues(_.rawScalarProductWith(input)).toMap ) private[thylacine] def rawSubtract( - other: IndexedVectorCollection + other: IndexedVectorCollection ): IndexedVectorCollection = rawSumWith(other.rawScalarMultiplyWith(-1.0)) private[thylacine] def rawNudgeComponents( - diff: Double + diff: Double ): Map[ModelParameterIdentifier, List[IndexedVectorCollection]] = { val differentials: Map[GenericIdentifier, List[VectorContainer]] = index.map { i => i._1 -> i._2.rawNudgeComponents(diff) } - index.collect { k => - differentials.get(k._1) match { - case Some(vs) => - k._1 -> vs.map(v => IndexedVectorCollection(index + (k._1 -> v))) + index.flatMap { k => + differentials.get(k._1).map { vs => + k._1 -> vs.map(v => IndexedVectorCollection(index + (k._1 -> v))) } } } @@ -125,35 +124,30 @@ private[thylacine] object IndexedVectorCollection { private[thylacine] val empty: IndexedVectorCollection = IndexedVectorCollection( - index = Map(), + index = Map(), validated = true ) private[thylacine] def apply( - identifier: ModelParameterIdentifier, - vector: VectorContainer + identifier: ModelParameterIdentifier, + vector: VectorContainer ): IndexedVectorCollection = IndexedVectorCollection( - index = Map(identifier -> vector.getValidated), + index = Map(identifier -> vector.getValidated), validated = true ) private[thylacine] def apply( - identifierLabel: String, - values: Vector[Double] + identifierLabel: String, + values: Vector[Double] ): IndexedVectorCollection = apply(ModelParameterIdentifier(identifierLabel), VectorContainer(values)) private[thylacine] def apply( - labeledLists: Map[String, Vector[Double]] + labeledLists: Map[String, Vector[Double]] ): IndexedVectorCollection = if (labeledLists.nonEmpty) labeledLists.map(i => apply(i._1, i._2)).reduce(_ rawMergeWith _) else empty - - private[thylacine] def merge( - modelParameters: Seq[IndexedVectorCollection] - ): IndexedVectorCollection = - modelParameters.reduce(_ rawMergeWith _) } diff --git a/src/main/scala/thylacine/model/core/values/MatrixContainer.scala b/src/main/scala/thylacine/model/core/values/MatrixContainer.scala index e1702ba..529cb4b 100644 --- a/src/main/scala/thylacine/model/core/values/MatrixContainer.scala +++ b/src/main/scala/thylacine/model/core/values/MatrixContainer.scala @@ -22,13 +22,13 @@ import thylacine.model.core.CanValidate import breeze.linalg._ import cats.implicits._ -import scala.{Vector => ScalaVector} +import scala.{ Vector => ScalaVector } private[thylacine] case class MatrixContainer( - values: Map[(Int, Int), Double], - rowTotalNumber: Int, - columnTotalNumber: Int, - validated: Boolean = false + values: Map[(Int, Int), Double], + rowTotalNumber: Int, + columnTotalNumber: Int, + validated: Boolean = false ) extends Container with CanValidate[MatrixContainer] { if (!validated) { @@ -67,11 +67,11 @@ private[thylacine] case class MatrixContainer( // the number of columns, under the assumption that row numbers // are equal and have been checked outside of this private[thylacine] def columnMergeWith( - input: MatrixContainer + input: MatrixContainer ): MatrixContainer = MatrixContainer( values ++ input.getValidated.values.map(i => (i._1._1, i._1._2 + columnTotalNumber) -> i._2), - rowTotalNumber = rowTotalNumber, + rowTotalNumber = rowTotalNumber, columnTotalNumber = columnTotalNumber + input.columnTotalNumber ).getValidated @@ -79,18 +79,18 @@ private[thylacine] case class MatrixContainer( private[thylacine] def rowMergeWith(input: MatrixContainer): MatrixContainer = MatrixContainer( values ++ input.getValidated.values.map(i => (i._1._1 + rowTotalNumber, i._1._2) -> i._2), - rowTotalNumber = rowTotalNumber + input.rowTotalNumber, + rowTotalNumber = rowTotalNumber + input.rowTotalNumber, columnTotalNumber = columnTotalNumber ).getValidated // Diagonally combines two matrices with zero'd upper-right // and lower-left submatrices private[thylacine] def diagonalMergeWith( - input: MatrixContainer + input: MatrixContainer ): MatrixContainer = MatrixContainer( values ++ input.getValidated.values.map(i => (i._1._1 + rowTotalNumber, i._1._2 + columnTotalNumber) -> i._2), - rowTotalNumber = rowTotalNumber + input.columnTotalNumber, + rowTotalNumber = rowTotalNumber + input.columnTotalNumber, columnTotalNumber = columnTotalNumber + input.columnTotalNumber ).getValidated } @@ -98,41 +98,42 @@ private[thylacine] case class MatrixContainer( private[thylacine] object MatrixContainer { private[thylacine] def zeros( - rowDimension: Int, - columnDimension: Int + rowDimension: Int, + columnDimension: Int ): MatrixContainer = MatrixContainer( - values = Map(), - rowTotalNumber = rowDimension, + values = Map(), + rowTotalNumber = rowDimension, columnTotalNumber = columnDimension, - validated = true + validated = true ) private[thylacine] def squareIdentity( - dimension: Int + dimension: Int ): MatrixContainer = MatrixContainer( - values = (1 to dimension).map(i => (i, i) -> 1d).toMap, - rowTotalNumber = dimension, + values = (1 to dimension).map(i => (i, i) -> 1d).toMap, + rowTotalNumber = dimension, columnTotalNumber = dimension, - validated = true + validated = true ) private[thylacine] def apply( - input: ScalaVector[ScalaVector[Double]] + input: ScalaVector[ScalaVector[Double]] ): MatrixContainer = { val valueMap = input .foldLeft((1, Map[(Int, Int), Double]())) { (i, j) => - (i._1 + 1, - j.foldLeft((1, i._2)) { (k, l) => - (k._1 + 1, k._2 + ((i._1, k._1) -> l)) - }._2 + ( + i._1 + 1, + j.foldLeft((1, i._2)) { (k, l) => + (k._1 + 1, k._2 + ((i._1, k._1) -> l)) + }._2 ) } ._2 MatrixContainer( - values = valueMap, - rowTotalNumber = valueMap.keySet.map(_._1).max, + values = valueMap, + rowTotalNumber = valueMap.keySet.map(_._1).max, columnTotalNumber = valueMap.keySet.map(_._2).max ) } diff --git a/src/main/scala/thylacine/model/core/values/VectorContainer.scala b/src/main/scala/thylacine/model/core/values/VectorContainer.scala index 5e35685..c5a789c 100644 --- a/src/main/scala/thylacine/model/core/values/VectorContainer.scala +++ b/src/main/scala/thylacine/model/core/values/VectorContainer.scala @@ -22,13 +22,13 @@ import thylacine.util.MathOps import breeze.linalg._ -import scala.{Vector => ScalaVector} +import scala.{ Vector => ScalaVector } private[thylacine] case class VectorContainer( - values: Map[Int, Double], - dimension: Int, - validated: Boolean = false, - parameterLabel: Option[String] = None + values: Map[Int, Double], + dimension: Int, + validated: Boolean = false, + parameterLabel: Option[String] = None ) extends Container with CanValidate[VectorContainer] { if (!validated) { @@ -55,11 +55,6 @@ private[thylacine] case class VectorContainer( private[thylacine] lazy val magnitude: Double = Math.sqrt(squaredMagnitude) - private[thylacine] lazy val normalised: VectorContainer = - if (magnitude != 1.0d) { - this.copy(values = values.map(v => v._1 -> v._2 / magnitude)) - } else this - private[thylacine] lazy val scalaVector: ScalaVector[Double] = rawVector.toScalaVector @@ -68,7 +63,7 @@ private[thylacine] case class VectorContainer( // Low-level API // Disjoint concatenation of vectors private[thylacine] def rawConcatenateWith( - input: VectorContainer + input: VectorContainer ): VectorContainer = VectorContainer( values ++ input.getValidated.values.map(i => i._1 + dimension -> i._2), @@ -88,7 +83,7 @@ private[thylacine] case class VectorContainer( private[thylacine] def rawScalarProductWith(input: Double): VectorContainer = VectorContainer( - values = values.view.mapValues(_ * input).toMap, + values = values.view.mapValues(_ * input).toMap, dimension = dimension ).getValidated @@ -96,7 +91,7 @@ private[thylacine] case class VectorContainer( rawSumWith(input.rawScalarProductWith(-1.0)) private[thylacine] def rawProductWith( - input: VectorContainer + input: VectorContainer ): VectorContainer = VectorContainer( values = (values.keySet ++ input.values.keySet).map { k => @@ -117,7 +112,7 @@ private[thylacine] case class VectorContainer( this.copy(values = values + (index -> (values.getOrElse(index, 0d) + diff))).getValidated private[thylacine] def rawNudgeComponents( - diff: Double + diff: Double ): List[VectorContainer] = (1 to dimension).map(rawNudgeComponent(diff, _)).toList diff --git a/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterContext.scala b/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterContext.scala index 79faa8d..d0fdf52 100644 --- a/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterContext.scala +++ b/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterContext.scala @@ -23,6 +23,8 @@ import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} import breeze.linalg.DenseVector +import scala.annotation.unused + private[thylacine] trait ModelParameterContext { private[thylacine] def orderedParameterIdentifiersWithDimension: Vector[(ModelParameterIdentifier, Int)] @@ -33,16 +35,17 @@ private[thylacine] trait ModelParameterContext { .toMap ) + @unused final def zeroParameterMapping: Map[String, Vector[Double]] = zeroModelParameterCollection.genericScalaRepresentation private[thylacine] final def rawVectorToModelParameterCollection( - input: DenseVector[Double] + input: DenseVector[Double] ): ModelParameterCollection = vectorValuesToModelParameterCollection(input.toArray.toVector) private[thylacine] final def vectorValuesToModelParameterCollection( - input: Vector[Double] + input: Vector[Double] ): ModelParameterCollection = orderedParameterIdentifiersWithDimension .foldLeft( @@ -50,16 +53,17 @@ private[thylacine] trait ModelParameterContext { ) { (i, j) => val (vector, remainder) = i._1.splitAt(j._2) - (remainder, - i._2.rawMergeWith( - IndexedVectorCollection(j._1, VectorContainer(vector)) - ) + ( + remainder, + i._2.rawMergeWith( + IndexedVectorCollection(j._1, VectorContainer(vector)) + ) ) } ._2 private[thylacine] final def modelParameterCollectionToVectorValues( - input: ModelParameterCollection + input: ModelParameterCollection ): Vector[Double] = orderedParameterIdentifiersWithDimension .foldLeft(Vector[Vector[Double]]()) { case (current, (identifier, _)) => @@ -69,7 +73,7 @@ private[thylacine] trait ModelParameterContext { .reduce(_ ++ _) private[thylacine] final def modelParameterCollectionToRawVector( - input: ModelParameterCollection + input: ModelParameterCollection ): DenseVector[Double] = DenseVector { modelParameterCollectionToVectorValues(input).toArray diff --git a/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterPdf.scala b/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterPdf.scala index f26393f..8444f15 100644 --- a/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterPdf.scala +++ b/src/main/scala/thylacine/model/core/values/modelparameters/ModelParameterPdf.scala @@ -25,27 +25,30 @@ import cats.effect.Async import cats.effect.implicits._ import cats.syntax.all._ +import scala.annotation.unused + private[thylacine] trait ModelParameterPdf[F[_]] extends GenericScalarValuedMapping { this: AsyncImplicits[F] => private[thylacine] def logPdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] // Will work most of the time but will require // adjustment for pathological cases (e.g. Uniform distributions) private[thylacine] def pdfAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[Double] = logPdfAt(input).map(Math.exp) private[thylacine] def logPdfGradientAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[ModelParameterCollection] + @unused final private[thylacine] def logPdfFiniteDifferenceGradientAt( - input: ModelParameterCollection, - differential: Double + input: ModelParameterCollection, + differential: Double ): F[ModelParameterCollection] = for { currentEvaluation <- logPdfAt(input) @@ -56,8 +59,9 @@ private[thylacine] trait ModelParameterPdf[F[_]] extends GenericScalarValuedMapp nudgedEval <- logPdfAt(nudge) } yield (nudgedEval - currentEvaluation) / differential } - } yield IndexedVectorCollection(identifier, - VectorContainer(finiteDifferenceResults.toVector) + } yield IndexedVectorCollection( + identifier, + VectorContainer(finiteDifferenceResults.toVector) ) } result <- Async[F].delay(componentResults.reduce(_ rawMergeWith _)) @@ -66,29 +70,33 @@ private[thylacine] trait ModelParameterPdf[F[_]] extends GenericScalarValuedMapp // Will work most of the time but will require // adjustment for pathological cases (e.g. Uniform distributions) private[thylacine] def pdfGradientAt( - input: ModelParameterCollection + input: ModelParameterCollection ): F[ModelParameterCollection] = for { pdf <- pdfAt(input) gradLogs <- logPdfGradientAt(input) - } yield gradLogs.index.toList.map { gl => - IndexedVectorCollection(gl._1, VectorContainer(pdf * gl._2.rawVector)) - }.reduce(_ rawMergeWith _) + } yield gradLogs.index.toList + .map { gl => + IndexedVectorCollection(gl._1, VectorContainer(pdf * gl._2.rawVector)) + } + .reduce(_ rawMergeWith _) final def logPdfAt(input: Map[String, Vector[Double]]): F[Double] = logPdfAt(IndexedVectorCollection(input)) + @unused final def pdfAt(input: Map[String, Vector[Double]]): F[Double] = logPdfAt(input).map(Math.exp) final def logPdfGradientAt( - input: Map[String, Vector[Double]] + input: Map[String, Vector[Double]] ): F[Map[String, Vector[Double]]] = logPdfGradientAt(IndexedVectorCollection(input)) .map(_.genericScalaRepresentation) + @unused final def pdfGradientAt( - input: Map[String, Vector[Double]] + input: Map[String, Vector[Double]] ): F[Map[String, Vector[Double]]] = pdfGradientAt(IndexedVectorCollection(input)) .map(_.genericScalaRepresentation) diff --git a/src/main/scala/thylacine/model/distributions/CauchyDistribution.scala b/src/main/scala/thylacine/model/distributions/CauchyDistribution.scala index 92c5fde..290c72c 100644 --- a/src/main/scala/thylacine/model/distributions/CauchyDistribution.scala +++ b/src/main/scala/thylacine/model/distributions/CauchyDistribution.scala @@ -17,8 +17,8 @@ package ai.entrolution package thylacine.model.distributions -import thylacine.model.core.values.{MatrixContainer, VectorContainer} -import thylacine.model.core.{CanValidate, RecordedData} +import thylacine.model.core.values.{ MatrixContainer, VectorContainer } +import thylacine.model.core.{ CanValidate, RecordedData } import breeze.linalg._ import breeze.stats.distributions._ @@ -27,9 +27,9 @@ import org.apache.commons.math3.special.Gamma.gamma import org.apache.commons.math3.util.FastMath private[thylacine] case class CauchyDistribution( - mean: VectorContainer, - covariance: MatrixContainer, - validated: Boolean = false + mean: VectorContainer, + covariance: MatrixContainer, + validated: Boolean = false ) extends Distribution with CanValidate[CauchyDistribution] { if (!validated) { @@ -60,17 +60,18 @@ private[thylacine] case class CauchyDistribution( inv(covariance.rawMatrix) private[thylacine] override def logPdfAt( - input: VectorContainer + input: VectorContainer ): Double = { val differentialFromMean = input.rawVector - mean.rawVector - multiplier * Math.pow(1 + differentialFromMean.t * rawInverseCovariance * differentialFromMean, - (1.0 + domainDimension) / 2.0 + multiplier * Math.pow( + 1 + differentialFromMean.t * rawInverseCovariance * differentialFromMean, + (1.0 + domainDimension) / 2.0 ) } private[thylacine] override def logPdfGradientAt( - input: VectorContainer + input: VectorContainer ): VectorContainer = { val differentialFromMean = input.rawVector - mean.rawVector val multiplierResult = diff --git a/src/main/scala/thylacine/model/distributions/Distribution.scala b/src/main/scala/thylacine/model/distributions/Distribution.scala index 5a9be79..a47b090 100644 --- a/src/main/scala/thylacine/model/distributions/Distribution.scala +++ b/src/main/scala/thylacine/model/distributions/Distribution.scala @@ -24,6 +24,6 @@ private[thylacine] trait Distribution extends GenericScalarValuedMapping { private[thylacine] def logPdfAt(input: VectorContainer): Double private[thylacine] def logPdfGradientAt( - input: VectorContainer + input: VectorContainer ): VectorContainer } diff --git a/src/main/scala/thylacine/model/distributions/GaussianDistribution.scala b/src/main/scala/thylacine/model/distributions/GaussianDistribution.scala index 69e4d2d..5de7159 100644 --- a/src/main/scala/thylacine/model/distributions/GaussianDistribution.scala +++ b/src/main/scala/thylacine/model/distributions/GaussianDistribution.scala @@ -17,17 +17,17 @@ package ai.entrolution package thylacine.model.distributions -import thylacine.model.core.values.{MatrixContainer, VectorContainer} -import thylacine.model.core.{CanValidate, RecordedData} +import thylacine.model.core.values.{ MatrixContainer, VectorContainer } +import thylacine.model.core.{ CanValidate, RecordedData } import breeze.linalg._ import breeze.stats.distributions._ import org.apache.commons.math3.random.MersenneTwister private[thylacine] case class GaussianDistribution( - mean: VectorContainer, - covariance: MatrixContainer, - validated: Boolean = false + mean: VectorContainer, + covariance: MatrixContainer, + validated: Boolean = false ) extends Distribution with CanValidate[GaussianDistribution] { if (!validated) { @@ -56,12 +56,12 @@ private[thylacine] case class GaussianDistribution( inv(covariance.rawMatrix) private[thylacine] override def logPdfAt( - input: VectorContainer + input: VectorContainer ): Double = rawDistribution.logPdf(input.rawVector) private[thylacine] override def logPdfGradientAt( - input: VectorContainer + input: VectorContainer ): VectorContainer = VectorContainer( rawInverseCovariance * (mean.rawVector - input.rawVector) diff --git a/src/main/scala/thylacine/model/distributions/UniformDistribution.scala b/src/main/scala/thylacine/model/distributions/UniformDistribution.scala index 67a81d6..1dfb5ff 100644 --- a/src/main/scala/thylacine/model/distributions/UniformDistribution.scala +++ b/src/main/scala/thylacine/model/distributions/UniformDistribution.scala @@ -20,13 +20,13 @@ package thylacine.model.distributions import thylacine.model.core.CanValidate import thylacine.model.core.values.VectorContainer -import scala.collection.immutable.{Vector => ScalaVector} +import scala.collection.immutable.{ Vector => ScalaVector } import scala.collection.parallel.CollectionConverters._ private[thylacine] case class UniformDistribution( - upperBounds: VectorContainer, - lowerBounds: VectorContainer, - validated: Boolean = false + upperBounds: VectorContainer, + lowerBounds: VectorContainer, + validated: Boolean = false ) extends Distribution with CanValidate[UniformDistribution] { @@ -67,7 +67,7 @@ private[thylacine] case class UniformDistribution( } private[thylacine] override def logPdfAt( - input: VectorContainer + input: VectorContainer ): Double = if (insideBounds(input)) { negLogVolume @@ -76,7 +76,7 @@ private[thylacine] case class UniformDistribution( } private[thylacine] override def logPdfGradientAt( - input: VectorContainer + input: VectorContainer ): VectorContainer = zeroVector diff --git a/src/main/scala/thylacine/model/integration/slq/PointInCube.scala b/src/main/scala/thylacine/model/integration/slq/PointInCube.scala index 3906a5a..3837cb9 100644 --- a/src/main/scala/thylacine/model/integration/slq/PointInCube.scala +++ b/src/main/scala/thylacine/model/integration/slq/PointInCube.scala @@ -21,8 +21,8 @@ import thylacine.model.core._ import thylacine.model.core.values.VectorContainer private[thylacine] case class PointInCube( - pointInIntervals: Vector[PointInInterval], - validated: Boolean = false + pointInIntervals: Vector[PointInInterval], + validated: Boolean = false ) extends CanValidate[PointInCube] { private[thylacine] val dimension: Int = pointInIntervals.size @@ -48,8 +48,8 @@ private[thylacine] case class PointInCube( dimensionIndex(index) private[thylacine] def replaceIndex( - index: Int, - newInput: PointInInterval + index: Int, + newInput: PointInInterval ): PointInCube = if (index > 0 && index <= dimension) { val (foreList, aftList) = pointInIntervals.splitAt(index - 1) @@ -69,11 +69,12 @@ private[thylacine] case class PointInCube( .forall(i => i._1.isIntersectingWith(i._2)) private[thylacine] def dimensionOfLargestSeparation(input: PointInCube): Int = - (1 to pointInIntervals.size).zip { - pointInIntervals - .zip(input.pointInIntervals) - .map(i => i._1.distanceSquaredFrom(i._2)) - } + (1 to pointInIntervals.size) + .zip { + pointInIntervals + .zip(input.pointInIntervals) + .map(i => i._1.distanceSquaredFrom(i._2)) + } .maxBy(_._2) ._1 } @@ -81,8 +82,8 @@ private[thylacine] case class PointInCube( private[thylacine] object PointInCube { private[thylacine] def makeDisjoint( - pic1: PointInCube, - pic2: PointInCube + pic1: PointInCube, + pic2: PointInCube ): (PointInCube, PointInCube) = if (pic1.isIntersectingWith(pic2)) { val dimensionOfLargestSeparation = pic1.dimensionOfLargestSeparation(pic2) @@ -99,15 +100,15 @@ private[thylacine] object PointInCube { } private[thylacine] def makeDisjoint( - cubes: Vector[PointInCube] + cubes: Vector[PointInCube] ): Vector[PointInCube] = { def makeNewCubeDisjoint( - newCube: PointInCube, - disjointCubes: Vector[PointInCube] + newCube: PointInCube, + disjointCubes: Vector[PointInCube] ): Vector[PointInCube] = disjointCubes.foldLeft(Vector(newCube)) { case (cubeAccumulation, currentCube) => - val (disjoingCube1, disjointCube2) = PointInCube.makeDisjoint(cubeAccumulation.head, currentCube) - Vector(disjoingCube1, disjointCube2) ++ cubeAccumulation.tail + val (disjointCube1, disjointCube2) = PointInCube.makeDisjoint(cubeAccumulation.head, currentCube) + Vector(disjointCube1, disjointCube2) ++ cubeAccumulation.tail } cubes.foldLeft(Vector[PointInCube]()) { case (pointInCubeAccumulation, currentCube) => diff --git a/src/main/scala/thylacine/model/integration/slq/PointInCubeCollection.scala b/src/main/scala/thylacine/model/integration/slq/PointInCubeCollection.scala index aead450..af06b43 100644 --- a/src/main/scala/thylacine/model/integration/slq/PointInCubeCollection.scala +++ b/src/main/scala/thylacine/model/integration/slq/PointInCubeCollection.scala @@ -22,8 +22,8 @@ import thylacine.model.core.values.VectorContainer import thylacine.util.MathOps private[thylacine] case class PointInCubeCollection( - pointsInCube: Vector[PointInCube], - validated: Boolean = false + pointsInCube: Vector[PointInCube], + validated: Boolean = false ) extends CanValidate[PointInCubeCollection] { if (!validated) { assert(pointsInCube.size > 1) @@ -48,7 +48,7 @@ private[thylacine] case class PointInCubeCollection( } else { PointInCubeCollection( pointsInCube = pointsInCube.map(_.getValidated), - validated = true + validated = true ) } @@ -71,10 +71,10 @@ private[thylacine] case class PointInCubeCollection( .values .getOrElse(k._1, 0d) PointInInterval( - point = k._2, + point = k._2, lowerBound = k._2 - maxDiff, upperBound = k._2 + maxDiff, - validated = true + validated = true ) } PointInCube(piiList, validated = true) @@ -96,14 +96,18 @@ private[thylacine] case class PointInCubeCollection( MathOps.cdfStaircase(pointsInCube.map(_.cubeVolume)).zip(pointsInCube) private[thylacine] def getSample( - scaleParameter: Double + scaleParameter: Double ): VectorContainer = { val randomIndex = BigDecimal(Math.random().toString) - sampleMapping.collect { - case ((stairCaseLower, stairCaseUpper), cube) if randomIndex >= stairCaseLower && randomIndex < stairCaseUpper => - cube - }.head.getSample(scaleParameter) + sampleMapping + .collect { + case ((stairCaseLower, stairCaseUpper), cube) + if randomIndex >= stairCaseLower && randomIndex < stairCaseUpper => + cube + } + .head + .getSample(scaleParameter) } } diff --git a/src/main/scala/thylacine/model/integration/slq/PointInInterval.scala b/src/main/scala/thylacine/model/integration/slq/PointInInterval.scala index 0640efc..d490b85 100644 --- a/src/main/scala/thylacine/model/integration/slq/PointInInterval.scala +++ b/src/main/scala/thylacine/model/integration/slq/PointInInterval.scala @@ -20,10 +20,10 @@ package thylacine.model.integration.slq import thylacine.model.core._ private[thylacine] case class PointInInterval( - point: Double, - lowerBound: Double, - upperBound: Double, - validated: Boolean = false + point: Double, + lowerBound: Double, + upperBound: Double, + validated: Boolean = false ) extends CanValidate[PointInInterval] { if (!validated) { assert(upperBound > lowerBound) @@ -42,12 +42,12 @@ private[thylacine] case class PointInInterval( val length2 = point - lowerBound if (length1 > length2) { - this.copy(upperBound = point + length2) - } else if (length1 < length2) { - this.copy(lowerBound = point - length1) - } else { - this - } + this.copy(upperBound = point + length2) + } else if (length1 < length2) { + this.copy(lowerBound = point - length1) + } else { + this + } } private[thylacine] def isIntersectingWith(input: PointInInterval): Boolean = @@ -71,15 +71,15 @@ private[thylacine] object PointInInterval { // interval (that's valid) private[thylacine] def apply(point: Double): PointInInterval = PointInInterval( - point = point, + point = point, lowerBound = point - 1, upperBound = point + 1, - validated = true + validated = true ) private[thylacine] def findDisjointBoundary( - pii1: PointInInterval, - pii2: PointInInterval + pii1: PointInInterval, + pii2: PointInInterval ): (PointInInterval, PointInInterval) = { val isPii1Larger = pii1.point > pii2.point diff --git a/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissa.scala b/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissa.scala index 534c052..3387e14 100644 --- a/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissa.scala +++ b/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissa.scala @@ -23,8 +23,8 @@ import scala.annotation.tailrec // of points. This should not be accessed outside of a transactional // variable private[thylacine] case class QuadratureAbscissa( - samplePool: Set[Double], - abscissa: Vector[Double] + samplePool: Set[Double], + abscissa: Vector[Double] ) { import QuadratureAbscissa._ @@ -68,7 +68,7 @@ private[thylacine] object QuadratureAbscissa { QuadratureAbscissa( samplePool = randomVector, - abscissa = Vector() + abscissa = Vector() ) } } diff --git a/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissaCollection.scala b/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissaCollection.scala index 88518cf..2f27e27 100644 --- a/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissaCollection.scala +++ b/src/main/scala/thylacine/model/integration/slq/QuadratureAbscissaCollection.scala @@ -18,7 +18,7 @@ package ai.entrolution package thylacine.model.integration.slq private[thylacine] case class QuadratureAbscissaCollection( - abscissas: Vector[QuadratureAbscissa] + abscissas: Vector[QuadratureAbscissa] ) { assert(abscissas.map(_.abscissa.size).toSet.size <= 1) @@ -38,8 +38,8 @@ private[thylacine] case class QuadratureAbscissaCollection( private[thylacine] object QuadratureAbscissaCollection { private[thylacine] def apply( - numberOfAbscissas: Int, - numberOfSamplesPerAbscissa: Int + numberOfAbscissas: Int, + numberOfSamplesPerAbscissa: Int ): QuadratureAbscissaCollection = { val abscissas: Vector[QuadratureAbscissa] = (1 to numberOfAbscissas) diff --git a/src/main/scala/thylacine/model/integration/slq/QuadratureDomainTelemetry.scala b/src/main/scala/thylacine/model/integration/slq/QuadratureDomainTelemetry.scala index 1f82a13..b28bcd8 100644 --- a/src/main/scala/thylacine/model/integration/slq/QuadratureDomainTelemetry.scala +++ b/src/main/scala/thylacine/model/integration/slq/QuadratureDomainTelemetry.scala @@ -25,14 +25,14 @@ package thylacine.model.integration.slq // problems. There may be ways to re-introduce the scaling, but it will probably // only be reasonable in very specific scenarios. private[thylacine] case class QuadratureDomainTelemetry( - currentScaleFactor: Double, - acceptances: Int, - rejections: Int, - nominalAcceptance: Double, - minValue: Double, - acceptancesSinceLastRebuild: Int, - rejectionStreak: Int, - rescalingEnabled: Boolean = false // Do not enable (see comment above) + currentScaleFactor: Double, + acceptances: Int, + rejections: Int, + nominalAcceptance: Double, + minValue: Double, + acceptancesSinceLastRebuild: Int, + rejectionStreak: Int, + rescalingEnabled: Boolean = false // Do not enable (see comment above) ) { assert( currentScaleFactor >= Double.MinPositiveValue && currentScaleFactor <= 1.0 @@ -57,30 +57,30 @@ private[thylacine] case class QuadratureDomainTelemetry( this.copy( currentScaleFactor = Math .min(currentScaleFactor + (1.0 - currentScaleFactor) / 2.0, 1.0), - acceptances = newAcceptance, + acceptances = newAcceptance, acceptancesSinceLastRebuild = newAcceptanceSinceRebuild, - rejectionStreak = 0 + rejectionStreak = 0 ) } else { this.copy( - acceptances = newAcceptance, + acceptances = newAcceptance, acceptancesSinceLastRebuild = newAcceptanceSinceRebuild, - rejectionStreak = 0 + rejectionStreak = 0 ) } } else if (newAcceptanceRatio < nominalAcceptance) { if (rescalingEnabled) { this.copy( - currentScaleFactor = Math.max(currentScaleFactor / 2.0, Double.MinPositiveValue), - acceptances = newAcceptance, + currentScaleFactor = Math.max(currentScaleFactor / 2.0, Double.MinPositiveValue), + acceptances = newAcceptance, acceptancesSinceLastRebuild = newAcceptanceSinceRebuild, - rejectionStreak = 0 + rejectionStreak = 0 ) } else { this.copy( - acceptances = newAcceptance, + acceptances = newAcceptance, acceptancesSinceLastRebuild = newAcceptanceSinceRebuild, - rejectionStreak = 0 + rejectionStreak = 0 ) } } else { @@ -97,14 +97,14 @@ private[thylacine] case class QuadratureDomainTelemetry( this.copy( // currentScaleFactor = // Math.min(currentScaleFactor + (1.0 - currentScaleFactor) / 2.0, 1.0), - rejections = newRejection, + rejections = newRejection, rejectionStreak = rejectionStreak + 1 ) } else if (newAcceptanceRatio < nominalAcceptance) { this.copy( // currentScaleFactor = // Math.max(currentScaleFactor / 2.0, Double.MinPositiveValue), - rejections = newRejection, + rejections = newRejection, rejectionStreak = rejectionStreak + 1 ) } else { @@ -118,12 +118,12 @@ private[thylacine] object QuadratureDomainTelemetry { private[thylacine] val init: QuadratureDomainTelemetry = QuadratureDomainTelemetry( - currentScaleFactor = 1.0, - acceptances = 0, - rejections = 0, - nominalAcceptance = 0, - minValue = 0.0001, + currentScaleFactor = 1.0, + acceptances = 0, + rejections = 0, + nominalAcceptance = 0, + minValue = 0.0001, acceptancesSinceLastRebuild = 0, - rejectionStreak = 0 + rejectionStreak = 0 ) } diff --git a/src/main/scala/thylacine/model/integration/slq/QuadratureIntegrator.scala b/src/main/scala/thylacine/model/integration/slq/QuadratureIntegrator.scala index cb8dd38..92fc335 100644 --- a/src/main/scala/thylacine/model/integration/slq/QuadratureIntegrator.scala +++ b/src/main/scala/thylacine/model/integration/slq/QuadratureIntegrator.scala @@ -22,8 +22,8 @@ import ch.obermuhlner.math.big.DefaultBigDecimalMath import scala.collection.parallel.CollectionConverters._ private[thylacine] case class QuadratureIntegrator( - logPdfs: Vector[Double], - quadratures: Vector[Vector[Double]] + logPdfs: Vector[Double], + quadratures: Vector[Vector[Double]] ) { private lazy val minLogPdf: BigDecimal = (logPdfs.max + logPdfs.min) / 2.0 @@ -43,14 +43,16 @@ private[thylacine] case class QuadratureIntegrator( // Can be used in a number of places and is worth memoizing private[thylacine] lazy val negativeEntropyStats: Vector[BigDecimal] = - integrandGraphs.map { ig => - ig.par - .map(p => - BigDecimal(DefaultBigDecimalMath.exp(p._2.bigDecimal)) - * p._2 * p._1 - ) - .sum - }.zip(evidenceStats) + integrandGraphs + .map { ig => + ig.par + .map(p => + BigDecimal(DefaultBigDecimalMath.exp(p._2.bigDecimal)) + * p._2 * p._1 + ) + .sum + } + .zip(evidenceStats) .par .map { evs => evs._1 / evs._2 - BigDecimal( @@ -60,16 +62,18 @@ private[thylacine] case class QuadratureIntegrator( .toVector private[thylacine] def getIntegrationStats( - integrand: BigDecimal => BigDecimal + integrand: BigDecimal => BigDecimal ): Vector[BigDecimal] = - integrandGraphs.map { ig => - ig.par - .map(p => - integrand(BigDecimal(DefaultBigDecimalMath.exp(p._2.bigDecimal))) - * p._1 - ) - .sum - }.zip(evidenceStats) + integrandGraphs + .map { ig => + ig.par + .map(p => + integrand(BigDecimal(DefaultBigDecimalMath.exp(p._2.bigDecimal))) + * p._1 + ) + .sum + } + .zip(evidenceStats) .par .map { evs => evs._1 / evs._2 - BigDecimal( diff --git a/src/main/scala/thylacine/model/integration/slq/SamplingSimulation.scala b/src/main/scala/thylacine/model/integration/slq/SamplingSimulation.scala index 822b078..4e6de8b 100644 --- a/src/main/scala/thylacine/model/integration/slq/SamplingSimulation.scala +++ b/src/main/scala/thylacine/model/integration/slq/SamplingSimulation.scala @@ -31,7 +31,7 @@ private[thylacine] sealed trait SamplingSimulation { private[thylacine] object SamplingSimulation { - private[thylacine] case object SamplingSimulationUnconstructed extends SamplingSimulation { + private[thylacine] case object SamplingSimulationDeconstructed extends SamplingSimulation { private[thylacine] override final val isConstructed: Boolean = false private[thylacine] override final def getSample: ModelParameterCollection = @@ -39,8 +39,8 @@ private[thylacine] object SamplingSimulation { } private[thylacine] case class SamplingSimulationConstructed( - logPdfResults: Vector[(Double, ModelParameterCollection)], - abscissas: Vector[Vector[Double]] + logPdfResults: Vector[(Double, ModelParameterCollection)], + abscissas: Vector[Vector[Double]] ) extends SamplingSimulation { private val numberOfResults = logPdfResults.size private val numberOfAbscissas = abscissas.size @@ -58,8 +58,8 @@ private[thylacine] object SamplingSimulation { .map { ig => ((0d, ig.head._2) +: ig.dropRight(1)).zip(ig).map { avs => val ((x1, f1), (x2, f2)): ( - (Double, BigDecimal), - (Double, BigDecimal) + (Double, BigDecimal), + (Double, BigDecimal) ) = avs BigDecimal("0.5") * (f2 + f1) * BigDecimal((x2 - x1).toString) } @@ -86,17 +86,20 @@ private[thylacine] object SamplingSimulation { private[thylacine] override def getSample: ModelParameterCollection = { lazy val continuousRandom = BigDecimal(Math.random().toString) - indexedStaircase(random.nextInt(numberOfAbscissas) + 1).find { - case ((staircaseLower, staircaseUpper), _) - if staircaseLower <= continuousRandom && staircaseUpper > continuousRandom => - true - case _ => - false - }.map { case (_, index) => - indexedModelParameters(index) - }.get + indexedStaircase(random.nextInt(numberOfAbscissas) + 1) + .find { + case ((staircaseLower, staircaseUpper), _) + if staircaseLower <= continuousRandom && staircaseUpper > continuousRandom => + true + case _ => + false + } + .map { case (_, index) => + indexedModelParameters(index) + } + .get } } - private[thylacine] val empty: SamplingSimulation = SamplingSimulationUnconstructed + private[thylacine] val empty: SamplingSimulation = SamplingSimulationDeconstructed } diff --git a/src/main/scala/thylacine/model/integration/slq/SlqEngine.scala b/src/main/scala/thylacine/model/integration/slq/SlqEngine.scala index 9222120..47b968c 100644 --- a/src/main/scala/thylacine/model/integration/slq/SlqEngine.scala +++ b/src/main/scala/thylacine/model/integration/slq/SlqEngine.scala @@ -23,42 +23,31 @@ import bengal.stm.syntax.all._ import thylacine.model.components.posterior._ import thylacine.model.components.prior._ import thylacine.model.core._ +import thylacine.model.core.telemetry.SlqTelemetryUpdate import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection import thylacine.model.core.values.VectorContainer import thylacine.model.integration.ModelParameterIntegrator import thylacine.model.integration.slq.SamplingSimulation._ import thylacine.model.sampling.ModelParameterSampler -import ai.entrolution.thylacine.model.core.telemetry.SlqTelemetryUpdate import cats.effect.implicits._ import cats.effect.kernel.Async import cats.syntax.all._ -/** Implementation of the SLQ (pronounced like "slick") algorithm - * introduced in Appendix B of [3]. +/** Implementation of the SLQ (pronounced like "slick") algorithm introduced in Appendix B of [3]. * - * While an evolution of Skilling's Nested Sampling technique - * (see chapter 9 in [1] for an introduction or the Appendix - * in [2] for a more mathematical analysis of the approach), this - * algorithm still generally suffers from excessively high rejection - * rates in high-dimensional inferences. Attempts to alleviate this issue - * result in posterior integrations and sample simulations that are - * typically far-too concentrated around the PDF maxima. + * While an evolution of Skilling's Nested Sampling technique (see chapter 9 in [1] for an introduction or the Appendix + * in [2] for a more mathematical analysis of the approach), this algorithm still generally suffers from excessively + * high rejection rates in high-dimensional inferences. Attempts to alleviate this issue result in posterior + * integrations and sample simulations that are typically far-too concentrated around the PDF maxima. * - * TL;DR - Expect this algorithm to take a very long time to build its - * quadratures for high-dimensional problems. + * TL;DR - Expect this algorithm to take a very long time to build its quadratures for high-dimensional problems. * - * [1] Sivia D and Skilling J - * Data Analysis: A Bayesian Tutorial - * Second Edition (2006) Oxford University Press - * [2] von Nessi G T, Hole M J and The MAST Team - * A unified method for inference of tokamak equilibria and validation - * of force-balance models based on Bayesian analysis - * J. Phys. A.: Math. Theor. 46 (2013) 185501 - * [3] von Nessi G T, Hole M J and The MAST Team - * Recent developments in Bayesian inference of tokamak plasma - * equilibria and high-dimensional stochastic quadratures - * Plasma Phys. Control Fusion 56 (2014) 114011 + * [1] Sivia D and Skilling J Data Analysis: A Bayesian Tutorial Second Edition (2006) Oxford University Press [2] von + * Nessi G T, Hole M J and The MAST Team A unified method for inference of tokamak equilibria and validation of + * force-balance models based on Bayesian analysis J. Phys. A.: Math. Theor. 46 (2013) 185501 [3] von Nessi G T, Hole M + * J and The MAST Team Recent developments in Bayesian inference of tokamak plasma equilibria and high-dimensional + * stochastic quadratures Plasma Phys. Control Fusion 56 (2014) 114011 */ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] with ModelParameterSampler[F] { this: StmImplicits[F] with Posterior[F, Prior[F, _], _] => @@ -124,13 +113,13 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit .get(logPdf) .flatMap { case Some(_) => jitter(jitterResult) - case _ => STM[F].pure(logPdf) + case _ => STM[F].pure(logPdf) } .handleErrorWith(_ => STM[F].pure(logPdf)) } private def recordMinimumLogPdf( - minLogPdf: Option[Double] = None + minLogPdf: Option[Double] = None ): Txn[Unit] = for { currentMinimum <- minLogPdf match { @@ -185,15 +174,15 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit samplePoolNonEmpty <- samplePool.get.map(_.nonEmpty) result <- if (samplePoolNonEmpty) { recordMinimumLogPdf() >> - STM[F].pure(true) + STM[F].pure(true) } else { STM[F].pure(false) } } yield result).commit.flatMap(continue => if (continue) drainSamplePool else Async[F].unit) private def updateStateWithSampleCalculation( - logPdf: Double, - modelParameters: ModelParameterCollection + logPdf: Double, + modelParameters: ModelParameterCollection ): Txn[Boolean] = for { currentMinimum <- samplePoolMinimumLogPdf.get @@ -229,14 +218,14 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit negEntStats <- Async[F].delay(telemetry._3.negativeEntropyStats) result <- slqTelemetryUpdateCallback( SlqTelemetryUpdate( - negEntropyAvg = negEntStats.sum.toDouble / negEntStats.size, - logPdf = logPdf, - samplePoolMinimumLogPdf = telemetry._1.keySet.min, - domainVolumeScaling = telemetry._2.currentScaleFactor, + negEntropyAvg = negEntStats.sum.toDouble / negEntStats.size, + logPdf = logPdf, + samplePoolMinimumLogPdf = telemetry._1.keySet.min, + domainVolumeScaling = telemetry._2.currentScaleFactor, acceptancesSinceDomainRebuild = telemetry._2.acceptancesSinceLastRebuild, - samplePoolSize = telemetry._1.size, - domainCubeCount = domain.pointsInCube.size, - iterationCount = telemetry._3.logPdfs.size + samplePoolSize = telemetry._1.size, + domainCubeCount = domain.pointsInCube.size, + iterationCount = telemetry._3.logPdfs.size ) ) } yield result @@ -260,14 +249,14 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit private val setConverged: F[Unit] = { def testConverged( - iterationCount: Int, - numberSamplePoints: Int, - negativeEntropyStats: Vector[BigDecimal] + iterationCount: Int, + numberSamplePoints: Int, + negativeEntropyStats: Vector[BigDecimal] ): Boolean = if (negativeEntropyStats.nonEmpty) { (iterationCount > minIterationCount && - iterationCount >= 10 * numberSamplePoints * negativeEntropyStats.max) || - iterationCount >= maxIterationCount + iterationCount >= 10 * numberSamplePoints * negativeEntropyStats.max) || + iterationCount >= maxIterationCount } else { false } @@ -301,14 +290,14 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit samplingRecursion } else { drainSamplePool >> - (for { - abscissasRaw <- abscissas.get.map(_.getAbscissas) - logPdfRes <- logPdfResults.get - _ <- - samplingSimulation.set( - SamplingSimulationConstructed(logPdfRes, abscissasRaw) - ) - } yield ()).commit + (for { + abscissasRaw <- abscissas.get.map(_.getAbscissas) + logPdfRes <- logPdfResults.get + _ <- + samplingSimulation.set( + SamplingSimulationConstructed(logPdfRes, abscissasRaw) + ) + } yield ()).commit } ) @@ -360,7 +349,7 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit private val domainRebuildRecursion: F[Unit] = rebuildDomain.flatMap { case continueProcessing if continueProcessing => domainRebuildRecursion - case _ => Async[F].unit + case _ => Async[F].unit } /* @@ -373,13 +362,13 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit (for { _ <- sampleDomainScalingState.set { QuadratureDomainTelemetry( - currentScaleFactor = 1.0, - acceptances = 0, - rejections = 0, - nominalAcceptance = slqNominalAcceptanceRatio, - minValue = slqScalingIncrement, + currentScaleFactor = 1.0, + acceptances = 0, + rejections = 0, + nominalAcceptance = slqNominalAcceptanceRatio, + minValue = slqScalingIncrement, acceptancesSinceLastRebuild = 0, - rejectionStreak = 0 + rejectionStreak = 0 ) } _ <- workTokenPool.set(slqSampleParallelism) @@ -394,12 +383,14 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit } yield ()).commit private val analyseSeeds: F[Map[Double, ModelParameterCollection]] = - seeds.toVector.traverse { s => - logPdfAt(s).map(i => (i, s)) - }.map(_.toMap) + seeds.toVector + .traverse { s => + logPdfAt(s).map(i => (i, s)) + } + .map(_.toMap) private def getInitialSample( - logPdfs: Set[Double] + logPdfs: Set[Double] ): F[(Double, ModelParameterCollection)] = (for { sample <- samplePriors @@ -458,7 +449,7 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit // Run assuming the sample simulation has completed. It is possible to // put a `waitForSimulationConstruction` in this call but it adds unnecessary // overhead on the transaction runtime - protected val getSimulatedSample: F[ModelParameterCollection] = + private val getSimulatedSample: F[ModelParameterCollection] = for { sampleSimulationRaw <- samplingSimulation.get.commit result <- Async[F].delay(sampleSimulationRaw.getSample) @@ -468,7 +459,7 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit // We return the mean for the integrations, as SLQ produces inferences of these // integrations. override final def integrate( - integrand: BigDecimal => BigDecimal + integrand: BigDecimal => BigDecimal ): F[BigDecimal] = for { quadratureRaw <- quadratureIntegrations.get.commit @@ -489,12 +480,12 @@ private[thylacine] trait SlqEngine[F[_]] extends ModelParameterIntegrator[F] wit private[thylacine] object SlqEngine { private[thylacine] def getPointInCubeCollection( - inputs: Vector[ModelParameterCollection], - toVector: ModelParameterCollection => Vector[Double] + inputs: Vector[ModelParameterCollection], + toVector: ModelParameterCollection => Vector[Double] ): PointInCubeCollection = { def getPointInCube( - input: ModelParameterCollection, - toVector: ModelParameterCollection => Vector[Double] + input: ModelParameterCollection, + toVector: ModelParameterCollection => Vector[Double] ): PointInCube = PointInCube( toVector(input).map(PointInInterval(_)), diff --git a/src/main/scala/thylacine/model/optimization/ModelParameterOptimizer.scala b/src/main/scala/thylacine/model/optimization/ModelParameterOptimizer.scala index b61faee..ad4b354 100644 --- a/src/main/scala/thylacine/model/optimization/ModelParameterOptimizer.scala +++ b/src/main/scala/thylacine/model/optimization/ModelParameterOptimizer.scala @@ -29,7 +29,7 @@ private[thylacine] trait ModelParameterOptimizer[F[_]] { this: AsyncImplicits[F] => protected def calculateMaximumLogPdf( - startingPt: ModelParameterCollection + startingPt: ModelParameterCollection ): F[(Double, ModelParameterCollection)] final def findMaximumLogPdf(startPt: Map[String, Vector[Double]]): F[(Double, Map[String, Vector[Double]])] = diff --git a/src/main/scala/thylacine/model/optimization/gradientdescent/ConjugateGradientEngine.scala b/src/main/scala/thylacine/model/optimization/gradientdescent/ConjugateGradientEngine.scala index 305a0ab..6826d63 100644 --- a/src/main/scala/thylacine/model/optimization/gradientdescent/ConjugateGradientEngine.scala +++ b/src/main/scala/thylacine/model/optimization/gradientdescent/ConjugateGradientEngine.scala @@ -22,7 +22,7 @@ import thylacine.model.components.prior.Prior import thylacine.model.core.AsyncImplicits import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection import thylacine.model.optimization.ModelParameterOptimizer -import thylacine.model.optimization.line.{GoldenSectionSearch, LineEvaluationResult} +import thylacine.model.optimization.line.{ GoldenSectionSearch, LineEvaluationResult } import thylacine.util.ScalaVectorOps.Implicits._ import cats.effect.implicits._ @@ -43,10 +43,10 @@ trait ConjugateGradientEngine[F[_]] extends ModelParameterOptimizer[F] with Gold protected def isConvergedCallback: Unit => F[Unit] private def calculateNextLogPdf( - startingEvaluation: LineEvaluationResult, - previousGradient: Vector[Double], - previousSearchDirection: Vector[Double], - previousResults: Queue[Double] + startingEvaluation: LineEvaluationResult, + previousGradient: Vector[Double], + previousSearchDirection: Vector[Double], + previousResults: Queue[Double] ): F[(Double, ModelParameterCollection)] = (for { gradientLogPdf <- logPdfGradientAt(startingEvaluation.modelParameterArgument) @@ -110,16 +110,17 @@ trait ConjugateGradientEngine[F[_]] extends ModelParameterOptimizer[F] with Gold } protected def calculateMaximumLogPdf( - startingPt: ModelParameterCollection + startingPt: ModelParameterCollection ): F[(Double, ModelParameterCollection)] = for { logPdf <- logPdfAt(startingPt) gradientLogPdfVector <- logPdfGradientAt(startingPt).map(modelParameterCollectionToVectorValues) startingPointVector <- Async[F].delay(modelParameterCollectionToVectorValues(startingPt)) - result <- calculateNextLogPdf(LineEvaluationResult(logPdf, startingPointVector, startingPt), - gradientLogPdfVector, - gradientLogPdfVector, - Queue[Double]() + result <- calculateNextLogPdf( + LineEvaluationResult(logPdf, startingPointVector, startingPt), + gradientLogPdfVector, + gradientLogPdfVector, + Queue[Double]() ) } yield result diff --git a/src/main/scala/thylacine/model/optimization/hookeandjeeves/CoordinateSlideEngine.scala b/src/main/scala/thylacine/model/optimization/hookeandjeeves/CoordinateSlideEngine.scala index a975c51..01e3e33 100644 --- a/src/main/scala/thylacine/model/optimization/hookeandjeeves/CoordinateSlideEngine.scala +++ b/src/main/scala/thylacine/model/optimization/hookeandjeeves/CoordinateSlideEngine.scala @@ -26,7 +26,7 @@ import cats.effect.kernel.Async import cats.syntax.all._ import scala.util.Random -import scala.{Vector => ScalaVector} +import scala.{ Vector => ScalaVector } // Modification of standard Hooke and Jeeves to leverage // line searches along each coordinate direction. @@ -39,9 +39,9 @@ private[thylacine] trait CoordinateSlideEngine[F[_]] extends HookeAndJeevesEngin protected override val telemetryPrefix: String = "Coordinate Slide" protected override def dimensionScan( - nudgeAmount: Double, - startingPoint: ScalaVector[Double], - startingLogPdf: Double + nudgeAmount: Double, + startingPoint: ScalaVector[Double], + startingLogPdf: Double ): F[(Double, ScalaVector[Double])] = Random .shuffle(startingPoint.indices.toList) diff --git a/src/main/scala/thylacine/model/optimization/hookeandjeeves/HookeAndJeevesEngine.scala b/src/main/scala/thylacine/model/optimization/hookeandjeeves/HookeAndJeevesEngine.scala index c717a03..0084afa 100644 --- a/src/main/scala/thylacine/model/optimization/hookeandjeeves/HookeAndJeevesEngine.scala +++ b/src/main/scala/thylacine/model/optimization/hookeandjeeves/HookeAndJeevesEngine.scala @@ -33,7 +33,7 @@ import cats.effect.kernel.Async import cats.syntax.all._ import scala.util.Random -import scala.{Vector => ScalaVector} +import scala.{ Vector => ScalaVector } private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimizer[F] { this: StmImplicits[F] with Posterior[F, Prior[F, _], _] => @@ -52,7 +52,7 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi protected val isConverged: TxnVar[F, Boolean] - protected def findMaxDimensionalDifference(input: List[Vector[Double]]): Double = + private def findMaxDimensionalDifference(input: List[Vector[Double]]): Double = input.tail .foldLeft(input.head.zip(input.head)) { (i, j) => i.zip(j).map { k => @@ -63,8 +63,8 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi .max // Can parallel traverse, as there probably isn't much else going on - protected def initialize( - numberOfPriorSamples: Int + private def initialize( + numberOfPriorSamples: Int ): F[Unit] = for { samples <- (1 to numberOfPriorSamples).toList.parTraverse(_ => samplePriors) @@ -84,9 +84,9 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi } yield () protected def nudgeAndEvaluate( - index: Int, - nudgeAmount: Double, - input: ScalaVector[Double] + index: Int, + nudgeAmount: Double, + input: ScalaVector[Double] ): F[(Double, ScalaVector[Double])] = for { nudgedRawVector <- Async[F].delay(MathOps.modifyVectorIndex(input)(index, _ + nudgeAmount)) @@ -96,9 +96,9 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi } yield (logPdf, nudgedRawVector) protected def dimensionScan( - nudgeAmount: Double, - startingPoint: ScalaVector[Double], - startingLogPdf: Double + nudgeAmount: Double, + startingPoint: ScalaVector[Double], + startingLogPdf: Double ): F[(Double, ScalaVector[Double])] = Random .shuffle(startingPoint.indices.toList) @@ -112,7 +112,7 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi } } - protected val runDimensionalIteration: F[Unit] = + private val runDimensionalIteration: F[Unit] = for { scaleAndBest <- (for { scale <- currentScale.get @@ -125,9 +125,10 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi _ <- currentBest.set(scanResult).commit scale <- currentScale.get.commit _ <- iterationUpdateCallback( - OptimisationTelemetryUpdate(maxLogPdf = scanResult._1, - currentScale = scale, - prefix = telemetryPrefix + OptimisationTelemetryUpdate( + maxLogPdf = scanResult._1, + currentScale = scale, + prefix = telemetryPrefix ) ).start } yield (), @@ -140,9 +141,10 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi } yield newScale).commit maxLogPdf <- currentBest.get.commit _ <- iterationUpdateCallback( - OptimisationTelemetryUpdate(maxLogPdf = maxLogPdf._1, - currentScale = scaleResult, - prefix = telemetryPrefix + OptimisationTelemetryUpdate( + maxLogPdf = maxLogPdf._1, + currentScale = scaleResult, + prefix = telemetryPrefix ) ).start } yield (), @@ -151,7 +153,7 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi ) } yield () - protected val optimisationRecursion: F[Unit] = + private val optimisationRecursion: F[Unit] = runDimensionalIteration.flatMap { _ => for { converged <- isConverged.get.commit @@ -160,7 +162,7 @@ private[thylacine] trait HookeAndJeevesEngine[F[_]] extends ModelParameterOptimi } override protected def calculateMaximumLogPdf( - startPt: ModelParameterCollection + startPt: ModelParameterCollection ): F[(Double, ModelParameterCollection)] = for { _ <- initialize(numberOfSamplesToSetScale) diff --git a/src/main/scala/thylacine/model/optimization/line/GoldenSectionSearch.scala b/src/main/scala/thylacine/model/optimization/line/GoldenSectionSearch.scala index 357e39a..5dc02b4 100644 --- a/src/main/scala/thylacine/model/optimization/line/GoldenSectionSearch.scala +++ b/src/main/scala/thylacine/model/optimization/line/GoldenSectionSearch.scala @@ -20,10 +20,10 @@ package thylacine.model.optimization.line import thylacine.model.core.AsyncImplicits import thylacine.model.core.values.modelparameters.{ModelParameterContext, ModelParameterPdf} import thylacine.model.optimization.line.GoldenSectionSearch.{inversePhi, inversePhiSquared} +import thylacine.util.ScalaVectorOps.Implicits._ import cats.effect.kernel.Async import cats.syntax.all._ -import ai.entrolution.thylacine.util.ScalaVectorOps.Implicits._ import scala.util.Random @@ -33,9 +33,9 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin protected def goldenSectionTolerance: Double private[thylacine] override final def searchColinearTriple( - startPointEvaluation: (Double, Vector[Double]), - midPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + midPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] = { val bestNew: (Double, Vector[Double]) = if (startPointEvaluation._1 == endPointEvaluation._1) { Random.shuffle(List(startPointEvaluation, endPointEvaluation)).maxBy(_._1) @@ -51,10 +51,10 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin } private def exploreLine( - startPointEvaluation: (Double, Vector[Double]), - direction: Vector[Double], - probeDifferential: Double, - directionNormalised: Boolean = false + startPointEvaluation: (Double, Vector[Double]), + direction: Vector[Double], + probeDifferential: Double, + directionNormalised: Boolean = false ): F[(Double, Vector[Double])] = { val normalisedDirection = if (directionNormalised) { @@ -72,29 +72,30 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin } yield (forwardPointResult, reversePointResult)).flatMap { case (forwardPointResult, reversePointResult) if forwardPointResult == reversePointResult && startPointEvaluation._1 == forwardPointResult => - exploreLine(startPointEvaluation, - normalisedDirection, - probeDifferential * lineProbeExpansionFactor, - directionNormalised = true + exploreLine( + startPointEvaluation, + normalisedDirection, + probeDifferential * lineProbeExpansionFactor, + directionNormalised = true ) case (forwardPointResult, reversePointResult) => searchColinearTriple( startPointEvaluation = (reversePointResult, reversePoint), - midPointEvaluation = startPointEvaluation, - endPointEvaluation = (forwardPointResult, forwardPoint) + midPointEvaluation = startPointEvaluation, + endPointEvaluation = (forwardPointResult, forwardPoint) ) } } private[thylacine] override final def searchDirectionAlong( - startPointEvaluation: (Double, Vector[Double]), - direction: Vector[Double] + startPointEvaluation: (Double, Vector[Double]), + direction: Vector[Double] ): F[(Double, Vector[Double])] = exploreLine(startPointEvaluation, direction, goldenSectionTolerance / 10.0) private[thylacine] override final def searchAlongLineJoining( - startPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] = { val startEvaluation: LineEvaluationResult = LineEvaluationResult( startPointEvaluation._1, @@ -115,8 +116,8 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin } private[thylacine] override final def searchLineBetween( - startPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] = { val startEvaluation: LineEvaluationResult = LineEvaluationResult( startPointEvaluation._1, @@ -136,11 +137,11 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin } private def goldenSectionSearch( - firstPoint: LineEvaluationResult, - secondPoint: LineEvaluationResult, - inputH: Option[Vector[Double]] = None, - inputCEvaluation: Option[LineEvaluationResult] = None, - inputDEvaluation: Option[LineEvaluationResult] = None + firstPoint: LineEvaluationResult, + secondPoint: LineEvaluationResult, + inputH: Option[Vector[Double]] = None, + inputCEvaluation: Option[LineEvaluationResult] = None, + inputDEvaluation: Option[LineEvaluationResult] = None ): F[LineEvaluationResult] = { val vectorDifference: Vector[Double] = inputH.getOrElse { @@ -170,9 +171,10 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin val modelParameters = vectorValuesToModelParameterCollection(newFirstPointVectorArgument) logPdfAt(modelParameters).map { evaluationResult => - LineEvaluationResult(result = evaluationResult, - vectorArgument = newFirstPointVectorArgument, - modelParameterArgument = modelParameters + LineEvaluationResult( + result = evaluationResult, + vectorArgument = newFirstPointVectorArgument, + modelParameterArgument = modelParameters ) } } @@ -181,9 +183,10 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin val modelParameters = vectorValuesToModelParameterCollection(newSecondPointVectorArgument) logPdfAt(modelParameters).map { evaluationResult => - LineEvaluationResult(result = evaluationResult, - vectorArgument = newSecondPointVectorArgument, - modelParameterArgument = modelParameters + LineEvaluationResult( + result = evaluationResult, + vectorArgument = newSecondPointVectorArgument, + modelParameterArgument = modelParameters ) } } @@ -194,17 +197,17 @@ private[thylacine] trait GoldenSectionSearch[F[_]] extends LineProbe[F] with Lin } yield (newCEvaluation, newDEvaluation)).flatMap { case (newCEvaluation, newDEvaluation) if newCEvaluation.result > newDEvaluation.result => goldenSectionSearch( - firstPoint = firstPoint, - secondPoint = newDEvaluation, - inputH = Some(newH), + firstPoint = firstPoint, + secondPoint = newDEvaluation, + inputH = Some(newH), inputCEvaluation = None, inputDEvaluation = Some(newCEvaluation) ) case (newCEvaluation, newDEvaluation) => goldenSectionSearch( - firstPoint = newCEvaluation, - secondPoint = secondPoint, - inputH = Some(newH), + firstPoint = newCEvaluation, + secondPoint = secondPoint, + inputH = Some(newH), inputCEvaluation = Some(newDEvaluation), inputDEvaluation = None ) diff --git a/src/main/scala/thylacine/model/optimization/line/LineEvaluationResult.scala b/src/main/scala/thylacine/model/optimization/line/LineEvaluationResult.scala index 342f32f..6556fab 100644 --- a/src/main/scala/thylacine/model/optimization/line/LineEvaluationResult.scala +++ b/src/main/scala/thylacine/model/optimization/line/LineEvaluationResult.scala @@ -20,7 +20,7 @@ package thylacine.model.optimization.line import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection private[thylacine] case class LineEvaluationResult( - private[thylacine] val result: Double, - private[thylacine] val vectorArgument: Vector[Double], - private[thylacine] val modelParameterArgument: ModelParameterCollection + private[thylacine] val result: Double, + private[thylacine] val vectorArgument: Vector[Double], + private[thylacine] val modelParameterArgument: ModelParameterCollection ) diff --git a/src/main/scala/thylacine/model/optimization/line/LineEvaluationTriple.scala b/src/main/scala/thylacine/model/optimization/line/LineEvaluationTriple.scala index 1973951..807f8b9 100644 --- a/src/main/scala/thylacine/model/optimization/line/LineEvaluationTriple.scala +++ b/src/main/scala/thylacine/model/optimization/line/LineEvaluationTriple.scala @@ -18,7 +18,7 @@ package ai.entrolution package thylacine.model.optimization.line private[thylacine] case class LineEvaluationTriple( - private[thylacine] val firstEndPoint: LineEvaluationResult, - private[thylacine] val middlePoint: LineEvaluationResult, - private[thylacine] val secondEndPoint: LineEvaluationResult + private[thylacine] val firstEndPoint: LineEvaluationResult, + private[thylacine] val middlePoint: LineEvaluationResult, + private[thylacine] val secondEndPoint: LineEvaluationResult ) diff --git a/src/main/scala/thylacine/model/optimization/line/LineProbe.scala b/src/main/scala/thylacine/model/optimization/line/LineProbe.scala index 0357e2f..e77e947 100644 --- a/src/main/scala/thylacine/model/optimization/line/LineProbe.scala +++ b/src/main/scala/thylacine/model/optimization/line/LineProbe.scala @@ -19,7 +19,7 @@ package thylacine.model.optimization.line import thylacine.model.core.AsyncImplicits import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.modelparameters.{ModelParameterContext, ModelParameterPdf} +import thylacine.model.core.values.modelparameters.{ ModelParameterContext, ModelParameterPdf } import cats.effect.kernel.Async import cats.syntax.all._ @@ -30,9 +30,9 @@ private[thylacine] trait LineProbe[F[_]] { protected def lineProbeExpansionFactor: Double protected def probeLine( - pt1: LineEvaluationResult, - pt2: LineEvaluationResult, - ordered: Boolean = false + pt1: LineEvaluationResult, + pt2: LineEvaluationResult, + ordered: Boolean = false ): F[LineEvaluationTriple] = { val (lowerPoint, upperPoint) = if (ordered || pt1.result < pt2.result) { @@ -53,9 +53,10 @@ private[thylacine] trait LineProbe[F[_]] { val probeEvaluationF: F[Double] = logPdfAt(probePointModelParameters) probeEvaluationF.flatMap { probeEvaluation => - val evaluationResult = LineEvaluationResult(result = probeEvaluation, - vectorArgument = probePointVector, - modelParameterArgument = probePointModelParameters + val evaluationResult = LineEvaluationResult( + result = probeEvaluation, + vectorArgument = probePointVector, + modelParameterArgument = probePointModelParameters ) if (probeEvaluation >= upperPoint.result) { @@ -63,8 +64,8 @@ private[thylacine] trait LineProbe[F[_]] { } else { Async[F].delay { LineEvaluationTriple( - firstEndPoint = lowerPoint, - middlePoint = upperPoint, + firstEndPoint = lowerPoint, + middlePoint = upperPoint, secondEndPoint = evaluationResult ) } diff --git a/src/main/scala/thylacine/model/optimization/line/LineSearch.scala b/src/main/scala/thylacine/model/optimization/line/LineSearch.scala index 74e8446..5ee62ef 100644 --- a/src/main/scala/thylacine/model/optimization/line/LineSearch.scala +++ b/src/main/scala/thylacine/model/optimization/line/LineSearch.scala @@ -20,24 +20,24 @@ package thylacine.model.optimization.line private[thylacine] trait LineSearch[F[_]] { private[thylacine] def searchColinearTriple( - startPointEvaluation: (Double, Vector[Double]), - midPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + midPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] private[thylacine] def searchDirectionAlong( - startPointEvaluation: (Double, Vector[Double]), - direction: Vector[Double] + startPointEvaluation: (Double, Vector[Double]), + direction: Vector[Double] ): F[(Double, Vector[Double])] private[thylacine] def searchAlongLineJoining( - startPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] private[thylacine] def searchLineBetween( - startPointEvaluation: (Double, Vector[Double]), - endPointEvaluation: (Double, Vector[Double]) + startPointEvaluation: (Double, Vector[Double]), + endPointEvaluation: (Double, Vector[Double]) ): F[(Double, Vector[Double])] } diff --git a/src/main/scala/thylacine/model/optimization/mds/MdsEngine.scala b/src/main/scala/thylacine/model/optimization/mds/MdsEngine.scala index 2a0b5b1..f488f73 100644 --- a/src/main/scala/thylacine/model/optimization/mds/MdsEngine.scala +++ b/src/main/scala/thylacine/model/optimization/mds/MdsEngine.scala @@ -120,7 +120,7 @@ trait MdsEngine[F[_]] extends ModelParameterOptimizer[F] { } private def getStartingPoint( - numberOfPriorSamples: Int + numberOfPriorSamples: Int ): F[ModelParameterCollection] = for { samples <- (1 to numberOfPriorSamples).toList.parTraverse(_ => samplePriors) @@ -141,7 +141,7 @@ trait MdsEngine[F[_]] extends ModelParameterOptimizer[F] { } override protected def calculateMaximumLogPdf( - startPt: ModelParameterCollection + startPt: ModelParameterCollection ): F[(Double, ModelParameterCollection)] = { // Do a parallel traversal here, as there is probably // not going to be much else going on at this stage diff --git a/src/main/scala/thylacine/model/optimization/mds/ModelParameterSimplex.scala b/src/main/scala/thylacine/model/optimization/mds/ModelParameterSimplex.scala index 1de5110..96c7017 100644 --- a/src/main/scala/thylacine/model/optimization/mds/ModelParameterSimplex.scala +++ b/src/main/scala/thylacine/model/optimization/mds/ModelParameterSimplex.scala @@ -24,9 +24,9 @@ import thylacine.model.optimization.mds.ModelParameterSimplex.differenceMagnitud import thylacine.util.MathOps private[thylacine] case class ModelParameterSimplex( - private[thylacine] val vertices: Map[Int, Vector[Double]], - private[thylacine] val isRegular: Boolean = false, - private[thylacine] val validated: Boolean = false + private[thylacine] val vertices: Map[Int, Vector[Double]], + private[thylacine] val isRegular: Boolean = false, + private[thylacine] val validated: Boolean = false ) extends CanValidate[ModelParameterSimplex] { private lazy val randomAdjacentEdgeLength: Double = @@ -58,7 +58,7 @@ private[thylacine] case class ModelParameterSimplex( this.copy(validated = true) private[thylacine] def verticesAsModelParameters( - modelParameterContext: ModelParameterContext + modelParameterContext: ModelParameterContext ): Map[Int, ModelParameterCollection] = vertices.view.mapValues(modelParameterContext.vectorValuesToModelParameterCollection).toMap @@ -93,8 +93,8 @@ private[thylacine] object ModelParameterSimplex { Math.sqrt(input1.zip(input2).map(i => Math.pow(i._1 - i._2, 2.0)).sum) private[thylacine] def unitRegularCenteredOn( - input: ModelParameterCollection, - rawMappings: ModelParameterContext + input: ModelParameterCollection, + rawMappings: ModelParameterContext ): ModelParameterSimplex = { val n = input.totalDimension val baseScalar = -Math.pow(n.toDouble, -1.5) * (Math.sqrt(n + 1d) + 1) @@ -112,15 +112,16 @@ private[thylacine] object ModelParameterSimplex { .map(index => MathOps.modifyVectorIndex(baseVertex)(index, _ + nudgeScalar)) :+ lastVertex) .map(_.zip(vectorInput).map(i => i._1 + i._2)) - ModelParameterSimplex(recenteredVertices.indices - .zip(recenteredVertices) - .toMap, - isRegular = true + ModelParameterSimplex( + recenteredVertices.indices + .zip(recenteredVertices) + .toMap, + isRegular = true ).getValidated } private[thylacine] def unitRegularCenteredOnZero( - rawMappings: ModelParameterContext + rawMappings: ModelParameterContext ): ModelParameterSimplex = unitRegularCenteredOn( rawMappings.zeroModelParameterCollection, diff --git a/src/main/scala/thylacine/model/sampling/hmcmc/HmcmcEngine.scala b/src/main/scala/thylacine/model/sampling/hmcmc/HmcmcEngine.scala index e63af16..81b6c9e 100644 --- a/src/main/scala/thylacine/model/sampling/hmcmc/HmcmcEngine.scala +++ b/src/main/scala/thylacine/model/sampling/hmcmc/HmcmcEngine.scala @@ -25,7 +25,7 @@ import thylacine.model.components.prior.Prior import thylacine.model.core.StmImplicits import thylacine.model.core.telemetry.HmcmcTelemetryUpdate import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} +import thylacine.model.core.values.{ IndexedVectorCollection, VectorContainer } import thylacine.model.sampling.ModelParameterSampler import cats.effect.implicits._ @@ -86,10 +86,10 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { } yield () private def runLeapfrogAt( - input: ModelParameterCollection, - rawP: VectorContainer, - gradNegLogPdf: ModelParameterCollection, - iterationCount: Int = 1 + input: ModelParameterCollection, + rawP: VectorContainer, + gradNegLogPdf: ModelParameterCollection, + iterationCount: Int = 1 ): F[(ModelParameterCollection, VectorContainer)] = if (iterationCount > stepsInSimulation) { Async[F].pure((input, rawP)) @@ -116,27 +116,27 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { p.rawDotProductWith(p) / 2.0 + E private def runDynamicSimulationFrom( - input: ModelParameterCollection, - maxIterations: Int, - logPdfOpt: Option[Double] = None, - gradLogPdfOpt: Option[ModelParameterCollection] = None, - burnIn: Boolean = false, - iterationCount: Int = 1 + input: ModelParameterCollection, + maxIterations: Int, + logPdfOpt: Option[Double] = None, + gradLogPdfOpt: Option[ModelParameterCollection] = None, + burnIn: Boolean = false, + iterationCount: Int = 1 ): F[ModelParameterCollection] = if (iterationCount <= maxIterations) { (for { _ <- if (burnIn) { - Async[F].delay(print(s"\rHMCMC Sampling :: Burn-in Iteration - $iterationCount/$warmUpSimulationCount")) - } else { - Async[F].unit - } + Async[F].delay(print(s"\rHMCMC Sampling :: Burn-in Iteration - $iterationCount/$warmUpSimulationCount")) + } else { + Async[F].unit + } negLogPdf <- logPdfOpt match { case Some(res) => Async[F].pure(res) - case _ => logPdfAt(input).map(_ * -1) + case _ => logPdfAt(input).map(_ * -1) } gradNegLogPdf <- gradLogPdfOpt match { case Some(res) => Async[F].pure(res) - case _ => logPdfGradientAt(input).map(_.rawScalarMultiplyWith(-1)) + case _ => logPdfGradientAt(input).map(_.rawScalarMultiplyWith(-1)) } p <- Async[F].delay(VectorContainer.random(domainDimension)) hamiltonian <- Async[F].delay(getHamiltonianValue(p, negLogPdf)) @@ -146,10 +146,10 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { hNew <- Async[F].delay(getHamiltonianValue(pNew, eNew)) dH <- Async[F].delay(hNew - hamiltonian) _ <- if (burnIn) { - Async[F].unit - } else { - hamiltonianDifferentialUpdateCallback(dH) - } + Async[F].unit + } else { + hamiltonianDifferentialUpdateCallback(dH) + } result <- Async[F].ifM(Async[F].delay(dH < 0 || Math.random() < Math.exp(-dH)))( for { _ <- Async[F].ifM(Async[F].pure(burnIn))( @@ -179,7 +179,7 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { */ private def setAndAcquireNewSample( - position: ModelParameterCollection + position: ModelParameterCollection ): F[ModelParameterCollection] = runDynamicSimulationFrom(position, simulationsBetweenSamples).flatMap { case result if result != position => @@ -218,7 +218,7 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { for { _ <- burnInComplete.set(false).commit burninResult <- burnIn - _ <- Async[F].delay(print(s"\nHMCMC Sampling :: Burn-in complete!\n")) + _ <- Async[F].delay(print(s"\nHMCMC Sampling :: Burn-in complete!\n")) _ <- (for { _ <- workTokenPool.set(sampleParallelism) _ <- currentMcmcPositions.set(Queue.from(burninResult)) @@ -246,7 +246,7 @@ private[thylacine] trait HmcmcEngine[F[_]] extends ModelParameterSampler[F] { _ <- sampleProcessedCallback(telemetryResult) } yield () - protected val getHmcmcSample: F[ModelParameterCollection] = + private val getHmcmcSample: F[ModelParameterCollection] = for { _ <- sampleUpdateReport(1).start position <- (for { diff --git a/src/main/scala/thylacine/model/sampling/leapfrog/LeapfrogMcmcEngine.scala b/src/main/scala/thylacine/model/sampling/leapfrog/LeapfrogMcmcEngine.scala index 0b3ea97..f3fef73 100644 --- a/src/main/scala/thylacine/model/sampling/leapfrog/LeapfrogMcmcEngine.scala +++ b/src/main/scala/thylacine/model/sampling/leapfrog/LeapfrogMcmcEngine.scala @@ -24,8 +24,8 @@ import thylacine.model.components.posterior._ import thylacine.model.components.prior.Prior import thylacine.model.core.StmImplicits import thylacine.model.core.values.IndexedVectorCollection.ModelParameterCollection -import thylacine.model.core.values.{IndexedVectorCollection, VectorContainer} -import thylacine.model.sampling.{ModelParameterSampler, SampleRequest} +import thylacine.model.core.values.{ IndexedVectorCollection, VectorContainer } +import thylacine.model.sampling.{ ModelParameterSampler, SampleRequest } import thylacine.util.MathOps import thylacine.util.ScalaVectorOps.Implicits._ @@ -89,10 +89,10 @@ private[thylacine] trait LeapfrogMcmcEngine[F[_]] extends ModelParameterSampler[ leapOver.scalarMultiplyWith(2.0).subtract(leapFrom) private def getDistanceCalculations( - samplePoints: Vector[Vector[Double]], - newPoint: Vector[Double], - replaceIndex: Int, - jumpIndex: Int + samplePoints: Vector[Vector[Double]], + newPoint: Vector[Double], + replaceIndex: Int, + jumpIndex: Int ): F[(Vector[Double], Double)] = for { newDistances <- Async[F].delay { @@ -279,7 +279,7 @@ private[thylacine] trait LeapfrogMcmcEngine[F[_]] extends ModelParameterSampler[ _ <- STM[F].waitFor(burnInCompleted) } yield ()).commit - protected val getLeapfromMcmcSample: F[ModelParameterCollection] = + private val getLeapfrogMcmcSample: F[ModelParameterCollection] = for { deferred <- Deferred[F, ModelParameterCollection] newRequestSize <- (for { @@ -291,7 +291,7 @@ private[thylacine] trait LeapfrogMcmcEngine[F[_]] extends ModelParameterSampler[ } yield result protected override val sampleModelParameters: F[ModelParameterCollection] = - getLeapfromMcmcSample + getLeapfrogMcmcSample protected override val rawSampleModelParameters: F[VectorContainer] = sampleModelParameters.map(s => VectorContainer(modelParameterCollectionToRawVector(s))) diff --git a/src/main/scala/thylacine/util/MathOps.scala b/src/main/scala/thylacine/util/MathOps.scala index 445b6b4..58a5e6a 100644 --- a/src/main/scala/thylacine/util/MathOps.scala +++ b/src/main/scala/thylacine/util/MathOps.scala @@ -24,8 +24,8 @@ private[thylacine] object MathOps { private val randomGenerator = Random private[thylacine] def trapezoidalQuadrature( - abscissa: Vector[Double], - values: Vector[Double] + abscissa: Vector[Double], + values: Vector[Double] ): Double = if (abscissa.size == values.size && abscissa.size > 1) { trapezoidalQuadrature(abscissa.zip(values)) @@ -34,7 +34,7 @@ private[thylacine] object MathOps { } private[thylacine] def trapezoidalQuadrature( - graphPoints: Vector[(Double, Double)] + graphPoints: Vector[(Double, Double)] ): Double = if ( graphPoints.size > 1 && graphPoints @@ -57,7 +57,7 @@ private[thylacine] object MathOps { // Creates a discretised CDF that facilitates sampling over a // discrete set of outcomes via uniform sampling on [0, 1) private[thylacine] def cdfStaircase( - values: Vector[BigDecimal] + values: Vector[BigDecimal] ): Vector[(BigDecimal, BigDecimal)] = { val cdfReversed = values .foldLeft(Vector[BigDecimal](BigDecimal(0))) { (i, j) => @@ -74,7 +74,7 @@ private[thylacine] object MathOps { } private[thylacine] def vectorCdfStaircase( - values: Vector[Double] + values: Vector[Double] ): Vector[(Double, Double)] = { val cdfReversed = values .foldLeft(Vector(0d)) { (i, j) => @@ -96,24 +96,19 @@ private[thylacine] object MathOps { private[thylacine] def scalarMultipleVector(input: Vector[Double], multiplier: Double): Vector[Double] = input.map(multiplier * _) - private[thylacine] def vectorMagnitude(input: Vector[Double]): Double = - Math.sqrt(vectorMagnitudeSquared(input)) - private[thylacine] def vectorMagnitudeSquared(input: Vector[Double]): Double = input.map(Math.pow(_, 2.0)).sum private[thylacine] def vectorDotProduct(input1: Vector[Double], input2: Vector[Double]): Double = input1 .zip(input2) - .map { case (coord1, coord2) => - coord1 * coord2 - } + .map { case (e1, e2) => e1 * e2 } .sum private[thylacine] def vectorAddition(input1: Vector[Double], input2: Vector[Double]): Vector[Double] = - input1.zip(input2).map { case (coord1, coord2) => - coord1 + coord2 - } + input1 + .zip(input2) + .map { case (e1, e2) => e1 + e2 } private[thylacine] def nextGaussian: Double = randomGenerator.nextGaussian() diff --git a/src/main/scala/thylacine/visualisation/CartesianSurface.scala b/src/main/scala/thylacine/visualisation/CartesianSurface.scala index beb55de..5341168 100644 --- a/src/main/scala/thylacine/visualisation/CartesianSurface.scala +++ b/src/main/scala/thylacine/visualisation/CartesianSurface.scala @@ -30,12 +30,12 @@ import cats.syntax.all._ import scala.annotation.unused case class CartesianSurface[F[_]: STM: Async]( - xAbscissa: Vector[Double], - yAbscissa: Vector[Double], - progressSetCallback: Int => F[Unit], - progressIncrementCallback: Unit => F[Unit], - progressFinishCallback: Unit => F[Unit], - private val scalarValues: TxnVar[F, Map[GraphPoint, Double]] + xAbscissa: Vector[Double], + yAbscissa: Vector[Double], + progressSetCallback: Int => F[Unit], + progressIncrementCallback: Unit => F[Unit], + progressFinishCallback: Unit => F[Unit], + private val scalarValues: TxnVar[F, Map[GraphPoint, Double]] ) { private val xScale = xAbscissa.max - xAbscissa.min @@ -53,20 +53,24 @@ case class CartesianSurface[F[_]: STM: Async]( values <- scalarValues.get groupMap = (values.groupBy(_._1.x).toVector match { case h +: t => - NonEmptyVector(h, t).parTraverse { col => - Async[F].delay { - trapezoidalQuadrature( - yAbscissa, - col._2.values.toVector - ) - }.map { integration => - if (integration > 0) { - col._2.view.mapValues(v => v / integration).toSeq - } else { - col._2.toSeq - } + NonEmptyVector(h, t) + .parTraverse { col => + Async[F] + .delay { + trapezoidalQuadrature( + yAbscissa, + col._2.values.toVector + ) + } + .map { integration => + if (integration > 0) { + col._2.view.mapValues(v => v / integration).toSeq + } else { + col._2.toSeq + } + } } - }.map(_.reduce) + .map(_.reduce) case _ => Async[F].pure(Seq[(GraphPoint, Double)]()) }).map(_.toMap) @@ -80,54 +84,56 @@ case class CartesianSurface[F[_]: STM: Async]( } private def addSimplexChain( - input: SimplexChain, - kernelVariance: Double + input: SimplexChain, + kernelVariance: Double ): F[Unit] = scalarValues.modifyF { pvs => concatenateMapping(pvs, input, kernelVariance).map(_.toMap) }.commit private def addValues( - abcissa: Vector[Double], - values: Vector[Double], - ds: Double, - kernelVariance: Double + abscissa: Vector[Double], + values: Vector[Double], + ds: Double, + kernelVariance: Double ): F[Unit] = for { _ <- progressIncrementCallback(()) chain <- Async[F].delay( SimplexChain( - abcissa.zip(values).map(GraphPoint(_)) + abscissa.zip(values).map(GraphPoint(_)) ).linearInterpolationUsing(ds) ) _ <- addSimplexChain(chain, kernelVariance) } yield () private def concatenateMapping( - startMap: Map[GraphPoint, Double], - chain: SimplexChain, - kernelVariance: Double + startMap: Map[GraphPoint, Double], + chain: SimplexChain, + kernelVariance: Double ): F[Vector[(GraphPoint, Double)]] = startMap.toVector match { case h +: t => - NonEmptyVector(h, t).parTraverse { pv => - Async[F].delay { - pv._1 -> chain.getPoints.foldLeft(pv._2) { (i, j) => - i + Math.exp( - -0.5 * j.scaledDistSquaredTo(pv._1, xScale, yScale) / kernelVariance - ) + NonEmptyVector(h, t) + .parTraverse { pv => + Async[F].delay { + pv._1 -> chain.getPoints.foldLeft(pv._2) { (i, j) => + i + Math.exp( + -0.5 * j.scaledDistSquaredTo(pv._1, xScale, yScale) / kernelVariance + ) + } } } - }.map(_.toVector) + .map(_.toVector) case _ => Async[F].pure(Vector[(GraphPoint, Double)]()) } @unused def addSamples( - abcissa: Vector[Double], - samples: Vector[Vector[Double]], - ds: Double, - kernelVariance: Double + abscissa: Vector[Double], + samples: Vector[Vector[Double]], + ds: Double, + kernelVariance: Double ): F[Unit] = samples match { case h +: t => @@ -135,7 +141,7 @@ case class CartesianSurface[F[_]: STM: Async]( _ <- progressSetCallback(samples.size) _ <- NonEmptyVector(h, t) - .traverse(i => addValues(abcissa, i, ds, kernelVariance)) + .traverse(i => addValues(abscissa, i, ds, kernelVariance)) _ <- progressIncrementCallback(()) _ <- progressFinishCallback(()) } yield () @@ -151,26 +157,28 @@ case class CartesianSurface[F[_]: STM: Async]( } yield plotData.map(i => i._1.primitiveValue -> i._2)).commit } +@unused object CartesianSurface { @unused def of[F[_]: STM: Async]( - xAbscissa: Vector[Double], - yAbscissa: Vector[Double], - progressSetCallback: Int => F[Unit], - progressIncrementCallback: Unit => F[Unit], - progressFinishCallback: Unit => F[Unit] + xAbscissa: Vector[Double], + yAbscissa: Vector[Double], + progressSetCallback: Int => F[Unit], + progressIncrementCallback: Unit => F[Unit], + progressFinishCallback: Unit => F[Unit] ): F[CartesianSurface[F]] = for { scalarValues <- TxnVar.of(Map[GraphPoint, Double]()) result <- Async[F].delay( - CartesianSurface(xAbscissa, - yAbscissa, - progressSetCallback, - progressIncrementCallback, - progressFinishCallback, - scalarValues + CartesianSurface( + xAbscissa, + yAbscissa, + progressSetCallback, + progressIncrementCallback, + progressFinishCallback, + scalarValues ) ) _ <- result.zeroValuesSpec diff --git a/src/main/scala/thylacine/visualisation/GraphPoint.scala b/src/main/scala/thylacine/visualisation/GraphPoint.scala index a2a66f0..926165b 100644 --- a/src/main/scala/thylacine/visualisation/GraphPoint.scala +++ b/src/main/scala/thylacine/visualisation/GraphPoint.scala @@ -25,9 +25,9 @@ private[thylacine] case class GraphPoint(x: Double, y: Double) { Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2) private[thylacine] def scaledDistSquaredTo( - point: GraphPoint, - xScale: Double, - yScale: Double + point: GraphPoint, + xScale: Double, + yScale: Double ): Double = Math.pow((x - point.x) / xScale, 2) + Math.pow((y - point.y) / yScale, 2) diff --git a/src/main/scala/thylacine/visualisation/Simplex.scala b/src/main/scala/thylacine/visualisation/Simplex.scala index e79e26d..6d988ea 100644 --- a/src/main/scala/thylacine/visualisation/Simplex.scala +++ b/src/main/scala/thylacine/visualisation/Simplex.scala @@ -25,7 +25,7 @@ private[thylacine] case class Simplex(start: GraphPoint, end: GraphPoint) { private[thylacine] lazy val length: Double = start.distanceTo(end) - private[thylacine] def getInterp(t: Double): GraphPoint = + private[thylacine] def getInterpolation(t: Double): GraphPoint = if (t <= 0) { start } else if (t >= 1) { @@ -36,14 +36,14 @@ private[thylacine] case class Simplex(start: GraphPoint, end: GraphPoint) { @tailrec private[thylacine] final def getInterpolationPoints( - start: Double, - ds: Double, - prev: List[GraphPoint] = List() + start: Double, + ds: Double, + prev: List[GraphPoint] = List() ): (List[GraphPoint], Double) = if (start >= length) { (prev, start - length) } else { - getInterpolationPoints(start + ds, ds, List(getInterp(start / length)) ++ prev) + getInterpolationPoints(start + ds, ds, List(getInterpolation(start / length)) ++ prev) } } diff --git a/src/main/scala/thylacine/visualisation/SimplexChain.scala b/src/main/scala/thylacine/visualisation/SimplexChain.scala index 2de37f5..7766448 100644 --- a/src/main/scala/thylacine/visualisation/SimplexChain.scala +++ b/src/main/scala/thylacine/visualisation/SimplexChain.scala @@ -24,8 +24,8 @@ private[thylacine] case class SimplexChain(chain: Seq[Simplex]) { SimplexChain( chain .foldLeft((0d, Vector[GraphPoint]())) { (i, j) => - val nextInterp = j.getInterpolationPoints(i._1, ds) - (nextInterp._2, i._2 ++ nextInterp._1) + val nextInterpolation = j.getInterpolationPoints(i._1, ds) + (nextInterpolation._2, i._2 ++ nextInterpolation._1) } ._2 ) diff --git a/src/test/scala/thylacine/model/components/ComponentFixture.scala b/src/test/scala/thylacine/model/components/ComponentFixture.scala index 43097a5..769f2e6 100644 --- a/src/test/scala/thylacine/model/components/ComponentFixture.scala +++ b/src/test/scala/thylacine/model/components/ComponentFixture.scala @@ -18,11 +18,11 @@ package ai.entrolution package thylacine.model.components import bengal.stm.STM -import thylacine.config.{ConjugateGradientConfig, CoordinateSlideConfig, HookeAndJeevesConfig, MdsConfig} +import thylacine.config.{ ConjugateGradientConfig, CoordinateSlideConfig, HookeAndJeevesConfig, MdsConfig } import thylacine.model.components.forwardmodel.NonLinearForwardModel -import thylacine.model.components.likelihood.{GaussianLikelihood, GaussianLinearLikelihood} +import thylacine.model.components.likelihood.{ GaussianLikelihood, GaussianLinearLikelihood } import thylacine.model.components.posterior._ -import thylacine.model.components.prior.{GaussianPrior, UniformPrior} +import thylacine.model.components.prior.{ GaussianPrior, UniformPrior } import cats.effect.IO @@ -30,23 +30,23 @@ object ComponentFixture { val fooPrior: GaussianPrior[IO] = GaussianPrior.fromConfidenceIntervals[IO]( - label = "foo", - values = Vector(1, 2), + label = "foo", + values = Vector(1, 2), confidenceIntervals = Vector(3, 5) ) val fooUniformPrior: UniformPrior[IO] = UniformPrior.fromBounds[IO]( - label = "fooniform", + label = "fooniform", maxBounds = Vector(5, 5), minBounds = Vector(-5, -5) ) - def fooLikeliHoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( - coefficients = Vector(Vector(1, 3), Vector(2, 4)), - measurements = Vector(7, 10), - uncertainties = Vector(0.01, 0.01), - prior = fooPrior, + def fooLikelihoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( + coefficients = Vector(Vector(1, 3), Vector(2, 4)), + measurements = Vector(7, 10), + uncertainties = Vector(0.01, 0.01), + prior = fooPrior, evalCacheDepth = None ) @@ -57,69 +57,69 @@ object ComponentFixture { private def fooNonAnalyticForwardModelF(implicit stm: STM[IO]): IO[NonLinearForwardModel[IO]] = NonLinearForwardModel.of[IO]( - evaluation = linearMapping, - differential = .0001, - domainDimensions = Map("foo" -> 2), - rangeDimension = 2, - evalCacheDepth = None, + evaluation = linearMapping, + differential = .0001, + domainDimensions = Map("foo" -> 2), + rangeDimension = 2, + evalCacheDepth = None, jacobianCacheDepth = None ) - def fooNonAnalyticLikeliHoodF(implicit stm: STM[IO]): IO[GaussianLikelihood[IO, NonLinearForwardModel[IO]]] = + def fooNonAnalyticLikelihoodF(implicit stm: STM[IO]): IO[GaussianLikelihood[IO, NonLinearForwardModel[IO]]] = for { nonAnalyticForwardModel <- fooNonAnalyticForwardModelF } yield GaussianLikelihood.from[IO, NonLinearForwardModel[IO]]( - forwardModel = nonAnalyticForwardModel, - measurements = Vector(7, 10), + forwardModel = nonAnalyticForwardModel, + measurements = Vector(7, 10), uncertainties = Vector(0.01, 0.01) ) - def fooTwoLikeliHoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( - coefficients = Vector(Vector(1, 3), Vector(2, 4)), - measurements = Vector(7, 10), - uncertainties = Vector(0.01, 0.01), - prior = fooUniformPrior, + def fooTwoLikelihoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( + coefficients = Vector(Vector(1, 3), Vector(2, 4)), + measurements = Vector(7, 10), + uncertainties = Vector(0.01, 0.01), + prior = fooUniformPrior, evalCacheDepth = None ) val barPrior: GaussianPrior[IO] = GaussianPrior.fromConfidenceIntervals[IO]( - label = "bar", - values = Vector(5), + label = "bar", + values = Vector(5), confidenceIntervals = Vector(.1) ) val barUniformPrior: UniformPrior[IO] = UniformPrior.fromBounds[IO]( - label = "barniform", + label = "barniform", maxBounds = Vector(10), minBounds = Vector(-10) ) - def barLikeliHoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( - coefficients = Vector(Vector(3), Vector(4)), - measurements = Vector(15, 20), - uncertainties = Vector(0.00001, 0.00001), - prior = barPrior, + def barLikelihoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( + coefficients = Vector(Vector(3), Vector(4)), + measurements = Vector(15, 20), + uncertainties = Vector(0.00001, 0.00001), + prior = barPrior, evalCacheDepth = None ) - def barTwoLikeliHoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( - coefficients = Vector(Vector(3), Vector(4)), - measurements = Vector(15, 20), - uncertainties = Vector(0.00001, 0.00001), - prior = barUniformPrior, + def barTwoLikelihoodF(implicit stm: STM[IO]): IO[GaussianLinearLikelihood[IO]] = GaussianLinearLikelihood.of[IO]( + coefficients = Vector(Vector(3), Vector(4)), + measurements = Vector(15, 20), + uncertainties = Vector(0.00001, 0.00001), + prior = barUniformPrior, evalCacheDepth = None ) def analyticPosteriorF(implicit stm: STM[IO]): IO[GaussianAnalyticPosterior[IO]] = for { - fooLikeliHood <- fooLikeliHoodF - barLikeliHood <- barLikeliHoodF + fooLikelihood <- fooLikelihoodF + barLikelihood <- barLikelihoodF posterior <- IO { GaussianAnalyticPosterior[IO]( - priors = Set(fooPrior, barPrior), - likelihoods = Set(fooLikeliHood, barLikeliHood) + priors = Set(fooPrior, barPrior), + likelihoods = Set(fooLikelihood, barLikelihood) ) } _ <- posterior.init @@ -127,18 +127,18 @@ object ComponentFixture { def unnormalisedPosteriorF(implicit stm: STM[IO]): IO[UnnormalisedPosterior[IO]] = for { - fooTwoLikeliHood <- fooTwoLikeliHoodF - barTwoLikeliHood <- barTwoLikeliHoodF + fooTwoLikelihood <- fooTwoLikelihoodF + barTwoLikelihood <- barTwoLikelihoodF posterior <- IO { UnnormalisedPosterior[IO]( - priors = Set(fooUniformPrior, barUniformPrior), - likelihoods = Set(fooTwoLikeliHood, barTwoLikeliHood) + priors = Set(fooUniformPrior, barUniformPrior), + likelihoods = Set(fooTwoLikelihood, barTwoLikelihood) ) } } yield posterior - val hookesAndJeevesConfig: HookeAndJeevesConfig = HookeAndJeevesConfig( - convergenceThreshold = 1e-7, + val hookeAndJeevesConfig: HookeAndJeevesConfig = HookeAndJeevesConfig( + convergenceThreshold = 1e-7, numberOfPriorSamplesToSetScale = Some(100) ) @@ -146,17 +146,17 @@ object ComponentFixture { for { unnormalisedPosterior <- unnormalisedPosteriorF posterior <- HookeAndJeevesOptimisedPosterior.of[IO]( - hookeAndJeevesConfig = hookesAndJeevesConfig, - posterior = unnormalisedPosterior, + hookeAndJeevesConfig = hookeAndJeevesConfig, + posterior = unnormalisedPosterior, iterationUpdateCallback = _ => IO.unit, - isConvergedCallback = _ => IO.unit + isConvergedCallback = _ => IO.unit ) } yield posterior val coordinateSlideConfig: CoordinateSlideConfig = CoordinateSlideConfig( - convergenceThreshold = 1e-7, - goldenSectionTolerance = 1e-10, - lineProbeExpansionFactor = 2.0, + convergenceThreshold = 1e-7, + goldenSectionTolerance = 1e-10, + lineProbeExpansionFactor = 2.0, numberOfPriorSamplesToSetScale = Some(100) ) @@ -164,18 +164,18 @@ object ComponentFixture { for { unnormalisedPosterior <- unnormalisedPosteriorF posterior <- CoordinateSlideOptimisedPosterior.of[IO]( - coordinateSlideConfig = coordinateSlideConfig, - posterior = unnormalisedPosterior, + coordinateSlideConfig = coordinateSlideConfig, + posterior = unnormalisedPosterior, iterationUpdateCallback = _ => IO.unit, - isConvergedCallback = _ => IO.unit + isConvergedCallback = _ => IO.unit ) } yield posterior val conjugateGradientConfig: ConjugateGradientConfig = ConjugateGradientConfig( - convergenceThreshold = 1e-20, - goldenSectionTolerance = 1e-10, + convergenceThreshold = 1e-20, + goldenSectionTolerance = 1e-10, lineProbeExpansionFactor = 2.0, - numberOfResultsToRetain = 100 + numberOfResultsToRetain = 100 ) def conjugateGradientOptimisedPosteriorF(implicit stm: STM[IO]): IO[ConjugateGradientOptimisedPosterior[IO]] = @@ -183,15 +183,15 @@ object ComponentFixture { unnormalisedPosterior <- unnormalisedPosteriorF } yield ConjugateGradientOptimisedPosterior.from[IO]( conjugateGradientConfig = conjugateGradientConfig, - posterior = unnormalisedPosterior, - newMaximumCallback = _ => IO.unit, - isConvergedCallback = _ => IO.unit + posterior = unnormalisedPosterior, + newMaximumCallback = _ => IO.unit, + isConvergedCallback = _ => IO.unit ) val mdsConfig: MdsConfig = MdsConfig( - convergenceThreshold = 1e-15, - expansionMultiplier = 2.0, - contractionMultiplier = .5, + convergenceThreshold = 1e-15, + expansionMultiplier = 2.0, + contractionMultiplier = .5, numberOfPriorSamplesToSetStartingPoint = Some(100) ) @@ -199,10 +199,10 @@ object ComponentFixture { for { unnormalisedPosterior <- unnormalisedPosteriorF posterior <- MdsOptimisedPosterior.of[IO]( - mdsConfig = mdsConfig, - posterior = unnormalisedPosterior, + mdsConfig = mdsConfig, + posterior = unnormalisedPosterior, iterationUpdateCallback = _ => IO.unit, - isConvergedCallback = _ => IO.unit + isConvergedCallback = _ => IO.unit ) } yield posterior } diff --git a/src/test/scala/thylacine/model/components/likelihood/GaussianLikelihoodSpec.scala b/src/test/scala/thylacine/model/components/likelihood/GaussianLikelihoodSpec.scala index 291027c..f2e178b 100644 --- a/src/test/scala/thylacine/model/components/likelihood/GaussianLikelihoodSpec.scala +++ b/src/test/scala/thylacine/model/components/likelihood/GaussianLikelihoodSpec.scala @@ -19,7 +19,7 @@ package thylacine.model.components.likelihood import bengal.stm.STM import thylacine.TestUtils.maxIndexVectorDiff -import thylacine.model.components.ComponentFixture.fooNonAnalyticLikeliHoodF +import thylacine.model.components.ComponentFixture.fooNonAnalyticLikelihoodF import thylacine.model.core.values.IndexedVectorCollection import cats.effect.IO @@ -33,7 +33,7 @@ class GaussianLikelihoodSpec extends AsyncFreeSpec with AsyncIOSpec with Matcher "generate the a zero gradient at the likelihood maximum" in { (for { implicit0(stm: STM[IO]) <- STM.runtime[IO] - likelihood <- fooNonAnalyticLikeliHoodF + likelihood <- fooNonAnalyticLikelihoodF result <- likelihood.logPdfGradientAt(IndexedVectorCollection(Map("foo" -> Vector(1d, 2d)))) } yield result.genericScalaRepresentation) .asserting(_ shouldBe Map("foo" -> Vector(0d, 0d))) @@ -42,7 +42,7 @@ class GaussianLikelihoodSpec extends AsyncFreeSpec with AsyncIOSpec with Matcher "generate the correct gradient of the logPdf for a given point" in { (for { implicit0(stm: STM[IO]) <- STM.runtime[IO] - likelihood <- fooNonAnalyticLikeliHoodF + likelihood <- fooNonAnalyticLikelihoodF result <- likelihood.logPdfGradientAt(IndexedVectorCollection(Map("foo" -> Vector(3d, 2d)))) } yield result.genericScalaRepresentation) .asserting(result => maxIndexVectorDiff(result, Map("foo" -> Vector(-4e5, -88e4))) shouldBe (0d +- 1e-4)) diff --git a/src/test/scala/thylacine/model/components/likelihood/GaussianLinearLikelihoodSpec.scala b/src/test/scala/thylacine/model/components/likelihood/GaussianLinearLikelihoodSpec.scala index 4d894be..567e6ab 100644 --- a/src/test/scala/thylacine/model/components/likelihood/GaussianLinearLikelihoodSpec.scala +++ b/src/test/scala/thylacine/model/components/likelihood/GaussianLinearLikelihoodSpec.scala @@ -18,7 +18,7 @@ package ai.entrolution package thylacine.model.components.likelihood import bengal.stm.STM -import thylacine.model.components.ComponentFixture.fooLikeliHoodF +import thylacine.model.components.ComponentFixture.fooLikelihoodF import thylacine.model.core.values.IndexedVectorCollection import cats.effect.IO @@ -32,7 +32,7 @@ class GaussianLinearLikelihoodSpec extends AsyncFreeSpec with AsyncIOSpec with M "generate the a zero gradient at the likelihood maximum" in { (for { implicit0(stm: STM[IO]) <- STM.runtime[IO] - likelihood <- fooLikeliHoodF + likelihood <- fooLikelihoodF result <- likelihood.logPdfGradientAt(IndexedVectorCollection(Map("foo" -> Vector(1d, 2d)))) } yield result.genericScalaRepresentation) .asserting(_ shouldBe Map("foo" -> Vector(0d, 0d))) @@ -41,7 +41,7 @@ class GaussianLinearLikelihoodSpec extends AsyncFreeSpec with AsyncIOSpec with M "generate the correct gradient of the logPdf for a given point" in { (for { implicit0(stm: STM[IO]) <- STM.runtime[IO] - likelihood <- fooLikeliHoodF + likelihood <- fooLikelihoodF result <- likelihood.logPdfGradientAt(IndexedVectorCollection(Map("foo" -> Vector(3d, 2d)))) } yield result.genericScalaRepresentation) .asserting(_ shouldBe Map("foo" -> Vector(-4e5, -88e4))) diff --git a/src/test/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosteriorSpec.scala b/src/test/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosteriorSpec.scala index 67630b6..e7357d0 100644 --- a/src/test/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosteriorSpec.scala +++ b/src/test/scala/thylacine/model/components/posterior/CoordinateSlideOptimisedPosteriorSpec.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.model.components.posterior import bengal.stm.STM -import ai.entrolution.thylacine.model.components.ComponentFixture.coordinateSlideOptimisedPosteriorF import thylacine.TestUtils._ +import thylacine.model.components.ComponentFixture.coordinateSlideOptimisedPosteriorF import cats.effect.IO import cats.effect.testing.scalatest.AsyncIOSpec diff --git a/src/test/scala/thylacine/model/components/posterior/GaussianAnalyticPosteriorSpec.scala b/src/test/scala/thylacine/model/components/posterior/GaussianAnalyticPosteriorSpec.scala index 33fc9c8..c69d563 100644 --- a/src/test/scala/thylacine/model/components/posterior/GaussianAnalyticPosteriorSpec.scala +++ b/src/test/scala/thylacine/model/components/posterior/GaussianAnalyticPosteriorSpec.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.model.components.posterior import bengal.stm.STM -import ai.entrolution.thylacine.model.components.ComponentFixture._ import thylacine.TestUtils._ +import thylacine.model.components.ComponentFixture._ import cats.effect.IO import cats.effect.testing.scalatest.AsyncIOSpec diff --git a/src/test/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosteriorSpec.scala b/src/test/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosteriorSpec.scala index 452e12e..1693241 100644 --- a/src/test/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosteriorSpec.scala +++ b/src/test/scala/thylacine/model/components/posterior/HookeAndJeevesOptimisedPosteriorSpec.scala @@ -18,8 +18,8 @@ package ai.entrolution package thylacine.model.components.posterior import bengal.stm.STM -import ai.entrolution.thylacine.model.components.ComponentFixture.hookeAndJeevesOptimisedPosteriorF import thylacine.TestUtils._ +import thylacine.model.components.ComponentFixture.hookeAndJeevesOptimisedPosteriorF import cats.effect.IO import cats.effect.testing.scalatest.AsyncIOSpec diff --git a/src/test/scala/thylacine/model/components/prior/GaussianPriorSpec.scala b/src/test/scala/thylacine/model/components/prior/GaussianPriorSpec.scala index 4d6033f..a2d22ec 100644 --- a/src/test/scala/thylacine/model/components/prior/GaussianPriorSpec.scala +++ b/src/test/scala/thylacine/model/components/prior/GaussianPriorSpec.scala @@ -25,8 +25,8 @@ class GaussianPriorSpec extends AnyFlatSpec with should.Matchers { "GaussianPrior" should "generate the correct mean and covariance for a 1D distribution" in { val fooPrior: GaussianPrior[IO] = GaussianPrior.fromConfidenceIntervals[IO]( - label = "foo", - values = Vector(2), + label = "foo", + values = Vector(2), confidenceIntervals = Vector(3) ) @@ -37,8 +37,8 @@ class GaussianPriorSpec extends AnyFlatSpec with should.Matchers { it should "generate the correct mean and covariance for a multi-variate distribution" in { val fooPrior: GaussianPrior[IO] = GaussianPrior.fromConfidenceIntervals[IO]( - label = "foo", - values = Vector(2, 3, 4), + label = "foo", + values = Vector(2, 3, 4), confidenceIntervals = Vector(4, 3, 2) ) @@ -49,8 +49,8 @@ class GaussianPriorSpec extends AnyFlatSpec with should.Matchers { it should "generate the correct gradient of the logPdf" in { val fooPrior: GaussianPrior[IO] = GaussianPrior.fromConfidenceIntervals[IO]( - label = "foo", - values = Vector(2, 3, 4), + label = "foo", + values = Vector(2, 3, 4), confidenceIntervals = Vector(4, 3, 2) )