WeightedRandom.Backend behaviour (weighted_random v1.0.0-rc.2)

Copy Markdown View Source

WeightedRandom.Backend offers a contract to all who implement it:

  1. The main WeightedRandom package presents a novel interface for generating a list of probabilities or weights
  2. The custom Backend module decides what to do with those probabilities once they are generated.

Using Backends

To use a different backend, you can either pass it in as an opt (see function docs), or you can put it in your config file:

config :weighted_random,
  backend: Your.Backend.Module

Developing a new backend

To create your own backend, copy and change an existing one (like WeightedRandom.Backend.WalkerAlias or WeightedRandom.Backend.Linear).

Your module must implement the callbacks listed below: preprocess/2, take/2, and optionally options/0.

The simplest backend might look like this.

defmodule My.Backend do
  use WeightedRandom.Backend

  defstruct [:list]

  @impl true
  def preprocess(probabilities, _opts) do
    struct(__MODULE__, %{list: probabilities})
  end

  @impl true
  def take(%__MODULE__{list: li}, count) do
    for _ <- 1..count do
      Enum.random(li)
    end
  end 

end

This backend doesn't actually apply probability, it is just a glorified Enum.rand(list) for now. But you can use it in any of the main functions that take an opt of backend

WeightedRandom.rand(0..10, weights, [backend: My.Backend])
=> 3

Summary

Callbacks

A keyword list of options that the backend requires from the WeightedRandom library.

input is a list of floats.

Given the struct returned by preprocess/2, return a list of random indices equal to count.

Types

backend_opts()

@type backend_opts() :: [{:probability_type, probability_type()}]

index()

@type index() :: integer()

indices()

@type indices() :: [index()]

opts()

@type opts() :: keyword()

percentage()

@type percentage() :: float()

probabilities()

@type probabilities() :: [percentage()]

probability_type()

@type probability_type() :: :weights | :probabilities

resolved_weights()

@type resolved_weights() :: [float()]

t()

@type t() :: %WeightedRandom.Backend{
  backend: atom(),
  outcomes: list(),
  table: struct()
}

table()

@type table() :: struct()

Callbacks

options()

(optional)
@callback options() :: backend_opts()

A keyword list of options that the backend requires from the WeightedRandom library.

Currently the only option is :probability_type, which influences the input argument of preprocess/2. When set to :probabilities (default), the input will be a list of floats that roughly sum to 1.0. When set to :weights, the input will be a list of floats.

preprocess(input, opts)

@callback preprocess(input :: probabilities() | resolved_weights(), opts :: opts()) ::
  table()

input is a list of floats.

This function must return some kind of struct that will later be passed into take/2.

For details about opts, see WeightedRandom.preprocess and WeightedRandom.preprocess_p.

take(table, count)

@callback take(table :: struct(), count :: pos_integer()) :: indices :: indices()

Given the struct returned by preprocess/2, return a list of random indices equal to count.