View Source Introduction to Integrator

Mix.install(
  [
    {:integrator, path: "/Users/Greg/Dynamics/Development/integrator/", env: :dev},
    {:kino_vega_lite, "~> 0.1.7"}
  ],
  # config_path: Path.join("/Users/Greg/Dynamics/Development/integrator/", "config/config.exs"),
  lockfile: Path.join("/Users/Greg/Dynamics/Development/integrator/", "mix.lock")
)

numerical-integration-in-elixir

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.SampleEqns

t_initial = 0.0
t_final = 20.0
x_initial = Nx.tensor([2.0, 0.0])
solution = Integrator.integrate(&SampleEqns.van_der_pol_fn/2, [t_initial, t_final], x_initial)

Now you can plot the results via Kino:

alias VegaLite, as: VL

data =
  Enum.zip(solution.output_t, solution.output_x)
  |> Enum.map(fn {t, x} ->
    [
      %{t: Nx.to_number(t), x: Nx.to_number(x[0]), x_value: "x[0]"},
      %{t: Nx.to_number(t), x: Nx.to_number(x[1]), x_value: "x[1]"}
    ]
  end)
  |> List.flatten()

chart =
  VL.new(width: 600, height: 400, title: "Solution of van der Pol Equation (μ = 1) with ode45")
  |> 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