Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a restore flag to the Event constructor. #180

Merged
merged 2 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ double_variable_queue_shrink_bitset <- function(variable, index) {
invisible(.Call(`_individual_double_variable_queue_shrink_bitset`, variable, index))
}

create_event <- function() {
.Call(`_individual_create_event`)
create_event <- function(restoreable) {
.Call(`_individual_create_event`, restoreable)
}

create_targeted_event <- function(size) {
Expand Down
14 changes: 10 additions & 4 deletions R/event.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,24 @@ EventBase <- R6Class(
#' @export
Event <- R6Class(
'Event',
inherit=EventBase,
inherit = EventBase,
public = list(
#' @description Initialise an Event.
initialize = function() {
self$.event <- create_event()
#' @param restore if true, the schedule of this event is restored when restoring from a saved
#' simulation.
initialize = function(restore = TRUE) {
self$.event <- create_event(restore)
},

#' @description Schedule this event to occur in the future.
#' @param delay the number of time steps to wait before triggering the event,
#' can be a scalar or a vector of values for events that should be triggered
#' multiple times.
schedule = function(delay) event_schedule(self$.event, delay),
schedule = function(delay) {
if (!is.null(delay)) {
event_schedule(self$.event, delay)
}
},

#' @description Stop a future event from triggering.
clear_schedule = function() event_clear_schedule(self$.event),
Expand Down
16 changes: 14 additions & 2 deletions inst/include/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ inline size_t EventBase::get_time() const {
class Event : public EventBase {

std::set<size_t> simple_schedule;
bool restoreable;

public:
Event(bool restoreable);
virtual ~Event() = default;

virtual void process(Rcpp::XPtr<listener_t> listener);
Expand All @@ -90,6 +92,9 @@ inline void Event::process(Rcpp::XPtr<listener_t> listener) {
(*listener)(get_time());
}

inline Event::Event(bool restoreable) : restoreable(restoreable) {
}

//' @title should first event fire on this timestep?
inline bool Event::should_trigger() {
if (simple_schedule.empty()) {
Expand Down Expand Up @@ -124,8 +129,15 @@ inline std::vector<size_t> Event::checkpoint() {
//' @title restore this event's state from a previous checkpoint
inline void Event::restore(size_t time, std::vector<size_t> schedule) {
t = time;
simple_schedule.clear();
simple_schedule.insert(schedule.begin(), schedule.end());
if (restoreable) {
simple_schedule.clear();
simple_schedule.insert(schedule.begin(), schedule.end());
} else {
// We don't restore the event, but it is possible that the resume time
// is beyond some already scheduled timesteps. These need to be removed.
auto it = simple_schedule.lower_bound(time);
simple_schedule.erase(simple_schedule.begin(), it);
}
}

//' @title a targeted event in the simulation
Expand Down
9 changes: 5 additions & 4 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,13 @@ BEGIN_RCPP
END_RCPP
}
// create_event
Rcpp::XPtr<Event> create_event();
RcppExport SEXP _individual_create_event() {
Rcpp::XPtr<Event> create_event(bool restoreable);
RcppExport SEXP _individual_create_event(SEXP restoreableSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
rcpp_result_gen = Rcpp::wrap(create_event());
Rcpp::traits::input_parameter< bool >::type restoreable(restoreableSEXP);
rcpp_result_gen = Rcpp::wrap(create_event(restoreable));
return rcpp_result_gen;
END_RCPP
}
Expand Down Expand Up @@ -1487,7 +1488,7 @@ static const R_CallMethodDef CallEntries[] = {
{"_individual_double_variable_queue_extend", (DL_FUNC) &_individual_double_variable_queue_extend, 2},
{"_individual_double_variable_queue_shrink", (DL_FUNC) &_individual_double_variable_queue_shrink, 2},
{"_individual_double_variable_queue_shrink_bitset", (DL_FUNC) &_individual_double_variable_queue_shrink_bitset, 2},
{"_individual_create_event", (DL_FUNC) &_individual_create_event, 0},
{"_individual_create_event", (DL_FUNC) &_individual_create_event, 1},
{"_individual_create_targeted_event", (DL_FUNC) &_individual_create_targeted_event, 1},
{"_individual_event_base_tick", (DL_FUNC) &_individual_event_base_tick, 1},
{"_individual_event_base_get_timestep", (DL_FUNC) &_individual_event_base_get_timestep, 1},
Expand Down
4 changes: 2 additions & 2 deletions src/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
#include "utils.h"

//[[Rcpp::export]]
Rcpp::XPtr<Event> create_event() {
return Rcpp::XPtr<Event>(new Event(), true);
Rcpp::XPtr<Event> create_event(bool restoreable) {
return Rcpp::XPtr<Event>(new Event(restoreable), true);
}

//[[Rcpp::export]]
Expand Down Expand Up @@ -52,7 +52,7 @@
void event_restore(const Rcpp::XPtr<Event> event, size_t time, std::vector<size_t> schedule) {
for (size_t event_ts: schedule) {
if (event_ts < time) {
Rcpp::stop("schedule is in the past");

Check warning on line 55 in src/event.cpp

View check run for this annotation

Codecov / codecov/patch

src/event.cpp#L55

Added line #L55 was not covered by tests
}
}
event->restore(time, schedule);
Expand Down Expand Up @@ -207,13 +207,13 @@
std::vector<std::vector<size_t>> targets = state["targets"];;

if (timesteps.size() != targets.size()) {
Rcpp::stop("length mismatch between timesteps and targets");

Check warning on line 210 in src/event.cpp

View check run for this annotation

Codecov / codecov/patch

src/event.cpp#L210

Added line #L210 was not covered by tests
}

std::vector<std::pair<size_t, individual_index_t>> schedule;
for (size_t i = 0; i < timesteps.size(); i++) {
if (timesteps[i] < time) {
Rcpp::stop("schedule is in the past");

Check warning on line 216 in src/event.cpp

View check run for this annotation

Codecov / codecov/patch

src/event.cpp#L216

Added line #L216 was not covered by tests
}
decrement(targets[i]);
auto bitmap = individual_index_t(event->size());
Expand Down
52 changes: 51 additions & 1 deletion tests/testthat/test-events.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ test_that("events can be saved and restored", {
old_event$.timestep(),
old_event$.checkpoint())

expect_equal(new_event$.timestep(), 3)

#time = 3
new_event$.process()
mockery::expect_called(listener, 1)
Expand Down Expand Up @@ -164,7 +166,7 @@ test_that("events are cleared when restored", {
mockery::expect_called(listener, 0)
new_event$.tick()

#time=2
#time=3
new_event$.process()
mockery::expect_called(listener, 0)
new_event$.tick()
Expand All @@ -176,6 +178,54 @@ test_that("events are cleared when restored", {
new_event$.tick()
})

test_that("event is not cleared when restore is disabled", {
old_event <- Event$new()
old_event$schedule(3) # t=4

# time = 1
old_event$.process()
old_event$.tick()

# time = 2
old_event$.process()
old_event$.tick()

listener <- mockery::mock()
new_event <- Event$new(restore=FALSE)
new_event$add_listener(listener)
new_event$schedule(1) # t=2, this is before the restore timestep and will not fire.
new_event$schedule(5) # t=6, this will trigger

# This should restore the timestep, but not any of the old event's schedule.
new_event$.restore(
old_event$.timestep(),
old_event$.checkpoint())

expect_equal(new_event$.timestep(), 3)

# time = 3
new_event$.process()
mockery::expect_called(listener, 0)
new_event$.tick()

# time = 4
# The saved event from the `old_event` is not restored.
new_event$.process()
mockery::expect_called(listener, 0)
new_event$.tick()

# time = 5
new_event$.process()
mockery::expect_called(listener, 0)
new_event$.tick()

# time = 6
new_event$.process()
mockery::expect_called(listener, 1)
mockery::expect_args(listener, 1, t = 6)
new_event$.tick()
})

test_that("empty event never triggers", {
event <- Event$new()
listener <- mockery::mock()
Expand Down
Loading
Loading