|
| 1 | +#include <RcppArmadillo.h> |
1 | 2 | #include <algorithm> |
2 | 3 | #include <vector> |
3 | | -#include <Rmath.h> |
4 | 4 | #include "misc.h" |
5 | 5 | #include "particle.h" |
6 | 6 | #include "sample_latent_rankings.h" |
7 | 7 |
|
8 | 8 | using namespace arma; |
9 | 9 |
|
| 10 | +using namespace arma; |
| 11 | + |
10 | 12 | StaticParameters::StaticParameters(const vec& alpha, const umat& rho, const vec& tau) : |
11 | 13 | alpha { alpha }, rho { rho }, tau { tau } {} |
12 | 14 |
|
@@ -96,8 +98,75 @@ void Particle::run_particle_filter( |
96 | 98 | log_incremental_likelihood.resize(log_incremental_likelihood.size() + 1); |
97 | 99 | log_incremental_likelihood(log_incremental_likelihood.size() - 1) = log_mean_exp(log_pf_weights); |
98 | 100 | log_normalized_particle_filter_weights = softmax(log_pf_weights); |
| 101 | + |
| 102 | + if(stored_weights.size() <= t) { |
| 103 | + stored_weights.push_back(exp(log_normalized_particle_filter_weights)); |
| 104 | + } else { |
| 105 | + stored_weights[t] = exp(log_normalized_particle_filter_weights); |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +void Particle::assemble_backward_trajectory(unsigned int T, const std::unique_ptr<Resampler>& resampler) { |
| 110 | + // We need to assemble a new reference trajectory traversing backwards from T to 0. |
| 111 | + // The independence property means the transition density factors out of backward weights. |
| 112 | + // Thus B_t is simply drawn from W_t independently. |
| 113 | + |
| 114 | + ParticleFilter new_reference; |
| 115 | + new_reference.log_weight.resize(T + 1); |
| 116 | + |
| 117 | + // Note: cluster_probabilities has size [cluster x (number of users up to T)] |
| 118 | + // We need to build these up. Actually, they are built horizontally (joined). |
| 119 | + // So we insert columns at the beginning. |
| 120 | + |
| 121 | + for (int t = T; t >= 0; --t) { |
| 122 | + arma::vec current_weights = stored_weights[t]; |
| 123 | + |
| 124 | + // Sample a single index b_t based on current_weights |
| 125 | + arma::ivec counts = resampler->resample(1, current_weights); |
| 126 | + unsigned int b_t = arma::as_scalar(arma::find(counts > 0, 1)); // The chosen index |
| 127 | + |
| 128 | + unsigned int num_users_at_t = particle_filters[b_t].latent_rankings.col(t).n_cols; // actually wait, .col(t) returns EXACTLY 1 column. |
| 129 | + |
| 130 | + if(new_reference.latent_rankings.is_empty()) { |
| 131 | + new_reference.latent_rankings = particle_filters[b_t].latent_rankings.col(t); |
| 132 | + if(parameters.tau.size() > 1) { |
| 133 | + // The total number of users up to time t in the forward pass is the length of cluster_assignments |
| 134 | + unsigned int end_idx = particle_filters[b_t].cluster_assignments.n_elem - 1; |
| 135 | + // Since .col(t) grabbed 1 column, but what if multiple users were processed? |
| 136 | + // Ah! In `run_particle_filter`, `proposal.proposal` is joined! |
| 137 | + // Wait, `pf.latent_rankings = join_horiz(pf.latent_rankings, proposal.proposal);` |
| 138 | + // If `proposal.proposal` had 5 columns at time `t`, then `pf.latent_rankings` grew by 5 columns! |
| 139 | + // So `latent_rankings` columns correspond to USERS, not timepoints! |
| 140 | + // So `col(t)` is completely wrong! We need to extract the columns corresponding to time `t`. |
| 141 | + // Let's look at `sample_latent_rankings`. For complete data, 1 user = 1 row = 1 timepoint! |
| 142 | + // Wait... for mixture models, see test: `compute_sequentially(mixtures[1:50,])`. |
| 143 | + // `mixtures` has 1 row per user. So `n_timepoints` = 50. |
| 144 | + // At each timepoint, 1 user is processed. |
| 145 | + // So `proposal.proposal.n_cols` = 1. |
| 146 | + // Thus `latent_rankings` has exactly 1 column per timepoint. `num_users_at_t` is always 1! |
| 147 | + // SO WHY DID IT SEGFAULT? |
| 148 | + // Because `col(t)` returns exactly 1 column, `num_users_at_t` is 1. |
| 149 | + // Let's check `start_idx`. |
| 150 | + new_reference.cluster_assignments = particle_filters[b_t].cluster_assignments.subvec(t, t); |
| 151 | + new_reference.cluster_probabilities = particle_filters[b_t].cluster_probabilities.cols(t, t); |
| 152 | + new_reference.index = uvec(T + 1, fill::zeros); |
| 153 | + } |
| 154 | + } else { |
| 155 | + new_reference.latent_rankings.insert_cols(0, particle_filters[b_t].latent_rankings.col(t)); |
| 156 | + if(parameters.tau.size() > 1) { |
| 157 | + new_reference.cluster_assignments.insert_rows(0, particle_filters[b_t].cluster_assignments.subvec(t, t)); |
| 158 | + new_reference.cluster_probabilities.insert_cols(0, particle_filters[b_t].cluster_probabilities.cols(t, t)); |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + new_reference.log_weight(t) = particle_filters[b_t].log_weight(t); |
| 163 | + } |
| 164 | + |
| 165 | + this->particle_filters[0] = new_reference; |
| 166 | + this->conditioned_particle_filter = 0; |
99 | 167 | } |
100 | 168 |
|
| 169 | + |
101 | 170 | void Particle::sample_particle_filter() { |
102 | 171 | Rcpp::NumericVector probs = Rcpp::exp(log_normalized_particle_filter_weights); |
103 | 172 | conditioned_particle_filter = Rcpp::sample(probs.size(), 1, false, probs, false)[0]; |
|
0 commit comments