I am creating a program that creates 1,500 "scientists" (turtles), in a world where there are 1500 "disciplines".
I need to assign each turtle a "discipline" as a number between 1-500, and ensure that there are 3 turtles for each discipline.
This means that set(random) isn't appropriate. Is there a primitive I can use?
Nevermind, I think I have figured it out. Does this make sense?
to set-discipline
ask turtles [ set discipline -1 ]
let unassigned turtles
let current 1
while [any? unassigned]
[
ask n-of (min (list group-size (count unassigned))) unassigned
[ set discipline current ]
set current current + 1
set unassigned unassigned with [discipline = -1]
]
end
Your code works, but is very... unnetlogoish. while loops and indexes, in NetLogo, are often a sign that you're not taking full advantage of the capabilities of the language. At the very minimum, I would replace your while with a foreach:
ask turtles [ set discipline -1 ]
foreach n-values 500 [ 1 + ? ] [
let unassigned turtles with [ discipline = -1 ]
ask n-of (min (list group-size (count unassigned))) unassigned [
set discipline ?
]
]
You can check the result in the command center with:
observer> print remove-duplicates map [ count turtles with [ discipline = ? ] ] n-values 500 [ 1 + ? ]
[3]
But I would suggest taking it a step further.
Having disciplines just be numbers is a bit limiting. That is, of course, the sort of thing that you would do in a traditional mathematical model. But this is an ABM. And you have all of NetLogo at your disposal. You could make your disciplines... turtles! This would make your model much more flexible. Your disciplines could eventually be heterogeneous: having their own variables, by which they could differ from one another. And I think you'll find that replacing numbers with agents makes code more readable in general.
Here is a full example of what I have in mind:
breed [ scientists scientist ]
scientists-own [ my-discipline ]
breed [ disciplines discipline ]
to setup
clear-all
let nb-scientists 1500
let nb-disciplines 500
let group-size 3
create-scientists nb-scientists [
set my-discipline nobody
]
create-disciplines nb-disciplines [
let unassigned scientists with [ my-discipline = nobody ]
ask n-of (min (list group-size (count unassigned))) unassigned [
set my-discipline myself
]
]
end
You can check that it worked:
observer> setup
observer> print remove-duplicates [ count scientists with [ my-discipline = myself ] ] of disciplines
[3]
But you know what? If it was me, I would take it another step further, and make the relation between a scientist and its discipline an actual NetLogo link. This way, you could eventually envision a scientist belonging to (gasp!) more than one discipline. Here is what it would look like:
breed [ scientists scientist ]
breed [ disciplines discipline ]
to setup
clear-all
let nb-scientists 1500
let nb-disciplines 500
let group-size 3
create-scientists nb-scientists
create-disciplines nb-disciplines [
let unassigned scientists with [ not any? my-links ]
ask n-of (min (list group-size (count unassigned))) unassigned [
create-link-with myself
]
]
end
Don't you think the code is even nicer this way? And it works:
observer> print remove-duplicates [ count my-links ] of disciplines
[3]
What Nicolas said. But consider also this alternate approach:
to test
let disciplines reduce sentence map [(list ? ? ?)] n-values 500 [? + 1]
ask turtles [
set discipline first disciplines
set disciplines butfirst disciplines
]
end
ask is in random order, so we don't need to shuffle the disciplines list.
Or, if the turtles don't exist yet and you're free to create them as you go:
to test
let disciplines reduce sentence map [(list ? ? ?)] n-values 500 [? + 1]
foreach shuffle disciplines [
create-turtles 1 [ set discipline ? ]
]
end
In this one, we have to shuffle the list of disciplines, since the turtles are being created in who number order.
Related
I use the nw extension in NetLogo to create a network.
My aim is the following:
Check if any turtles are close by
If any turtles are close by (I tried with next 3 patches, but that might change), connect to them + there is an upper limit of possible links
I tried (and, I think, succeeded) to implement the approach, described here. Which means that the upper limit works. However, I am not able to only link to the turtles close by. I also need something to catch the error if no turtle is close by. I tried to use the in-radius command but for some reasons, it doesn't do what I want.
extensions [nw]
breed [ AT1s AT1]
turtles-own [ friends ]
to setup
ca
create-AT1s 20 [
setxy random-xcor random-ycor
set friends 3
set color green
get-radius-friends friends AT1s
]
end
to get-radius-friends [ x AgentT]
let lonely AgentT with [count my-links < x]
let candidates other AgentT with [ any? AgentT in-radius 3 AND count my-links < x ]
let new-links x - count my-links
if new-links > 0 AND any? AgentT in-radius 3
[ let chosen n-of min (list new-links count other candidates) other candidates
create-links-with chosen [ hide-link ]
set candidates other candidates
ask chosen [ if my-links = x [ set candidates other candidates ] ]
]
end
I also found the neighbors and the distance commands but these only consider the immediate surroundings which isn't what I need.
Actually, that's not the best question to pull from if you want to spatially restrict the turtles. And there's a serious problem with having the connection within the turtle creation block because there are no potential friends for the turtles created first. Unless you have a very large number of turtles, you probably don't need to worry about efficiency.
I also think the variable 'x' is unnecessary because you have the variable 'friends' available (which appears to be the number of links you want the turtle to have). And there is a new reporter up-to-n-of which makes the whole list and min unnecessary.
I think this does what you want. You may want to test is without the hide-link so you can see what it does.
breed [ AT1s AT1]
turtles-own [ friends ]
to setup
clear-all
create-AT1s 100
[ setxy random-xcor random-ycor
set friends 3
set color green
]
get-radius-friends 10 AT1s
end
to get-radius-friends [ #radius #breed ]
let linkers turtles with [breed = #breed ]
ask linkers
[ let new-links friends - count my-links
if new-links > 0
[ let candidates other linkers with [ count my-links < friends ] in-radius #radius
let chosen up-to-n-of new-links candidates
create-links-with chosen [ hide-link ]
]
]
end
I have two turtle breeds (products and consumers) each with a 3-dimension list that defines their needs (consumers) and their attributes (products).
What I'd like is to have each consumer (i) look for a product that satisfies all their needs and create a link with them. If that product does not exist, I'd like them to (ii) drop the one need with the lowest value and look for a product that satisfies the other two. If that product does not exist, then I want them to (iii) look for another one that only satisfies the need with the highest value.
So say that consumer 20 has needs [0.2 0.5 0.3]. If they find a product whith exactly the same list of attributes [0.2 0.5 0.3] I want the link to take place. If there's no such product then I want the consumer to ignore the lowest value (0.2 in the example) and look for a product that has attributes [xx 0.5 0.3], where xx stands for "whatever".
Using examples from elsewhere here in SO, I have cobbled up the following code that (almost!) does the first part of the trick (i), but can't manage to do (ii) and (iii) despite multiple efforts. Would anyone have an indea on how to do this?
breed [consumers consumer]
breed [products product]
consumers-own [
needs
]
products-own [
attributes
number-buyers
]
to setup
ca
create-consumers 100 [ setxy random-xcor random-ycor]
create-products 100 [ setxy random-xcor random-ycor]
set-default-shape consumers "person"
set-default-shape products "box"
ask consumers [
set needs n-values 3 [ precision (random-float 1) 1 ]
]
ask products [
set attributes n-values 3 [ precision (random-float 1) 1 ]
]
reset-ticks
end
to go
buy
tick
end
to buy
ask links [ die ]
ask consumers [
carefully [ create-link-with one-of products with [reduce and (map = attributes [ needs ] of myself)] ] [ show "how do I find a sub-optimal product by ignoring my need with the lowest value ?" ]
]
ask products [
set number-buyers count link-neighbors
]
end
You are overthinking the full match - just check if the two lists are the same. However, the almost match is a bit more complicated. Here is a complete example model that finds the position in the list of the lowest and then checks that the other items are the same.
breed [consumers consumer]
breed [products product]
consumers-own [
needs
]
products-own [
attributes
number-buyers
]
to setup
clear-all
ask patches [set pcolor white]
create-consumers 10
[ setxy random-xcor random-ycor
set shape "person"
set color blue
set needs n-values 3 [ one-of [1 2 3] ]
]
create-products 10
[ setxy random-xcor random-ycor
set shape "box"
set color red
set attributes n-values 3 [ one-of [1 2 3] ]
]
reset-ticks
end
to go
ask consumers [buy]
tick
end
to buy ; set up as turtle procedure for testing purposes
ask my-links [ die ]
let candidates products with [attributes = [needs] of myself]
ifelse any? candidates
[ create-link-with one-of candidates ]
[ type self type " attributes: " type needs print " no matches"
let lowpoint position (min needs) needs ; note will take first if two equal min
set candidates products with
[ ((item 0 attributes = item 0 [needs] of myself) or lowpoint = 0) and
((item 1 attributes = item 1 [needs] of myself) or lowpoint = 1) and
((item 2 attributes = item 2 [needs] of myself) or lowpoint = 2)
]
ifelse any? candidates
[ create-link-with one-of candidates ]
[ print "no almost-match available" ]
]
end
I created the agentset (called candidates) of potential products to link to and then created the link. This makes the code much more readable. It also allows the if any? construction if no matches were found. And it also makes it easier to debug because you can put print statements reporting numbers of matches and similar. I recommend that you always do this if you are applying some sort of condition that restricts the choices.
Also, you have a three item list with 10 possible values for each item. This means there will be 1000 combinations. You only have 100 consumers and 100 products, so matching will be fairly rare.
I want to ask turtles to do something in two cases: 1) if ALL the members of their network show a particular feature, 2) if AT LEAST one member of their network show a particular feature. For the first case I tried
ask turtles [ if all? link-neighbors [audit?-last-tick = false] [
set delta 0]
and it works.
For the second case I tried
ask turtles [ if any? link-neighbors [audit?-last-tick = true] [
set delta 1]
or
ask turtles [ if one-of link-neighbors [audit?-last-tick = true] [
set delta 1]
but it does not work.
Can you help me, please?
You need with:
ask turtles [
if any? link-neighbors with [ audit?-last-tick ] [
set delta 1
]
]
Note that you can also use with instead of if:
ask turtles with [ any? link-neighbors with [ audit?-last-tick ] ] [
set delta 1
]
And that, unless you expect audit?-last-tick to possibly have a non-boolean value, you can directly write [ audit?-last-tick ] instead of [ audit?-last-tick = true ].
how can i create a dynamic number of breeds at runtime?
I want my user to be able to choose the amount of breeds.
I thought about something like
to setup_breeds
let j 1
while[j <= n_groups][
breed[j]
]
end
where n_groups is the number of breeds whick is taken from a slider.
But unfortunatly i cannot use breed inside a method...
Any ideas?
Thanks!
You need to explicitly declare each breed with the breed keyword, so the short answer is: no, you can't have a dynamic number of breeds.
But do you really need actual NetLogo breeds? The main purpose of having different breeds is to have different variables for each breed. If that is not case, perhaps you can get away with having a group-id turtle variable. To create a certain number of turtles for each of n_groups, you could just do something like:
turtles-own [ group-id ]
to setup
clear-all
let n_groups 10
let n-turtles-per-group 5
foreach n-values n_groups [ ? ] [
create-turtles 10 [ set group-id ? ]
]
ask turtles [ set label group-id ]
; do something with only turtles of, e.g., group 2:
ask turtles with [ group-id = 2 ] [
fd 5
]
end
If you think you really need breeds, edit your question to tell us why, and we'll see if we can find a solution for you.
Side note:
I used foreach n-values n_groups [ ? ] to loop through your n groups. That's the equivalent of:
let i 0
while [ i < n_groups ] [
set i i + 1
]
...but arguably more "NetLogo-ish".
I'm constructing a model of cockle growth for fisheries. One aspect of cockle biology is predation. I want to ask a specified number of turtles in a patch to die each tick. The Dictionary doesn't really provide the answer and I don't know how to model this. Any suggestions?
Thanks a lot!
Supposing you want five turtles on each patch to die, then:
ask patches [
let limit count turtles-here
ask n-of (min 5 limit) turtles-here [
die
]
]
The use of min is necessary because if I just said n-of 5 turtles-here, I would get an error on any patches with fewer than five.
You might also write:
ask patches [
repeat 5 [
if any? turtles-here [
ask one-of turtles-here [
die
]
]
]
]
which amounts to the same thing, but the n-of version should run faster.
If you want to kill 'n' turtles (replace 'n' in the code by an integer)
to kill-n-turtles
repeat 'n' [
ask one-of turtles [die]
]
end