Program Listing for File event.h

Return to documentation for file (include/amici/event.h)

#ifndef AMICI_EVENT_H
#define AMICI_EVENT_H

#include "amici/defines.h"
#include "amici/model_state.h"

#include <algorithm>
#include <iterator>
#include <list>
#include <optional>
#include <stdexcept>
#include <string>

namespace amici {


class Event {
  public:
    Event(
        std::string id, bool use_values_from_trigger_time, bool initial_value,
        realtype priority
    )
        : id_(id)
        , use_values_from_trigger_time_(use_values_from_trigger_time)
        , initial_value_(initial_value)
        , priority_(priority) {}

    std::string const& get_id() const { return id_; }

    bool get_initial_value() const { return initial_value_; }

    realtype get_priority() const { return priority_; }

    bool uses_values_from_trigger_time() const {
        return use_values_from_trigger_time_;
    }

  private:
    std::string id_;

    bool use_values_from_trigger_time_;


    bool initial_value_ = true;


    realtype priority_ = NAN;
};

struct PendingEvent {
    Event const& event;
    int idx;
    std::optional<SimulationState> state_old;
};

class EventQueue {
  public:
    EventQueue() = default;

    bool empty() const { return pending_events_.empty(); }

    void push(PendingEvent event) { pending_events_.push_back(event); }

    PendingEvent pop() {
        if (empty()) {
            throw std::runtime_error("No pending events");
        }

        // Sort events by priority from high to low.
        pending_events_.sort([](PendingEvent const& a, PendingEvent const& b) {
            // The priority is to NaN in AMICI if not defined.
            // In this case, the execution order is undefined,
            // so this does not need any special treatment.
            return a.event.get_priority() > b.event.get_priority();
        });

        // If there is any undefined (NaN) priority, the order of all
        // simulataneously executed events is undefined. We just proceed
        // with the first one in the list.
        if (std::any_of(
                pending_events_.begin(), pending_events_.end(),
                [](PendingEvent const& e) {
                    return std::isnan(e.event.get_priority());
                }
            )) {
            auto event = pending_events_.front();
            pending_events_.pop_front();
            return event;
        }

        // If there are multiple events with the same not-NaN priority,
        // then *their* ordering is random (not undefined).
        int num_equal_priority = 0;
        for (auto it = pending_events_.begin(); it != pending_events_.end();
             ++it) {
            if (it->event.get_priority()
                == pending_events_.front().event.get_priority()) {
                num_equal_priority++;
            } else {
                break;
            }
        }

        auto it = pending_events_.begin();
        if (num_equal_priority > 1) {
            // choose randomly among the events with the same priority
            std::advance(it, rand() % num_equal_priority);
        }

        auto event = *it;
        pending_events_.erase(it);
        return event;
    }

  private:
    std::list<PendingEvent> pending_events_;
};
} // namespace amici

#endif // AMICI_EVENT_H