Repository navigation
Option to convert model assignments to experiments in petab1to2 - #527
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #527 +/- ##
==========================================
+ Coverage 77.16% 77.43% +0.26%
==========================================
Files 67 67
Lines 7568 7670 +102
Branches 1349 1374 +25
==========================================
+ Hits 5840 5939 +99
Misses 1242 1242
- Partials 486 489 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dilpath
left a comment
There was a problem hiding this comment.
I did not receive a notification to review this somehow...
Thanks! I think it's a useful addition but I am a bit unsure whether some cases are handled currently.
| replaced_columns |= {str(s) for s in expr.free_symbols} | ||
|
|
||
| for sim_id, preeq_id in pairs: | ||
| t_sim = float(t_switch.subs(overrides[sim_id])) |
There was a problem hiding this comment.
Is it possible that an error arises here (or downstream) if t_switch has species, i.e. t_switch cannot be solved for a specific float time but might still be some sp.Expr involving species?
There was a problem hiding this comment.
The overrides could also be problematic. If a species appears in a v1 condition table column, then its initial condition is being set there. Hence, the initial value of a species might be substituted here via overrides I think, which could silently cause an error because species should rather raise an error.
More specifically, a t_switch that contains species cannot be solved in general for specific time(s), before simulation.
There was a problem hiding this comment.
I've added a check that the switch can be resolved to a number and throw if not. I think that should also cover the case where t_switch contains species?
There was a problem hiding this comment.
Thanks. I am wondering if overrides can contain an "override" for a species. If so, that override (which represents the species at the initial condition) will be implemented into t_switch. This would be a difference to the SBML model, which would implement the dynamic value of the species into t_switch over the course of the simulation.
There was a problem hiding this comment.
That sounds like it might have to be highlighted as a case we can't support with this conversion?
There was a problem hiding this comment.
Agreed, was just pointing out that the case should be handled with some NotImplementedError.
There was a problem hiding this comment.
Not sure if this is too strict but do you think this covers it? 1fc1744
There was a problem hiding this comment.
Thanks! I think it's good like this -- if someone complains, then we can broaden the implementation at that point.
| shutil.copy(str(src), str(dest)) | ||
|
|
||
|
|
||
| def _assignments_to_periods( |
There was a problem hiding this comment.
I think this replaces SBML piecewise terms with their values from the simulation condition. Maybe this can change the simulation (and thereby likelihood). For example, I think these piecewises would have triggered during preequilibration in v1. If we move them to the experiment table, then they will no longer trigger during preequilibration.
So maybe the only safe way to implement this is to only perform _assignments_to_periods for v1 simulations that do not involve preequilibration. But this seems complicated...
There was a problem hiding this comment.
I'm not sure I understand the use case, but now that I've added the real number check around switching times, I think a piecewise triggered during preequilibration would raise a NotImplemented. I could make it more explicit and not support problems with prequilibration at all if you think that would be safer.
There was a problem hiding this comment.
I also do not understand the use case -- but when events are in the SBML model, they are triggered during both preequilibration and simulation. However, when your code converts these to v2 experiments, they are no longer "triggered" during preequilibration. One would need to hack them in somehow so that they also trigger during preequilibration...
Thanks for @dweindl for clarifying elsewhere -- for this case, could you add a warning in bold to the docstring that, in the converted problem, the changes are not implemented during preequilibration? And also detect whether the PEtab v1 problem has preeq and if so, issue a warning about this.
There was a problem hiding this comment.
Added warning in the docstring and in the code.
| elif {str(s) for s in values[t_sim].free_symbols} <= parameter_ids: | ||
| # the model already has the pre-switch value, and the switch | ||
| # is valid as first period (only parameter table symbols) | ||
| del values[0.0] |
There was a problem hiding this comment.
I am not sure I follow the logic here but could there be an issue if, for example, the model contains an SBML event that changes the value of a PEtab parameter at some timepoint
There was a problem hiding this comment.
I think that could be an issue. Assignment rules would get re-evaluated after the event triggerd but the condition wouldn't.
I could protect against that by iterating through the event assignments and checking that no petab parameters in there appear in the converted piecewise assignments?
There was a problem hiding this comment.
Nevermind, v2 explicitly excludes this case: the parameter table must contain truly constant parameters (e.g. the parameters cannot be changed by an SBML event).
Hence, throw an error if it looks like you are about to implement an estimated parameter in the condition table (which is anyway invalid, so alternatively you could detect the issue by validating your generated PEtab problem --this approach is also fine for me). In any case, please add a small test for this.
There was a problem hiding this comment.
Added a test with this case and a linting step at the end of the function.
|
I see how this can be useful. However, I am wondering if it wouldn't be substantially more maintainable if we would separate that from the v1->v2 conversion, and do that as an independent post-conversion pass. In that case, the input would be a v2 problem with a couple of additional things to handle, but those could be rejected (non-zero starting time, non-sbml models, ...). |
9dc1783 to
3714d62
Compare
|
Thanks both! I've moved the feature into its own function as @dweindl suggested and added safety guards around certain cases. For now I'm keeping the scope pretty narrow, just piecewise assignments that are resolvable to a specific time are supported, other cases raise |
dilpath
left a comment
There was a problem hiding this comment.
Thanks! Happy to review again after the discussed changes. As discussed, I would suggest we explicitly support the following cases only:
A single piecewise function with exactly 3 arguments, where the 2nd argument is an inequality that can be re-arranged explicitly into either of t > ... or t >= ..., and error on anything else.
The first and third arguments should be some numeric value or anything else that is a valid targetValue in the v2 condition table (also check this -- linting the problem afterwards is fine for me).
If there is preequilibration, then there will be the discussed breaking change to warn about.
Also write this scope into the docstring of the function.
|
|
||
| t_end = periods[i + 1].time if i + 1 < len(periods) else sp.oo | ||
| t = t_switch.subs(subs) | ||
| if not (t.is_number and t.is_real): |
There was a problem hiding this comment.
I would additionally suggest t.is_finite. If t evaluates to negative infinity then maybe preequilibration is erroneously implemented 🤷
| replaced_columns |= {str(s) for s in expr.free_symbols} | ||
|
|
||
| for sim_id, preeq_id in pairs: | ||
| t_sim = float(t_switch.subs(overrides[sim_id])) |
There was a problem hiding this comment.
Thanks. I am wondering if overrides can contain an "override" for a species. If so, that override (which represents the species at the initial condition) will be implemented into t_switch. This would be a difference to the SBML model, which would implement the dynamic value of the species into t_switch over the course of the simulation.
| shutil.copy(str(src), str(dest)) | ||
|
|
||
|
|
||
| def _assignments_to_periods( |
There was a problem hiding this comment.
I also do not understand the use case -- but when events are in the SBML model, they are triggered during both preequilibration and simulation. However, when your code converts these to v2 experiments, they are no longer "triggered" during preequilibration. One would need to hack them in somehow so that they also trigger during preequilibration...
Thanks for @dweindl for clarifying elsewhere -- for this case, could you add a warning in bold to the docstring that, in the converted problem, the changes are not implemented during preequilibration? And also detect whether the PEtab v1 problem has preeq and if so, issue a warning about this.
| elif {str(s) for s in values[t_sim].free_symbols} <= parameter_ids: | ||
| # the model already has the pre-switch value, and the switch | ||
| # is valid as first period (only parameter table symbols) | ||
| del values[0.0] |
There was a problem hiding this comment.
Nevermind, v2 explicitly excludes this case: the parameter table must contain truly constant parameters (e.g. the parameters cannot be changed by an SBML event).
Hence, throw an error if it looks like you are about to implement an estimated parameter in the condition table (which is anyway invalid, so alternatively you could detect the issue by validating your generated PEtab problem --this approach is also fine for me). In any case, please add a small test for this.
| "piecewise(0, time < 10, 1) + piecewise(0, time < 20, 1)", | ||
| "piecewise(0, time < 10, 1, time < 20, 2)", |
There was a problem hiding this comment.
Is this case handled? e.g. time < 10 || time > 15?
There was a problem hiding this comment.
This will now throw a NotImplemented because it is not a single inequality.
dilpath
left a comment
There was a problem hiding this comment.
Thanks! Fine to merge now I think
Co-authored-by: Dilan Pathirana <59329744+dilpath@users.noreply.github.com>
92af18b to
5720c99
Compare
The option identifies
piecewisetime dependent assignments from the model, converts them into rows in the experiments/condition tables in the petab v1 problem, and removes those assignments from the model.