2. Spatial interactions on a grid

2.3. Other types of neighborhoods and other revision protocols

1. Goal

Our goal in this section is to extend the model we have created in the previous section by adding two features that are crucial to assess the impact of space on evolutionary models:

  • The possibility to model different types of neighborhoods of arbitrary size. Besides Moore neighborhoods, we will implement Von Neumann neighborhoods, and both of them of any size.
  • The possibility to model other revision protocols besides the “imitate the best neighbor” protocol. In particular, we will implement the imitative pairwise-difference protocol, the imitative positive-proportional protocol, and the imitative logit protocol.[1]

2. Motivation. Robustness of cooperation in spatial settings

In section 2.1 we explored the impact of different assumptions on the robustness of cooperation in spatial settings. However, one assumption we did not change was the revision protocol (aka update rule). Roca et al. (2009a, 2009b) conducted an impressive simulation study of the effect of spatial structure in 2×2 games, and discovered that the revision protocol can play a major role. In particular, they found that the “imitate the best neighbor” protocol (which Roca et al. (2009a, 2009b) call “unconditional imitation”) favors cooperation in the Prisoner’s Dilemma more than any other revision protocol they studied.

In what concerns the Prisoner’s Dilemma, the above results also prove that the promotion of cooperation in this game is not robust against changes in the update rule, because the beneficial effect of spatial lattices practically disappears for rules different from unconditional imitation, when seen in the wider scope of the ST plane. Roca et al. (2009b, p. 9)

In this section we are going to implement several revision protocols. This will allow us to replicate Roca et al.’s (2009a, 2009b) results… and many others (see the proposed exercises).

3. Description of the model

The model we are going to develop here is a generalization of the model implemented in the previous section. In particular, we are going to add the following four parameters:

  • neighborhood-type. This parameter is used to define the agents’ neighborhood (both for playing and for strategy updating).[2] The parameter can have two possible values: “Moore” for a Moore neighborhood, or Von Neumann” for a Von Neumann neighborhood.
  • neighborhood-range. This parameter determines the range of the neighborhood. A patch’s Moore neighborhood of range r consists of the patches within Chebyshev distance r. A patch’s Von Neumann neighborhood of range r is composed of the patches within Manhattan distance r. Note that in our descriptions of revision protocols below, we do not consider a patch to be a neighbor of itself unless otherwise stated.
  • protocol. This parameter determines the revision protocol that agents will follow. It will be implemented with a chooser, with four possible values:
    • best-neighbor” (Nowak and May, 1992, 1993). This is the “imitate the best neighbor” protocol, already implemented in our model.[3]
    • imitative-pairwise-difference” (Hauert 2002,[4] 2006). This is the imitative pairwise-difference protocol we saw in section 0.1 adapted to networks. Under this protocol, the revising agent looks at a random neighbor and considers copying her strategy only if the observed neighbor’s average payoff is higher than the revising agent’s average payoff; in that case, the revising agent switches with probability proportional to the payoff difference.[5]
    • imitative-positive-proportional-m” (Nowak et al., 1994a, b). Under this revision protocol, the revising agent copies the strategy of one of her neighbors (including herself, in this case) with probability proportional to their total payoff raised to parameter m.[6] Parameter m controls the intensity of selection (see below). We do not allow for negative payoffs when using this protocol.[7]
    • imitative-logit-m” (Weibull, 1995, p. 161; Szabó and Tőke, 1998). Under this revision protocol, the revising agent i looks at one of her neighbors j at random and copies her strategy with probability \frac{e^{m \pi_j}}{e^{m \pi_i}+e^{m \pi_j}}, where  \pi_z denotes agent z‘s average payoff and m\ge0.[8] Parameter m controls the intensity of selection (see below).
  • m. This is a parameter that controls the intensity of selection in revision protocols “imitative-positive-proportional-m” and “imitative-logit-m” (see above). High values of m imply that selection is strongly based on payoffs, i.e. the agents with the highest payoffs will be chosen with very high probability. Lower values of m mean that the sensitivity of selection to payoffs is weak (e.g. if m = 0, selection among agents is random, i.e. independent of their payoffs).

Everything else stays as described in the previous section.

 CODE  4. Interface design

We depart from the model we developed in the previous section (so if you want to preserve it, now is a good time to duplicate it).

Figure 1. Interface design.

In the new interface (see figure 1 above), we have to add:

  • One chooser for new parameter neighborhood-type (with possible values “Moore” and Von Neumann“), and a slider for parameter neighborhood-range (with minimum = 1 and increment = 1).
  • One chooser for new parameter protocol (with possible values best-neighbor“, imitative-pairwise-difference“, imitative-positive-proportional-m” and imitative-logit-m), and a slider for parameter m (with minimum = 0 and increment = 0.1).

We have also added a button (labeled show player’s neighborhood) to see any patch’s neighborhood on the 2D view and another button (labeled clear) to clear the display of the neighborhood.

 CODE  5. Code

5.1. Skeleton of the code

The skeleton of the code for procedures to setup and to go is the same as in the previous model. In this section we will modify mainly the following two procedures:

  • procedure to setup-players, to set the players’ neighborhoods according to parameters neighborhood-type and neighborhood-range (see figure 2).
Figure 2. Calls to other procedures from procedure to setup-players.
  • procedure to update-strategy-after-revision, to run the revision protocol indicated in parameter protocol (see figure 3).
Figure 3. Calls to other procedures from procedure to update-strategy-after-revision.

5.2. Extension I. Implementation of different neighborhoods

Recall that patches have the following two individually owned variables:

  • my-coplayers, which contains the set of agents with whom the patch plays the game (and is affected by parameter self-matching?), and
  • my-nbrs-and-me, which is the set of agents the player considers when revising its strategy.

These two agentsets are the same if self-matching? is true, and differ only in the focal patch if self-matching? is false. We will keep this distinction for any type of neighborhood.

To implement the different neighborhoods, primitive at-points will be very useful. This primitive reports a subset of a given agentset that includes only the agents on the patches at the given coordinates (relative to the calling agent). As an example, the following code gives a patch’s Von Neumann neighborhood of range 1 (including the patch itself):

patches at-points [[-1 0] [0 -1] [0 0] [0 1] [1 0]]

And the following code gives a patch’s Moore neighborhood of range 1 (including the patch itself):

patches at-points [[-1 -1] [-1 0] [-1 1] [0 -1] [0 0] [0 1] [1 -1] [1 0] [1 1]]

Thus, the key is to implement procedures that report the appropriate lists of relative coordinates.[9] Let’s do this!

Implementation of relative coordinates for Moore neighborhoods

Our goal here is to implement a procedure that reports the appropriate list of relative coordinates for Moore neighborhoods, for any given range and letting the user choose whether the list should include the item [0 0] or not. Let us call this reporter to-report moore-offsets. Note that this reporter takes two inputs, which we can call r (for range) and include-center?.

to-report moore-offsets [r include-center?]
   ;; code to be written
end

Below we provide some examples of what reporter to-report moore-offsets should produce:

  • If r = 1 and include-center? is true:
    [[-1 -1] [-1 0] [-1 1] [0 -1] [0 0] [0 1] [1 -1] [1 0] [1 1]]
  • If r = 1 and include-center? is false:
    [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]
  • If r = 2 and include-center? is true:
    [[-2 -2] [-2 -1] [-2 0] [-2 1] [-2 2] [-1 -2] [-1 -1] [-1 0] [-1 1] [-1 2] [0 -2] [0 -1] [0 0] [0 1] [0 2] [1 -2] [1 -1] [1 0] [1 1] [1 2] [2 -2] [2 -1] [2 0] [2 1] [2 2]]

Note that the effect of input include-center? is just to include or exclude [0 0] from the output list, so we can worry about that at the end of the implementation. To produce the list of Moore offsets for range r, we can start building the list l = [ –r, –r+1, …, r-1, ] and then build 2-item lists which each element of the list l together with each element of the same list l. Thus, let us build the list l = [ –r, –r+1, …, r-1, ]:

let l (range (- r) (r + 1))

To build 2-items lists with first element el1 and second element each of the elements of list l, we can use primitive map as follows:

map [el2 -> list el1 el2] l

And now we should make el1 be each of the elements of list l. For this we can use primitive map again:

map [ el1 -> map [el2 -> list el1 el2] l] l

The only problem now is that we have several nested lists. For example, the code above for l = [ -1, 0, 1 ] produces:

[[[-1 -1] [-1 0] [-1 1]] [[0 -1] [0 0] [0 1]] [[1 -1] [1 0] [1 1]]]

Note that the outmost list contains three sublists, each of which contains three 2-item lists. We can get rid of the extra lists using primitives reduce and sentence:

let result reduce sentence map [ el1 -> map [el2 -> list el1 el2] l] l

The code above creates the list of required offsets, including [0 0]. Now we just have to remove [0 0] if and only if input include-center? is false. Thus, the final code for reporter to-report moore-offsets is as follows:

to-report moore-offsets [r include-center?]
  let l (range (- r) (r + 1))
  let result reduce sentence map [ el1 -> map [el2 -> list el1 el2] l] l
  report ifelse-value include-center?
    [ result ]
    [ remove [0 0] result ]
end

Implementation of relative coordinates for Von Neumann neighborhoods

Our goal here is to implement a procedure that reports the appropriate list of relative coordinates for Von Neumann neighborhoods. Let us call this reporter to-report von-neumann-offsets and let us call its inputs r (for range) and include-center?, just like before.

to-report von-neumann-offsets [r include-center?]
   ;; code to be written
end

Note that, for any given r and include-center?, the list of offsets for a Von Neumann neighborhood is a subset of the list of offsets for the corresponding Moore neighborhood. In particular, the list of Von Neumann offsets is composed of the elements [el1 el2] in the list of Moore offsets that satisfy the condition |el1| + |el2| ≤ r . Thus, we can create the corresponding list of Moore offsets and filter those coordinates that satisfy the condition using primitive filter. Do you want to give it a try?

Implementation of procedure to-report von-neumann-offsets

Yes, well done!

to-report von-neumann-offsets [r include-center?]
  let moore-list (moore-offsets r include-center?)
  report filter [l -> abs first l + abs last l <= r] moore-list
end

Putting everything together

Now that we have implemented to-report moore-offsets and to-report von-neumann-offsets, we can use them in procedure to setup-players to create the right neighborhood for each patch. The only lines we have to modify are the ones highlighted in bold below:

to setup-players
  let initial-distribution read-from-string n-of-players-for-each-strategy
  if length initial-distribution != length payoff-matrix [
    user-message (word "The number of items in\n"
      "n-of-players-for-each-strategy (i.e. "
      length initial-distribution "):\n" n-of-players-for-each-strategy
      "\nshould be equal to the number of rows\n"
      "in the payoff matrix (i.e. "
      length payoff-matrix "):\n"
      payoffs
    )
  ]
  if sum initial-distribution != count patches [
    user-message (word "The total number of agents in\n"
      "n-of-agents-for-each-strategy (i.e. "
      sum initial-distribution "):\n" n-of-players-for-each-strategy
      "\nshould be equal to the number of patches (i.e. "
      count patches ")"
    )
  ]
  ask patches [set strategy false]
  let i 0
  let offsets ifelse-value (neighborhood-type = "Von Neumann")
        [ von-neumann-offsets neighborhood-range self-matching? ]
        [ moore-offsets neighborhood-range self-matching? ]
  foreach initial-distribution [ j ->
    ask n-of j (patches with [strategy = false]) [
      set payoff 0
      set strategy i
      set my-coplayers patches at-points offsets
      set n-of-my-coplayers (count my-coplayers)
      set my-nbrs-and-me (patch-set my-coplayers self)
    ]
    set i (i + 1)
  ]
  set n-of-players count patches
end

Note that variable my-coplayers may or may not contain the patch itself (depending on the value of parameter self-matching?), but variable my-nbrs-and-me will always contain it, since we are setting its value to (patch-set my-coplayers self). This is perfectly fine even if my-coplayers already contains the patch itself, since agentsets do not contain duplicates. Adding an agent a to an agentset that already contains that agent a has no effect whatsoever.[10]

A nice final touch

Finally, we are going to write some code to let the user see the neighborhood of any patch in the 2D view by clicking on it. This is just for displaying purposes, so feel free to skip this if you’re not really interested. Let us start by implementing a new procedure called to show-neighborhood as follows:

to show-neighborhood
  if mouse-down? [
    ask patch mouse-xcor mouse-ycor [
      ask my-coplayers [set pcolor white]
    ]
  ]
  display
end

You may want to read the documentation for primitives mouse-down?mouse-xcor and mouse-ycor. Basically, this procedure paints in white the patches contained in the variable my-coplayers of the patch you click with your mouse. However, for this to work, the procedure must be running all the time. To do this, we can run this procedure within the button labeled show player’s neighborhood in the interface, and make this button be a “forever” button.

Insert the following code within the button labeled show player’s neighborhood:

with-local-randomness [show-neighborhood]

The use of primitive with-local-randomness guarantees that this piece of code does not interfere with the generation of pseudorandom numbers for the rest of the model.

To clear the white paint, insert the following code within the button labeled clear (which should not be a “forever” button):

with-local-randomness [ ask patches [update-color] ]

Figure 4 shows a patch’s Von Neumann neighborhood of range 5 (excluding the patch itself) painted in white.

Figure 4. The 2D view shows in white a patch’s set my-coplayers. Relevant parameters: neighborhood-type = “Von Neumann“, neighborhood-range = 5, and self-matching? = false.

5.3. Extension II. Implementation of different revision protocols

The implementation of the revision protocol takes place in procedure to update-strategy-after-revision. Note that the effect of noise is the same regardless of the revision protocol, so we can deal with noise in a unified way, regardless of which revision protocol will be executed. One possible way of doing this is as follows:

to update-strategy-after-revision
  ifelse (random-float 1 < noise)
    [ set strategy-after-revision random n-of-strategies ]
    [ 
      ;; code to set strategy-after-revision 
      ;; using the revision protocol indicated by the user
      ;; through parameter protocol
    ]
end

To make the implementation of revision protocols elegant and modular, we should implement a different procedure for each revision protocol. Let us call these procedures: best-neighbor-protocolimitative-pairwise-difference-protocol, imitative-positive-proportional-m-protocol and imitative-logit-m-protocol. With these procedures in place, the code for procedure to update-strategy-after-revision would just look as follows:

to update-strategy-after-revision
  ifelse (random-float 1 < noise)
    [ set strategy-after-revision random n-of-strategies ]
    [ (ifelse
        protocol = "best-neighbor"
                  [ best-neighbor-protocol ]
        protocol = "imitative-pairwise-difference"
                  [ imitative-pairwise-difference-protocol ]
        protocol = "imitative-positive-proportional-m"
                  [ imitative-positive-proportional-m-protocol ]
        protocol = "imitative-logit-m"
                  [ imitative-logit-m-protocol ]
      )
    ]
end

Note that primitive ifelse can work with multiple conditions, just like switch statements in other programming languages.[11]

Now we just have to implement the procedures for each of the four revision protocols. Do you want to give it a try? The first one is not very difficult.

Implementation of procedure to best-neighbor-protocol
to best-neighbor-protocol
  set strategy-after-revision 
      [strategy] of one-of my-nbrs-and-me with-max [payoff]
end

The implementation of procedure to imitative-pairwise-difference-protocol is significantly more difficult than the previous one, but if you have managed to read this book until here, you certainly have what it takes to do it!

Implementation of procedure to imitative-pairwise-difference-protocol

A possible implementation of this procedure is as follows:

to imitative-pairwise-difference-protocol
  let observed-player one-of other my-coplayers
  ;; compute difference in average payoffs
  let payoff-diff ([payoff / n-of-my-coplayers] of observed-player   
                 - (payoff / n-of-my-coplayers))
  set strategy-after-revision   
     ifelse-value (random-float 1 < (payoff-diff / max-payoff-difference))
       [ [strategy] of observed-player ]
       [ strategy ]
      ;; If your strategy is the better, payoff-diff is negative,
      ;; so you are going to stick with it.
      ;; If it's not, you switch with probability
      ;; (payoff-diff / max-payoff-difference)
end

Note that we are using a new variable, i.e. max-payoff-difference, to make sure that the agent changes strategy with probability proportional to the average payoff difference. Thus, we should define it as global (since this max-payoff difference will not change over the course of a run), as follows:

globals [
  payoff-matrix
  n-of-strategies
  n-of-players
  max-payoff-difference     ;; <== New line
]

We also have to set the value of this new variable. We can do that at the end of the setup-payoffs method, as follows:

to setup-payoffs
  set payoff-matrix read-from-string payoffs
  set n-of-strategies length payoff-matrix
  ;;New lines below
  set max-payoff-difference
     (max-of-matrix payoff-matrix) - (min-of-matrix payoff-matrix)
end

Finally, note that we have implemented two new procedures to compute the minimum and the maximum value of a matrix:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; SUPPORTING PROCEDURES ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;
;;; Matrices ;;;
;;;;;;;;;;;;;;;;
to-report max-of-matrix [m]
  report max reduce sentence m
end
to-report min-of-matrix [m]
  report min reduce sentence m
end

To implement procedure to imitative-positive-proportional-m-protocol, there is an extension that comes bundled with NetLogo which will make our life much easier: extension rnd. To use it, you just have to add the following line at the beginning of your code

extensions [rnd]

Having loaded the rnd extension, you can use primitive rnd:weighted-one-of, which will be very handy. With this information, you may want to try to implement this short procedure yourself.

Implementation of procedure to imitative-positive-proportional-m-protocol
to imitative-positive-proportional-m-protocol
  let chosen-nbr rnd:weighted-one-of my-nbrs-and-me [ payoff ^ m ]
  set strategy-after-revision [strategy] of chosen-nbr
end

If you wanted to use average rather than total payoffs, you would only have to divide the payoff by n-of-my-coplayers.

To avoid errors when payoffs are negative and this protocol is used, it would be nice to check that payoffs are non-negative, and if they are, let the user know. We can do this implementing the following procedure:

to check-payoffs-are-non-negative
  if min reduce sentence payoff-matrix < 0 [
    user-message (word
      "Since you are using protocol = imitative-positive-proportional-m, all elements in the payoff matrix\n"
      payoffs
      "\nshould be non-negative numbers.")
  ]
end

An appropriate place to call this procedure would be at the end of procedure to setup-payoffs, which would then be as follows:

to setup-payoffs
  set payoff-matrix read-from-string payoffs
  set n-of-strategies length payoff-matrix
  ;; New lines below
  set max-payoff-difference 
     (max-of-matrix payoff-matrix) - (min-of-matrix payoff-matrix)
  if protocol = "imitative-positive-proportional-m" 
     [ check-payoffs-are-non-negative ]
end

The implementation of procedure to imitative-logit-m-protocol is not extremely hard after having seen the implementation of to imitative-pairwise-difference-protocol, and realizing that \frac{e^{m \pi_j}}{e^{m \pi_j}+e^{m \pi_i}} = \frac{1}{1+e^{-m (\pi_j - \pi_i)}}.

Implementation of procedure to imitative-logit-m-protocol

A possible implementation of this procedure is as follows:

to imitative-logit-m-protocol
  let observed-player one-of other my-coplayers
  ;; compute difference in average payoffs 
  let payoff-diff ([payoff / n-of-my-coplayers] of observed-player   
                 - (payoff / n-of-my-coplayers))
  set strategy-after-revision   
    ifelse-value (random-float 1 < (1 / (1 + exp (- m * payoff-diff))))
      [ [strategy] of observed-player ]
      [ strategy ]
end

5.5. Complete code in the Code tab

The Code tab is ready! Congratulations! With this model you can rigorously explore the effect of space in 2-player games.

extensions [rnd]         ;; <== New line

globals [
  payoff-matrix
  n-of-strategies
  n-of-players
  max-payoff-difference  ;; <== New line
]

patches-own [
  strategy
  strategy-after-revision
  payoff
  my-nbrs-and-me
  my-coplayers
  n-of-my-coplayers
]

to setup
  clear-all
  setup-payoffs
  setup-players
  setup-graph
  reset-ticks
  update-graph
  ask patches [update-color]
end

to setup-payoffs
  set payoff-matrix read-from-string payoffs
  set n-of-strategies length payoff-matrix
  ;; New lines below
  set max-payoff-difference
     (max-of-matrix payoff-matrix) - (min-of-matrix payoff-matrix)
  if protocol = "imitative-positive-proportional-m" 
     [ check-payoffs-are-non-negative ]
end

to setup-players
  let initial-distribution read-from-string n-of-players-for-each-strategy
  if length initial-distribution != length payoff-matrix [
    user-message (word "The number of items in\n"
      "n-of-players-for-each-strategy (i.e. "
      length initial-distribution "):\n" n-of-players-for-each-strategy
      "\nshould be equal to the number of rows\n"
      "in the payoff matrix (i.e. "
      length payoff-matrix "):\n"
      payoffs
    )
  ]
  if sum initial-distribution != count patches [
    user-message (word "The total number of agents in\n"
      "n-of-agents-for-each-strategy (i.e. "
      sum initial-distribution "):\n" n-of-players-for-each-strategy
      "\nshould be equal to the number of patches (i.e. "
      count patches ")"
    )
  ]
  ask patches [set strategy false]
  let i 0
  let offsets ifelse-value (neighborhood-type = "Von Neumann")
        [ von-neumann-offsets neighborhood-range self-matching? ]
        [ moore-offsets neighborhood-range self-matching? ]
  foreach initial-distribution [ j ->
    ask n-of j (patches with [strategy = false]) [
      set payoff 0
      set strategy i
      set my-coplayers patches at-points offsets
      set n-of-my-coplayers (count my-coplayers)
      set my-nbrs-and-me (patch-set my-coplayers self)
    ]
    set i (i + 1)
  ]
  set n-of-players count patches
end

to setup-graph
  set-current-plot "Strategy Distribution"
  foreach (range n-of-strategies) [ i ->
    create-temporary-plot-pen (word i)
    set-plot-pen-mode 1
    set-plot-pen-color 25 + 40 * i
  ]
end

to go
  ifelse synchronous-updating?
    [
      ask patches [ play ]
      ask patches [ update-strategy-after-revision ]
        ;; here we are not updating the agent's strategy yet
      ask patches [ update-strategy ]
        ;; now we update every agent's strategy at the same time
    ]
    [
      ask patches [
        play
        ask my-coplayers [ play ]
           ;; since your coplayers' strategies or
           ;; your coplayers' coplayers' strategies
           ;; could have changed since the last time
           ;; your coplayers played
        update-strategy-after-revision
        update-strategy
      ]
    ]
  tick
  update-graph
  ask patches [update-color]
end

to play
  let n-of-coplayers-with-strategy-? n-values n-of-strategies [ i ->
    count my-coplayers with [strategy = i] ]
  let my-payoffs (item strategy payoff-matrix)
  set payoff sum (map * my-payoffs n-of-coplayers-with-strategy-?)
end

to update-strategy-after-revision
  ifelse (random-float 1 < noise)
    [ set strategy-after-revision random n-of-strategies ]
    [ (ifelse
        protocol = "best-neighbor"
                  [ best-neighbor-protocol ]
        protocol = "imitative-pairwise-difference"
                  [ imitative-pairwise-difference-protocol ]
        protocol = "imitative-positive-proportional-m"
                  [ imitative-positive-proportional-m-protocol ]
        protocol = "imitative-logit-m"
                  [ imitative-logit-m-protocol ]
      )
    ]
end

to best-neighbor-protocol
  set strategy-after-revision 
      [strategy] of one-of my-nbrs-and-me with-max [payoff]
end

to imitative-pairwise-difference-protocol
  let observed-player one-of other my-coplayers
  ;; compute difference in average payoffs
  let payoff-diff ([payoff / n-of-my-coplayers] of observed-player 
                 - (payoff / n-of-my-coplayers))
  set strategy-after-revision 
      ifelse-value (random-float 1 < (payoff-diff / max-payoff-difference))
        [ [strategy] of observed-player ]
        [ strategy ]
      ;; If your strategy is the better, payoff-diff is negative,
      ;; so you are going to stick with it.
      ;; If it's not, you switch with probability
      ;; (payoff-diff / max-payoff-difference)
end

to imitative-positive-proportional-m-protocol
  let chosen-nbr rnd:weighted-one-of my-nbrs-and-me [ payoff ^ m ]
      ;; https://ccl.northwestern.edu/netlogo/docs/rnd.html#rnd:weighted-one-of
  set strategy-after-revision [strategy] of chosen-nbr
end

to imitative-logit-m-protocol
  let observed-player one-of other my-coplayers
  ;; compute difference in average payoffs
  let payoff-diff ([payoff / n-of-my-coplayers] of observed-player 
                 - (payoff / n-of-my-coplayers))
  set strategy-after-revision 
    ifelse-value (random-float 1 < (1 / (1 + exp (- m * payoff-diff))))
      [ [strategy] of observed-player ]
      [ strategy ]
end

to update-strategy
  set strategy strategy-after-revision
end

to update-graph
  let strategy-numbers (range n-of-strategies)
  let strategy-frequencies map [ n ->
    count patches with [strategy = n] / n-of-players ] strategy-numbers
  set-current-plot "Strategy Distribution"
  let bar 1
  foreach strategy-numbers [ n ->
    set-current-plot-pen (word n)
    plotxy ticks bar
    set bar (bar - (item n strategy-frequencies))
  ]
  set-plot-y-range 0 1
end

to update-color
  set pcolor 25 + 40 * strategy
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; SUPPORTING PROCEDURES ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

to-report moore-offsets [r include-center?]
  let l (range (- r) (r + 1))
  let result reduce sentence map [ el1 -> map [el2 -> list el1 el2] l] l
  report ifelse-value include-center?
    [ result ]
    [ remove [0 0] result ]
end

to-report von-neumann-offsets [r include-center?]
  let moore-list (moore-offsets r include-center?) 
  report filter [l -> abs first l + abs last l <= r] moore-list 
end

to show-neighborhood
  if mouse-down? [
    ask patch mouse-xcor mouse-ycor [
      ask my-coplayers [set pcolor white]
    ]
  ]
  display
end

to check-payoffs-are-non-negative
  if min reduce sentence payoff-matrix < 0 [
    user-message (word
      "Since you are using protocol = imitative-positive-proportional-m, all elements in the payoff matrix\n"
      payoffs
      "\nshould be non-negative numbers.")
  ]
end

;;;;;;;;;;;;;;;;
;;; Matrices ;;;
;;;;;;;;;;;;;;;;

to-report max-of-matrix [matrix]
  report max reduce sentence matrix
end

to-report min-of-matrix [matrix]
  report min reduce sentence matrix
end

6. Sample runs

Revision protocols

Now that we have implemented the model, we can explore the impact of the revision protocol on the promotion of cooperation in the spatially embedded Prisoner’s Dilemma. Let us explore this question using the parameter values shown in figure 1 above. We start with the “imitate the best neighbor” protocol. The simulation below shows a representative run.

As you can see in the video above, with the “imitate the best neighbor” protocol, high levels of cooperation are achieved (i.e. about 90% of the patches cooperate in the long run). But, how robust is this result to changes in the revision protocol? To explore this question, let us run the same simulation with the other three revision protocols we have implemented. The simulation below shows a representative run with the “imitative-pairwise-difference” protocol.

With the “imitative-pairwise-difference” protocol, there is no cooperation at all in the long run. Let us now try the “imitative-positive-proportional-m” protocol with m = 1:[12]

We do not observe any cooperation at all with the “imitative-positive-proportional-m” (with m = 1) either. Finally, let us try the “imitative-logit-m” protocol with m = 1 (or any other positive value, for that matter):[13]

And we observe universal defection again! Clearly, as Roca et al. (2009a, 2009b) point out, the “imitate the best neighbor” protocol seems to favor cooperation in the Prisoner’s Dilemma more than other revision protocols.

Neighborhoods

What about if we change the type of neighborhood? You can check that the simulations shown in the videos above do not change qualitatively if we use Von Neumann neighborhoods rather than Moore neighborhoods. However, what does make a difference is the size of the neighborhood. Note that if the size is so large that agents’ neighborhoods encompass the whole population, we have a global interaction model (i.e. a model without population structure). Thus, the size of the neighborhood allows us to study the effect of population structure in a gradual manner. Having seen this, we can predict that, for the “imitate the best neighbor” protocol, if we increase the size of the neighborhood enough, at some point cooperation will disappear. As an exercise, try to find the minimum neighborhood-range (for both types of neighborhood) at which cooperation cannot be sustained anymore, with the other parameter values as shown in figure 1.

Critical neighborhood size at which cooperation does not emerge in the simulation parameterized as in figure 1.
  • neighborhood-type = “Moore“. With neighborhood-range = 1, we observe significant levels of cooperation (about 90%) cooperation, but with neighborhood-range ≥ 2, we observe no cooperation at all.
  • neighborhood-type = “Von Neumann“.  With neighborhood-range = 1, we observe significant levels of cooperation (about 75%) cooperation. Interestingly, with neighborhood-range = 2, we observe even more cooperation (usually more than 90%), but with neighborhood-range ≥ 3, we observe no cooperation at all.

Discussion

We would like to conclude this section emphasizing that the influence of population structure on evolutionary dynamics generally depends on many factors that may seem insignificant at first sight, and whose effects interact in complex ways. This may sound discouraging, since it suggests that a simple general theory of the influence of population structure on evolutionary dynamics cannot be derived. On a more positive note, it also highlights the importance of the skills you are learning with this book. Without the aid of computer simulation, it seems impossible to explore this type of questions. But using computer simulation we can gain some understanding of the complexity and the beauty of these apparently simple –yet surprisingly intricate– systems. The following quote from Roca et al. (2009b) nicely summarizes this view.

To conclude, we must recognize the strong dependence on details of evolutionary games on spatial networks. As a consequence, it does not seem plausible to expect general laws that could be applied in a wide range of practical settings. On the contrary, a close modeling including the kind of game, the evolutionary dynamics and the population structure of the concrete problem seems mandatory to reach sound and compelling conclusions. With no doubt this is an enormous challenge, but we believe that this is one of the most promising paths that the community working in the field can explore. Roca et al. (2009b, pp. 14-15) 

7. Exercises

You can use the following link to download the complete NetLogo model: nxn-imitate-best-nbr-extended.

A frame of the simulation shown in figure 11 of Nowak and May (1993).

Exercise 1. How can we parameterize our model to replicate the results shown in figures 1b and 1d of Hauert and Doebeli (2004)?

Exercise 2. How can we parameterize our model to replicate the results shown in figures 1, 2, 6 and 7 of Nowak et al. (1994a)?[14]

Exercise 3. How can we parameterize our model to replicate the results shown in figure 1 of Szabó and Tőke (1998)? Note that Szabó and Tőke (1998) use total payoffs and we use average payoffs in our “imitative-logit-m” protocol, but a clever parameterization can save you any programming efforts.

Exercise 4. How can we parameterize our model to replicate the results shown in figure 11 of Nowak and May (1993)?

Exercise 5. To appreciate the impact that neighborhood-type may have, run a simulation parameterized as in figure 1 above, but with protocol = “imitative-logit-m”, m = 10, and self-matching? = true. Compare the results obtained using neighborhood-type = “Moore” and neighborhood-type = “Von Neumann”. What do you observe?

 CODE  Exercise 6. The following block of code (in procedure to update-strategy-after-revision) can be replaced by one simple line using run (a primitive that can take a string containing the name of a command as an input, and it runs the command).

[ (ifelse
    protocol = "best-neighbor"
              [ best-neighbor-protocol ]
    protocol = "imitative-pairwise-difference"
              [ imitative-pairwise-difference-protocol ]
    protocol = "imitative-positive-proportional-m"
              [ imitative-positive-proportional-m-protocol ]
    protocol = "imitative-logit-m"
              [ imitative-logit-m-protocol ]
  )
]

Can you come up with the simple line?


  1. The names given to the different protocols follow Izquierdo et al. (2019).
  2. Recall that the set of patches that a patch plays with also depends on the value of self-matching?.
  3. Roca et al. (2009a, 2009b) call this revision protocol "unconditional imitation" and Hauert (2002) calls it "best takes over". Also, note that Hauert (2002) resolves ties differently.
  4. See Roca et al. (2009b) for an important and illuminating discussion of this paper.
  5. Roca et al. (2009a, 2009b) call this revision protocol "replicator rule or proportional imitation rule", though their implementation is not exactly the same as ours. They use total payoffs and we use average payoffs. The two implementations differ only if it is possible that two agents have different number of neighbors (e.g. as with non-periodic boundary conditions). Note, however, that Roca et al. (2009a, 2009b)'s experiments have periodic boundary conditions, i.e. all agents have the same number of neighbors, so for all these cases the two implementations are equivalent. Hauert (2002) calls this revision protocol "imitate the better".
  6. It may make more sense to use average rather than total payoffs. We use total payoffs here to be able to replicate Nowak et al.'s (1994a, b) experiments. To use average payoffs, the change in the code is minimal.
  7. Roca et al. (2009a, 2009b) call this revision protocol with m = 1 "Moran rule". Their implementation is not exactly the same as ours, since they do allow for negative payoffs by introducing a constant. Note, however, that the two implementations are equivalent if payoffs are non-negative. Hauert (2002) calls this revision protocol with m = 1 "proportional update".
  8. Roca et al. (2009a, 2009b) and other authors (see e.g. Traulsen and Hauert (2009), Perc and Szolnoki (2010) and Adami et al. (2016)) call this revision protocol the "Fermi rule". Note also that some authors (e.g. Szabó and Tőke (1998)) use total payoffs rather than average payoffs.
  9. Our implementation is heavily based on code example titled "Moore & Von Neumann Example", which you can find in NetLogo models library. Note, however, that this code example does not compute the correct neighborhoods if the center of the world lies at the edge or at a corner of the world (at least in version 6.1 and previous ones).
  10. For instance, the following code reports true.
    show (patch-set patch 0 0 patch 0 0) = (patch-set patch 0 0)
  11. This functionality was added in NetLogo 6.1.
  12. As long as m ≤ 5, results are very similar, but for greater values of m (i.e. greater intensity of selection) cooperation starts to emerge.
  13. In contrast with what happens under the "imitative-positive-proportional-m" protocol, parameter m does not seem to have a great influence in this case. This changes if self-matching? is turned on. In that case, for high values of m, cooperation can emerge.
  14. Figures 1 and 2 in Nowak et al. (1994a) are the same as figures 1 and 2 in Nowak et al. (1994b).

License

Icon for the Creative Commons Attribution 4.0 International License

Agent-Based Evolutionary Game Dynamics by Luis R. Izquierdo, Segismundo S. Izquierdo & William H. Sandholm is licensed under a Creative Commons Attribution 4.0 International License, except where otherwise noted.

Share This Book