Search code examples
netlogoroulette-wheel-selection

Roulette Wheel Selection in Netlogo using Agent Variables, not Constants


I hope this is a simple solution, but I'm having a difficult time with it.


Problem:

I would like to weight the probability of something occurring by an variable not a constant

Setup

  1. My agent is a farm.

  2. Farms own four variables that represent the number of cows, goats, pigs, and sheep on it.

  3. When a farm wants to remove an animal, I'd like the likelihood to remove a member of a particular species to be directly proportional to quantity of each species on the farm (i.e. if there are 7 goats, 2 cows, and 1 pig, there is a 70% probability of taking a goat and a zero percent probability of taking a sheep)

I have found formula like this for when you know the exact numerical weight that each value will have:

to-report random-weighted [values weights]        
  let selector (random-float sum weights) 
  let running-sum 0 
    (foreach values weights [ 
      set running-sum (running-sum + ?2)    ; Random-Weighted Created by NickBenn
      if (running-sum > selector) [ 
        report ?1 
      ] 
    ]) 
end 

and the methods described in the rnd extension. But both of these throw the "expected a constant" error when i put "Cow" in instead of a constant.

Something like:

 to example1
  let values ["Cow" "Sheep" "Goat" "Pig"]
  let probabilities [2 0 7 1]
  let indices n-values length values [ ? ]               ; Made by Nicolas Payette
  let index rnd:weighted-one-of indices [ item ? probabilities ]
  let loca item index values
end

works well, but if I were to replace it with:

to example1
      let values ["Cow" "Sheep" "Goat" "Pig"]
      let probabilities [Num-Cows Num-Sheep Num-Goats Num-Pigs]
      let indices n-values length values [ ? ]               ; Made by Nicolas Payette
      let index rnd:weighted-one-of indices [ item ? probabilities ]
      let loca item index values
    end

it fails.


Solution

  • Alan is right: you need to use the list primitive (as opposed to just brackets) when you want to construct a list from anything else than constants.

    I would add two things to that:

    You could also have something like:

    to example1
      let values ["Cow" "Sheep" "Goat" "Pig"]
      let probabilities (list Num-Cows Num-Sheep Num-Goats Num-Pigs)
      let loca first rnd:weighted-one-of-list (map list values probabilities) last
    end
    

    This may be a bit trickier to understand, but here is the gist of it:

    • The (map list values probabilities) expression takes both your values list and your probabilities list and "zips" them together using the list primitive, resulting in a list of pairs: [["Cow" 2] ["Sheep" 0] ["Goat" 7] ["Pig" 1]].

    • We pass the last reporter to the rnd:weighted-one-of-list primitive to tell it that the last (i.e., second) item of each of these pairs should be used as the probability.

    • Since rnd:weighted-one-of-list operates on a list of pairs, the item it returns will be a pair (e.g., ["Goat" 7]). We are only interested in the first item of the pair, so we extract it with the first reporter.

    Note that we use the NetLogo's concise syntax for tasks when passing list as an argument to map and last as an argument to rnd:weighted-n-of. You could replace list with [ (list ?1 ?2) ] and last with [ last ? ], but it would be uglier.