-
Notifications
You must be signed in to change notification settings - Fork 47
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
Petab Importer reforge #1442
Petab Importer reforge #1442
Conversation
…eaving the importer really independent of amici
…n with different models. Created a basic PetabSimulator supported (inefficent most likely but as a starter)
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## develop #1442 +/- ##
===========================================
- Coverage 83.44% 82.87% -0.57%
===========================================
Files 160 163 +3
Lines 13502 13760 +258
===========================================
+ Hits 11267 11404 +137
- Misses 2235 2356 +121 ☔ View full report in Codecov by Sentry. |
… solution to amici Objective
…tab_simulator_support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Some first feedback:
Overall, it makes sense to split up the PEtab-related functionality.
I am not sure how much demand there really is for the petab.Simulator support, but fine to have it. (It should be relatively easy to include a new simulator, but for really using it during parameter estimation, one would probably want a more efficient implementation).
I am not so happy with the naming yet, see specific comments.
pypesto/petab/factory.py
Outdated
) | ||
return mapping | ||
|
||
def create_objective( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think anything besides this function could/should be private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could if we view the sole purpose of the factory as the objective generation. If we view the other objects like datas and/or model part of its tasks as well, they should not be private. The petabImporter had them previously also as not private. If we want them to be private, there would be no need to have a lot of arguments in them allowing to separately call them as they anways were not meant to be used that way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will have to have a closer look what might be useful elsewhere. Not everything that was public before should have been public before. If the other functions are to be used separately, then they should not be in this class, or this class should not be called ObjectiveFactory or similar. A XFactory has (or should have) a single purpose, which is to produce an instance of X.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, in this case we can make them private any day (would also introduce potential breaking changes?) i am also very much in favor of removing some of these functions at least in the future as i dont really see a point in such light wrappers (see e.g. rdatas_to_measurement_df
). For now i moved everything away from the importer, but happy to also remove not needed functions (i already removed check gradients match).
pypesto/petab/importer.py
Outdated
rdatas, model, measurement_df | ||
"""See :meth:`AmiciFactory.rdatas_to_measurement_df`.""" | ||
raise NotImplementedError( | ||
"This function has been moved to `AmiciFactory`." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this function really belongs to AmiciFactory
. Also this would be a breaking change without deprecation warning, which would require pypesto 1.0 (there are a couple more cases like that).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'll be honest, i dont think they even belong to pyPESTO, as it is a really light wrapper around it. Can create a deprecation warning as well.
Why would it require a 1.0? we could go with a 0.6.0 as we have done before on breaking changes?
… the parameter update function
…tab_simulator_support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks nice, thanks 🙂
self.simulator_type = simulator_type | ||
self.simulator = simulator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the importer only needs to use this in create_objective
, why not remove these attributes and implement them as arguments of create_objective
instead?
e.g. if I want to compare the libroadrunner and AMICI objectives, why do I need two importer objects? I could use the same importer like importer.create_objective_creator(..., simulator_id=AMICI)
and importer.create_objective_creator(..., simulator_id=ROADRUNNER)
No strong opinion on this though, fine as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
Think this makes sense. Also not strong opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
having this would make it imo much easier to
- init the importer -> 2. importer.create_problem()
without having to pass these arguments constantly to create_problem. Could add it as additional possibility tocreate_objective
such that people can do either? Argument increate_objective
would then take precedence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the cleanest implementation would be that default behavior (AMICI simulator) is usable as you said like
importer = PetabImporter(petab_problem)
problem = importer.create_problem()
but custom behavior is like
importer = PetabImporter(petab_problem)
obj = importer.create_objective(AMICI)
obj.amici_solver.setAbsoluteTolerance(1e-8)
problem = importer.create_problem(obj)
Since it's quite common for me to customize things like simulator tolerances, passing obj
to create_problem
is no change for me.
Just to explain my (weak) preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
passing the objective to problem is still possible. And the other behavior would look like (+- details)
importer = PetabImporter(petab_problem, AMICI)
obj = importer.create_objective()
obj.amici_solver.setAbsoluteTolerance(1e-8)
problem = importer.create_problem(obj)
I think going with allowing both would be best solution to be able to adjust to individual preferences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer to only provide users with one way of doing things 😅 So my preference would be to only permit specification via __init__
xor create_objective
, and between those I prefer create_objective
.
Since you have done the work, I'll defer to whatever you decide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By now, PetabImporter
doesn't have much functionality or internal state left. Maybe we could directly go for something like
importer = AmiciPetabImporter(petab_problem, ...)
problem = importer.create_problem()
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By now,
PetabImporter
doesn't have much functionality or internal state left. Maybe we could directly go for something likeimporter = AmiciPetabImporter(petab_problem, ...) problem = importer.create_problem() ...
I think currently the main advantage is that we can easily switch by just changing an argument in the Importer to switch simulators if needed/wanted which we would lose with the above. additionally the create_problem
function is for all of them kind of the same which just in my mind creates this natural hierarchy of "Importer -> Inner thing (=Objective creator)"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
easily switch by just changing an argument
In the other case, it's just changing the class name.
It has the advantage of better static code analysis support and avoids having PetabImporter.__init__
with the superset of arguments for all simulators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean like this?
class AmiciPetabImporter(PetabImporter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def create_objective(self, *args, **kwargs):
super().create_objective(simulator_type=AMICI, *args, **kwargs)
Fine for me. Having the argument in the PetabImporter.create_objective
makes sense to me since that's where it's used, but the other helpful alternatives (like PetabImporter(simulator_type=...)
or AmiciPetabImporter
) also make sense 👍
Co-authored-by: Dilan Pathirana <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Good changes.
self.simulator_type = simulator_type | ||
self.simulator = simulator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
Think this makes sense. Also not strong opinion.
Co-authored-by: Fabian Fröhlich <[email protected]>
As no one had a strong opinion, I would put it up to vote how to organize the code:
I would keep the poll open till end of tomorrow :) PS: sorry for the emojis of vote, these were the two most neutral ones I saw one can react with under a comment. |
CHanged the way the petab importer works. It used to be tailored to amici models and contain a lot of functionality that were only necessary for objective function creation of amici. As we now support other simulators, changed it to a
Importer-Factory
design.The importer only creates the problem now. You can however supply a
simulator_type
to it, which then creates the corresponding factory, creating the objective. All Functions that are simulator specific are in the factory.This should make the Code more clear in my opinion, we are getting rid of the PetabImporter depending on the AmiciObjectBuilder and make it much more versatile to support a new simulator.
Also added a basic support for any PEtab simulator. This allows every simulator who is supporting PEtab to directly use pyPESTO. This is not an efficient implementation, but it is also not meant to be one, rather lowering hurdles to use pyPESTO.