JuliaDynamics / DiscreteEvents.jl

Discrete event generation and simulation in Julia
MIT License
55 stars 9 forks source link

Feature request: remove events from priority queue #34

Open itsdfish opened 3 years ago

itsdfish commented 3 years ago

In some simulations, it is necessary to remove an event from the priority queue based on an event identifier. I was wondering if this functionality would be worth adding to DiscreteEvents.jl. Here is what I have in mind as a starting point. First, add a string id to DiscreteEvent with a default value (such as "event" or "") that can be ignored when it is not relevant.

struct DiscreteEvent{T<:Action, X} <: AbstractEvent
    ex::T
    t::Float64
    Δt::X
    n::Int
    id::String
end

When an event is added to the clock, an id can be optionally passed as a keyword argument:

event!(clock, fun(f, args...), after, 1; id="id1")

An event can be removed with the following function call:

remove_events!(clock, "id1")

By default, it would remove by exact match. However, in some cases it might be desirable to remove by partial match, in which case, a function can be passed:

remove_events!(clock, "id", (x,id)->occursin(id, x.id))

This would remove all events containing "id" in the id field.

The code might look like this:

function remove_events!(clock::Clock, id, f=(x,id)->x.id == id)
    events = filter(x->f(x, id), clock.sc.events)
    for event in events
        delete!(clock.sc.events, event)
    end
end
pbayer commented 3 years ago

For curiosity: in which cases do you need this? Can you give me an example?

itsdfish commented 3 years ago

Much of my work pertains to cognitive modeling. This involves developing a model of how people think, reason and perceive in experimental tasks. Some of these models are mathematical abstractions, while others are discrete event simulations representing the underlying cognitive mechanisms and processes. Since these models interact with simulated tasks, it is necessary to handle events like response deadlines. This would involve scheduling an event in advance to handle the deadline, and canceling the event if a response is given within the deadline. There are other cases that involve the internal workings of the model, but this is the easiest to communicate.

pbayer commented 3 years ago

ok, that makes sense. I will look at it. That could be handled also by a state machine, which accepts certain events in a given state and ignores others. But I know e.g. from Cassandras/Lafortune (Introduction to Discrete Event Systems) that they use that feature in simulations regularly.

itsdfish commented 3 years ago

Much appreciated. Thanks for considering this feature.

itsdfish commented 3 years ago

By the way, I made a light weight simulator to hold me over in the meantime. No worries if this feature is not something you want to add.

hdavid16 commented 1 year ago

It might be worth looking into adding priority queues, which are already available in DataStructures.jl: https://juliacollections.github.io/DataStructures.jl/latest/priority-queue/ (I believe @itsdfish used these in DiscreteEventsLite.jl).

This would allow changing priorities in the queue during the simulation or adding an element to the queue with a specified priority/rank beforehand.

hdavid16 commented 1 year ago

Back to @itsdfish suggestion, I think it would make sense to have an Int or UInt id for every event (as is done with every process). This seems to be an extension to how we interrupt! processes, but for scheduled events.

pbayer commented 1 year ago

With your suggestion we can implement this, but I am still a bit sceptical about the possibility of side effects, e.g. if we have processes that wait for events and then starve when they don't happen.

Maybe we should implement it and mark it as experimental until we understand it better.

hdavid16 commented 1 year ago

Yes I think making this experimental would be good.