Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
9323e95
fix: active field to avoid infinite recursion (#1938)
janfb Jul 20, 2026
3adf008
build(deps): bump torch from 2.11.0 to 2.13.0 (#1935)
dependabot[bot] Jul 20, 2026
fb06850
bind implementation still using set_x as fallback yet
Jocho-Smith Jul 20, 2026
ad66cff
implement bind for VectorFieldBasedPotential, where neural_ode_backen…
Jocho-Smith Jul 21, 2026
963efcf
removed warning comments
Jocho-Smith Jul 21, 2026
a976fb5
added set_x back into PosteriorBasedPotential.bind for uniform iid ha…
Jocho-Smith Jul 21, 2026
02d8be8
added bind to FakePotential and TractablePotential in test files
Jocho-Smith Jul 21, 2026
be2eb8a
added bind to mcmc and vector_field. This will likely raise errors, b…
Jocho-Smith Jul 21, 2026
35d4dea
implement bind for EnsemblePotential
Jocho-Smith Jul 21, 2026
4e3bfb7
fixed closure late-binding bug by moving init_fn inside the loop. Rem…
Jocho-Smith Jul 21, 2026
66bf2aa
attempt to fix vector_field error
Jocho-Smith Jul 21, 2026
d449cd0
fix:'wrapper has no attribute prior' torch distribution lost attribut…
Jocho-Smith Jul 21, 2026
8ed0076
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
323fdc6
potential fix for test_prior_wrappers due to version differences in CI
Jocho-Smith Jul 21, 2026
c9173d0
error changed from prior to rate, so I 'touched the right place'. Nex…
Jocho-Smith Jul 21, 2026
54c2e4f
bind implementation still using set_x as fallback yet
Jocho-Smith Jul 20, 2026
e5efa02
implement bind for VectorFieldBasedPotential, where neural_ode_backen…
Jocho-Smith Jul 21, 2026
aca0b2f
removed warning comments
Jocho-Smith Jul 21, 2026
18394fe
added set_x back into PosteriorBasedPotential.bind for uniform iid ha…
Jocho-Smith Jul 21, 2026
e773937
added bind to FakePotential and TractablePotential in test files
Jocho-Smith Jul 21, 2026
72125c8
added bind to mcmc and vector_field. This will likely raise errors, b…
Jocho-Smith Jul 21, 2026
0d5f16a
implement bind for EnsemblePotential
Jocho-Smith Jul 21, 2026
d4c62e2
fixed closure late-binding bug by moving init_fn inside the loop. Rem…
Jocho-Smith Jul 21, 2026
41c0a98
attempt to fix vector_field error
Jocho-Smith Jul 21, 2026
f38bf44
fix:'wrapper has no attribute prior' torch distribution lost attribut…
Jocho-Smith Jul 21, 2026
b33e300
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
d0c0e38
potential fix for test_prior_wrappers due to version differences in CI
Jocho-Smith Jul 21, 2026
42dce39
error changed from prior to rate, so I 'touched the right place'. Nex…
Jocho-Smith Jul 21, 2026
dbf9ca5
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
6817f0e
Merge branch 'oop-bind' of github.com:jocho-smith/sbi into oop-bind
Jocho-Smith Jul 21, 2026
b1b11c2
remove the failed _get_bound_potential fix again
Jocho-Smith Jul 21, 2026
58d6d38
docs: add list of maintainers to docs (#1934)
dgedon Jul 21, 2026
7927d8c
docs: add guidance for coding agents (#1868)
janfb Jul 21, 2026
2c67b91
build(deps): bump pillow from 12.2.0 to 12.3.0 (#1941)
dependabot[bot] Jul 21, 2026
4425525
Merge branch 'main' into gsoc-2026
janfb Jul 21, 2026
165905e
tried manual fix to recursion error based on #1938
Jocho-Smith Jul 21, 2026
e21c90a
Revert "tried manual fix to recursion error based on #1938"
Jocho-Smith Jul 21, 2026
8da6eb4
bind implementation still using set_x as fallback yet
Jocho-Smith Jul 20, 2026
f10969a
implement bind for VectorFieldBasedPotential, where neural_ode_backen…
Jocho-Smith Jul 21, 2026
8482980
removed warning comments
Jocho-Smith Jul 21, 2026
27bfbac
added set_x back into PosteriorBasedPotential.bind for uniform iid ha…
Jocho-Smith Jul 21, 2026
4efce23
added bind to FakePotential and TractablePotential in test files
Jocho-Smith Jul 21, 2026
a08d0fc
added bind to mcmc and vector_field. This will likely raise errors, b…
Jocho-Smith Jul 21, 2026
0c53752
implement bind for EnsemblePotential
Jocho-Smith Jul 21, 2026
162a733
fixed closure late-binding bug by moving init_fn inside the loop. Rem…
Jocho-Smith Jul 21, 2026
f70ac4a
attempt to fix vector_field error
Jocho-Smith Jul 21, 2026
280e722
fix:'wrapper has no attribute prior' torch distribution lost attribut…
Jocho-Smith Jul 21, 2026
8286c83
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
2966cb1
potential fix for test_prior_wrappers due to version differences in CI
Jocho-Smith Jul 21, 2026
f4aaefc
error changed from prior to rate, so I 'touched the right place'. Nex…
Jocho-Smith Jul 21, 2026
b63af74
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
c3dccf3
added bind to mcmc and vector_field. This will likely raise errors, b…
Jocho-Smith Jul 21, 2026
d1b8106
attempt to fix vector_field error
Jocho-Smith Jul 21, 2026
32f164b
Empty commit to trigger the pull-requests pipeline
Jocho-Smith Jul 21, 2026
2a5c2ce
remove the failed _get_bound_potential fix again
Jocho-Smith Jul 21, 2026
32c6835
Revert "tried manual fix to recursion error based on #1938"
Jocho-Smith Jul 21, 2026
62e0f3c
Merge branch 'oop-bind' of github.com:jocho-smith/sbi into oop-bind
Jocho-Smith Jul 21, 2026
e6053d6
fixing issues after main merge...
Jocho-Smith Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions sbi/inference/posteriors/base_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def potential(
This can be helpful for e.g. sensitivity analysis, but increases memory
consumption.
"""
self.potential_fn.set_x(self._x_else_default_x(x))
self.potential_fn = self.potential_fn.bind(self._x_else_default_x(x))

theta = ensure_theta_batched(torch.as_tensor(theta))
return self.potential_fn(
Expand Down Expand Up @@ -294,7 +294,7 @@ def map(
)

if self._map is None or force_update:
self.potential_fn.set_x(self.default_x)
self.potential_fn = self.potential_fn.bind(self.default_x)
self._map = self._calculate_map(
num_iter=num_iter,
num_to_optimize=num_to_optimize,
Expand Down
2 changes: 1 addition & 1 deletion sbi/inference/posteriors/ensemble_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def map(
return log_weights, maps

else:
self.potential_fn.set_x(self._x_else_default_x(x))
self.potential_fn = self.potential_fn.bind(self._x_else_default_x(x))

if init_method == "posterior":
inits = self.sample((num_init_samples,), self._x_else_default_x(x))
Expand Down
4 changes: 2 additions & 2 deletions sbi/inference/posteriors/importance_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def log_prob(
`len($\theta$)`-shaped log-probability.
"""
x = self._x_else_default_x(x)
self.potential_fn.set_x(x)
self.potential_fn = self.potential_fn.bind(x)

theta = ensure_theta_batched(torch.as_tensor(theta))

Expand Down Expand Up @@ -212,7 +212,7 @@ def sample(

method = self.method if method is None else method

self.potential_fn.set_x(self._x_else_default_x(x))
self.potential_fn = self.potential_fn.bind(self._x_else_default_x(x))

if method == "sir":
return self._sir_sample(
Expand Down
4 changes: 2 additions & 2 deletions sbi/inference/posteriors/rejection_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def log_prob(
)
warn("The log-probability is unnormalized!", stacklevel=2)

self.potential_fn.set_x(self._x_else_default_x(x))
self.potential_fn = self.potential_fn.bind(self._x_else_default_x(x))

theta = ensure_theta_batched(torch.as_tensor(theta))
return self.potential_fn(
Expand Down Expand Up @@ -174,7 +174,7 @@ def sample(
Samples from posterior.
"""
num_samples = torch.Size(sample_shape).numel()
self.potential_fn.set_x(self._x_else_default_x(x))
self.potential_fn = self.potential_fn.bind(self._x_else_default_x(x))

potential = partial(self.potential_fn, track_gradients=True)

Expand Down
2 changes: 1 addition & 1 deletion sbi/inference/posteriors/vi_posterior.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ def _compute_amortized_elbo_loss(self, x_batch: Tensor, n_particles: int) -> Ten
x_expanded = x_batch.repeat(n_particles, 1)

# Set x_o for batched evaluation (x_is_iid=False: each θ paired with its x)
self.potential_fn.set_x(x_expanded, x_is_iid=False)
self.potential_fn = self.potential_fn.bind(x_expanded, x_is_iid=False)
log_potential_flat = self.potential_fn(theta_flat)

# Reshape: (n_particles * batch_size,) -> (n_particles, batch_size)
Expand Down
33 changes: 31 additions & 2 deletions sbi/inference/potentials/base_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,17 @@ def x_is_iid(self) -> bool:
return self._x_is_iid
else:
raise ValueError(
"No observed data is available. Use `potential_fn.set_x(x_o)`."
"No observed data is available. Use `potential_fn.bind(x_o)`."
)

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."""
# import warnings
# warnings.warn(
# "set_x() is deprecated, use .bind(x) instead for stateless binding",
# FutureWarning,
# stacklevel=2,
# )

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.

Remove those comments

if x_o is not None:
x_o = process_x(x_o).to(self.device)
self._x_o = x_o
Expand All @@ -66,7 +72,7 @@ def x_o(self) -> Tensor:
return self._x_o
else:
raise ValueError(
"No observed data is available. Use `potential_fn.set_x(x_o)`."
"No observed data is available. Use `potential_fn.bind(x_o)`."
)

@x_o.setter
Expand All @@ -82,6 +88,20 @@ def return_x_o(self) -> Optional[Tensor]:
"""
return self._x_o

def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "BasePotential":
"""Create new potential with x bound, without mutable state.

Args:
x_o: Observed data to bind.
x_is_iid: Whether x represents iid observations.

Returns:
New potential instance with x bound.

Subclasses must implement this method.
"""
raise NotImplementedError(f"{self.__class__.__name__} must implement bind()")

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.

why can we not define a general binding function?

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.

What would that be?

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.

Actually if it is covered in all methods that inherit from the base potential, then this is fine. There is no clear default way.


def to(self, device: Union[str, torch.device]) -> "BasePotential":
"""Move prior and x_o to the given device.

Expand Down Expand Up @@ -154,3 +174,12 @@ def __call__(self, theta, track_gradients: bool = True):
"""
with torch.set_grad_enabled(track_gradients):
return self.potential_fn(theta, self.x_o)

def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "CustomPotentialWrapper":
"""Create new potential with x bound, without mutable state."""
return CustomPotentialWrapper(
potential_fn=self.potential_fn,
prior=self.prior,
x_o=x_o,
device=self.device,
)
Comment on lines +172 to +179

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 x_is_iid is silently discarded

11 changes: 11 additions & 0 deletions sbi/inference/potentials/likelihood_based_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ def to(self, device: Union[str, torch.device]) -> "LikelihoodBasedPotential":
self.likelihood_estimator.to(device)
return self

def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "LikelihoodBasedPotential":
"""Create new potential with x bound, without mutable state."""
bound = LikelihoodBasedPotential(
likelihood_estimator=self.likelihood_estimator,
prior=self.prior,
x_o=None,
device=self.device,
)
bound.set_x(x_o, x_is_iid=x_is_iid)
return bound
Comment on lines +99 to +106

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 is a different way of building bind than in the other cases. Is taht because the LikelihoodBasedPotential requires a set_x afterwards as of now? Do we plan to change that?

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.

In the current implementation of sbi the x_is_iid handling is done through set_x, this is why I kept it for now. After all set_x calls are replaced with bind I'll build an alternative way for the iid handling, so bind is independent of this, and set_x stays for deprecation. So the PR after this one will implement this.


def __call__(self, theta: Tensor, track_gradients: bool = True) -> Tensor:
r"""Returns the potential $\log(p(x_o|\theta)p(\theta))$.

Expand Down
9 changes: 9 additions & 0 deletions sbi/inference/potentials/posterior_based_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ def set_x(self, x_o: Optional[Tensor], x_is_iid: Optional[bool] = False):
"""
super().set_x(x_o, x_is_iid=x_is_iid)

def bind(self, x_o: Tensor, x_is_iid: bool = False) -> "PosteriorBasedPotential":
"""Create new potential with x bound, without mutable state."""
return PosteriorBasedPotential(
posterior_estimator=self.posterior_estimator,
prior=self.prior,
x_o=x_o,
device=self.device,
)
Comment on lines +109 to +116

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 x_is_iid is silently discarded.


def __call__(self, theta: Tensor, track_gradients: bool = True) -> Tensor:
r"""Returns the potential for posterior-based methods.

Expand Down
11 changes: 11 additions & 0 deletions sbi/inference/potentials/ratio_based_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ def to(self, device: Union[str, torch.device]) -> "RatioBasedPotential":
self.ratio_estimator.to(device)
return self

def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "RatioBasedPotential":
"""Create new potential with x bound, without mutable state."""
bound = RatioBasedPotential(
ratio_estimator=self.ratio_estimator,
prior=self.prior,
x_o=None,
device=self.device,
)
bound.set_x(x_o, x_is_iid=x_is_iid)
return bound
Comment on lines +85 to +94

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 comment as for the LikelihodBasedPotential


def __call__(self, theta: Tensor, track_gradients: bool = True) -> Tensor:
r"""Returns the potential for likelihood-ratio-based methods.

Expand Down
12 changes: 10 additions & 2 deletions sbi/utils/conditional_density_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,23 @@ def x_is_iid(self) -> bool:
return self._x_is_iid
else:
raise ValueError(
"No observed data is available. Use `potential_fn.set_x(x_o)`."
"No observed data is available. Use `potential_fn.bind(x_o)`."
)

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_is_iid = x_is_iid
self.potential_fn.set_x(x_o, x_is_iid=x_is_iid)
self.potential_fn = self.potential_fn.bind(x_o, x_is_iid=x_is_iid)

def bind(self, x_o: Tensor, x_is_iid: bool = True) -> "ConditionedPotential":
"""Create new potential with x bound, without mutable state."""
return ConditionedPotential(
potential_fn=self.potential_fn.bind(x_o, x_is_iid),
condition=self.condition,
dims_to_sample=self.dims_to_sample,
)

@property
def x_o(self) -> Tensor:
Expand Down
Loading