Passing information between two models WITHOUT the use of Levelspace or Hubnet - netlogo

Is it possible to pass information between Netlogo models without using LevelSpace or Hubnet? I'm trying to output variables from one model and into a second model. The reason I don't want to use LevelSpace for this is because I'm using the xw (xtraWidget) extension as a frontend UI for my models and LevelSpace does not allow the xw extension to activate when models open. Hubnet is for LAN operations and these models will be on a server online.

Do all of the models involved use xw? If only one does, you could have the model be the LevelSpace parent.
If more than one does, probably the simplest way to do this is by communicating by writing to and reading from a file. The safest version of this is to have the sender wait for a file to not exist, open the file, write to it, and closes the file. The receiver, similarly, waits for the file to exist, opens the file, reads from it, then closes the file and deletes it, indicating to the sender that it has finished reading. This should avoid most weird timing issues and, for instance, Windows locking open files and the like, but it does require the two models stay in lock step, as they will block on sending and receiving. This would look like so:
Sender:
to send-data [ filename data ]
while [ file-exists? filename ] [] ; Could add a `wait` so this doesn't eat as much cpu while waiting
file-open filename
file-write data
file-close
end
Receiver:
to-report receive-data [ filename ]
while [ not file-exists? filename ] [] ; Could also add a `wait` here
file-open "filename"
let data file-read
file-close
file-delete filename
report data
end
Example usage:
Sender:
observer> send-data "foo" 123 send-data "foo" 456
Receiver:
observer> show receive-data "foo" show receive-data "foo"
observer: 123
observer: 456
Another way to do this (and probably much better) would be to use the Python extension to perform inter-process communication via the multiprocessing library. If you're interested in this method, add a comment, and I can write up some example code. Here are the related docs to get you started: https://docs.python.org/3/library/multiprocessing.html#using-a-remote-manager
Update with Python solution:
The idea here is to start a multiprocessing server that the models then connect to. One model will start the server, then both models will create clients that connect to that server. They will then pass data via a queue in that server.
First, we setup python and import the libraries we care about. This will be run on both models.
to setup-python
py:setup py:python3
(py:run
"from multiprocessing.managers import BaseManager"
"from multiprocessing import Process"
"from queue import Queue")
end
We start the server (in just one of the models) like so:
to start-server
(py:run
"def start_server():"
" class QueueManager(BaseManager): pass"
" queue = Queue()"
" QueueManager.register('get_queue', callable=lambda:queue)"
" m = QueueManager(address=('', 50000), authkey=b'turtles')"
" s = m.get_server()"
" s.serve_forever()"
"proc = Process(target=start_server) "
"proc.start()")
end
We then start the client in both models like so:
to start-client
(py:run
"class QueueManager(BaseManager): pass"
"QueueManager.register('get_queue')"
"m = QueueManager(address=('', 50000), authkey=b'turtles')"
"m.connect()"
"queue = m.get_queue()")
end
We can then send and receive data with the following:
to send-data [ data ]
py:set "data" data
(py:run "queue.put(data)")
end
to-report receive-data
report (py:runresult "queue.get(block=False)")
end
Note that I set receive-data to not wait for data; it will just error if there's no data waiting. If you do block, it's pretty easy to freeze NetLogo (and then you have to go and kill the underlying python process manually). You'll probably want to adjust that behavior. See https://docs.python.org/3/library/queue.html#queue.Queue.get for various options, or just throw a carefully around the whole thing and report a default value when you get an error.
Putting it all together, on one model we have something like:
observer> setup-python
observer> start-server
observer> start-client
observer> send-data 123
observer> send-data 456
and in the other model we have:
observer> setup-python
observer> start-client
observer> show receive-data
observer: 123
observer> show receive-data
observer: 456
A few notes:
start-client and start-server should be run exactly once after setup-python. If you want to call them again, you need to call setup-python again to shutdown the python process. You might get something like [Errno 98] Address already in use when restarting a client after restarting the server; just wait a sec for the old server to die and try again.
If you want to send and receive data in both directions, I recommend adding an additional queue. While a queue is bidirectional here, you might have the senders eating their own messages if you try to reuse it.
Be prepared to manually kill the python process if NetLogo locks up. It's supposed respect halt (and does sometimes), but not always.
Hope that helps!

Related

How do you call a script from another script in roblox studio

I was following a tutorial of AlvinBlox and came across not an error but it does not run something I wanted to happen.
Basically when the egg is clicked it need to hatch in a studio but it just prints the pet name and does not hatch the egg
This is the script where I need to code to call the script with the hatching mechanism:
local cost = 500
local petModule = require(game.ServerScriptService:WaitForChild("PetModule"))
script.Parent.ClickDetector.MouseClick:Connect(function(player)
if player.leaderstats.Cash.Value >= cost then
player.leaderstats.Cash.Value = player.leaderstats.Cash.Value - cost
local pet = petModule.chooseRandomPet()
print(pet.Name.." selected")
game.ReplicatedStorage.HatchEgg:FireClient(player)
end
end)
I was trying to hatch an egg but it doesn't hatch it.
You never passed the pet to the client.
game.ReplicatedStorage.HatchEgg:FireClient(player, pet)

Changing Breeds: How to tell if breeds are actually changing?

This is my code to switch breeds. How can I tell if the agents are changing breeds. I have a monitor that counts but the number is not changing. It is supposed to count passives vs. actives which should be changing.
to switch-A
ask managers-A
with [Rhat-A < mean-Rhat-P] [set breed managers-P]
end
That code looks fine. What does the code box in the monitor have? I assume you are counting As in one monitor and Ps in another.
If nothing's happening, it's likely that you either never call the switch-A procedure, or that there aren't any A's that meet the condition. Try this:
to switch-A
type "As with low value:" print count managers-A with [Rhat-A < mean-Rhat-P]
ask managers-A with [Rhat-A < mean-Rhat-P]
[ set breed managers-P
]
end
If you don't get output then it's not being called. If the count is 0 then you know to find out the values that are being tested etc.

Discrete Event Scheduling in NetLogo: Executing some command on some specific tick

My code has one condition:
if ticks = 10^7 [ do-something ]
Now this condition is checked on every ticks though I know exactly when I have to execute the command. This might be slowing my code. The time extension does exactly this by time:go command. My command using time extension is:
time:schedule-event patches task do-something 1000000
But this throwing an error:
Extension exception: Attempted to schedule an event for tick 999999.0 which is before the present 'moment' of 1000000.0
error while observer running TIME:SCHEDULE-EVENT
called by procedure GO
called by Button 'go'
Is there something I am missing? Or any other efficient way to schedule some event at specific tick without checking the ticks condition every tick?
If this looks like a bug in the time extension, please post it at https://github.com/colinsheppard/time/issues
and tell us where we can find your code. (Or email it to me.)

Auction implementation similar to Dining Philosophers

I am trying to implement an auction concept in netlogo - it is similar to the dining philosophers problem program.
My program deals with computers and processors that correspond to philosophers and forks in the dining philosopher program. In the philosophers program a user needs 2 forks to eat but in computers and processors, one computer needs one processor to work.
The states defined in my program are IDLE, NEED, USING, corresponding to THINKING, HUNGRY, EATING in the philosopher program.
Currently, my program goes to the point where all computers state changes to NEED. I am having issues in acquiring of server for the computers.
The code snippet is -
// ... lines of code for declaration etc
.
.
.
to update
if state = "IDLE" [
if random-float 1.0 < hungry-chance [
set state "NEED"
]
stop
]
if state = "USING" [
set total-used (total-used + 1)
if random-float 1.0 < full-chance
[ release-servers ]
set state "IDLE"
stop
]
if state = "NEED"
[ acquire-servers ]
if we've got both forks, eat.
if got? servers
[ set state "USING" ]
stop
end
//lines of code in between
.
.
to acquire-servers ;; philosopher procedure
ask servers [
if [owner] of servers = nobody[
set owner myself
move-to owner
set heading [heading] of owner
]
]
end
// lines of code at end
First, you need to change your if for testing state into ifelse and remove the stop commands. That way, the computer will find its correct state and do the relevant procedures and then not look at the other states.
Second, the acquire-servers procedure currently assigns all the servers to the first computer that needs one. You probably want a procedure that looks more like:
to acquire-one-server
let candidates servers with [ owner = nobody ]
if any? candidates
[ ask one-of candidates
[ set owner myself
move-to owner
set heading [heading] of owner
]
]
end

Breaking network links without creating orphans?

I have a grid of 10x10 nodes where all adjacent nodes are linked to create a graph of 180 links - i.e:
What I hope to do is create a procedure which will break x% of these links, but importantly do so without creating any isolated subgraphs - that is, so that any 1 of the 100 nodes remain accessible from all other 99 nodes - i.e.
I initially (and naively) thought I could do something like the following:
ask n-of 45 nodes with [count my-links > 1]
[
ask one-of my-links [die]
]
However, this does not prevent sub-graphs from being orphaned from the main graph - i.e.
Another option might be to repeatedly ask single links to die and then check the total number of graphs does not exceed 1 - if it does, recreate the link and choose another to break. However, (1) this does not seem particularly elegant; and (2) I have been unable to find a way to count the number of distinct graphs using the inbuilt link or nw procedures.
I do think that part of my problem is not knowing the correct terminology for these subgraphs/isolates/cliques etc...
If anyone has any ideas on how this might be done (or can correct my woefully inadequate comprehension of graph theory terminology) it would be much appreciated.
Many thanks for the heads-up re nw:weak-component-clusters - I wasn't aware of that.
I have now implemented the following simple recursive procedure which will kill a link and if that link splits the graph recreate it and recall the procedure. This works well for what I wanted to do. Do note however that if you run this enough times you will end up stuck in a recursive loop as there will be no further links to break without splitting the graph and the procedure will keep calling itself.
to breakLink
let breakCandidate one-of links
let end1Candidate [end1] of breakCandidate
let end2Candidate [end2] of breakCandidate
ask breakCandidate [die]
if (length nw:weak-component-clusters > 1)
[
ask end1Candidate
[
create-link-with end2Candidate
breakLink
]
]
end
Cheers
For the solution you thought of, you can use the nw extension to check if there's still a path between the nodes you just disconnected by checking if nw:distance-to returns false or a number.
Alternatively, you can count the number of connected components.
I think removing the link and testing connectedness is a perfectly reasonable way to go. Any algorithm you implement will have to awkwardly pretend the link doesn't exist anyway, so will likely be more complicated than just removing the thing.
Edit: Adding code for the distance version to clarify:
to break-link
let break-candidate one-of links
let end-one [end1] of break-candidate
let end-two [end2] of break-candidate
ask break-candidate [die]
if [nw:distance-to end-two] of end-one = false [
ask end-one [ create-link-with end-two ]
break-link
]
end
Not sure if this or the nw:weak-component-cluster version will be faster. That will likely depend on the size of the network.