reset password
Author Message
rabbott
Posts: 1649
Posted 18:44 Apr 03, 2020 |

I'm splitting Ricardo's three NetLogo procedures into three posts so that (a) they are not so forbidding and (b) we can comment on them separately.

===============================================================================================================

to end-commute   ; turtle procedure
  ; handles reporting the travel time when the commuter reaches the end of its route
  set travel-time (ticks - birth-tick) / 450
  ifelse avg = 0
  [
    set avg travel-time
  ]
  [
    set avg ((19 * avg + travel-time) / 20)
  ]
  ifelse route = 0
  [
    ifelse top = 0
    [
      set top travel-time
    ]
    [
      set top ((travel-time) + ((smoothing - 1) * top)) / smoothing
    ]
  ]
  [
  ifelse route = 1
    [
    ifelse bottom = 0
      [
        set bottom travel-time
      ]
      [
      set bottom ((travel-time) + ((smoothing - 1) * bottom)) / smoothing
      ]
    ]
    [
      ifelse middle = 0
      [
        set middle travel-time
      ]
      [
        set middle ((travel-time) + ((smoothing - 1) * middle)) / smoothing
      ]
    ]
  ]
  die

end

def end_commute(commuter):
    #calculates travel times and adds them to the appropurate route tracker
    #kills commuter

    #rate is the frame rate at wchich the sim is running
    travel_time = (World.ticks - commuter.birth_tick)/World.rate
    
    #calculate avg travel time across completed commutes
    if avg = 0:
        avg = travel_time
    else:
        #based on number of 20 agents
        avg = ((19 * avg + travel_time)/20)
    
    #if the route is a _______
    if commuter.route == 0:
           #top ; travel time of last turtle to complete top route
        if top == 0
            top = travel_time
        else:
            top = ((travel_time) + ((smoothing - 1) * top)) / smoothing
    else 
        if commuter.route == 1:
            if bottom == 0:
                bottom = travel_time
            else:
                bottom = ((travel_time) + ((smoothing - 1) * top)) / smoothing
        else:
            if middle == 0:
                middle = travel_time
            else:
                middle = ((travel_time) + ((smoothing - 1) * top)) / smoothing
    #remove the commuter from the model
    commuter.die()

To reiterate, the route variable is taken as 0 = top route, 1 = bottom route, and 2 is the route taken with braess road. The bottom = 0 top = 0 middle = 0 checks are in place in case there was no current travel time set yet.
The tedious part here was unraveling the nested if else statements. I believe this is the correct interpretation of them. An upcoming post will try to explain the smoothing part of the code.

Last edited by rabbott at 18:44 Apr 03, 2020.
tkitcha
Posts: 19
Posted 15:10 Apr 05, 2020 |

My understanding of this function in English:

Set the time value of how long this agent travels from start to finish to global if it was the first to finish, otherwise update the global average time of all agents taken to travel from start to finish. Also similarly set or update the global time of the route which this agent took: top, middle, or bottom route. Then remove this agent from the world.

rabbott
Posts: 1649
Posted 16:28 Apr 05, 2020 |

Thanks, Tanya,

1. Do we really need three (or perhaps four) chunks of code that do essentially the same thing? Why not factor out that computation and call it when needed?

2. I asked earlier what the line set avg ((19 * avg + travel-time) / 20) is all about. We still don't have an answer to that. Ricardo suggests that the 20 in the expression is the number of agents? Do you agree?

3. Is that line a more concrete version of the line set top ((travel-time) + ((smoothing - 1) * top)) / smoothing? If so, what does that suggest?

rmedina0531
Posts: 19
Posted 17:58 Apr 05, 2020 |

The smoothing in the method helps in weighting the travel time to the most recent ones. Best way to illustrate is with an example. For the set 3,1,2,3,1,2 the average is equal to 2. If another element was added, lets say 6, then the new average would be 2.5. This average does not reflect the fact that the most recent one was much different than the rest, there would have to be a series of numbers around 6 until the average would finally start rising. (ex  3,1,2,3,1,2,6,5,7 avg = 4.75 |  3,1,2,3,1,2,6,5,7,6,7,8 avg = 5.5) In other words it lags like described in the article linked, it is not reflective of the most recent elements added. Instead we use the previous average and add the new element using a weighted system. For example if we want our new number to influence the average very little we can add it as an element to a large list of elements equal to the previous average (5.5, 5.5, 5.5, 5.5,5.5, 5.5,5.5, 5.5,5.5, 20 avg = 6.95 | 5.5, 5.5, 5.5, 20 avg = 9.125) . In other words, more elements less of an impact, less elements more of an impact. 

In this method, this process is being done to the avg travel-time. In this case we are always assuming 20 elements to average. we spread the previous average across 19 elements then add the new travel time, then finding the average

avg = ((19 * avg + travel-time) / 20)

The 20 here has nothing to do with the number of agents, it is the number that was decided the new travel time will influence the running average. 

For the route travel time smoothing, we are instead using the shortest travel time for the path. We are assuming as many elements as the gui smoothing is set (let say 5 for this example) and then averaging it out

((travel-time) + ((smoothing - 1) * top)) / smoothing

((travel-time) + (5- 1) * top)) / 5

((travel-time) + 4 * top)) / 5

or to better compare to the previous method

((4 * top + travel-time) / 5)

 

If smoothing is larger, then the most recent time will have a smaller effect as its effect is 1/smoothing (for example if smoothing = 20 then the effect of the new travel time is only 1/20 = 5%). .

If smoothing is smaller, then the most recent element will influence the average much more (in our example ⅕ = 20%). 

rabbott
Posts: 1649
Posted 19:53 Apr 05, 2020 |

Thanks, Ricardo,

As Ricardo said, the lines of code all represent what is called a moving average. One way to take a moving average is to take the average of the most recent n elements. This is called a simple moving average. It has a number of defects. One of them is that the number that drops off the end of the list (when it becomes the n+1 most recent value) has as much of an effect on the average as the new number that is added.

A better approach (in my view) is the one the NetLogo code uses. It's called an exponential moving average. At any point, you have the average to that moment. To incorporate new data you take a weighted average of the current average and the new data. For example, if you want to give the current average a weight of 4/5 and the new number a weight of 1/5 the expression would be: 4/5 * current_average + 1/5 * new_data.

In the NetLogo code, if smoothing is set to 20, the weights are 19/20 and 1/20. The current average is given a weight of 19/20, and the new data is given a weight of 1/20. Look at Ricardo's post to see how that plays out.

Last edited by rabbott at 19:55 Apr 05, 2020.