Citlali
Loading...
Searching...
No Matches
clean.h
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4#include <Eigen/Core>
5#include <Eigen/Eigenvalues>
6#include <Spectra/SymEigsSolver.h>
7
9
10namespace timestream {
11
12class Cleaner {
13public:
14 // get logger
15 std::shared_ptr<spdlog::logger> logger = spdlog::get("citlali_logger");
16
17 // eigen solver backend to use
22
23 // standard deviation limit
25
26 // number of eigenvalues to remove
27 std::map<Eigen::Index,Eigen::VectorXI> n_eig_to_cut;
28
29 // number of eigenvalues to calculate
30 int n_calc = 64;
31
32 // tolerance for strength of correlation
33 double tau;
34
35 // grouping
36 std::vector<std::string> grouping;
37
38 template <typename Derived>
39 auto get_stddev_index(const Eigen::DenseBase<Derived> &evals) {
40 // copy eigenvalues
41 Eigen::VectorXd ev = evals.derived().array().abs().log10();
42
43 auto n_dets = evals.size();
44 // mean of eigenvalues
45 auto m_ev = ev.mean();
46 // standard deviation of eigenvalues
47 auto stddev = engine_utils::calc_std_dev(ev);
48
49 bool keep_going = true;
50 int n_keep_last = n_dets;
51
52 // vector of eigenvalues below stddev cut
53 Eigen::Matrix<bool,Eigen::Dynamic,1> good(n_dets);
54 good.setOnes(n_dets);
55
56 int iterator = 0;
57 while (keep_going) {
58 // count up number of eigenvalues that pass the cut
59 int count = 0;
60 for (Eigen::Index i=0; i<n_dets; i++) {
61 if (good(i)) {
62 if (abs(ev(i) - m_ev) > abs(stddev_limit*stddev)) {
63 good(i) = false;
64 }
65 else {
66 count++;
67 }
68 }
69 }
70
71 if (count >= n_keep_last) {
72 keep_going = false;
73 }
74 else {
75 // get mean for good eigen values
76 m_ev = 0.;
77 for (Eigen::Index i=0; i<n_dets; i++) {
78 if (good(i)) {
79 m_ev += ev(i);
80 }
81 }
82 // get stddev for good eigen values
83 m_ev /= count;
84 stddev = 0.;
85 for (Eigen::Index i=0; i<n_dets; i++) {
86 if (good(i)) {
87 stddev += (ev(i) - m_ev)*(ev(i) - m_ev);
88 }
89 }
90 stddev = stddev/(count-1.);
91 stddev = sqrt(stddev);
92 n_keep_last = count;
93 }
94 iterator++;
95 }
96
97 // stddev limit
98 double limit = pow(10.,m_ev + stddev_limit*stddev);
99 // index where limit occurs
100 Eigen::Index limit_index = 0;
101
102 // find index
103 for (Eigen::Index i=0; i<n_dets; i++) {
104 if (evals(i) <= limit){
105 limit_index = i;
106 break;
107 }
108 }
109
110 return limit_index;
111 }
112
113 // calculate the eigenvalues from a matrix while removing flags
114 template <EigenSolverBackend backend, typename DerivedA, typename DerivedB, typename DerivedC>
115 auto calc_eig_values(const Eigen::DenseBase<DerivedA> &, const Eigen::DenseBase<DerivedB> &, Eigen::DenseBase<DerivedC> &,
116 const Eigen::Index);
117
118 // remove computed eigenvalues from matrix and recompute tods
119 template <EigenSolverBackend backend, typename DerivedA, typename DerivedB, typename DerivedC, typename DerivedD>
120 auto remove_eig_values(const Eigen::DenseBase<DerivedA> &, const Eigen::DenseBase<DerivedB> &,
121 const Eigen::DenseBase<DerivedC> &, const Eigen::DenseBase<DerivedD> &,
122 Eigen::DenseBase<DerivedA> &, const Eigen::Index);
123};
124
125template <Cleaner::EigenSolverBackend backend, typename DerivedA, typename DerivedB, typename DerivedC>
126auto Cleaner::calc_eig_values(const Eigen::DenseBase<DerivedA> &scans, const Eigen::DenseBase<DerivedB> &flags,
127 Eigen::DenseBase<DerivedC> &apt_flags, const Eigen::Index group_n_eig) {
128
129 // dimensions
130 Eigen::Index n_dets = scans.cols();
131
132 // make copy of flags
133 Eigen::MatrixXd f = abs(flags.derived().template cast<double> ().array() - 1);
134
135 // zero out flagged detectors in apt table. we need to do this because we want
136 // to make maps of all detectors when in detector mode so the timestreams cannot
137 // be completely flagged.
138 for (Eigen::Index i=0; i<n_dets; i++) {
139 if (apt_flags.derived()(i)!=0) {
140 f.col(i).setZero();
141 }
142 }
143
144 // container for covariance matrix
145 Eigen::MatrixXd pca_cov(n_dets, n_dets);
146
147 // number of unflagged samples
148 auto denom = (f.adjoint() * f).array() - 1;
149
150 // multiply scans by flags to remove flagged signal
151 auto det = (scans.derived().array()*f.array()).matrix();
152
153 // calculate the covariance matrix
154 pca_cov.noalias() = ((det.adjoint() * det).array() / denom.array()).matrix();
155
156 /*Eigen::VectorXd avg_corrs(n_dets);
157 avg_corrs.setZero();
158
159 // remove weakly correlated detectors
160 for (Eigen::Index i=0; i<n_dets; i++) {
161 for (Eigen::Index j=0; j<n_dets; j++) {
162 if (i!=j) {
163 avg_corrs(i) += pca_cov(i,j);
164 }
165 }
166 avg_corrs(i) /= (n_dets - 1);
167 }
168
169 double mean_corr = avg_corrs.mean();
170*/
171
172 //logger->info("average correlations {}", avg_corrs);
173 //logger->info("mean global correlation {}", mean_corr);
174
175 /*for (Eigen::Index i=0; i<n_dets; i++) {
176 if (avg_corrs(i) < tau*mean_corr) {
177 pca_cov.row(i).setZero();
178 pca_cov.col(i).setZero();
179 }
180 }*/
181
182 // eigenvalues
183 Eigen::VectorXd evals;
184 // eigenvectors
185 Eigen::MatrixXd evecs;
186
187 if constexpr (backend == SpectraBackend) {
188 // if using std dev limit and n_eig_to_cut is zero, use all detectors (-1 for spectra requirement)
189 int n_ev = (stddev_limit > 0 && group_n_eig==0) ? n_dets - 1: group_n_eig;
190
191 // number of values to calculate
192 int n_cv;
193
194 if (n_calc==0) {
195 n_cv = n_ev * 2.5 < n_dets?int(n_ev * 2.5):n_dets;
196 }
197 else {
198 n_cv = n_calc * 2.5 < n_dets?int(n_calc * 2.5):n_dets;
199 n_ev = n_calc;
200 }
201
202 // set up spectra
203 Spectra::DenseSymMatProd<double> op(pca_cov);
204 Spectra::SymEigsSolver<Spectra::DenseSymMatProd<double>> eigs(op, n_ev, n_cv);
205
206 eigs.init();
207 // largest eigenvalues first
208 int n_conv = eigs.compute(Spectra::SortRule::LargestAlge);
209
210 // retrieve results
211 evals = Eigen::VectorXd::Zero(n_dets);
212 evecs = Eigen::MatrixXd::Zero(n_dets, n_dets);
213
214 // copy the eigenvalues and eigenvectors
215 if (eigs.info() == Spectra::CompInfo::Successful) {
216 evals.head(n_ev) = eigs.eigenvalues();
217 evecs.leftCols(n_ev) = eigs.eigenvectors();
218 }
219 else {
220 throw std::runtime_error("spectra failed to compute eigen values");
221 }
222 }
223
224 else if constexpr (backend == EigenBackend) {
225 // use Eigen's eigen solver
226 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solution(pca_cov);
227
228 // copy the eigenvalues and eigenvectors
229 if (!solution.info()) {
230 evals = solution.eigenvalues();
231 evecs = solution.eigenvectors();
232
233 evals.reverseInPlace();
234 evecs.rowwise().reverseInPlace();
235 }
236 else {
237 throw std::runtime_error("eigen failed to compute eigen values");
238 }
239 }
240
241 return std::tuple<Eigen::VectorXd, Eigen::MatrixXd> {evals, evecs};
242}
243
244template <Cleaner::EigenSolverBackend backend,typename DerivedA, typename DerivedB, typename DerivedC, typename DerivedD>
245auto Cleaner::remove_eig_values(const Eigen::DenseBase<DerivedA> &scans, const Eigen::DenseBase<DerivedB> &flags,
246 const Eigen::DenseBase<DerivedC> &evals, const Eigen::DenseBase<DerivedD> &evecs,
247 Eigen::DenseBase<DerivedA> &cleaned_scans, const Eigen::Index group_n_eig) {
248
249 // number of detectors
250 Eigen::Index n_dets = scans.cols();
251
252 // number of eigenvalues to remove
253 Eigen::Index limit_index;
254
255 // if using std dev limit, calculate index
256 if (stddev_limit > 0) {
257 int n_ev;
258 // if using std dev limit and n_eig_to_cut is zero, use all detectors
259 if (group_n_eig == 0) {
260 if constexpr (backend == SpectraBackend) {
261 n_ev = n_dets - 1;
262 }
263 else if constexpr (backend == EigenBackend) {
264 n_ev = n_dets;
265 }
266 }
267 // if n_eig_to_cut is not zero, calc std dev for those eigs only
268 else {
269 n_ev = group_n_eig;
270 }
271 // calculate index above which to remove eigenvalues
272 limit_index = get_stddev_index(evals.head(n_ev));
273 }
274 // otherwise use number of eigenvalues from config
275 else {
276 limit_index = group_n_eig;
277 }
278
279 logger->debug("removing {} largest eigenvalue(s)", limit_index);
280
281 // subtract out the desired eigenvectors
282 Eigen::MatrixXd proj = scans.derived() * evecs.derived().leftCols(limit_index);
283 cleaned_scans.derived().noalias() = scans.derived() - proj * evecs.derived().adjoint().topRows(limit_index);
284}
285
286} // namespace timestream
Definition clean.h:12
auto get_stddev_index(const Eigen::DenseBase< Derived > &evals)
Definition clean.h:39
double stddev_limit
Definition clean.h:24
auto remove_eig_values(const Eigen::DenseBase< DerivedA > &, const Eigen::DenseBase< DerivedB > &, const Eigen::DenseBase< DerivedC > &, const Eigen::DenseBase< DerivedD > &, Eigen::DenseBase< DerivedA > &, const Eigen::Index)
Definition clean.h:245
double tau
Definition clean.h:33
std::map< Eigen::Index, Eigen::VectorXI > n_eig_to_cut
Definition clean.h:27
int n_calc
Definition clean.h:30
auto calc_eig_values(const Eigen::DenseBase< DerivedA > &, const Eigen::DenseBase< DerivedB > &, Eigen::DenseBase< DerivedC > &, const Eigen::Index)
Definition clean.h:126
EigenSolverBackend
Definition clean.h:18
@ SpectraBackend
Definition clean.h:20
@ EigenBackend
Definition clean.h:19
std::shared_ptr< spdlog::logger > logger
Definition clean.h:15
std::vector< std::string > grouping
Definition clean.h:36
auto calc_std_dev(Eigen::DenseBase< DerivedA > &data)
Definition utils.h:336
Definition clean.h:10