def _twofilter_smoothing_ON(self, t, ti, info, phi, lwinfo, return_ess, modif_forward, modif_info): """O(N) version of two-filter smoothing. This method should not be called directly, see twofilter_smoothing. """ if modif_info is not None: lwinfo += modif_info Winfo = rs.exp_and_normalise(lwinfo) I = rs.multinomial(Winfo) if modif_forward is not None: lw = self.wgts[t].lw + modif_forward W = rs.exp_and_normalise(lw) else: W = self.wgts[t].W J = rs.multinomial(W) log_omega = self.fk.logpt(t + 1, self.X[t][J], info.hist.X[ti][I]) if modif_forward is not None: log_omega -= modif_forward[J] if modif_info is not None: log_omega -= modif_info[I] Om = rs.exp_and_normalise(log_omega) est = np.average(phi(self.X[t][J], info.hist.X[ti][I]), axis=0, weights=Om) if return_ess: return (est, 1. / np.sum(Om**2)) else: return est
def backward_sampling(self, M, linear_cost=False, return_ar=False): """Generate smoothing trajectories using FFBS. FFBS (forward filtering backward smoothing) is a class of off-line smoothing algorithms, which generate smoothing trajectories constructed from the history of a particle filter. Arguments --------- M: int number of trajectories we want to generate linear_cost: bool if set to True, the O(N) version is used, see below. return_ar: bool (default=False) if set to True, change the output, see below. Returns ------- paths: a list of ndarrays paths[t][n] is component t of trajectory m. ar: float the overall acceptance rate of the rejection procedure Notes ----- 1. if ``linear_cost=False``, complexity is O(TMN); i.e. O(TN^2) for M=N; if ``linear_cost=True``, complexity is O(T(M+N)), i.e. O(TN) for M=N. This requires that model has method `upper_bound_trans`, which provides the log of a constant C_t such that :math:`p_t(x_t|x_{t-1}) \leq C_t`. 2. main output is ``paths``, a list of T arrays such that ``paths[t][m]`` is component t of trajectory m. 3. if ``linear_cost=True`` and ``return_ar=True``, output is tuple ``(paths, ar)``, where ``paths`` is as above, and ``ar`` is the overall acceptance rate (of the rejection steps that choose the ancestors); otherwise output is simply ``paths``. """ idx = np.empty((self.T, M), dtype=int) idx[-1, :] = rs.multinomial(self.wgts[-1].W, M=M) if linear_cost: ar = self._backward_sampling_ON(M, idx) else: self._backward_sampling_ON2(M, idx) # When M=1, we want a list of states, not a list of arrays containing # one state if M == 1: idx = idx.squeeze(axis=1) paths = [self.X[t][idx[t]] for t in range(self.T)] if linear_cost and return_ar: return (paths, ar) else: return paths
def backward_sampling(self, M, linear_cost=False, return_ar=False): """Generate trajectories using the FFBS (forward filtering backward sampling) algorithm. Arguments --------- M: int number of trajectories we want to generate linear_cost: bool if set to True, the O(N) version is used, see below. return_ar: bool (default=False) if set to True, change the output, see below. Returns ------- paths: a list of ndarrays paths[t][n] is component t of trajectory m. ar: float the overall acceptance rate of the rejection procedure Notes ----- 1. if linear_cost=False, complexity is O(TMN); i.e. O(TN^2) for M=N; if =True, complexity is O(T(M+N)), i.e. O(TN) for M=N. This requires that model has method `upper_bound_trans(self,t)`, which provides the log of a constant C_t such that p_t(x_t|x_{t-1})<=C_t 2. main output is *paths*, a list of T arrays such that paths[t][m] is component t of trajectory m. 3. if linear_cost=True and return_ar=True, output is tuple (paths, ar), where paths is as above, and ar is the overall acceptance rate (of the rejection steps that choose the ancestors); otherwise output is simply paths """ idx = np.empty((self.T, M), dtype=int) idx[-1, :] = rs.multinomial(self.wgt[-1].W, M=M) if linear_cost: ar = self._backward_sampling_ON(M, idx) else: self._backward_sampling_ON2(M, idx) # When M=1, we want a list of states, not a list of arrays containing # one state if M == 1: idx = idx.squeeze(axis=1) paths = [self.X[t][idx[t]] for t in range(self.T)] if linear_cost and return_ar: return (paths, ar) else: return paths
def sample(self, N=1): """Sample N trajectories from the posterior. Note ---- Performs the forward step in case it has not been performed. """ if not self.filt: self.forward() paths = np.empty((len(self.filt), N), np.int) paths[-1, :] = rs.multinomial(self.filt[-1], M=N) log_trans = np.log(self.hmm.trans_mat) for t, f in reversed(list(enumerate(self.filt[:-1]))): for n in range(N): probs = rs.exp_and_normalise(log_trans[:, paths[t + 1, n]] + np.log(f)) paths[t, n] = rs.multinomial_once(probs) return paths