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.
Related
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
I'm struggling with the following problem using these variables:
set variablex .5
set threshhold-list [0 .3 .6]
set variable-list [0 0 1]
I have three agenttypes 0,1,2 that correspond to the index position of threshhold-list and variable-list. So Agent 0 has threshold 0 and variable 0, Agent 1 has threshold .3 and variable 0, and Agent 2 has threshold .6 and variable 1.
What I'd like to do is check if any agent has a threshold greater than zero and less than variablex. If so, update that agent's variable on the variable list to variablex. That is, for the variables above I'd like to run logic that produces a new variable-list like this one:
variable-list [0 .5 1]
But if variablex was .7, it would produce [0 .7 .7].
I've got some code I've been hacking away at but I feel like it's way more complicated than the problem and so I'm wondering if someone could point me in the right direction. Thanks so much!
There are a few different ways to approach the problem, but if I was in your situation, I would first write a small reporter that gives me the value that should be stored at each index:
to-report new-value [ i ]
let t item i threshhold-list
report ifelse-value (t > 0 and t < variablex)
[ variablex ] ; the variable's new value should be variable x
[ item i variable-list ] ; the variable's value should not change
end
Once you have that, you can use either foreach or map to change your variable list:
to update-variables-with-foreach
foreach range length variable-list [ i ->
set variable-list replace-item i variable-list new-value i
]
end
to update-variables-with-map
set variable-list map new-value range length variable-list
end
Here is a somewhat verbose test to check that both versions would give you the expected results:
globals [
variablex
threshhold-list
variable-list
]
to test
clear-all
set threshhold-list [0 .3 .6]
set variablex .5
set variable-list [0 0 1]
update-variables-with-foreach
print variable-list
set variablex .5
set variable-list [0 0 1]
update-variables-with-map
print variable-list
set variablex .7
set variable-list [0 0 1]
update-variables-with-foreach
print variable-list
set variablex .7
set variable-list [0 0 1]
update-variables-with-map
print variable-list
end
That being said, as much as I think it is fun to play with lists, I think you are approaching your problem in a very unnetlogoish way.
NetLogo's world is a world of turtles and patches and links, not a world of arrays and indices and numbers.
You could do something along the lines of:
globals [
variable-x
]
turtles-own [
threshhold
variable
]
to setup
clear-all
set variable-x .5
(foreach [0 .3 .6] [0 0 1] [ [t v] ->
create-turtles 1 [
set threshhold t
set variable v
]
])
ask turtles [ update-variable ]
ask turtles [ show variable ]
end
to update-variable ; turtle procedure
if threshhold > 0 and threshhold < variable-x [
set variable variable-x
]
end
I don't know what you're ultimately trying to achieve, but if I could offer general advice, it would be to try to embrace to NetLogo mindset. Every time you're tempted to use an index of some kind in your code, take a step back and think again: there is probably a better (as in "more netlogoish") way to do it.
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).
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
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.