Healpix C++  3.83
weight_utils.cc
Go to the documentation of this file.
1 /*
2  * This file is part of Healpix_cxx.
3  *
4  * Healpix_cxx is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * Healpix_cxx is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Healpix_cxx; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * For more information about HEALPix, see http://healpix.sourceforge.net
19  */
20 
21 /*
22  * Healpix_cxx is being developed at the Max-Planck-Institut fuer Astrophysik
23  * and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt
24  * (DLR).
25  */
26 
27 /*! \file weight_utils.cc
28  *
29  * Functionality for computing ring weights and full map weights
30  *
31  * Helpful literature:
32  * Graef, Kunis, Potts: On the computation of nonnegative quadrature weights
33  * on the sphere
34  * (https://www-user.tu-chemnitz.de/~potts/paper/quadgewS2.pdf)
35  * Lambers: Minimum Norm Solutions of Underdetermined Systems
36  * (http://www.math.usm.edu/lambers/mat419/lecture15.pdf)
37  * Shewchuk: An Introduction to the Conjugate Gradient Method Without the
38  * Agonizing Pain
39  * (https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf)
40  *
41  * Copyright (C) 2016-2019 Max-Planck-Society
42  * \author Martin Reinecke
43  */
44 
45 #include <vector>
46 #include <numeric>
47 #include <algorithm>
48 #include "healpix_map.h"
49 #include "alm_healpix_tools.h"
50 #include "weight_utils.h"
51 #include "alm.h"
52 #include "libsharp/sharp_cxx.h"
53 #include "lsconstants.h"
54 
55 using namespace std;
56 
57 /* General considerations concerning the full weights and their a_lm
58  coefficients:
59  The Healpix grid geometry has several symmetries:
60  - mirror symmetry with respect to phi=0
61  - mirror symmetry with respect to the equator
62  - invariance with respect to rotation around the z-axis by multiples of
63  pi/2
64  This reduces the number of pixels with distinct weights to roughly 1/16 of
65  the total number of map pixels.
66  The geometrical symmetries also reduce the number of a_lm coefficients
67  required to describe the weight map:
68  - the mirror symmetry in phi means that all a_lm are purely real-valued
69  - the mirror symmetry in theta means that all a_lm with odd l are zero
70  - the rotational symmetry means that all a_lm with m not a multiple of 4
71  vanish as well.
72 
73  These symmetries are used when storing a_lm coefficients and weight maps to
74  save memory. So far, the SHTs themselves are carried out with full maps and
75  a_lm sets, because there is no libsharp support for this kind of symmetries
76  yet. */
77 
78 namespace weight_utils_detail {
79 
80 using namespace std;
81 
82 // inner product of two vectors
83 double dprod(const vector<double> &a, const vector<double> &b)
84  { return inner_product(a.begin(),a.end(),b.begin(),0.); }
85 
86 tsize n_fullweights (int nside)
87  { return ((3*nside+1)*(nside+1))/4; }
88 
89 template <typename T> void apply_weight (T &pix, double w, bool setwgt)
90  {
91  if (setwgt)
92  pix=T(w);
93  else
94  if (!approx<double>(pix,Healpix_undef)) pix*=T(1.+w);
95  }
96 
97 template <typename T> void apply_fullweights (Healpix_Map<T> &map,
98  const vector<double> &wgt, bool setwgt)
99  {
100  planck_assert (map.Scheme()==RING, "bad map ordering scheme");
101  int nside=map.Nside();
102  planck_assert(wgt.size()==n_fullweights(nside),
103  "incorrect size of weight array");
104  int pix=0, vpix=0;
105  for (int i=0; i<2*nside; ++i)
106  {
107  bool shifted = (i<nside-1) || ((i+nside)&1);
108  int qpix=min(nside,i+1);
109  bool odd=qpix&1;
110  int wpix=((qpix+1)>>1) + ((odd||shifted) ? 0 : 1);
111  int psouth=map.Npix()-pix-(qpix<<2);
112  for (int j=0; j<(qpix<<2); ++j)
113  {
114  int j4=j%qpix;
115  int rpix=min(j4,qpix - (shifted ? 1:0) - j4);
116  apply_weight (map[pix+j],wgt[vpix+rpix],setwgt);
117  if (i!=2*nside-1) // everywhere except on equator
118  apply_weight(map[psouth+j],wgt[vpix+rpix],setwgt);
119  }
120  pix+=qpix<<2;
121  vpix+=wpix;
122  }
123  }
124 
125 vector<double> extract_fullweights (const Healpix_Map<double> &map)
126  {
127  planck_assert (map.Scheme()==RING, "bad map ordering scheme");
128  int nside=map.Nside();
129  vector<double> res; res.reserve(n_fullweights(nside));
130  int pix=0;
131  for (int i=0; i<2*nside; ++i)
132  {
133  bool shifted = (i<nside-1) || ((i+nside)&1);
134  int qpix=min(nside,i+1);
135  bool odd=qpix&1;
136  int wpix=((qpix+1)>>1) + ((odd||shifted) ? 0 : 1);
137  for (int j=0; j<wpix; ++j)
138  res.push_back(map[pix+j]);
139  pix+=qpix<<2;
140  }
141  return res;
142  }
143 
144 tsize n_weightalm (int lmax, int mmax)
145  {
146  int nmsteps=(mmax>>2)+1;
147  return nmsteps * (((lmax+2)>>1) - nmsteps+1);
148  }
149 vector<double> extract_weightalm (const Alm<xcomplex<double> > &alm)
150  {
151  vector<double> res; res.reserve(n_weightalm(alm.Lmax(),alm.Mmax()));
152  for (int m=0; m<=alm.Mmax(); m+=4)
153  for (int l=m; l<=alm.Lmax(); l+=2)
154  res.push_back(real(alm(l,m)) * ((m==0) ? 1 : sqrt(2.)));
155  return res;
156  }
157 void expand_weightalm (const vector<double> &calm, Alm<xcomplex<double> > &alm)
158  {
159  planck_assert(calm.size()==n_weightalm(alm.Lmax(), alm.Mmax()),
160  "incorrect size of weight array");
161  alm.SetToZero();
162  tsize idx=0;
163  for (int m=0; m<=alm.Mmax(); m+=4)
164  for (int l=m; l<=alm.Lmax(); l+=2)
165  alm(l,m) = calm[idx++] * ((m==0) ? 1 : sqrt(0.5));
166  }
167 
168 class STS_hpwgt
169  {
170  private:
171  int lmax, mmax, nside;
172 
173  public:
174  using vectype=vector<double>;
175  STS_hpwgt (int lmax_, int mmax_, int nside_)
176  : lmax(lmax_), mmax(mmax_), nside(nside_)
177  { planck_assert((lmax&1)==0,"lmax must be even"); }
178  vectype S (const vectype &x) const
179  {
180  Alm<xcomplex<double> > ta(lmax,mmax);
181  expand_weightalm(x,ta);
182  Healpix_Map<double> tm(nside,RING, SET_NSIDE);
183  alm2map(ta,tm);
184  return extract_fullweights(tm);
185  }
186  vectype ST (const vectype &x) const
187  {
188  Healpix_Map<double> tm(nside,RING, SET_NSIDE);
189  apply_fullweights(tm,x,true);
190  Alm<xcomplex<double> > ta(lmax,mmax);
191  alm2map_adjoint(tm,ta);
192  return extract_weightalm(ta);
193  }
194  vectype apply (const vectype &x) const
195  { return ST(S(x)); }
196  };
197 
198 class STS_hpring
199  {
200  private:
201  int lmax, nside;
202  sharp_cxxjob<double> job;
203 
204  public:
205  using vectype=vector<double>;
206  STS_hpring (int lmax_, int nside_)
207  : lmax(lmax_), nside(nside_)
208  {
209  planck_assert((lmax&1)==0,"lmax must be even");
210  int nring=2*nside;
211  vector<double> dbl0(nring,0.),theta(nring);
212  vector<int> int1(nring,1);
213  vector<ptrdiff_t> ofs(nring);
214  Healpix_Base base(nside, RING, SET_NSIDE);
215  for (int i=0; i<nring; ++i)
216  {
217  ofs[i]=i;
218  int idum1,idum2;
219  bool bdum;
220  base.get_ring_info2 (i+1, idum1, idum2, theta[i], bdum);
221  }
222  job.set_general_geometry (nring, int1.data(), ofs.data(),
223  int1.data(), dbl0.data(), theta.data(), dbl0.data());
224  job.set_triangular_alm_info (lmax, 0);
225  }
226  vectype S(const vectype &alm) const
227  {
228  planck_assert(int(alm.size())==lmax/2+1,"bad input size");
229  vectype res(2*nside);
230  vector<xcomplex<double> > alm2(2*alm.size()-1,0.);
231  for (tsize i=0; i<alm.size(); ++i)
232  alm2[2*i]=alm[i];
233  job.alm2map(&alm2[0],&res[0],false);
234  return res;
235  }
236  vectype ST(const vectype &map) const
237  {
238  planck_assert(int(map.size())==2*nside,"bad input size");
239  vector<xcomplex<double> > alm2(lmax+1,0.);
240  job.alm2map_adjoint(&map[0],&alm2[0], false);
241  vectype res(lmax/2+1);
242  for (tsize i=0; i<res.size(); ++i)
243  res[i]=real(alm2[2*i]);
244  return res;
245  }
246  vectype apply (const vectype &x) const
247  { return ST(S(x)); }
248  };
249 
250 vector<double> muladd (double fct, const vector<double> &a,
251  const vector<double> &b)
252  {
253  planck_assert(a.size()==b.size(),"types not conformable");
254  vector<double> res(b);
255  for (tsize i=0; i<a.size(); ++i)
256  res[i] += fct*a[i];
257  return res;
258  }
259 
260 // canned algorithm B2 from Shewchuk
261 template<typename M> double cg_solve (const M &A, typename M::vectype &x,
262  const typename M::vectype &b, double epsilon, int itmax)
263  {
264  typename M::vectype r=muladd(-1.,A.apply(x),b), d(r);
265  double delta0=dprod(r,r), deltanew=delta0;
266  cout << "res0: " << sqrt(delta0) << endl;
267  for (int iter=0; iter<itmax; ++iter)
268  {
269  auto q=A.apply(d);
270  double alpha = deltanew/dprod(d,q);
271  x=muladd(alpha,d,x);
272  if (iter%300==0) // get accurate residual
273  r=muladd(-1.,A.apply(x),b);
274  else
275  r=muladd(-alpha,q,r);
276  double deltaold=deltanew;
277  deltanew=dprod(r,r);
278  cout << "\rIteration " << iter
279  << ": residual=" << sqrt(deltanew/delta0)
280  << " " << flush;
281  if (deltanew<epsilon*epsilon*delta0) { cout << endl; break; } // convergence
282  double beta=deltanew/deltaold;
283  d=muladd(beta,d,r);
284  }
285  return sqrt(deltanew/delta0);
286  }
287 
288 class FullWeightImpl
289  {
290  private:
291  STS_hpwgt mat;
292  vector<double> x, rhs, r, d;
293  double delta0, delta_cur;
294  int iter;
295 
296  public:
297  FullWeightImpl(int nside, int lmax)
298  : mat(lmax, lmax, nside), x(n_weightalm(lmax,lmax),0.), iter(0)
299  {
300  planck_assert((lmax&1)==0,"lmax must be even");
301  rhs = mat.ST(vector<double>(n_fullweights(nside),-1.));
302  rhs[0]+=12*nside*nside/sqrt(4*pi);
303  r=muladd(-1.,mat.apply(x),rhs);
304  d=r;
305  delta0=dprod(r,r);
306  delta_cur=delta0;
307  }
308  void iterate(int niter)
309  {
310  int iend=iter+niter;
311  for (; iter<iend; ++iter)
312  {
313  auto q=mat.apply(d);
314  double alpha = delta_cur/dprod(d,q);
315  x=muladd(alpha,d,x);
316  if (iter%300==0) // get accurate residual
317  r=muladd(-1.,mat.apply(x),rhs);
318  else
319  r=muladd(-alpha,q,r);
320  double deltaold=delta_cur;
321  delta_cur=dprod(r,r);
322  cout << "\rIteration " << iter
323  << ": residual=" << sqrt(delta_cur/delta0)
324  << " " << flush;
325  double beta=delta_cur/deltaold;
326  d=muladd(beta,d,r);
327  }
328  }
329  std::vector<double> current_alm() const { return x; }
330  std::vector<double> alm2wgt(const vector<double> &alm) const
331  { return mat.S(alm); }
332  double current_epsilon() const { return sqrt(delta_cur/delta0); }
333  int current_iter() const { return iter; }
334  };
335 
336 } //namespace weight_utils_detail;
337 
338 using namespace weight_utils_detail;
339 
340 vector<double> get_fullweights(int nside, int lmax, double epsilon, int itmax,
341  double &epsilon_out)
342  {
343  planck_assert((lmax&1)==0,"lmax must be even");
344  STS_hpwgt mat(lmax, lmax, nside);
345  vector<double> x(n_weightalm(lmax,lmax),0.);
346  vector<double> rhs=mat.ST(vector<double> (n_fullweights(nside),-1.));
347  rhs[0]+=12*nside*nside/sqrt(4*pi);
348  epsilon_out=cg_solve(mat, x, rhs, epsilon, itmax);
349  return mat.S(x);
350  }
351 
352 FullWeightComputer::FullWeightComputer(int nside, int lmax)
353  : impl(new FullWeightImpl(nside, lmax)) {}
354 
355 FullWeightComputer::~FullWeightComputer() {}
356 
357 void FullWeightComputer::iterate(int niter) { impl->iterate(niter); }
358 
359 std::vector<double> FullWeightComputer::current_alm() const
360  { return impl->current_alm(); }
361 
362 std::vector<double> FullWeightComputer::alm2wgt(const vector<double> &alm) const
363  { return impl->alm2wgt(alm); }
364 
365 double FullWeightComputer::current_epsilon() const
366  { return impl->current_epsilon(); }
367 
368 int FullWeightComputer::current_iter() const
369  { return impl->current_iter(); }
370 
371 template <typename T> void apply_fullweights (Healpix_Map<T> &map,
372  const vector<double> &wgt)
373  { apply_fullweights (map,wgt,false); }
374 
375 template void apply_fullweights (Healpix_Map<float> &map,
376  const vector<double> &wgt);
377 template void apply_fullweights (Healpix_Map<double> &map,
378  const vector<double> &wgt);
379 
380 vector<double> get_ringweights(int nside, int lmax, double epsilon, int itmax,
381  double &epsilon_out)
382  {
383  planck_assert((lmax&1)==0,"lmax must be even");
384  STS_hpring mat(lmax,nside);
385  vector<double> nir(2*nside), x(lmax/2+1,0.);
386  for (tsize ith=0; ith<nir.size(); ++ith)
387  nir[ith]=8*min<int>(ith+1,nside);
388  nir[2*nside-1]/=2;
389  auto b=mat.ST(nir);
390  for (tsize i=0; i<b.size(); ++i)
391  b[i]=-b[i];
392  b[0]+=12*nside*nside/sqrt(4*pi);
393  epsilon_out=cg_solve(mat, x, b, epsilon, itmax);
394  auto mtmp=mat.S(x);
395  for (tsize i=0; i<mtmp.size(); ++i)
396  mtmp[i]/=nir[i];
397  return mtmp;
398  }
I Nside() const
Definition: healpix_base.h:427
vector< double > get_fullweights(int nside, int lmax, double epsilon, int itmax, double &epsilon_out)
Definition: alm.h:88
void apply_fullweights(Healpix_Map< T > &map, const std::vector< double > &wgt)
void alm2map_adjoint(const Healpix_Map< T > &map, Alm< xcomplex< T > > &alm, bool add_alm)
Healpix_Ordering_Scheme Scheme() const
Definition: healpix_base.h:431
void alm2map(const Alm< xcomplex< T > > &alm, Healpix_Map< T > &map, bool add_map)
const double Healpix_undef
Healpix value representing "undefined".
Definition: healpix_map.h:39
vector< double > get_ringweights(int nside, int lmax, double epsilon, int itmax, double &epsilon_out)
I Npix() const
Definition: healpix_base.h:429

Generated on Wed Nov 13 2024 12:18:30 for Healpix C++