How equivalent are the following two lines of code?
ask agentset [if (attr > 0) [dosomething]]
ask agentset with [attr > 0] [dosomething]
Are there any expected (and explainable) differences
in speed or the use of memory?
In particular, does the use of with in the second
case lead to the creation of a temporary agentset,
or does the ask ... with combination ensure
lazy evaluation?
Just to expand on what Nicolas said:
ask-with is intentionally not lazy. Consider code where the agents are modifying the attribute of other agents that determines whether or not they should be asked, so to speak:
to go
ask patches with [ pcolor = red ] [
ask neighbors4 [ set pcolor red ]
]
tick
end
Because with creates a temporary agentset, which agents are asked is not changed mid-ask. This causes the red region to grow evenly as go is called repeatedly:
Now, consider it with if instead.
to go
ask patches [
if pcolor = red [
ask neighbors4 [ set color red ]
]
]
tick
end
Because patches that have not been asked yet might have have their color changed to red, the red region grows unevenly:
Thus, ask-with actually has different behavior then ask-if, and so cannot be optimized to it. That said, as Nicolas mentioned, some similar optimizations are done.
(Disclaimer: my familiarity with the compiler is very superficial, so I might be missing something.)
A (possibly outdated) list of the optimizations performed by the NetLogo compiler is available here:
https://github.com/NetLogo/NetLogo/wiki/Compiler-architecture#optimizer
The actual code for those (in NetLogo 6.0) is here:
https://github.com/NetLogo/NetLogo/blob/bd3da2bf5495674ce5690cbb2992de4036c9db03/netlogo-core/src/main/compile/middle/optimize/package.scala
As far as I can tell, there is no optimization for ask ... with. It means that ask ... with does create an intermediate agentset and, as such, probably requires more memory and time than ask ... [ if ... ].
Which one you should use probably depends on where you stand on the efficiency/readability trade-off. (I personally find ask ... with more readable.)
Would it be a good idea to optimize ask ... with? In my opinion, absolutely. That being said, there might have been a technical reason not to do it at the time the compiler was written. I just don't know about it.
There is, indeed, a very good reason why ask ... with is not optimized. And there are behavioral differences between the two versions; it's not just a matter of readability vs. efficiency. See Bryan's answer.
Related
I have a model that tracks energy levels in sheep, which I have defined as a breed. I have set up a monitor to calculate the mean energy level for the sheep, but need to consider what happens if there are no sheep at some point. I have tried numerous variations, the simplest of which is:
ifelse (any? sheep) ["NA"] [mean [energy] of sheep]
Unfortunately, I keep getting the error
Expected reporter.
I can work around this by creating a new global variable and reporter in the code, but this seems to be a bit of a waste. Am I doing something wrong or are there limitations on what kind of calculations can be done in a monitor on the Interface? If so, where are these limitations summarized?
Answer in three steps:
Difference between ifelse and ifelse-value
You are getting a syntax error there because a monitor expects a reporter, but ifelse is made to handle commands. The reporter version of ifelse is ifelse-value. If you just change ifelse to ifelse-value in your example, you see that you don't get any syntax error anymore. However, you will also see that if you do so and start your model with sheep having their energy, the monitor will show NA - see next point.
Correct use of any?
This happens because you are using any? the other way around. As you can see, any? reports true if the agentset is not empty. This means that the following reporter:
ifelse-value (any? sheep) ["NA"] [mean [energy] of sheep]
will report "NA" when there are sheep in the model, because that is the first reporter block.
Switch to:
ifelse-value (any? sheep) [mean [energy] of sheep] ["NA"]
and you have what you want. Just as in English: "If there are any sheep, do the calculation, else, this does not apply".
In any case, this does the job but it is superflous - see next point.
What monitors can do
Monitors are able to handle some of their reporters' runtime errors. You can simply put mean [energy] of sheep in your monitor and it will automatically show N/A when there are no sheep, without the need for you to handle the case.
Looking for a way to store a turtle length of stay in the model after they have left the model. My model runs for several months and a few thousand turtles enter, undergo process then leave the area. It's complicate model (it's a hybrid DES and ABM) so I've tried to reproduce the simple bit below.
Turtles will be created at every tick and given a random length of stay but will only be able to begin process when they move to the right area (area-name) and when their time is up they leave the area. Their time-in-system reflects the wait for the area and the length-of-stay which I want to save once they're complete. If I leave them in the model it starts to break down after a couple of months and I suspect this is because the model has too many turtles still in the system for calculation and is inefficient.
go
create turtles 2
[
set time-in-system 0
set length-of-stay ceiling ((random-normal 48 4) + ticks)]
set shape "person"
if any? area-name with [not any? turtles-here]
[move-to one-of area-name]
]
undergo-process
end
to-undergo-process
ask turtles with [shape = "person"]
[
set time-in-system time-in-system + 1
]
ask turtles-on area-name
[if ticks = length-of-stay
[set shape "dot"
move-to exit-door]
end
I can then plot and see in realtime to make sure it is working
histogram time-in-system of turtles with [shape = "dot"]
but can't seem to figure out how to store them as unique values for plotting after the model has run and I have a dataset of outcomes without keeping them alive in the model. The real-time plot isn't necessary as long as I can store the unique values after they have left
If I ask them to die then I lose the unique values in the histogram. I don't want a tally of all values but each turtle's unique value at the end of the process after they left - at the moment the only solution I have to storing them is as an agent-set that stays alive in the exit-door patch but this takes up a lot of calculation power as the model progresses for months...
There may be a really simple command for this but I've been going round in circles through the programming manual trying to find it. Any tips appreciated
You should create a list storing the values of turtles that left.
Isolating only the code that is relevant for this purpose, it would be something like:
globals [
times
]
to setup
set times (list)
end
to leave-simulation ; This being executed by turtles.
set times lput (time-in-system times)
die
end
If your program is going to run for actual months, I recommend you use the file-write command to store your data. This way the data is preserved if the program halts for any reason; it gives you much more freedom to do the analysis you want without running the full simulation again.
If you write to a .csv (comma separated value) file, you can use almost any program (excel, R, matlab, python, C# or back to netlogo) to plot a histogram.
I'm writing a program that is testing the ability of turtles to navigate in an environment. This line of code has two checks. I want it to kill off the oldest turtle that has made the least amount of progress. It does what it is supposed to, but the program slows down dramatically, creating little hiccups every time it makes this check. I was wondering if someone knew a better way to go about this or make this line more efficient. Thanks!
ask one-of turtles with [XCOR = min [XCOR] of turtles with [age = max [age] of turtles]] [
die]
First of all, there are primitives that are specifically finding the turtles with maximum or minimum values of some variable using min-one-of or with-min. So your code would look something like this I think:
ask min-one-of (turtles with-max [age]) [xcor] [...]
I suspect that would solve your efficiency problem, since it may well be because of implicit brackets not being where you think they are so it's trying to loop everything a couple of times. But a much cleaner to read version that would solve the efficiency problem is to specifically limit the position loop to those who are oldest and then choose the lowest position from that set.
let old-turtles turtles with-max [age]
ask min-one-of old-turtles [xcor] [...]
I am struggling with trying to write this command.
Basically, what I want to do is this:
I have a collectors [own carryingcapacity], bins [own waste in bins] and warehouses [own waste_in_warehouse] as turtles.
The collectors have a maximum carrying capacity value.
I want to make the collectors check if they have carrying capacity when they find a bin, if trash in bin <= collectors max capacity, the collectors will collect, if not they'll ignore the bin.
I came up with something like this:
ask collectors
[
if any? bins with [distance myself <= 1]
[set carryingcapacity (carryingcapacity + (bins_holding_capacity of myself))]
]
Second command:
I want to make the collectors take the trash they collected to the warehouse. But this variable's value might change from one collector to another, and might vary with the quantity of bins they checked.
I want the warehouse to sum the value that it already had it on the previous days with the new daily values.
I came up with something like this as a line of command:
ask warehouses
[
if any? collectors with [distance myself <= 1]
[set waste_in_warehouse ( waste_in_warehouse + (carryingcapacity of myself))]
Thank you in advance.
Best wishes.
If I'm understanding correctly, your code for command #1 is doing the following:
ask collector agents
See if there are any bins close to a collector (but you didn't ask for this group)
increment the collector's carryingcapacity by (bins_holding_capacity) of myself (you can't use myself like this, because you aren't in a nested ask)
Step 3's logic also seems wrong, since this would be changing the collector's capacity each time, as opposed to checking if it is full.
It should be noted that by using any?, you are not also commanding that group of agents. any? only returns true or false, corresponding to whether there were agents that met the criteria or not. You need to use a second ask to command these agents.
Based on what you said, you'll need to do something like this:
ask collectors
[
ask bins with [distance myself <= 1] ;;myself refers to the collector asking
[
ifelse (waste_in_bin + [waste_in_collector] of myself) > [carryingcapacity] of myself
[;;add the waste to the collector]
[;;stop asking bins, collector is full]
]
]
These same steps should help with your second question, too.
I am currently making a simulation (for homework) using genetic algorithms. What I want to do is compare the fitness of agents on a specific patch and the one with the lowest fitness will die.
I have scoured the net and found this code: if any? breed1-here with [fitness > fitness-of myself] [die]]
But this doesn't seem to work and now I'm completely out of ideas.
let goner min-one-of breed1-here [fitness]
if is-turtle? goner [ ask goner [ die ] ]`
the is-turtle? check is necessary because the patch might be empty.
Yes, that code is from an old version of the NetLogo language. That line of code should be re-written as:
if any? breed1-here with [fitness > [fitness] of myself] [die]]
Of course, that code will kill all turtles in a patch except for the one(s) with maximum fitness, which is not exactly what you want.