Header menu logo bristlecone

ScriptNotebook

Defining an Estimation Engine

What is an estimation engine?

An estimation engine in Bristlecone defines how parameters of models are estimated from data. It's key elements are:

A single engine should typically be used across all model hypotheses in a study so that comparisons are fair and reproducible.

Bristlecone provides defaults for most components, but all parts are configurable. Below, the main configurable parts are outlined. Please check the API docs for further details on each option. Estimation engines are composed using functions within the Bristlecone module, as below.

Creating an engine

Bristlecone supports two ways of evolving a model through time:

Discrete-time engine

The model is evaluated once per time step. This is appropriate for any system where the process is naturally discrete. Call Bristlecone.mkDiscrete () for this engine type.

Continuous-time engine

The model is defined as a system of ordinary differential equations (ODEs) and must be integrated numerically. Call Bristlecone.mkContinuous () for this engine type.

ODE model systems are appropriate for processes that are most naturally represented as rates. Bristlecone will solve continuous-time models dependent on the time-steps of the datasets provided, whether those are on fixed time-stepping (e.g. annual ring widths) or variable time-stepping (e.g. sediment core data).

Bristlecone includes a classical Runge-Kutta integrator as the default for continuous-time models; see integration methods for more details.

Example:

let engine: EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.year,1> =
    Bristlecone.mkContinuous ()

Customising an engine

All engine components can be modified using the builder functions in the Bristlecone module.

Choosing an optimisation method

Use:

Bristlecone.withCustomOptimisation

to supply any optimiser with the correct type signature.

The default optimisation method is 'Bristlecone-style annealing'; for details of this method and others see optimisation methods for more details.

Convenience functions include:

Example:

engine |> Bristlecone.withCustomOptimisation Optimisation.MonteCarlo.``Adaptive-Metropolis-within Gibbs``

Conditioning the first time-point

The data may be conditioned such that the value of the data in the first point in the time-series is not lost. There are three in-built conditioning modes:

Example:

engine |> Bristlecone.withConditioning Conditioning.RepeatFirstDataPoint

Connecting model time to data time

The temporal resolution of the model must be linked to the temporal system of the data. Bristlecone's models and engines are strongly typed such that conformity of time systems is enforced by the compiler.

To link your model and data time, the Bristlecone builders contains a Bristlecone.withTimeConversion function. To illustrate, a model may be defined at annual resolution, with the data defined in radiocarbon dates. The engine just requires a simple conversion function so that it understands how radiocarbon dates relate to years. In the Bristlecone.Time.DateMode.Conversion module, each built-in time system (e.g. radiocarbon, annual, .NET calendars) has conversion functions to relate these to the basic temporal resolutions of day, month, and year.

For example, the below conversion makes an estimation engine that can directly consume a model that is defined in monthly resolution, and requires data defined in standard .NET DateTime times.

let forMonthlyModel: EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.month,1> =
    Bristlecone.mkDiscrete ()
    |> Bristlecone.withTimeConversion Time.DateMode.Conversion.CalendarDates.toMonths

You can see that the estimation engine has typed units of Time.month as the model resolution, and System.DateTime / System.TimeSpan as the date / timespan units.

Reproducibility

You may set a seed to ensure that any particular analysis is reproducable by calling Bristlecone.withSeed. Note that if a multi-threaded orchestrator is used, this will not guarantee reproducibility, as the orchestrator may run work packages in different orders depending on race conditions.

Logging

Use:

Bristlecone.withOutput

to set a custom logger. The default is a simple console logger that outputs a trace every 1000 iterations. More built-in loggers are defined in the Bristlecone.Logging module, such as a multi-threaded table logger. In addition, the Bristlecone.Charts.R package defines a graphical parameter trace output to see real-time traces during optimisation.

Example: continuous time with Nelder-Mead optimisation

let engine2: EstimationEngine.EstimationEngine<Time.DatingMethods.Annual,int<Time.year>,Time.year,1> =
    Bristlecone.mkContinuous ()
    |> Bristlecone.withCustomOptimisation (Optimisation.Amoeba.swarm 10 20 Optimisation.Amoeba.Solver.Default)
    |> Bristlecone.withConditioning Conditioning.NoConditioning
    |> Bristlecone.withSeed 1500
    |> Bristlecone.withTimeConversion Time.DateMode.Conversion.Annual.toYears

Here, an estimation engine is made that uses a swarm-based Nelder-Mead (amoeba) optimisation routine to fit annual time-series data to a model defined in terms of years.

Multiple items
module Bristlecone from Bristlecone
<namespacedoc><summary>The core library of Bristlecone, containing model-fitting functions.</summary></namespacedoc>
Main functionality of Bristlecone, including functions to scaffold `ModelSystem`s and for model-fitting (tests and real fits).


--------------------
namespace Bristlecone
val engine: EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.year,1>
module EstimationEngine from Bristlecone
<summary> The estimation engine represents the method used to calculate equations and optimise a likelihood function. The whole estimation engine is tensor-based internally, but may take float-based equations as a legacy option. </summary>
type EstimationEngine<'date,'timespan,'modelTimeUnit,'state> = { TimeHandling: TimeMode OptimiseWith: Optimiser Conditioning: Conditioning<'state> LogTo: WriteOut ToModelTime: ResolutionToModelUnits<'date,'timespan,'modelTimeUnit> InterpolationGlobal: InterpolationMode InterpolationPerVariable: CodedMap<InterpolationMode> Random: Random }
namespace System
Multiple items
[<Struct>] type DateTime = new: date: DateOnly * time: TimeOnly -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>

--------------------
System.DateTime ()
   (+0 other overloads)
System.DateTime(ticks: int64) : System.DateTime
   (+0 other overloads)
System.DateTime(date: System.DateOnly, time: System.TimeOnly) : System.DateTime
   (+0 other overloads)
System.DateTime(ticks: int64, kind: System.DateTimeKind) : System.DateTime
   (+0 other overloads)
System.DateTime(date: System.DateOnly, time: System.TimeOnly, kind: System.DateTimeKind) : System.DateTime
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int) : System.DateTime
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, calendar: System.Globalization.Calendar) : System.DateTime
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : System.DateTime
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: System.DateTimeKind) : System.DateTime
   (+0 other overloads)
System.DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: System.Globalization.Calendar) : System.DateTime
   (+0 other overloads)
Multiple items
[<Struct>] type TimeSpan = new: hours: int * minutes: int * seconds: int -> unit + 4 overloads member Add: ts: TimeSpan -> TimeSpan member CompareTo: value: obj -> int + 1 overload member Divide: divisor: float -> TimeSpan + 1 overload member Duration: unit -> TimeSpan member Equals: value: obj -> bool + 2 overloads member GetHashCode: unit -> int member Multiply: factor: float -> TimeSpan member Negate: unit -> TimeSpan member Subtract: ts: TimeSpan -> TimeSpan ...
<summary>Represents a time interval.</summary>

--------------------
System.TimeSpan ()
System.TimeSpan(ticks: int64) : System.TimeSpan
System.TimeSpan(hours: int, minutes: int, seconds: int) : System.TimeSpan
System.TimeSpan(days: int, hours: int, minutes: int, seconds: int) : System.TimeSpan
System.TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : System.TimeSpan
System.TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int, microseconds: int) : System.TimeSpan
module Time from Bristlecone
[<Measure>] type year
val mkContinuous: unit -> EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.year,'u>
<summary>A basic estimation engine for ordinary differential equations, using a Nelder-Mead optimiser.</summary>
val withCustomOptimisation: optim: EstimationEngine.Optimisation.Optimiser -> engine: EstimationEngine.EstimationEngine<'a,'b,'u,'v> -> EstimationEngine.EstimationEngine<'a,'b,'u,'v>
namespace Bristlecone.Optimisation
module MonteCarlo from Bristlecone.Optimisation
<summary> A module containing Monte Carlo Markov Chain (MCMC) methods for optimisation. An introduction to MCMC approaches is provided by [Reali, Priami, and Marchetti (2017)](https://doi.org/10.3389/fams.2017.00006) </summary>
val withConditioning: c: Conditioning.Conditioning<'u> -> engine: EstimationEngine.EstimationEngine<'a,'b,'v,'u> -> EstimationEngine.EstimationEngine<'a,'b,'v,'u>
<summary> Choose how the start point is chosen when solving the model system </summary>
module Conditioning from Bristlecone
union case Conditioning.Conditioning.RepeatFirstDataPoint: Conditioning.Conditioning<'u>
val forMonthlyModel: EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.month,1>
[<Measure>] type month
val mkDiscrete: unit -> EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,Time.year,'u>
<summary>A basic estimation engine for discrete-time equations, using a Nelder-Mead optimiser.</summary>
val withTimeConversion<'d,'d2,'timespan,'timespan2,'modelTimeUnit,'o1,'o2,'u> : fn: Time.DateMode.Conversion.ResolutionToModelUnits<'d2,'timespan2,'modelTimeUnit> -> engine: EstimationEngine.EstimationEngine<'d,'o1,'o2,'u> -> EstimationEngine.EstimationEngine<'d2,'timespan2,'modelTimeUnit,'u>
module DateMode from Bristlecone.Time
module Conversion from Bristlecone.Time.DateMode
<summary> Conversion functions that translate from one time unit into another. These functions are intended primarily for use in estimation engines when translating from data time to model time. </summary>
module CalendarDates from Bristlecone.Time.DateMode.Conversion
val toMonths: from: Time.DateMode.Conversion.ConvertFrom<System.DateTime,System.TimeSpan> -> float<Time.month>
val withOutput: out: EstimationEngine.WriteOut -> engine: EstimationEngine.EstimationEngine<'a,'b,'u,'v> -> EstimationEngine.EstimationEngine<'a,'b,'u,'v>
<summary>Substitute a specific logger into</summary>
<param name="out"></param>
<param name="engine"></param>
<typeparam name="'a"></typeparam>
<typeparam name="'b"></typeparam>
<returns></returns>
val engine2: EstimationEngine.EstimationEngine<Time.DatingMethods.Annual,int<Time.year>,Time.year,1>
module DatingMethods from Bristlecone.Time
<summary> Contains types representing common dating methods in long term data analysis. </summary>
Multiple items
union case Time.DatingMethods.Annual.Annual: int<Time.year> -> Time.DatingMethods.Annual

--------------------
type Annual = | Annual of int<year> static member (+) : e1: Annual * e2: int<year> -> Annual static member (-) : e1: Annual * e2: Annual -> int<year> static member AddYears: date: Annual -> years: int<year> -> Annual static member FractionalDifference: isSigned: bool -> d1: Annual -> d2: Annual -> TimeDifference<int<year>> static member TotalYearsElapsed: d1: Annual -> d2: Annual -> int<year> static member Unwrap: Annual -> int<year> member Value: int<year> with get
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
module Amoeba from Bristlecone.Optimisation
<summary> Nelder Mead implementation Adapted from original at: https://github.com/mathias-brandewinder/Amoeba </summary>
val swarm: levels: int -> amoebaAtLevel: int -> settings: Optimisation.Amoeba.Solver.Settings -> EstimationEngine.Optimisation.Optimiser
<summary> Optimisation heuristic that creates a swarm of amoeba (Nelder-Mead) solvers. The swarm proceeds for `numberOfLevels` levels, constraining the starting bounds at each level to the 80th percentile of the current set of best likelihoods. </summary>
module Solver from Bristlecone.Optimisation.Amoeba
<summary> Nelder–Mead downhill simplex </summary>
val Default: Optimisation.Amoeba.Solver.Settings
union case Conditioning.Conditioning.NoConditioning: Conditioning.Conditioning<'u>
val withSeed: seed: int -> engine: EstimationEngine.EstimationEngine<'a,'b,'u,'v> -> EstimationEngine.EstimationEngine<'a,'b,'u,'v>
<summary> Use a mersenne twister random number generator with a specific seed. </summary>
module Annual from Bristlecone.Time.DateMode.Conversion
val toYears: from: Time.DateMode.Conversion.ConvertFrom<Time.DatingMethods.Annual,int<Time.year>> -> float<Time.year>

Type something to start searching.