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 output fn #17

Merged
merged 9 commits into from
May 12, 2022
Merged

add output fn #17

merged 9 commits into from
May 12, 2022

Conversation

hillalex
Copy link
Contributor

@hillalex hillalex commented Apr 21, 2022

Ok, still not sure if this is right but see what you think. mod$run returns full state including output (constrained by index if present), and mod$update_state allows passing in state objects that have the full output size but in that case just truncates to the non-derivable values.

@codecov
Copy link

codecov bot commented Apr 21, 2022

Codecov Report

Merging #17 (1ff17f7) into main (65aa8b9) will not change coverage.
The diff coverage is n/a.

❗ Current head 1ff17f7 differs from pull request most recent head 17602c7. Consider uploading reports for the commit 17602c7 to get more accurate results

@@           Coverage Diff           @@
##             main      #17   +/-   ##
=======================================
  Coverage   95.71%   95.71%           
=======================================
  Files           3        3           
  Lines          70       70           
=======================================
  Hits           67       67           
  Misses          3        3           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 65aa8b9...17602c7. Read the comment docs.

@hillalex hillalex marked this pull request as ready for review April 25, 2022 12:46
@hillalex hillalex requested a review from richfitz April 25, 2022 12:47
size_t n_state_full() {
return solver_[0].size();
size_t n_output_full() {
return m_.size() + m_.n_output();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have renamed these to distinguish between total output including derived values, and just the model size. Not sure about these names though, thoughts?

},

update_state = function(pars = NULL, time = NULL,
state = NULL, index = NULL,
set_initial_state = NULL, reset_step_size = NULL) {
dim_state <- dim(state)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here just dropping extra values/columns if a vector or matrix corresponds to the full output size, but leaving all other validation to the c++ layer as before. So also not sure whether this splitting of validation logic makes sense, but it keeps the changes here to a minimum.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this in the C++ (mostly because that's the pattern that we've followed in dust) but also happy to defer moving it there until later

Copy link
Member

@richfitz richfitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a think about this and apologise for what looks like some false leads I've suggested. Below is a proposal that might help simplify this by tidying up the names and centralising into stepper the control over these things. We talked before about separation of concerns in terms of who knows about the model, and I am fairly sure that in the end we can end up with only the stepper holding a persistent copy of an initialised model object - this refactor should help with that.

Happy to discuss further tomorrow as this has raised way ambiguous bits than I was expecting


I think that we might refer to a few things

  • deterministic - the things that enter into the ODEs
  • stochastic - the things that enter into the stochastic update
  • variables - the set of deterministic + stochastic
  • output - quantities derived from the above at the point of reporting
  • state_full - variables + output
  • state_run - filtered state from run

We always pack our variables so that they are deterministic followed by stochastic, and so we can exclude the stochastic variables from the error calculation eventually. Or we could leave this distinction as a detail.

Our model then provides methods:

size_t n_variables() const;

The number of deterministic and stochastic variables

size_t n_output() const;

Returns the number of outputs

void rhs(double t,
         const std::vector<double>& y,
         std::vector<double>& dydt);

y and dydt are length n_variables

void output(double t, 
            const std::vector<double>& y,
            std::vector<double>& output);

y is a vector of length n_variables and output is a vector of length n_outputs (sorry, see below for getting this working).

I think that the stepper can lose the state_full_, but pick up a small vector output_ which is of length n_outputs()

We'll need to reflect these changes out into the solver object (replacing size_ with n_variables_ and n_output_.

More complicated on the container object though because this has the default index set which controls what is returned when we call run()

So that then ends up with methods

  • n_variables():, i.e., what does the model need to start up
  • n_outputs(): The number of additional outputs
  • n_state_full: n_variables() + n_outputs()
  • n_state_run: Filtered state, on [0, n_state_full()]

Then our interface run method looks like:

template <typename T>
cpp11::sexp mode_run(SEXP ptr, double end_time) {
  T *obj = cpp11::as_cpp<cpp11::external_pointer<T>>(ptr).get();
  auto time = obj->time();
  if (end_time < time) {
    cpp11::stop("'end_time' (%f) must be greater than current time (%f)",
                end_time, time);
  }
  obj->run(end_time);

  // these 3 change minimally:
  std::vector<double> dat(obj->n_state_run() * obj->n_particles());
  obj->state_run(dat);
  return mode::r::state_array(dat, obj->n_state_run(), obj->n_particles());
}

The solver::state() method needs changing then

  std::vector<double>::iterator
  state(const std::vector<size_t>& index,
             std::vector<double>::iterator end_state) {
    return stepper_.state(it, index);
  }

And the stepper (which is the thing that actually holds the model object - and I think we can reduce to just this holding it)

  void state(std::vector<double>::iterator end_state) {
    const n_variables = m_.n_variables();
    bool have_run_output = false;
    for (size_t i = 0; i < n; ++i, ++end_state) {
      if (i < n_variables) {
        *end_state = y[i];
      } else {
        if (!have_run_output) {
          m_.output(t_, y, output);
          have_run_output = true;
        }
        *end_state = output[i - n_variables];
      }
    }
  }

const double N1 = y[0];
const double N2 = y[1];
dydt[0] = shared->r1 * N1 * (1 - N1 / shared->K1);
dydt[1] = shared->r2 * N2 * (1 - N2 / shared->K2);
}

std::vector<double>::iterator
output(double t,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like its setting the variables as well as the output - I think that we want the mode function doing the first part

}

size_t n_state() const {
size_t n_output() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that n_state_index or n_state_run or something might capture this one best - it's the number of state variables that we will return when running the model due to the index and is capped as the total of state + output. If we come up with something good here we should backport to dust

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly if we introduce n_variables as a term we can make this work

},

update_state = function(pars = NULL, time = NULL,
state = NULL, index = NULL,
set_initial_state = NULL, reset_step_size = NULL) {
dim_state <- dim(state)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this in the C++ (mostly because that's the pattern that we've followed in dust) but also happy to defer moving it there until later

std::vector<double>::iterator end_state) const {
auto y = stepper_.output();
std::vector<double>::iterator end_state) {
auto it = state_full_.begin();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's definitely more churn of data here than we need

std::vector<double>::iterator end_state) {
auto it = state_full_.begin();
stepper_.state(it);
m_.output(t_, it);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can move to only the stepper holding m_ which would be preferable - so this definitely needs moving deeper

@hillalex hillalex requested a review from richfitz April 26, 2022 10:23
Copy link
Member

@richfitz richfitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good - you said

[11:24] Hill, Alexandra E
I'll also move that logic about dropping output values on a state update from the R to the c++

did you want to do that in this PR or a following one?

@@ -180,31 +179,22 @@ class solver {
}

void set_model(Model m) {
m_ = m;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@hillalex
Copy link
Contributor Author

did you want to do that in this PR or a following one?

I'll add here

@hillalex
Copy link
Contributor Author

this code is confusing and fiddly. is there a better way?!

@hillalex hillalex requested a review from richfitz April 27, 2022 12:49
@richfitz richfitz merged commit 7907b59 into main May 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants