Evolution and the Ellsberg Paradox

Dr J McKenzie Alexander

Department of Philosophy, Logic and Scientific Method
London School of Economics and Political Science

Description

The Ellsberg paradox concerns decision making under uncertainty. An urn is filled with 90 balls, 30 of which are red and the remaining 60 an unspecified mix of yellow and black. (None of the 60 balls may be yellow, all of the 60 balls may be yellow, or k balls may be yellow with the remaining 60-k being black.)

Consider, now, the following two gambles:

Option A Option B
Gamble 1: Win $100 if you draw red. Win $100 if you draw black.
Option C Option D
Gamble 2: Win $100 if you draw red or yellow. Win $100 if you draw black or yellow.

Most people, when presented with Gamble 1, prefer option A over option B. And again, most people, when presented with Gamble 2, prefer option D over option C. Yet this particular pattern of choice behaviour violates the Sure-Thing Principle and thus constitutes an irrational set of preferences according to the standard theory of expected utility.

One explanation for this pattern of choice behaviour is that people have an aversion to uncertainty. Although we know that option A has a 1/3rd chance of winning, and option D has a 2/3rds chance of winning, the chances attached to options B and C depend upon the composition of the urn, which is unknown. Yet even if it is true that individuals have an aversion to uncertainty, the question remains - why?

Consider the following evolutionary model: individuals are “programmed” with a type AC, AD, BC or BD. The type of an individual specifies which option they will choose if presented with either gamble 1 or gamble 2. At the start of each generation, Nature fills the urn with a randomly selected mix of yellow and black balls (selected using a uniform distribution), and decides which of gambles 1 or 2 to present to each person in the population.

Each person then draws a ball from the urn (with replacement, so that the composition of the urn does not change). If a person wins, then they have one offspring, which is of the same type as themselves and the parent continues to live. If a person does not win, they have no offspring and die. (Note that this is equivalent to having 2 offspring if you win, 0 if you lose, with everyone from the parent generation dying at the end of the current round of play. Although the second formulation is an easier way to think of the model, it is implemented in the actual program as the former.) We continue this process until the population converges to a single type, or until everyone dies off.

If we run repeated trials, what we find is that not only does the type AD tend to have the greatest fecundity (total number of offspring, tallied over all trials) but that more trials converge to AD than any of the other three types. Natural selection may select for uncertainly aversion.

How to use it

This model differs slightly from the usual NetLogo conventions, due to the desire to display aggregate data from a number of different trials. Click on Initialize to completely restart the simulation by putting it back into a clean state. (All information concerning the “Overall Fecundity” and “Convergence Data” plots are cleared.)

What do the plots show?

Current Type Distribution: The frequency of each of the four types in the current population. This plot is updated at the end of each generation (so whenever the step button is pressed or after each tick when the go button is active).

Overall Fecundity: The total number of offspring produced by each type, over all runs, since the last time the Initialize button was pressed. For example, suppose that after initializing the model you click go and let the model run until it stops. Suppose you press Reset to start the model in a different initial state, and click go again. And suppose you do this three more times. At this point, the “Overall Fecundity” plot shows the total number of offspring produced by each type over all 5 runs of the model.

Convergence data: The number of runs which have ended up in a pure state of the indicated type (i.e., all-AC, all-AD, all-BC, all-BD, or all dead).

Once the Initialize button has been pressed, you will see that the plots for “Overall Fecundity” and “Convergence data” have been reset. You will also see that initial-population-size many individuals have been created, with types specified according to a randomly selected probability distribution over the four types.

Although the individuals are randomly positioned in the world, the spatial positioning of them does not really matter because each person is playing a game against Nature, rather than a game against other people. The reason why positioning people at random is useful, though, is because as the generations tick along, this allows us to visualise the fecundity of individuals over time: whenever someone has an offspring, the offspring is positioned right around the area where the parent is (with a bit of distance to allow us to visually distinguish the two).

Pressing the step button moves the model forward one generation. The plots of “Overall Fecundity” and “Current Type Distribution” are updated as required. The monitors under the label “Current urn composition” show the state of the urn in the past generation, and the monitor under “Gamble selected by Nature” shows the gamble presented to each person in the population.

Pressing the go button steps the model forward until either it converges to a state where everyone is of the same type, or else everyone dies out. At this point, the “Convergence Data” plot is updated accordingly.

At this point, pressing Reset will start a new trial, creating a new population of initial-population-size individuals while keeping all of the information about the overall fecundity and convergence data.

Repeated trials can be run in a row by clicking the Run repeated trials button. When clicked upon, exactly number-of-trials trials will be run, with the fecundity and convergence data plots updated.

If you want to vary the capacity of the urn from the standard Ellsberg configuration, you can do so by specifying different values for number-of-red, number-of-rest and max-yellow. These values are used to select a random configuration for the urn at the start of each generation.

System Requirements.

The applet requires Java 5 or higher. Java must be enabled in your browser settings. Mac users must have Mac OS X 10.4 or higher. Windows and Linux users may obtain the latest Java from Sun's Java site.


view/download model file: Evolution and the Ellsberg Paradox.nlogo


Procedures
breed [ agents agent ]

agents-own [
  kind 
]

globals [
  initial-type-probabilities
  red-balls
  yellow-balls
  black-balls
  gamble
  fecundity-AC
  fecundity-AD
  fecundity-BC
  fecundity-BD
  converge-AC
  converge-AD
  converge-BC
  converge-BD
  converge-all-dead
]

to initialize
  clear-all
  reset
end

to reset
  reset-ticks
  clear-turtles
  clear-drawing
  ;clear-all-plots
  clear-output
  
  set initial-type-probabilities random-initial-state-vector
  
  create-agents initial-population-size [
    setxy random-xcor random-ycor
    set shape "circle"
    
    let t new-player-type initial-type-probabilities
    set kind t
    if (t = "AC") [ set color red ]
    if (t = "AD") [ set color green ]
    if (t = "BC") [ set color cyan ]
    if (t = "BD") [ set color magenta ]
  ]
   
  fill-urn-randomly
  ifelse (random-float 1.0 < probability-of-gamble-1) 
    [ set gamble 1 ]
    [ set gamble 2 ]
    
  update-frequency-plot
  update-fecundity-plot
end

to fill-urn-randomly
  set red-balls number-of-red
  set yellow-balls (random number-of-rest + 1) ; Hence 0 <= y <= number-of-rest
  set black-balls (number-of-rest - yellow-balls)
end

; Report 'true' if we are at a fixed point (all of one type
; or all dead), and 'false' otherwise.
to-report iterate
  
  ; We reconfigure the urn and set the gamble at the
  ; start of this method so that the urn and gamble 
  ; monitors correctly reflect the /cause/ of the current display
  ; in the view...
  fill-urn-randomly
  ifelse (random-float 1.0 < probability-of-gamble-1) 
    [ set gamble 1 ]
    [ set gamble 2 ]

  ask agents [
    ifelse (draw-ball-from-urn kind = true)
      [ 
        hatch-agents 1 [ 
          set heading (random 360)
          fd 1 
        ]
        fd 1  
        
        if (kind = "AC") [ set fecundity-AC (fecundity-AC + 1) ]
        if (kind = "AD") [ set fecundity-AD (fecundity-AD + 1) ]
        if (kind = "BC") [ set fecundity-BC (fecundity-BC + 1) ]
        if (kind = "BD") [ set fecundity-BD (fecundity-BD + 1) ]
      ]
      [ die ]
  ]
  
  let number-of-agents (count agents)
  let stop-now? false
  
  ifelse (
       number-of-agents != 0
       and
       (
         number-of-agents = (count agents with [kind = "AC"])
         or number-of-agents = (count agents with [kind = "AD"])
         or number-of-agents = (count agents with [kind = "BC"])
         or number-of-agents = (count agents with [kind = "BD"])
       )
     )
    [ 
      ; All agents are of the same kind, so we can increment
      ; the convergence data by sampling a random agent
      ask one-of agents [
        if (kind = "AC") [ set converge-AC (converge-AC + 1) ]
        if (kind = "AD") [ set converge-AD (converge-AD + 1) ] 
        if (kind = "BC") [ set converge-BC (converge-BC + 1) ] 
        if (kind = "BD") [ set converge-BD (converge-BD + 1) ] 
      ]
      
      update-frequency-plot
      update-fecundity-plot
      update-convergence-plot
      display
      set stop-now? true 
    ]
    [
      if (number-of-agents = 0)
      [ 
        set converge-all-dead (converge-all-dead + 1) 
        update-frequency-plot
        update-fecundity-plot
        update-convergence-plot
        display
        set stop-now? true
      ]
    ]
  
  if (stop-now? = false)
  [
    update-frequency-plot
    update-fecundity-plot
    tick
  ]
  
  report stop-now?
end

to step
  ; Have to do the following since we can't have a lone reporter...
  if (iterate) []
end

to go
  if (iterate = true)
    [ stop ]
end

; Report 'true' if the agent wins, 'false' if the agent loses
to-report draw-ball-from-urn [ k ]
  let outcome false
  
  if (k = "AC")
  [
    if ( (gamble = 1 and (random-float 1.0 <= red-balls / 90.)) or (gamble = 2 and (random-float 1.0 <= (red-balls + yellow-balls) / 90.)))
      [ set outcome true ]
  ]
  
  if (k = "AD")
  [
    if ( (gamble = 1 and (random-float 1.0 <= red-balls / 90.)) or (gamble = 2 and (random-float 1.0 <= (black-balls + yellow-balls) / 90. )) )
      [ set outcome true ]
  ]
  
  if (k = "BC")
  [
    if ( (gamble = 1 and (random-float 1.0 < black-balls / 90.)) or (gamble = 2 and (random-float 1.0 < (red-balls + yellow-balls) / 90.)) )
      [ set outcome true ]
  ]
  
  if (k = "BD")
  [
    if ( (gamble = 1 and (random-float 1.0 < black-balls / 90.)) or (gamble = 2 and (random-float 1.0 < (black-balls + yellow-balls) / 90.)) )
      [ set outcome true ]
  ]
  
  report outcome
end


to update-frequency-plot
  set-current-plot "Current Type Distribution"
  clear-plot
    
  set-current-plot-pen "AC"
  plot-pen-up
  let x-cor .75
  let y-cor (count agents with [kind = "AC"])
  while [x-cor <= 1.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor y-cor 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "AD"
  plot-pen-up
  set x-cor 1.75
  set y-cor (count agents with [kind = "AD"])
  while [x-cor <= 2.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor y-cor 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BC"
  plot-pen-up
  set x-cor 2.75
  set y-cor (count agents with [kind = "BC"])
  while [x-cor <= 3.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor y-cor 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BD"
  plot-pen-up
  set x-cor 3.75
  set y-cor (count agents with [kind = "BD"])
  while [x-cor <= 4.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor y-cor 
    set x-cor (x-cor + .02)
  ]
end

to update-fecundity-plot
  set-current-plot "Overall Fecundity"
  clear-plot
    
  set-current-plot-pen "AC"
  plot-pen-up
  let x-cor .75
  while [x-cor <= 1.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor fecundity-AC 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "AD"
  plot-pen-up
  set x-cor 1.75
  while [x-cor <= 2.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor fecundity-AD
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BC"
  plot-pen-up
  set x-cor 2.75
  while [x-cor <= 3.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor fecundity-BC
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BD"
  plot-pen-up
  set x-cor 3.75
  while [x-cor <= 4.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor fecundity-BD 
    set x-cor (x-cor + .02)
  ]
end

to update-convergence-plot
  set-current-plot "Convergence data"
  clear-plot
    
  set-current-plot-pen "AC"
  plot-pen-up
  let x-cor .75
  while [x-cor <= 1.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor converge-AC 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "AD"
  plot-pen-up
  set x-cor 1.75
  while [x-cor <= 2.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor converge-AD
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BC"
  plot-pen-up
  set x-cor 2.75
  while [x-cor <= 3.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor converge-BC
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "BD"
  plot-pen-up
  set x-cor 3.75
  while [x-cor <= 4.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor converge-BD 
    set x-cor (x-cor + .02)
  ]
  
  set-current-plot-pen "All dead"
  plot-pen-up
  set x-cor 4.75
  while [x-cor <= 5.25] [
    plot-pen-up plotxy x-cor 0
    plot-pen-down plotxy x-cor converge-all-dead 
    set x-cor (x-cor + .02)
  ]
end

; Return a list containing the probabilities of types AC, AD, BC, BD
to-report random-initial-state-vector
  let lst [ 0.0 1.0 ]
  set lst (sort (sentence lst (n-values 3 [random-float 1.0] )))
  report (map [ ?2 - ?1 ] (but-last lst) (but-first lst))
end

; select a new type of player according to the specified probability vector
to-report new-player-type [ vec ]
  let prob-cap (item 0 vec)
  let r (random-float 1.0)
  
  ifelse (r <= prob-cap)
    [ report "AC" ]
    [ set prob-cap (prob-cap + (item 1 vec)) ]
    
  ifelse (r <= prob-cap)
    [ report "AD" ]
    [ set prob-cap (prob-cap + (item 2 vec)) ]
    
  ifelse (r <= prob-cap)
    [ report "BC" ]
    [ set prob-cap (prob-cap + (item 3 vec)) ]
    
  report "BD"
end