77from torch import Tensor
88from torch .distributions import Distribution
99
10- from sbi .neural_nets .estimators . score_estimator import ConditionalScoreEstimator
10+ from sbi .neural_nets .estimators import ConditionalVectorFieldEstimator
1111from sbi .utils .score_utils import (
1212 add_diag_or_dense ,
1313 denoise ,
@@ -58,7 +58,7 @@ def decorator(cls: Type["IIDScoreFunction"]) -> Type["IIDScoreFunction"]:
5858class IIDScoreFunction (ABC ):
5959 def __init__ (
6060 self ,
61- score_estimator : "ConditionalScoreEstimator" ,
61+ vector_field_estimator : ConditionalVectorFieldEstimator ,
6262 prior : Distribution ,
6363 device : str = "cpu" ,
6464 ) -> None :
@@ -76,12 +76,34 @@ def __init__(
7676 this requires some adjustments.
7777
7878 Args:
79- score_estimator : The neural network modeling the score.
79+ vector_field_estimator : The neural network modeling the score.
8080 prior: The prior distribution.
8181 device: The device on which to evaluate the potential. Defaults to "cpu".
8282 """
8383
84- self .score_estimator = score_estimator .to (device ).eval ()
84+ if not vector_field_estimator .SCORE_DEFINED :
85+ raise ValueError (
86+ "Score is not defined for this vector field estimator. "
87+ "You are probably using a vector field estimator that does not "
88+ "implement the score function, e.g. an optimal transport-based "
89+ "flow matching. IID methods require the score function to be defined "
90+ "so they are not applicable to this vector field estimator. "
91+ "If you have implemented a custom vector field "
92+ "estimator with score defined, set SCORE_DEFINED to True."
93+ )
94+ if not vector_field_estimator .MARGINALS_DEFINED :
95+ raise ValueError (
96+ "Marginals are not defined for this vector field estimator. "
97+ "You are probably using a vector field estimator that does not "
98+ "implement the marginals mean_t_fn and std_fn, e.g. an "
99+ "optimal transport-based flow matching. IID methods require the "
100+ "marginals to be defined so they are not applicable to this "
101+ "vector field estimator. "
102+ "If you have implemented a custom vector field "
103+ "estimator with marginals defined, set MARGINALS_DEFINED to True."
104+ )
105+
106+ self .vector_field_estimator = vector_field_estimator .to (device ).eval ()
85107 self .prior = prior
86108
87109 @abstractmethod
@@ -106,17 +128,17 @@ def __call__(
106128
107129
108130@register_iid_method ("fnpe" )
109- class FNPEScoreFunction (IIDScoreFunction ):
131+ class FactorizedNPEScoreFunction (IIDScoreFunction ):
110132 def __init__ (
111133 self ,
112- score_estimator : "ConditionalScoreEstimator" ,
134+ vector_field_estimator : ConditionalVectorFieldEstimator ,
113135 prior : Distribution ,
114136 device : str = "cpu" ,
115137 prior_score_weight : Optional [Callable [[Tensor ], Tensor ]] = None ,
116138 ) -> None :
117139 r"""
118- The FNPEScoreFunction implments the "Factorized Neural Posterior Estimation"
119- method for score-based models [1].
140+ The FactorizedNPEScoreFunction implments the
141+ "Factorized Neural Posterior Estimation" method for score-based models [1].
120142
121143 This method does not apply the necessary corrections for the score function, but
122144 instead uses a simple weighting of the prior score. This is generally applicable
@@ -131,15 +153,15 @@ def __init__(
131153 (https://arxiv.org/abs/2209.14249)
132154
133155 Args:
134- score_estimator : The neural network modeling the score.
156+ vector_field_estimator : The neural network modeling the score.
135157 prior: The prior distribution.
136158 device: The device on which to evaluate the potential. Defaults to "cpu".
137159 prior_score_weight: A function to weight the prior score. Defaults to the
138160 linear interpolation between zero (at t=0) and one (at t=t_max).
139161 """
140- super ().__init__ (score_estimator , prior , device )
162+ super ().__init__ (vector_field_estimator , prior , device )
141163 if prior_score_weight is None :
142- t_max = score_estimator .t_max
164+ t_max = vector_field_estimator .t_max
143165
144166 def prior_score_weight_fn (t ):
145167 return (t_max - t ) / t_max
@@ -168,13 +190,13 @@ def __call__(
168190 assert conditions .ndim == 2 , "Conditions must have shape [iid,...]"
169191
170192 if time is None :
171- time = torch .tensor ([self .score_estimator .t_min ])
193+ time = torch .tensor ([self .vector_field_estimator .t_min ])
172194
173195 N = conditions .shape [0 ]
174196
175197 # Compute the per-sample score
176198 inputs = ensure_theta_batched (inputs )
177- base_score = self .score_estimator (inputs , conditions , time )
199+ base_score = self .vector_field_estimator . score (inputs , conditions , time )
178200
179201 # Compute the prior score
180202 prior_score = self .prior_score_weight_fn (time ) * self .prior_score_fn (inputs )
@@ -214,7 +236,7 @@ def prior_score_fn(self, theta: Tensor) -> Tensor:
214236class BaseGaussCorrectedScoreFunction (IIDScoreFunction ):
215237 def __init__ (
216238 self ,
217- score_estimator : "ConditionalScoreEstimator" ,
239+ vector_field_estimator : ConditionalVectorFieldEstimator ,
218240 prior : Distribution ,
219241 ensure_lam_psd : bool = True ,
220242 lam_psd_nugget : float = 0.01 ,
@@ -244,13 +266,13 @@ def __init__(
244266 (https://arxiv.org/abs/2411.02728)
245267
246268 Args:
247- score_estimator : The neural network modelling the score.
269+ vector_field_estimator : The neural network modelling the score.
248270 prior: The prior distribution.
249271 ensure_lam_psd: Whether to ensure the precision matrix is positive definite.
250272 lam_psd_nugget: The nugget value to ensure positive definiteness.
251273 device: The device on which to evaluate the potential. Defaults to "cpu".
252274 """
253- super ().__init__ (score_estimator , prior , device )
275+ super ().__init__ (vector_field_estimator , prior , device )
254276 self .ensure_lam_psd = ensure_lam_psd
255277 self .lam_psd_nugget = lam_psd_nugget
256278
@@ -289,8 +311,8 @@ def marginal_denoising_posterior_precision_est_fn(
289311 precisions_posteriors = self .posterior_precision_est_fn (conditions )
290312
291313 # Denoising posterior via Bayes rule
292- m = self .score_estimator .mean_t_fn (time )
293- std = self .score_estimator .std_fn (time )
314+ m = self .vector_field_estimator .mean_t_fn (time )
315+ std = self .vector_field_estimator .std_fn (time )
294316
295317 if precisions_posteriors .ndim == 4 :
296318 Ident = torch .eye (precisions_posteriors .shape [- 1 ])
@@ -315,8 +337,8 @@ def marginal_prior_score_fn(self, time: Tensor, inputs: Tensor) -> Tensor:
315337 try :
316338 with torch .enable_grad ():
317339 inputs = inputs .clone ().detach ().requires_grad_ (True )
318- m = self .score_estimator .mean_t_fn (time )
319- std = self .score_estimator .std_fn (time )
340+ m = self .vector_field_estimator .mean_t_fn (time )
341+ std = self .vector_field_estimator .std_fn (time )
320342 p = marginalize (self .prior , m , std )
321343 log_p = p .log_prob (inputs )
322344 prior_score = torch .autograd .grad (
@@ -342,8 +364,8 @@ def marginal_denoising_prior_precision_fn(
342364 Returns:
343365 Marginal denoising prior precision.
344366 """
345- m = self .score_estimator .mean_t_fn (time )
346- std = self .score_estimator .std_fn (time )
367+ m = self .vector_field_estimator .mean_t_fn (time )
368+ std = self .vector_field_estimator .std_fn (time )
347369
348370 p_denoise = denoise (self .prior , m , std , inputs )
349371
@@ -388,9 +410,11 @@ def __call__(
388410 N , * _ = conditions .shape
389411
390412 if time is None :
391- time = torch .tensor ([self .score_estimator .t_min ])
413+ time = torch .tensor ([self .vector_field_estimator .t_min ])
392414
393- base_score = self .score_estimator (inputs , conditions , time , ** kwargs )
415+ base_score = self .vector_field_estimator .score (
416+ inputs , conditions , time , ** kwargs
417+ )
394418 prior_score = self .marginal_prior_score_fn (time , inputs )
395419
396420 # Marginal prior precision
@@ -438,7 +462,7 @@ def __call__(
438462class GaussCorrectedScoreFn (BaseGaussCorrectedScoreFunction ):
439463 def __init__ (
440464 self ,
441- score_estimator : "ConditionalScoreEstimator" ,
465+ vector_field_estimator : ConditionalVectorFieldEstimator ,
442466 prior : Distribution ,
443467 posterior_precision : Optional [Tensor ] = None ,
444468 scale_from_prior_precision : float = 2.0 ,
@@ -453,7 +477,7 @@ def __init__(
453477 precision. This method simply scales the prior precision by a factor.
454478
455479 Args:
456- score_estimator : The neural network modeling the score.
480+ vector_field_estimator : The neural network modeling the score.
457481 prior: The prior distribution.
458482 posterior_precision: Optional preset posterior precision.
459483 scale_from_prior_precision: If not provided it simply increases the prior
@@ -463,7 +487,7 @@ def __init__(
463487 device: The device on which to evaluate the potential. Defaults to "cpu".
464488 """
465489 super ().__init__ (
466- score_estimator , prior , enable_lam_psd , lam_psd_nugget , device = device
490+ vector_field_estimator , prior , enable_lam_psd , lam_psd_nugget , device = device
467491 )
468492
469493 if posterior_precision is None :
@@ -511,7 +535,7 @@ def estimate_prior_precision(
511535 except Exception :
512536 d = prior .event_shape [0 ]
513537 num_samples = int (math .sqrt (d ) * 1000 )
514- prior_samples = prior .sample (( num_samples ,))
538+ prior_samples = prior .sample (torch . Size (( num_samples ,) ))
515539 prior_precision_estimate = 1 / torch .var (prior_samples , dim = 0 )
516540 posterior_precision = scale_from_prior_precision * prior_precision_estimate
517541 return posterior_precision
@@ -521,7 +545,7 @@ def estimate_prior_precision(
521545class AutoGaussCorrectedScoreFn (BaseGaussCorrectedScoreFunction ):
522546 def __init__ (
523547 self ,
524- score_estimator : "ConditionalScoreEstimator" ,
548+ vector_field_estimator : ConditionalVectorFieldEstimator ,
525549 prior : Distribution ,
526550 enable_lam_psd : bool = True ,
527551 lam_psd_nugget : float = 0.01 ,
@@ -533,11 +557,11 @@ def __init__(
533557 r"""
534558 This method extends the by estimating the posterior precision using
535559 approximate posterior samples obtained from a diffusion model (using the
536- score_estimator ) [1]. This method has a slight initialization overhead, but
537- generally provides more accurate results than simple heuristics.
560+ vector_field_estimator ) [1]. This method has a slight initialization overhead,
561+ but generally provides more accurate results than simple heuristics.
538562
539563 Args:
540- score_estimator : The neural network modeling the score.
564+ vector_field_estimator : The neural network modeling the score.
541565 prior: The prior distribution.
542566 enable_lam_psd: Whether to ensure the precision matrix is positive definite.
543567 lam_psd_nugget: The nugget value to ensure positive definiteness.
@@ -548,7 +572,7 @@ def __init__(
548572 device: The device on which to evaluate the potential. Defaults to "cpu".
549573 """
550574 super ().__init__ (
551- score_estimator , prior , enable_lam_psd , lam_psd_nugget , device = device
575+ vector_field_estimator , prior , enable_lam_psd , lam_psd_nugget , device = device
552576 )
553577 self .precision_est_only_diag = precision_est_only_diag
554578 self .precision_est_budget = precision_est_budget
@@ -565,7 +589,7 @@ def posterior_precision_est_fn(self, conditions: Tensor) -> Tensor:
565589 Estimated posterior precision.
566590 """
567591 return self .estimate_posterior_precision (
568- self .score_estimator ,
592+ self .vector_field_estimator ,
569593 self .prior ,
570594 conditions ,
571595 precision_est_only_diag = self .precision_est_only_diag ,
@@ -577,7 +601,7 @@ def posterior_precision_est_fn(self, conditions: Tensor) -> Tensor:
577601 @functools .lru_cache ()
578602 def estimate_posterior_precision (
579603 cls ,
580- score_estimator : "ConditionalScoreEstimator" ,
604+ vector_field_estimator : ConditionalVectorFieldEstimator ,
581605 prior : Distribution ,
582606 conditions : Tensor ,
583607 precision_est_only_diag : bool = False ,
@@ -589,7 +613,7 @@ def estimate_posterior_precision(
589613 for each observation, then empirically estimating the precision matrix.
590614
591615 Args:
592- score_estimator : The score estimator.
616+ vector_field_estimator : The score estimator.
593617 prior: The prior distribution.
594618 conditions: Observed data.
595619 precision_est_only_diag: Whether to estimate only the diagonal of the
@@ -606,9 +630,9 @@ def estimate_posterior_precision(
606630 # the results efficiently.
607631
608632 # NOTE: To avoid circular imports :(
609- from sbi .inference .posteriors .score_posterior import ScorePosterior
633+ from sbi .inference .posteriors .vector_field_posterior import VectorFieldPosterior
610634
611- posterior = ScorePosterior ( score_estimator , prior )
635+ posterior = VectorFieldPosterior ( vector_field_estimator , prior )
612636
613637 if precision_est_budget is None :
614638 if precision_est_only_diag :
@@ -674,16 +698,16 @@ def marginal_denoising_posterior_precision_est_fn(
674698 with torch .enable_grad ():
675699 # NOTE: torch.func can be realtively unstable...
676700 jac_fn = torch .func .jacrev ( # type: ignore
677- lambda x : self .score_estimator (x , conditions , time )
701+ lambda x : self .vector_field_estimator . score (x , conditions , time )
678702 )
679703 jac_fn = torch .func .vmap (torch .func .vmap (jac_fn )) # type: ignore
680704 jac = jac_fn (inputs ).squeeze (1 )
681705
682706 # Must be symmetrical
683707 jac = 0.5 * (jac + jac .transpose (- 1 , - 2 ))
684708
685- m = self .score_estimator .mean_t_fn (time )
686- std = self .score_estimator .std_fn (time )
709+ m = self .vector_field_estimator .mean_t_fn (time )
710+ std = self .vector_field_estimator .std_fn (time )
687711 cov0 = std ** 2 * jac + torch .eye (d )[None , None , :, :]
688712
689713 denoising_posterior_precision = m ** 2 / std ** 2 + torch .inverse (cov0 )
0 commit comments