Skip to content

Separate iid from set x - #1945

Merged
dgedon merged 12 commits into
sbi-dev:gsoc-2026from
Jocho-Smith:separate-iid-from-set-x
Jul 23, 2026
Merged

Separate iid from set x#1945
dgedon merged 12 commits into
sbi-dev:gsoc-2026from
Jocho-Smith:separate-iid-from-set-x

Conversation

@Jocho-Smith

@Jocho-Smith Jocho-Smith commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This branch builds upon #1943 . There I build bind() that internally still users set_x for setting _x_o and x_is_iid. This PR is about resolving this dependence entirely and removing set_x from inside bind.

3 things: remove set_x from BasePotential.init; replace overlooked set_x inside vi_divergence; remove set_x usage inside bind()

This PR experiments with the next step for the GSoC project: decouple iid from set_x.

It requires #1943 to be merged, which is not the case yet so, I took the commits from there with me.

  • more description is coming

Jocho-Smith and others added 10 commits July 22, 2026 07:33
- Add bind() method to BasePotential (abstract) and CustomPotentialWrapper
- Add bind() to LikelihoodBasedPotential, PosteriorBasedPotential, RatioBasedPotential
- Add bind() to VectorFieldBasedPotential with full parameter support
- Add bind() to EnsemblePotential in ensemble_posterior.py
- Add bind() to ConditionedPotential in conditional_density_utils.py
- Fix _base_recursor to use local variable _active_holder to avoid Python
  scoping issues with the _active parameter
- Update posteriors to use bind() instead of set_x() for immutability
- Fix PytorchReturnTypeWrapper, MultipleIndependent, OneDimPriorWrapper init order
  and add __deepcopy__ methods for proper deepcopy support
- Update error messages to reference bind() instead of set_x()
- Add bind() to test FakePotential and TractablePotential classes
- Add bind() method to BasePotential (abstract) and CustomPotentialWrapper
- Add bind() to LikelihoodBasedPotential, PosteriorBasedPotential, RatioBasedPotential
- Add bind() to VectorFieldBasedPotential with full parameter support
- Add bind() to EnsemblePotential in ensemble_posterior.py
- Add bind() to ConditionedPotential in conditional_density_utils.py
- Fix _base_recursor to use local variable _active_holder to avoid Python
  scoping issues with the _active parameter
- Update posteriors to use bind() instead of set_x() for immutability
- Fix PytorchReturnTypeWrapper, MultipleIndependent, OneDimPriorWrapper init order
  and add __deepcopy__ methods for proper deepcopy support
- Update error messages to reference bind() instead of set_x()
- Add bind() to test FakePotential and TractablePotential classes
- Replace remaining set_x calls in VectorFieldPosterior with bind
…d set_x inside vi_divergence; remove set_x usage inside bind()
@Jocho-Smith

Jocho-Smith commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

all tests passing, good! Now I'll work on merging #1943 and then merge this next.

Since this one already decouples set_x and bind I can continue preparing the deprecation here: #1946

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.91%. Comparing base (587f922) to head (caa994d).
⚠️ Report is 40 commits behind head on gsoc-2026.

Files with missing lines Patch % Lines
.../inference/potentials/posterior_based_potential.py 50.00% 4 Missing ⚠️
sbi/inference/potentials/vector_field_potential.py 87.50% 2 Missing ⚠️
sbi/samplers/vi/vi_divergence_optimizers.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           gsoc-2026    #1945      +/-   ##
=============================================
- Coverage      87.99%   87.91%   -0.09%     
=============================================
  Files            144      144              
  Lines          13499    13756     +257     
=============================================
+ Hits           11879    12094     +215     
- Misses          1620     1662      +42     
Flag Coverage Δ
fast 81.62% <85.71%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sbi/inference/posteriors/vi_posterior.py 78.60% <100.00%> (+0.04%) ⬆️
sbi/inference/potentials/base_potential.py 91.66% <100.00%> (-1.20%) ⬇️
...inference/potentials/likelihood_based_potential.py 83.33% <100.00%> (+1.22%) ⬆️
sbi/inference/potentials/ratio_based_potential.py 100.00% <100.00%> (ø)
sbi/utils/torchutils.py 70.53% <100.00%> (+2.33%) ⬆️
sbi/utils/user_input_checks_utils.py 89.06% <100.00%> (-1.05%) ⬇️
sbi/samplers/vi/vi_divergence_optimizers.py 84.69% <50.00%> (-0.33%) ⬇️
sbi/inference/potentials/vector_field_potential.py 90.84% <87.50%> (-0.14%) ⬇️
.../inference/potentials/posterior_based_potential.py 90.76% <50.00%> (-5.60%) ⬇️

... and 2 files with indirect coverage changes

Resolved conflicts:
- likelihood_based_potential.py: keep HEAD (direct attribute set)
- posterior_based_potential.py: keep HEAD (direct attribute set)
- ratio_based_potential.py: keep HEAD (direct attribute set)
- vector_field_potential.py: keep HEAD (direct attribute set + guidance_method)
- conditional_density_utils.py: accept upstream (fixes _x_is_iid on bound)
Comment on lines 58 to 63
def set_x(self, x_o: Optional[Tensor], x_is_iid: Optional[bool] = True):
"""Check the shape of the observed data and, if valid, set it."""
if x_o is not None:
x_o = process_x(x_o).to(self.device)
self._x_o = x_o
self._x_is_iid = x_is_iid

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we remove this part in this PR already?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ah no, we add the deprecation warning in #1946


def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "LikelihoodBasedPotential":
"""Create new potential with x bound, without mutable state."""
from sbi.utils.user_input_checks import process_x

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we should import that not within the function but at file level


def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "RatioBasedPotential":
"""Create new potential with x bound, without mutable state."""
from sbi.utils.user_input_checks import process_x

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same is in the likelihoood based potential


def bind(self, x_o: Tensor, x_is_iid: bool = False) -> "PosteriorBasedPotential":
"""Create new potential with x bound, without mutable state."""
from sbi.utils.user_input_checks import process_x

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same is in the likelihoood based potential

"""
super().set_x(x_o, x_is_iid)
if x_o is not None:
from sbi.utils.user_input_checks import process_x

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Import on file level

device=self.device,
iid_method=self.iid_method,
iid_params=self.iid_params,
iid_method=iid_method or self.iid_method,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this should proabbly also be a id_method if iid_method is not None else self.iid_method,

Comment thread sbi/utils/user_input_checks_utils.py Outdated
validate_args=(
prior._validate_args if validate_args is None else validate_args
),
validate_args=False,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

flag (see file level comment)

Comment thread sbi/utils/user_input_checks_utils.py Outdated
Comment on lines +237 to +245
def __deepcopy__(self, memo):
"""Ensure prior attribute is preserved during deepcopy."""
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
setattr(result, k, deepcopy(v, memo))
return result

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

flag (see file level comment)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here we seem to have the same modifications that we dicussed in #1943 before.

  • there is code duplication for __deepcopy__
  • I don't see where deepcopy is used and why it is necessary to introduce
  • we silently change the validate_args hard coded to False which seems to be a bug.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ouh sorry. I messed up the merge. Will fix it.

if x_o is not None:
x_o = process_x(x_o).to(self.device)
self._x_o = x_o
self._x_is_iid = True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

compared with set_x() we hard to _x_is_iid to True here. Since it's not a method argument, maybe True is a good default. We have to nesure though that it is set correctly in each inherited class. Because right now by calling super() in e.g. the likelihood potential, we will ahve x_is_iid=True by default

@Jocho-Smith Jocho-Smith Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

right now by calling super() in e.g. the likelihood potential, we will ahve x_is_iid=True by default

This is exactly what is happening right now on the main branch. likelihood calls super without _x_is_iid : https://github.com/sbi-dev/sbi/blob/main/sbi/inference/potentials/likelihood_based_potential.py#L80

BasePotential.init always calls set_x without any iid information: https://github.com/sbi-dev/sbi/blob/main/sbi/inference/potentials/base_potential.py#L33

x_is_iid is set with default value True: https://github.com/sbi-dev/sbi/blob/main/sbi/inference/potentials/base_potential.py#L60

@Jocho-Smith
Jocho-Smith requested a review from dgedon July 23, 2026 12:02
@dgedon
dgedon merged commit 88585d8 into sbi-dev:gsoc-2026 Jul 23, 2026
17 checks passed
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