Header menu logo bristlecone

ScriptNotebook

Bristlecone

Bristlecone is a library for easily composing theoretical models into larger systems, and subsequently conducting model-fitting and model-selection analyses on time-series data. Although originally designed for the investigation of non-linear dynamics within ecological and environmental sciences, the library can be used across finance, econometrics and other fields that apply non-linear modelling techniques.

Quick Start

Bristlecone is an F# .NET library. You can easily get started by installing the latest .NET SDK. You may then simply use the Bristlecone package in a script, application, or library. The nuget package is available here.

To use in an F# script

img/example.png

Example

This example demonstrates the layout of a model when defined in Bristlecone.

open Bristlecone // Opens Bristlecone core library and estimation engine
open Bristlecone.Language // Open the language for writing Bristlecone models
open Bristlecone.Time // Opens time units and time manipulation

[<Measure>] type millimetre
[<Measure>] type celsius

let hypothesis =

    let radius = state<millimetre> "radius"
    let T = state<celsius> "temperature"

    // Parameters with units
    let r_max = parameter "η" NoConstraints 0.01<millimetre/day> 1.0<millimetre/day>
    let T_opt = parameter "η" NoConstraints 5.0<celsius> 20.0<celsius>
    let σ = parameter "σ_radius" Positive 0.1<millimetre> 0.2<millimetre>

    let ``dR/dt`` =  
      let tempDiff = Environment T - P T_opt
      let tempResponse = Exponential ( - (tempDiff * tempDiff) / Constant 20.0<celsius ^ 2> )
      P r_max * tempResponse

    let NLL = ModelLibrary.NegLogLikelihood.Normal (Require.state radius) σ

    Model.empty
    |> Model.addRateEquation radius ``dR/dt``
    |> Model.estimateParameter r_max
    |> Model.estimateParameter T_opt
    |> Model.estimateParameter σ
    |> Model.useLikelihoodFunction NLL
    |> Model.compile

// Given some data (loaded using Bristlecone functions, FSharp.Data, etc.)...
fun data ->
  let engine = Bristlecone.mkContinuous () |> Bristlecone.forDailyModel
  let endCond = Optimisation.EndConditions.atIteration 10000<iteration>
  Bristlecone.fit engine endCond data hypothesis

In this example, we define a simple temperature‑driven growth model using Bristlecone’s modelling language. The state variable radius represents stem radius in millimetres, and the environmental driver T represents temperature in degrees Celsius. All quantities carry F# units of measure, and Bristlecone ensures that every expression in the model is dimensionally consistent.

The growth rate is defined as a Gaussian response to temperature. Because radius has units of millimetres and time is measured in days, the rate equation must produce a value in mm/day. This is enforced by the compiler, and cannot be run without being correct.

To complete the model system, we add a likelihood function for observed stem radius that assumes Gaussian observation error (with standard deviation σ_radius).

To confront the model with data, we create an EstimationEngine. An engine is a formal description of the method that will be used for model-fitting. Engines are built using the F# forward‑pipe (|>) operator, allowing composition of numerical integration methods, time‑conversion rules, conditioning behaviour, and optimisation strategies in a readable, declarative style.

To apply the model to data, the Bristlecone.fit function can be called with an end condition for the optimisation routine chosen in the given engine.

This pattern - define a model, setup an engine, and call fit - is the core workflow in Bristlecone, whether you are fitting simple growth curves or complex multi‑state ecological models.

Samples & documentation

An API reference is automatically generated from XML comments in the library implementation.

In addition, this documentation includes step-by-step example analyses. Each analysis may be downloaded as an F# script or Jupyter notebook using the buttons at the top of each example page.

Contributing and copyright

The project is hosted on GitHub where you can report issues, fork the project and submit pull requests.

The library is available under an MIT license, which allows modification and redistribution for both commercial and non-commercial purposes. For more information see the License file in the GitHub repository.

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
module Language from Bristlecone
<summary> An F# Domain Specific Language (DSL) for scripting with Bristlecone. </summary>
module Time from Bristlecone
Multiple items
val Measure: sId: MeasureId<'u> -> ModelExpression<'u>

--------------------
type MeasureAttribute = inherit Attribute new: unit -> MeasureAttribute

--------------------
new: unit -> MeasureAttribute
[<Measure>] type millimetre
[<Measure>] type celsius
val hypothesis: ModelSystem.ModelSystem<day>
val radius: StateId<millimetre>
val state: name: string -> StateId<'u>
val T: StateId<celsius>
val r_max: IncludedParameter<millimetre/day>
Multiple items
val parameter: code: string -> con: Parameter.Constraint<'u> -> lower: float<'u> -> upper: float<'u> -> IncludedParameter<'u>
<summary> Define an estimatable parameter for a Bristlecone model. </summary>

--------------------
[<Measure>] type parameter
val NoConstraints: Parameter.Constraint<'u>
[<Measure>] type day
val T_opt: IncludedParameter<celsius>
val σ: IncludedParameter<millimetre>
val Positive: Parameter.Constraint<'u>
val tempDiff: ModelExpression<celsius>
val Environment: sid: StateId<'u> -> ModelExpression<'u>
val P: name: IncludedParameter<'u> -> ModelExpression<'u>
val tempResponse: ModelExpression<1>
val Exponential: expr: ModelExpression<1> -> ModelExpression<1>
val Constant: x: float<'u> -> ModelExpression<'u>
val NLL: ModelSystem.Likelihood<ModelSystem.state>
namespace Bristlecone.ModelLibrary
module NegLogLikelihood from Bristlecone.ModelLibrary
<summary>Negative log likelihood (-logL) functions to represent a variety of distributions and data types.</summary>
<namespacedoc><summary>Pre-built model parts for use in Bristlecone</summary></namespacedoc>
val Normal: obs: Require.ObsForLikelihood<'u> -> sigma: IncludedParameter<'u> -> ModelSystem.Likelihood<'v>
module Require from Bristlecone.Language
val state: s: StateId<'u> -> Require.ObsForLikelihood<'u>
module Model from Bristlecone.Language
<summary> Terms for scaffolding a model system for use with Bristlecone. </summary>
val empty<'time> : ModelBuilder.ModelBuilder<'time>
val addRateEquation: name: StateId<'state> -> expr: ModelExpression<'state/'time> -> mb: ModelBuilder.ModelBuilder<'time> -> ModelBuilder.ModelBuilder<'time>
val estimateParameter: p: IncludedParameter<'u> -> builder: ModelBuilder.ModelBuilder<'time> -> ModelBuilder.ModelBuilder<'time>
val useLikelihoodFunction: likelihoodFn: ModelSystem.Likelihood<ModelSystem.state> -> builder: ModelBuilder.ModelBuilder<'u> -> ModelBuilder.ModelBuilder<'u>
val compile: (ModelBuilder.ModelBuilder<'u> -> ModelSystem.ModelSystem<'u>)
val data: CodedMap<TimeSeries<float<'u>,System.DateTime,'a,System.TimeSpan>>
val engine: EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,day,'u>
val mkContinuous: unit -> EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,year,'u>
<summary>A basic estimation engine for ordinary differential equations, using a Nelder-Mead optimiser.</summary>
val forDailyModel: engine: EstimationEngine.EstimationEngine<'a,'b,'u,'v> -> EstimationEngine.EstimationEngine<System.DateTime,System.TimeSpan,day,'v>
val endCond: EstimationEngine.EndCondition
namespace Bristlecone.Optimisation
module EndConditions from Bristlecone.Optimisation
<summary>Composable end conditions to specify when optimisation routines should end.</summary>
val atIteration: iteration: int<iteration> -> EstimationEngine.Solution list -> currentIteration: int<iteration> -> EstimationEngine.OptimStopReason
<summary> End on or after a minimum number of iterations. </summary>
[<Measure>] type iteration
val fit: engine: EstimationEngine.EstimationEngine<'a,'b,'modelTimeUnit,'u> -> endCondition: EstimationEngine.EndCondition -> timeSeriesData: CodedMap<TimeSeries<float<'u>,'a,'c,'b>> -> model: ModelSystem.ModelSystem<'modelTimeUnit> -> ModelSystem.EstimationResult<'a,'c,'b> (requires comparison and comparison)
<summary>Fit a time-series model to data.</summary>
<param name="engine">An estimation engine configured and tested for the given model.</param>
<param name="endCondition">The condition at which optimisation should cease.</param>
<param name="timeSeriesData">Time-series dataset that contains a series for each equation in the model system.</param>
<param name="model">A model system of equations, likelihood function, estimatible parameters, and optional measures.</param>
<returns>The result of the model-fitting procedure. If an error occurs, throws an exception.</returns>

Type something to start searching.