def append_simulations( self, theta: Tensor, x: Tensor, from_round: int = 0, ) -> "RatioEstimator": r""" Store parameters and simulation outputs to use them for later training. Data are stored as entries in lists for each type of variable (parameter/data). Stores $\theta$, $x$, prior_masks (indicating if simulations are coming from the prior or not) and an index indicating which round the batch of simulations came from. Args: theta: Parameter sets. x: Simulation outputs. from_round: Which round the data stemmed from. Round 0 means from the prior. With default settings, this is not used at all for `SNRE`. Only when the user later on requests `.train(discard_prior_samples=True)`, we use these indices to find which training data stemmed from the prior. Returns: NeuralInference object (returned so that this function is chainable). """ validate_theta_and_x(theta, x) self._theta_roundwise.append(theta) self._x_roundwise.append(x) self._prior_masks.append( mask_sims_from_prior(int(from_round), theta.size(0))) self._data_round_index.append(int(from_round)) return self
def append_simulations( self, theta: Tensor, x: Tensor, proposal: Optional[Any] = None, ) -> "PosteriorEstimator": r""" Store parameters and simulation outputs to use them for later training. Data are stored as entries in lists for each type of variable (parameter/data). Stores $\theta$, $x$, prior_masks (indicating if simulations are coming from the prior or not) and an index indicating which round the batch of simulations came from. Args: theta: Parameter sets. x: Simulation outputs. proposal: The distribution that the parameters $\theta$ were sampled from. Pass `None` if the parameters were sampled from the prior. If not `None`, it will trigger a different loss-function. Returns: NeuralInference object (returned so that this function is chainable). """ validate_theta_and_x(theta, x) self._check_proposal(proposal) if ( proposal is None or proposal is self._prior or ( isinstance(proposal, RestrictedPrior) and proposal._prior is self._prior ) ): # The `_data_round_index` will later be used to infer if one should train # with MLE loss or with atomic loss (see, in `train()`: # self._round = max(self._data_round_index)) self._data_round_index.append(0) self._prior_masks.append(mask_sims_from_prior(0, theta.size(0))) else: if not self._data_round_index: # This catches a pretty specific case: if, in the first round, one # passes data that does not come from the prior. self._data_round_index.append(1) else: self._data_round_index.append(max(self._data_round_index) + 1) self._prior_masks.append(mask_sims_from_prior(1, theta.size(0))) self._theta_roundwise.append(theta) self._x_roundwise.append(x) self._proposal_roundwise.append(proposal) return self
def append_simulations( self, theta: Tensor, x: Tensor, proposal: Optional[Any] = None, ) -> "PosteriorEstimator": r""" Store parameters and simulation outputs to use them for later training. Data are stored as entries in lists for each type of variable (parameter/data). Stores $\theta$, $x$, prior_masks (indicating if simulations are coming from the prior or not) and an index indicating which round the batch of simulations came from. Args: theta: Parameter sets. x: Simulation outputs. proposal: The distribution that the parameters $\theta$ were sampled from. Pass `None` if the parameters were sampled from the prior. If not `None`, it will trigger a different loss-function. Returns: NeuralInference object (returned so that this function is chainable). """ validate_theta_and_x(theta, x) self._check_proposal(proposal) self._prior_masks = [None for _ in self._prior_masks] if (proposal is None or proposal is self._prior or (isinstance(proposal, RestrictedPrior) and proposal._prior is self._prior)): # The `_data_round_index` will later be used to infer if one should train # with MLE loss or with atomic loss (see, in `train()`: # self._round = max(self._data_round_index)) self._data_round_index.append(0) self._prior_masks.append(mask_sims_from_prior(0, theta.size(0))) else: if not self._data_round_index: # This catches a pretty specific case: if, in the first round, one # passes data that does not come from the prior. self._data_round_index.append(1) else: self._data_round_index.append(max(self._data_round_index) + 1) self._prior_masks.append(mask_sims_from_prior(1, theta.size(0))) # Delete old samples as we do not want to use them self._theta_roundwise = [None for _ in self._theta_roundwise] self._x_roundwise = [None for _ in self._x_roundwise] self._proposal_roundwise = [None for _ in self._proposal_roundwise] self._theta_roundwise.append(theta) self._x_roundwise.append(x) self._proposal_roundwise.append(proposal) if self._prior is None or isinstance(self._prior, ImproperEmpirical): if proposal is not None: raise ValueError( "You had not passed a prior at initialization, but now you " "passed a proposal. If you want to run multi-round SNPE, you have " "to specify a prior (set the `.prior` argument or re-initialize " "the object with a prior distribution). If the samples you passed " "to `append_simulations()` were sampled from the prior, you can " "run single-round inference with " "`append_simulations(..., proposal=None)`.") theta_prior = self.get_simulations()[0] self._prior = ImproperEmpirical(theta_prior, ones(theta_prior.shape[0])) return self