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

PSO example

notebooks/pso_example.livemd

PSO example

Mix.install([
  {:bia, github: "matiascr/bia"},
  {:kino, "~> 0.10.0"},
  {:kino_vega_lite, "~> 0.1.10"},
  {:vega_lite, "~> 0.1.8"},
  {:explorer, "~> 0.7.1"}
])

Setup

We start by defining aliases for the libraries we’ll use for visualizing the PSO

alias VegaLite, as: Vl
require Kino.VegaLite, as: KVl

Now, we define a Visualizer module that contains a callback

defmodule Visualizer do
  def callback(args) do
    widget = args[:opts][:widget]

    widget
    |> KVl.clear()

    widget
    |> KVl.push_many(
      Enum.map(args[:particles], &GenServer.call(&1, :get_position))
      |> Nx.stack()
      |> Nx.to_list()
      |> Enum.map(fn [x, y] -> %{x: x, y: y} end)
    )

    Process.sleep(250)
  end
end

and create it with the parameters we want for the graph

bound_up = 10.0
bound_down = -10.0

widget = fn ->
  Vl.new(width: 400, height: 400)
  |> Vl.mark(:circle)
  |> Vl.encode_field(:x, "x", type: :quantitative, scale: [domain: [bound_down, bound_up]])
  |> Vl.encode_field(:y, "y", type: :quantitative, scale: [domain: [bound_down, bound_up]])
  |> KVl.new()
  |> Kino.render()
end

We add some functions for the PSO to optimize

defmodule OptimizationFunctions do
  import Nx.Defn

  defn unimodal(t) do
    0.26 * (t[0] ** 2 + t[1] ** 2) - 0.48 * t[0] * t[1]
  end

  defn inverted_pyramid(t) do
    Nx.abs(t[0]) + Nx.abs(t[1])
  end
end

Running the PSO

Finally, we run the PSO algorithm.

In the first step, we add the parameters of the heuristic, along with other optional parameters for in the callback and widget parameters. These will allow us to inject actions that will be performed after each iteration of the PSO. In this case, we have created a graph, and it will be updated each iteration with the position of the particles.

PSO.new(
  population_size: 50,
  num_iterations: 20,
  bound_up: bound_up,
  bound_down: bound_down,
  inertia: 0.1,
  coeff_p: 2.0,
  coeff_g: 1.0,
  callback: &Visualizer.callback/1,
  widget: widget.(),
  fun: &OptimizationFunctions.inverted_pyramid/1
)
|> PSO.run()