Efficient selection of turtle subsets that satisfy given condition on turtle properties - netlogo

I was wondering if there is an efficient method for selecting subsets of agents that satisfy some conditions on their properties in one shot (i.e., a multi-set of agents). I will try to explain with an example.
Let us suppose that we have three families of turtles, let's say A, B and C.
The turtles share some similar variables, such as p1, p2.
I want to efficiently determine the agent-sets A1, B1 and C1, with A1 \in A, B1 \in B, and C1 \in C, and that undergo some conditions on their variables, for example, p1 of A = p1 of B and p2 of B = p2 of C (of course this should be checked and verified for all the sets in parallel). My final goal is to determine the total number of agent combinations that satisfy the conditions on their variables (the cardinality of the multiset).
I know that I can do that with a series of nested "ask" command, with something like the following code:
;;I'm omitting for simplicity the case where A1, B1 or C1 are empty-sets.
let counter 0
let A1 A with [some condition on A]
ask A1 [
let myp1 [p1] of self
let B1 B with [p1 = myp1]
ask B1 [
let myp2 [p2] of self
let C1 C with [p2 = myp2]
set counter counter + count C1
]
]
;;at the end counter will contain the number of possible agent combinations
I was wondering if there is a more intelligent way to do that without the need to "unroll" the selection through a series of nested ask commands.

I don't believe the code you have outlined will work anything like you think it will. In NetLogo, ask loops through all the turtles/patches (in random order) and each one follows the instructions. So each of the A1s will create B1s and each of those will count C1s. So you will end up with two layers of double counting.
But I think all you want to do is count a common condition. If so, look at this example:
breed [typeAs typeA]
typeAs-own
[ var1
var2
]
breed [typeBs typeB]
typeBs-own
[ var1
var3
]
to testme
clear-all
create-typeAs 10
[ setxy random-xcor random-ycor
set var1 one-of ["A" "B"]
set var2 one-of ["C" "D"]
set color red
]
create-typeBs 10
[ setxy random-xcor random-ycor
set var1 one-of ["A" "B"]
set var3 one-of ["E" "F"]
set color blue
]
type "Number of As with value A is " print count typeAs with [var1 = "A"]
type "Number of Bs with value A is " print count typeBs with [var1 = "A"]
; combine and count the condition in the combined agentset
let both-types (turtle-set typeAs typeBs)
type "together there are: " print count both-types with [var1 = "A"]
end

Related

Netlogo, how to capture values of variables of turtles and add each turtles value, map them, and reduce them to a single #

I am trying to write a procedure where a turtle of a certain breed asks turtles of the same breed, within a certain distance, the value of a certain variable. The asking turtle will then capture the values add them to it's own, map + them and then reduce + to a single number. Here's the code
ask Teams
[ if AsgnE = "E 1"
[
ask Teams with [ distance myself < 25]
[
; assuming that there are no more then 2 teams within distance
let Val1 []
let Val2 []
let Val3 []
set Val1 Value
set Val2 Value
set Val3 [Value] of self
let Val4 (map + Val1 Val2 Val3)
set Val4 (reduce + Val4)
set Storys1 [Stories] of Epic 0
if Storys1 > 0 [ set TotValue1 Val4 ]
]
]
]
The values of each Team continuously update as long as the go button is pressed. The issue is that the resulting number never matches the aggregate of all the values. As the number updates they never match the totals of the separate Teams. Sometimes the number drops to a lower number (I'm assuming it's representing a single teams value) before jumping back to a higher number.
Any idea on how to fix this?
Thanks
Rudy
My guess is that it's a synchronicity problem. The ask will iterate (in random order) through all the turtles. Let's say it starts with turtle 1 - so turtle 1 updates its value to be the sum of its old value and all the values of the nearby turtles. Then the ask moves on to turtle 2, and turtle 2 happens to be nearby to turtle 1. That means turtle 2 adds all the numbers again, with turtle 1 having its adjusted value. With just these two turtles, the value for turtle 2 gets added in twice because turtle 1 also has it hidden in its new value.
If this is not the behaviour you want, the easiest thing to do is to have an extra variable called something like next-value. Calculate next-value for each turtle as the appropriate sum. Then, in a new ask, get each turtle to set value next-value to update them all at the same time.
Also, your map and reduce seems unnecessarily complicated. If what you are trying to achieve is to add the value of a variable over a bunch of turtles, then you can simply do a sum of the variable after constructing the relevant turtle agentset. But it may be that you simplified for the purposes of the question, in which case just ignore this!
UPDATE ---- added complete model example
turtles-own
[ team
myval
nextval
]
to setup
clear-all
create-turtles 20
[ setxy random-xcor random-ycor
set team one-of ["A" "B"]
set myval 1
]
reset-ticks
end
to go
ask turtles
[ let myteam turtles with [team = [team] of myself]
set nextval sum [myval] of myteam
]
type "total before: " print sum [myval] of turtles
ask turtles
[ set myval nextval
]
type "total after: " print sum [myval] of turtles
end

adding agentset from different agents togother into a let

my model is a network of agents connected to each other with links.
I try to create a agentset from the neighbors of an agents and their neigbors and so on (I need this to assign different values to it).
However when I create a let with the agentset in it. the agents asked to make this agentset all have their own, this is so far so good. But when I want the original agent to ask him his second line neighbors he just returns an agentset from one of this neighbors instead of the combined agentsets of all his second line neighbors
I want the neighbors to store their own neighbors into a agentset with all the neighbors from the different agents in that set.
I cant ask the let agentset to simple do turtleset current-agentset new-agentset since in a let you cant ask to let variable. So a code which would normally be set second-neighbors (turtle-set second-neighbors other-nieghbors doesnt work since I cant ask second-neighbors already in a let
I also cant make this a global or somethins since it is agent specific.
the code I have so far looks like this
ask companies [
let this-company self
let b link-neighbors
ask b [ let c link-neighbors with [self != this-company]
ask c [ let d link-neighbors with [not member? self b]
ask this-company [
set iburen b
set iiburen c
set iiiburen d
]
]
]
]
so what I want is that all the agents in the agentset c report their link-neighbors like they do now. But also store these link-neighbors into a new agentset which has all the link-neighbors of all the agents in c. like a simple i i + 1. but than with turtle-set (what I have) (what is new from the next agent asked)
the same goes for d
If I run the model now agents report different agentset almost every tick. They just pick one agentset from any of these agents instead of combining them all togother.
Here is what I think you need:
extensions [ nw ]
breed [ companies company ]
companies-own [
buren ; a list of agentsets, with one item for each "level" of neighbors
]
to setup
clear-all
; create a random network and lay it out:
create-companies 20 [ create-links-with n-of 3 other companies ]
repeat 30 [ layout-spring turtles links 0.2 5 1 ]
let num-levels 3
ask companies [
let all-neighbors other nw:turtles-in-radius num-levels
set buren (list) ; initialize to empty list
foreach range num-levels [ i ->
let neighbors-at-this-level all-neighbors with [
nw:distance-to myself = i + 1
]
set buren lput neighbors-at-this-level buren
]
]
; demonstrate how to access the levels (sorted only for display purposes)
ask one-of companies [
show sort item 0 buren ; first level neighbors
show sort item 1 buren ; second level neighbors
show sort item 2 buren ; third level neighbors
]
end
This might not be the most efficient code possible, because it goes through the list of all neighbors once for each level, but unless you have a humongous network, you should not notice.
If you really wanted to use variables like iburen, iiburen and iiiburen, you could always alias the items of the list:
set iburen item 0 buren
set iiburen item 1 buren
set iiiburen item 2 buren
...but I don't recommend it. Having your agentsets in a list should encourage you to think of your levels in a more general way.

Choosing action based on maximum value among multiple variables

My turtles have calculate several variables each tick: gain-stay, gain-move, gain-grow, gain-shrink. I would like them to either stay, move, grow or shrink according to whichever of those variables had the highest value. I have, procedures called to stay, to move, etc.
For simplicity, if several variables are tied for the maximum value, I don't care which of their strategy is taken. If it would be easier to name the variables just shrink, stay, etc., that's just fine.
It seems like I have two steps to solve:
Extract the name of the variable having the maximum value.
Run the strategy indicated by that name. I think this would use runresult and am less stumped by it than by the first step.
Any help would be very appreciated. Thank you very much.
This code selects the best strategy by comparing the maximum value found with the contents of each variable and constructing a list of names of all the matches. It then randomly selects one of the names. This is a complete program, you can open a new model and just copy it in to see what happens.
turtles-own [s1 s2 s3 s4]
to setup
clear-all
create-turtles 20
[ setxy random-xcor random-ycor
set s1 random 10
set s2 random 10
set s3 random 10
set s4 random 10
]
reset-ticks
end
to go
ask turtles
[ type "my values are " type s1 type s2 type s3 print s4
let maxval max (list s1 s2 s3 s4)
let strategy-list []
if maxval = s1 [set strategy-list fput "s1" strategy-list]
if maxval = s2 [set strategy-list fput "s2" strategy-list]
if maxval = s3 [set strategy-list fput "s3" strategy-list]
if maxval = s4 [set strategy-list fput "s4" strategy-list]
let chosen one-of strategy-list
run (word "do-" chosen)
]
tick
end
to do-s1
print "I chose s1"
end
to do-s2
print "I chose s2"
end
to do-s3
print "I chose s3"
end
to do-s4
print "I chose s4"
end

How to send parameters using NetLogo?

I am quite new to NetLogo and here is what I am stuck here for weeks.
What I want to do is to make agents to be in group of 4 for 2 teams.
My plan is to make a function hold 4 turtles id,
to assign-groupmates [a1 a2 a3 a4]
and assign them to team 1
assign-groupmates [a1 a2 a3 a4] =team1[]
if team1 > 4
assign-groupmates [a1 a2 a3 a4] =team2[]
What I done is:
to assign-groupmates [ f g h i]
let A who random f
let B who random g
let C who random h
let D who random i
ifelse f != g, g != h, h != i, i != g
[ set group-id 1
separate
align
cohere ]
[assign-groupmates [W X Y Z]
set group-id 2]
end
How can I find the turtles-id and how can i send them through parameter?
the turtles-id I used is who random.
Thank you.
There are many different ways to accomplish what you want, but let me start with a piece of general advice:
Don't use who numbers if you can avoid it.
They have some legitimate uses, but those are few and far between. They are error prone, they lead to ugly code, and they tend to make you think the wrong way about the problem you are trying to solve.
NetLogo lets you store direct references to agents, for example:
ask turtles [ set friend one-of other turtles ]
In general, use that instead.
In your case, though, you should probably not be storing individual references to agents. Since you are dealing with groups of agents, you should be working with agentsets instead.
Here is one suggestion: make a global list of agentsets called groups. In addition to that, have each agent store a reference to the agentset that constitute its group. Here is one way to accomplish that:
globals [ groups ]
turtles-own [ my-group ]
to setup
clear-all
let number-of-groups 2
let turtles-per-group 4
create-turtles turtles-per-group * number-of-groups
set groups [] ; empty list
repeat number-of-groups [
let new-group n-of turtles-per-group turtles with [
not is-agentset? my-group
]
ask new-group [ set my-group new-group ]
set groups lput new-group groups
]
print (word "First group: " sort first groups)
print (word "Second group: " sort last groups)
; each turtle can easily access other members of its group:
ask turtles [ show sort other my-group ]
end
This code has the advantage of being very flexible. If you ever want more than two groups, or more than four turtles per group, it's a very easy change.
Another piece of general advice: if you ever find yourself using multiple variables like a1, a2, a3, etc., you are probably doing something wrong. You should be using lists and/or agentsets instead.

Counting the number of different links

In my code all turtles own n-features represented by a n-tuple (a1,a2,...,an). where each ai can take values 0 or 1.
I have created some links between turtles. If two turtles share k-features (coordinate-wise matching) and there is a link between them then we call the link as k-link.
How can I find for each k (between 0 to n) how many k-links are there in total?
You don't tell us much about how you have structured your code, so I'm going to assume that your n-tuples are implemented as lists (which would make the most sense in NetLogo).
Here is a full example:
turtles-own [ a ]
links-own [ k ]
globals [ n ]
to setup
ca
set n 5
crt 10 [ ; create turtles with random feature lists
set a n-values n [ random 2 ]
]
ask turtles [ ; make a full network
create-links-with other turtles
]
ask links [ ; calculate k for all links
set k k-of-feature-lists ([a] of end1) ([a] of end2)
]
foreach n-values (n + 1) [ ? ] [ ; display number of k-links
show (word ? "-links: " count links with [ k = ? ])
]
end
to-report k-of-feature-lists [ a1 a2 ]
report length filter [?] (map = a1 a2)
end
Apart from k-of-feature-lists, this is fairly trivial code. What k-of-feature-lists does is to:
transform two lists of features into a single list of booleans containing a true value if the corresponding element is equal in both feature lists and false if it is not. This is accomplished using map and the concise task syntax for =;
filter the list of booleans to keep only the true values;
report the length of that filtered list, which is equal to the number of features that where the same in a1 and a2;
There are plenty of other ways to do that (some more efficient) but this one is nice and concise.