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

Fix return_dict when an AmiciObjective is inside AggregatedObjective #1424

Merged
merged 7 commits into from
Jul 3, 2024

Conversation

dilpath
Copy link
Member

@dilpath dilpath commented Jul 2, 2024

AmiciObjective previously returned all AMICI RData information when return_dict=True. However, this was handled via __call__. If the PEtab problem has a prior, then an AggregatedObjective is created, with an AmiciObjective and the prior inside. The AggregatedObjective calls call_unprocessed of each internal objective, skipping AmiciObjective.__call__ and hence one would see something like:

# call AggregatedObjective
>>> result = objective(x, return_dict=True)
>>> result['rdatas'][0].y is None
True
>>> result = objective(x, return_dict=True, amici_reporting=amici.RDataReporting.full)
>>> result['rdatas'][0].y is None
False

This PR changes the above to

>>> result = objective(x, return_dict=True)
>>> result['rdatas'][0].y is None
False

i.e., rdatas are now "full" and e.g. plotting is now possible.

An alternative fix would be to always pass return_dict to call_unprocessed. This would be fine for me, but might break someone's custom objective.

@codecov-commenter
Copy link

codecov-commenter commented Jul 2, 2024

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 93.75000% with 1 line in your changes missing coverage. Please review.

Project coverage is 83.35%. Comparing base (5a8e014) to head (b9ec235).

Files Patch % Lines
pypesto/objective/aggregated.py 83.33% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1424      +/-   ##
===========================================
+ Coverage    83.33%   83.35%   +0.01%     
===========================================
  Files          161      161              
  Lines        13226    13235       +9     
===========================================
+ Hits         11022    11032      +10     
+ Misses        2204     2203       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@PaulJonasJost PaulJonasJost left a comment

Choose a reason for hiding this comment

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

A test for this would be good as well ensuring it works as intended?

@@ -458,18 +439,23 @@ def call_unprocessed(

x_dct = self.par_arr_to_dct(x)

# only ask amici to compute required quantities
amici_reporting = (
self.amici_reporting
if amici_reporting is None
else amici_reporting
)
if amici_reporting is None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

It should be made clear in the documentation that amici_reporting takes precedence over return_dict imo.

Comment on lines 186 to 187
if self.share_return_dict:
kwargs["return_dict"] = return_dict
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if return_dict is in kwargs already?

Copy link
Member Author

Choose a reason for hiding this comment

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

It cannot be, since return_dict is it's own kwarg in this method's argument list.

Comment on lines 120 to 121
if objective_.share_return_dict:
objective_kwargs["return_dict"] = return_dict
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as in the base objective (see above or below comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

I would actually argue that the outer return_dict should take precedence over inner return_dict and that we don't need the if objective_.share_return_dict: check at all.

@@ -178,6 +183,8 @@ def __call__(
x_full = self.pre_post_processor.preprocess(x=x)

# compute result
if self.share_return_dict:
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

Otherwise there might be some argument error, because return_dict is not in every custom user objective.call_unprocessed signature. But I'll switch to Daniel's suggestion now

Comment on lines 120 to 121
if objective_.share_return_dict:
objective_kwargs["return_dict"] = return_dict
Copy link
Contributor

Choose a reason for hiding this comment

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

I would actually argue that the outer return_dict should take precedence over inner return_dict and that we don't need the if objective_.share_return_dict: check at all.

@dweindl
Copy link
Member

dweindl commented Jul 3, 2024

An alternative fix would be to always pass return_dict to call_unprocessed. This would be fine for me, but might break someone's custom objective.

I think this would make sense. Your current implementation would already enable issuing a DeprecationWarning and updating things accordingly.

Copy link
Member

@dweindl dweindl left a comment

Choose a reason for hiding this comment

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

Thx

@dilpath dilpath requested a review from m-philipps as a code owner July 3, 2024 08:14
Copy link
Contributor

@stephanmg stephanmg left a comment

Choose a reason for hiding this comment

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

Looks fine, many thanks for providing this fix.

@dilpath
Copy link
Member Author

dilpath commented Jul 3, 2024

An alternative fix would be to always pass return_dict to call_unprocessed. This would be fine for me, but might break someone's custom objective.

I think this would make sense. Your current implementation would already enable issuing a DeprecationWarning and updating things accordingly.

Thanks, I decided to simply switch to it by default now, with a DeprecationWarning.

@dilpath dilpath merged commit 10d6092 into develop Jul 3, 2024
18 checks passed
@dilpath dilpath deleted the fix_amici_posterior_return_dict branch July 3, 2024 10:43
This was referenced Jul 25, 2024
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.

6 participants