Netlogo - Sandpile Model - update count - runtime Error - netlogo

At each tick i am asking each patch to update its count dependent on that of its 8 neighbours. If more than 4 neighbours have a count more than or equal to 1 then they update by 1. If more than 4 neighbours have a count less than or equal to 1 then the patch count should be set to 0.
When i run the code i get the following error:
"The >= operator can only be used on two numbers, two strings, or two agents of the same type, but not on a TRUE/FALSE and a number.error while patch 27 -22 running >= called by procedure SPREAD-ERRORS called by procedure GO"
to spread-errors ;; Errors spread prior to addition of random error
;; This is dependent upon majority of neighbors with errors
ask patches [
ifelse count neighbors with [n >= 1] > 4
[update-n 1]
[set n n = 0]
]
end

You mean set n 0, not set n n = 0.
But why is set n n = 0 actually valid NetLogo syntax, and how did it end up leading to the error message you get?
Well, n = 0 is a boolean expression whose value is either true or false. Then you're taking that value and storing it in n. The effect is as if you had written:
ifelse n = 0 [ set n true ] [ set n false ]
after this command runs, n holds a boolean. Then the next time n >= 1 runs, you get the error message above, since n is no longer a number and can't be compared with 1.

Related

Multiple mutually exclusive events and probabilities in netlogo

consider the following 5 events that can occur in netlogo,
[a b c d e f]
Each event has a specific probability of occurring.
Say [0 0 0.3 0.5 0.1 0.1],
that is, p(a) = 0, p(b) = 0, p (d) = 0.5
One (and only one event)must occur . How do I model it in Netlogo.
What i did:
ask turtles
[
if random-float 1 < 0
[EventA]
if random-float 1 < 0.5
[EventD] ; and so on
]
But with this approach, sometimes no event will occur or sometimes more than 1.
Short answer: This is a classic use case for "roulette-wheel" selection.
You can use the NetLogo RND extension to do this, or roll your own following the Lottery example in the model library.
More:
The method abstracts the probabilities to weights, so any set of values from a list or from an agentset can be used; the probability of selection is the ratio of the individual value to the sum of all values in the set or list.
It does not require the set or lists to be sorted, and is scalable to any number of items.
You can write it to produce either the list index of the selected value, or a related value (like an agent or a value from another list).
It can be used for both with-replacement and without-replacement selection from the set.
Without-replacement can be done either by removing individuals from the selection list/set or by setting the selection weight/value of an individual to 0 once it is selected(use a copy of the real values if the selection is repeated.)
Link to NetLogo RND Extension
Simple roll-your-own example:
;; List weight has values to consider
;; Returns the index of the selected item
To RW-Select [ weight ]
Let last-index length weight
Let total sum weight
Let index 0
Let cumulative 0
Let rnd random-float total
While [cumulative <= rnd and index < last-index]
[
Set cumulative cumulative + item index weight
Set index index + 1
]
Report (index - 1)
End
For this, I suggest using just a single random-float between 0 and 1, saving it and then evaluating every probability based on that single number. The trick here is summing up your probabilities. If it is not p(a), check if it is p(a) + p(b). If it is not p(b), check if it is p(a) + p(b) + p(c) etc. In using this method, each option will have its own probability chance of being the chosen option and since the sum of all probabilities is 1, there is always 1 probability that will be chosen.
I use ifelse to evaluate this so option 2 is only considered is option 1 is rejected, option 3 only if 1 and 2 are rejected and so on. Normally ifelse only has 2 options but by including brackets around the entire thing, you can give it as many options as you want.
to go
let options ["a" "b" "c" "d" "e" "f"]
let probabilities [0 0 0.3 0.5 0.1 0.1]
let the-number random-float 1
show the-number
(ifelse
the-number < sum sublist probabilities 0 1 [show item 0 options]
the-number < sum sublist probabilities 0 2 [show item 1 options]
the-number < sum sublist probabilities 0 3 [show item 2 options]
the-number < sum sublist probabilities 0 4 [show item 3 options]
the-number < sum sublist probabilities 0 5 [show item 4 options]
the-number < sum sublist probabilities 0 6 [show item 5 options]
)
end
In your case, this would result in:
to go-2
let x random-float 1
(ifelse
x < 0 [show "a"] ;p(a) = p(x < 0) = 0
x < 0 [show "b"] ;p(b) = p(0 =< x < 0) = 0
x < 0.3 [show "c"] ;p(c) = p(0 =< x < 0.3) = 0.3
x < 0.8 [show "d"] ;p(d) = p(0.3 =< x < 0.8) = 0.5
x < 0.9 [show "e"] ;p(e) = p(0.8 =< x < 0.9) = 0.1
x < 1.0 [show "f"] ;p(f) = p(0.9 =< x < 1.0) = 0.1
)
end
First Edit:
Autogenerating this cumulative lists can for example be done using a map procedure. Here each new value of incremented-probabilities is the sum of the probability and all those that came before it.
let probabilities n-values 20 [0.05]
let incremented-probabilities (map [[this-probability index] -> sum sublist probabilities 0 (index + 1) ] probabilities range length probabilities)
show incremented-probabilities
Second Edit:
Now if you have a variable/high number of different options, you might not want to manually write this entire ifelse structure. Luckily Netlogo has the run primitive which allows you to read the content of a string and treat is as a command.
In the following example, I use foreach and word to, one by one, add an ifelse condition for each different option.
to go-3
let outcomes range 20
let probabilities n-values 20 [0.05]
let additive-probabilities (map [[this-probability index] -> sum sublist probabilities 0 (index + 1) ] probabilities range length probabilities)
let the-number random-float 1
show the-number
; Create your big ifelse code block as a string
let ifelse-string "(ifelse"
(foreach outcomes additive-probabilities [ [outcome probability] ->
set ifelse-string (word
ifelse-string
"\n the-number < "
probability
" [show "
outcome
" ]"
)
])
set ifelse-string word ifelse-string "\n)"
print ifelse-string
; Now run the string as if it was code
run ifelse-string
end
Lastly, you can take a look at "Lottery Example" in the models library. It looks a lot simpler than what i did here.
Thank you for posting the answers. The solution i used was to use cumulative probabilities.
let probabilities [0 0 0.3 0.5 0.1 0.1]
let cum-pmf partial-sums probabilities ; returns [0 0 0.3 0.8 0.9 1]
let n random-float 1
report length filter [ [?1] -> ?1 < n ] cum-pmf ; indexing starts at 0
and the function partial-sums is
to-report partial-sums [lst]
report butfirst reduce [[result-so-far next-item] -> lput (next-item + last
result-so-far) result-so-far] fput [0] lst
end

Selecting one of all possible pairs and deleting ones not selected

I will first try to indicate what I am trying to do. Then I will provide code that is not working, code I see two problems with but do not know how to fix. (There may be other problems with the code as well).
I had a file of 200 observations. I created a file that contains all possible combinations of two observations (minus each pair of an observation with itself). In the file there are two cases for each combination, one in which case A is "ego" and case B is "alter", and another where case B is "ego" and case A is "alter". For ease of discussion, I refer to both CaseAB and CaseBA as "pairs". I read this paired file into netlogo.
Each pair has two sets of variables: egoID egovar1 egovar2 egovar3 alterID altervar1 altervar2 altervar3. Ego* variables refer to characteristics of ego. Alter* variables refer to characteristics of alter. Each pair has a joint variable called "worth".
This file means that each individual appears as ego 199 times and as alter 199 times. One wrinkle is each ego has one of the variables that it prioritizes. So, each ego ignores most of the 199 pairs in which it is ego. Two more wrinkles here. First, there is one "priority rule" that lets an ego consider all remaining pairs. That is the rule of using egovar1.
A second wrinkle is that I removed all pairs that an ego cannot consider given its prioritization (I know this means egovar1 priorities are affected. That's substantively okay). But note: egoA can remove pair egoA,alterB as not worth considering, but alterB, when it is ego, can consider egoB,alterA, which is the same pair, just reversed roles.)
Sorry for the long set-up, but I wanted to be clear of the constraints. I want to do the following:
Randomly select an ego,
Have ego select the pair that, given its set of pairs that meet its prioritization, has the maximum worth
Have ego set paired = 1 for that pair (so this ego & alter are no longer available for consideration in any other pairs with egos or alters in action 2 above)
Kill all other pairs where egoID equals the ego's id
Kill all other pairs where egoID equals the alter's id
Kill all other pairs where alterID equals the ego's id
Kill all other pairs where alterID equals the alter's id
In the relevant code:
A. origin is defined elsewhere, to make completed pairs move to the center
B. idx is ego's id
C. idx2 is alter's id
D. myprioritygroup is meant to define the set of pairs the ego considers
NOTE: I've tried a few ways of doing this. This version is not working.
E. I do not know how to "kill" all pairs with ego & alter ids as ego or alter
Here is the snippet of relevant (failing) code:
to matchem
ask pairs with [paired = 0]
[
ifelse
(priority = egovar1)
[ask max-one-of pairs [pairworth]
[ set paired 1
move-to origin
ask other pairs with [idx = idx] [set paired 9]
ask other pairs with [idx2 = idx2] [set paired 9]
ask other pairs with [idx = idx2] [set paired 9]
ask other pairs with [idx2 = idx] [set paired 9]
ask other pairs with [paired = 9] [die]
]
]
[ask max-one-of pairs with [priority = myprioritygroup] [pairutil]
[set paired 1
move-to origin
ask other pairs with [idx = idx] [set paired 9]
ask other pairs with [idx2 = idx2] [set paired 9]
ask other pairs with [idx = idx2] [set paired 9]
ask other pairs with [idx2 = idx] [set paired 9]
ask other pairs with [paired = 9] [die]
]
]
]
tick
EDIT (after comment): In response to JenB, and based on work conducted in the last 24 hours, I have new code (should I delete the above?) and can offer a more focused question. Here is the newer code:
to matchem1
ask pairs with [priorityx >= 4] [die]
(ifelse priorityx = 3 and paired = 0
[ask max-one-of pairs [var2]
[ set paired 1
setxy 0 0
set idegotodelete idx
set idaltertodelete idx2
ask other pairs with [idx = idegotodelete and paired != 1] [die]
ask other pairs with [idx2 = idegotodelete and paired != 1] [die]
ask other pairs with [idx = idaltertodelete and paired != 1] [die]
ask other pairs with [idx2 = idaltertodelete and paired != 1] [die]
]
]
priorityx = 1 and paired = 0
[ask max-one-of mysupportlevelgroup [pairutil]
[set paired 1
setxy 0 0
set idegotodelete idx
set idaltertodelete idx2
ask other pairs with [idx = idegotodelete and paired != 1] [die]
ask other pairs with [idx2 = idegotodelete and paired != 1] [die]
ask other pairs with [idx = idaltertodelete and paired != 1] [die]
ask other pairs with [idx2 = idaltertodelete and paired != 1] [die]
]
])
end
In this version the first line successfully kills off most pairs so I can work with only two priorities, to simplify debugging. That's the end of success. To answer JenB, in this case the command file does not work in the following sense: I cannot get it to do anything in this block except the first line. It sets up the pairs, all pairs take places on the grid, then... nothing. For 18 hours. If I "show count pairs" there is no answer. So, as you can imagine, it is difficult to identify just where the problem is when there is almost no information. Still, I think two (of perhaps several) problems would be solved with answers to these two questions:
How do I a)identify a pair in an ifelse statement, b)get that pair to modify its values on a variable, and c)get that pair to kill selected other pairs.
How do I make a series of ifelse statements that randomly call interwoven subsets of pairs (i.e., a pair can be in multiple subsets) such that a pair identified in question 1 above does the murderous actions in 1) above for all pairs, not just those in the specific subset selected.

Sort list by one item and identify record that meets a condition in Netlogo

I have a list of sublists, something like the following
[[0 “coal” 500 1430] [0 “gas” 300 1300] [1 “coal” 600 1500] [1 “gas” 700 1400]]
I would like to do four things:
1. Sort the main list by item 3 of sublists
2. Cumulatively sum item 2 of sublists until a certain value is reached.
3. Identify item 3 of the last list added.
4. Then I’d like to identify items 0 and 1 of lists that were added to the loop in point 2 and ask those turtles to do something.
I've been exploring tables, lists, etc but struggling with this complex bit of code. Can people suggest how they would code this?
Thanks in advance for the help!
The following seems to answer the case, I think...though there may be more elegant ways
to go
create-turtles 2
let l [[0 "coal" 500 1430] [0 "gas" 300 1300] [1 "coal" 600 1500] [1 "gas" 700 1400]]
;sort the list by item 2
let sorted sort-by bigger l
show sorted
;accumulate item 3 until limit reached
let k 0
let n 0
let limit 2800
let turtleNos []
let fuels []
while [k < limit]
[
set k k + item 3 ( item n sorted )
;accumulate item 0 and 1
set turtleNos lput item 0 ( item n sorted ) turtleNos
set fuels lput item 1 ( item n sorted ) fuels
set n n + 1
]
show k
;show item 3 for the last item added to k
show item 3 (item ( n - 1 ) sorted)
;accumulated lists - note non-unique
show turtleNos
show fuels
(foreach turtleNos fuels
[ [x y] -> ask turtle x [show y] ])
end
to-report bigger [l1 l2]
report item 2 l1 > item 2 l2
end

How to add a row to a matrix in NetLogo?

I have a 2*2 matrix called orders:
0 0
0 0
created by:
set orders matrix:from-row-list [[0 0] [0 0]]
and I would like to change it to be a 3*2 matrix:
0 0
0 0
10 50
How to do this please? (I guess you have to create a new matrix also called orders to overwrite the existing orders, but I couldn't figure out the syntax.)
The easiest way would probably be to first convert your matrix to a list, than add the new row to the list, and convert it back to a matrix. Not very elegant, but the report function below should do the trick:
extensions [ matrix ]
to-report matrix-add-row [matrix row-added]
let temp-list matrix:to-row-list matrix ;; converts the matrix to a list
set temp-list lput row-added temp-list ;; the new row is added to the list
report matrix:from-row-list temp-list ;; converts the list back to a matrix
end
to test
let orders matrix:from-row-list [[0 0] [0 0]]
show orders
show matrix-add-row orders [ 10 50 ]
end
This would return you:
observer> test
observer: {{matrix: [ [ 0 0 ][ 0 0 ] ]}}
observer: {{matrix: [ [ 0 0 ][ 0 0 ][ 10 50 ] ]}}
Of course, you have to make sure that the dimensions of the matrix and the row added match.
Just in case this helps someone else, what I did in the end was use a new agent breed 'orders' in place of the matrix, with each orders turtle essentially being what would have been a row in the matrix. I told the orders to sit on the same patch as the turtle that owned it, which was easy as the turtles in that model don't move. The advantage is that I had access to a wide range of processing possibilities that I didn't have with the matrix. Of course if you do this and the order of the rows matters, you need to include some way of managing this (something like orders-own [index] would do).

Random sized array and exactly three ones in it

Good morning,
I need to create a list of [0,1], which length is defined by the user (global variable). There is an easy solution for this:
set listInd (list n-values numOfInd [random 2])
But I need to make sure, that the list has exactly three ones in it and they are placed on random positions. Is there a way to do this?
Regards.
You can create 3 random numbers (indices) and then create a list with 1 on the positions defined by this index list:
to-report rand-list[n k]
let ind-list n-of k n-values n [?]
report n-values n [ifelse-value (member? ? ind-list) [1][0]]
end
Usage:
show rand-list 10 3
; result:
; [0 1 0 0 0 0 1 0 0 1]
An alternative to bergant's solution would be to gather the desired amount of ones and zeros, then mix them together randomly:
to-report rand-list [n k]
let zeros n-values (n - k) [0]
let ones n-values k [1]
report shuffle sentence zeros ones
end