Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Introduction to Integrator

guides/intro_to_integrator.livemd

Introduction to Integrator

Mix.install([
  {:integrator, github: "woodward/integrator"},
  {:kino_vega_lite, "~> 0.1"}
])

Numerical Integration in Elixir

Numerical integration is easy with Integrator. For example, let’s integrate the Van der Pol equation Integrator.SampleEqns.van_der_pol_fn for 20 seconds:

alias Integrator.DataCollector
alias Integrator.Point
alias Integrator.SampleEqns

t_initial = Nx.f64(0.0)
t_final = Nx.f64(20.0)
x_initial = Nx.f64([2.0, 0.0])

{:ok, pid} = DataCollector.start_link()
output_fn = &DataCollector.add_data(pid, &1)

opts = [type: :f64, output_fn: output_fn]
Integrator.integrate(&SampleEqns.van_der_pol_fn/2, t_initial, t_final, x_initial, opts)

Now you can plot the results via Kino:

alias VegaLite, as: VL

data =
  DataCollector.get_data(pid)
  |> Enum.map(&Point.to_number(&1))
  |> Enum.map(fn point ->
    %{t: t, x: x} = point

    [
      %{t: t, x: List.first(x), x_value: "x[0]"},
      %{t: t, x: List.last(x), x_value: "x[1]"}
    ]
  end)
  |> List.flatten()

chart =
  VL.new(width: 600, height: 400, title: "Solution of van der Pol Equation (μ = 1) with Dormand-Prince45")
  |> VL.mark(:line, point: true, tooltip: true)
  |> VL.encode_field(:x, "t", type: :quantitative)
  |> VL.encode_field(:y, "x", type: :quantitative)
  |> VL.encode_field(:color, "x_value", type: :nominal)
  |> VL.data_from_values(data)
  |> Kino.VegaLite.new()
  |> Kino.render()

Compare this with the plot from the Matlab ode45 manual page:

van der pol