ALPSCore reference
piecewise_polynomial.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1998-2018 ALPS Collaboration. See COPYRIGHT.TXT
3  * All rights reserved. Use is subject to license terms. See LICENSE.TXT
4  * For use in publications, see ACKNOWLEDGE.TXT
5  */
6 
7 #ifndef ALPSCORE_PIEACEWISE_POLYNOMIAL_HPP
8 #define ALPSCORE_PIEACEWISE_POLYNOMIAL_HPP
9 
10 #include <complex>
11 #include <cmath>
12 #include <type_traits>
13 #include <vector>
14 #include <cassert>
15 #include <boost/multi_array.hpp>
16 #include <boost/typeof/typeof.hpp>
17 
18 #include <alps/hdf5/archive.hpp>
19 #include <alps/hdf5/complex.hpp>
20 #include <alps/hdf5/vector.hpp>
22 
23 #ifdef ALPS_HAVE_MPI
24 #include "mpi_bcast.hpp"
25 #endif
26 
27 namespace alps {
28  namespace gf {
29 
31  template<typename>
33 
34  namespace detail {
42  template<class T>
43  typename std::enable_if<boost::is_floating_point<T>::value, T>::type
44  outer_product(T a, T b) {
45  return a * b;
46  }
47 
48  template<class T>
49  std::complex<T>
50  outer_product(const std::complex<T> &a, const std::complex<T> &b) {
51  return std::conj(a) * b;
52  }
53 
54  template<class T>
55  typename std::enable_if<boost::is_floating_point<T>::value, T>::type
56  conjg(T a) {
57  return a;
58  }
59 
60  template<class T>
61  std::complex<T>
62  conjg(const std::complex<T> &a) {
63  return std::conj(a);
64  }
65 
66  template<typename T, typename Op>
67  struct pp_element_wise_op {
68  void perform(const T *p1, const T *p2, T *p_r, int k1, int k2, int k_r) const {
69  Op op;
70  int k = std::min(k1, k2);
71  assert(k_r >= k);
72  for (int i=0; i<k_r+1; ++i) {
73  p_r[i] = 0.0;
74  }
75  for (int i=0; i<k+1; ++i) {
76  p_r[i] = op(p1[i], p2[i]);
77  }
78  }
79  };
80 
81  template<typename T>
82  struct pp_plus : public pp_element_wise_op<T, std::plus<T> > {};
83 
84  template<typename T>
85  struct pp_minus : public pp_element_wise_op<T, std::minus<T> > {};
86 
88  template<typename T, typename Op>
90  do_op(const piecewise_polynomial<T> &f1, const piecewise_polynomial<T> &f2, const Op& op) {
91  if (f1.section_edges_ != f2.section_edges_) {
92  throw std::runtime_error("Cannot add two numerical functions with different sections!");
93  }
94 
95  const int k_new = std::max(f1.order(), f2.order());
97 
98  const int k_min = std::min(f1.order(), f2.order());
99 
100  for (int s=0; s < f1.num_sections(); ++s) {
101  op.perform(&f1.coeff_[s][0], &f2.coeff_[s][0], &result.coefficient(s,0), f1.order(), f2.order(), k_min);
102  }
103 
104  return result;
105  }
106  }
107 
112  template<typename T>
113  class piecewise_polynomial {
114  private:
115  int k_;
116 
117  typedef boost::multi_array<T, 2> coefficient_type;
118 
119  template<typename TT, typename Op>
121  detail::do_op(const piecewise_polynomial<TT> &f1, const piecewise_polynomial<TT> &f2, const Op& op);
122 
123  template<typename TT>
126 
127  template<typename TT>
130 
131  template<typename TT>
132  friend const piecewise_polynomial<TT> operator*(TT scalar, const piecewise_polynomial<TT> &pp);
133 
134  template<typename TT>
135  friend
136  class piecewise_polynomial;
137 
139  int n_sections_;
140 
142  std::vector<double> section_edges_;
143 
148  coefficient_type coeff_;
149 
150  bool valid_;
151 
152  void check_range(double x) const {
153  if (x < section_edges_[0] || x > section_edges_[section_edges_.size() - 1]) {
154  throw std::runtime_error("Give x is out of the range.");
155  }
156  }
157 
158  void check_validity() const {
159  if (!valid_) {
160  throw std::runtime_error("pieacewise_polynomial object is not properly constructed!");
161  }
162  }
163 
164  void set_validity() {
165  valid_ = true;
166  valid_ = valid_ && (n_sections_ >= 1);
167  assert(valid_);
168  valid_ = valid_ && (section_edges_.size() == std::size_t(n_sections_) + 1);
169  assert(valid_);
170  valid_ = valid_ && (coeff_.shape()[0] == std::size_t(n_sections_));
171  assert(valid_);
172  valid_ = valid_ && (coeff_.shape()[1] == std::size_t(k_+1));
173  assert(valid_);
174  for (int i = 0; i < n_sections_; ++i) {
175  valid_ = valid_ && (section_edges_[i] < section_edges_[i + 1]);
176  }
177  assert(valid_);
178  }
179 
180  public:
181  piecewise_polynomial() : k_(-1), n_sections_(0), valid_(false) {};
182 
184  piecewise_polynomial(int k, const std::vector<double> &section_edges) : k_(k),
185  n_sections_(section_edges.size()-1),
186  section_edges_(section_edges),
187  coeff_(boost::extents[n_sections_][k+1]),
188  valid_(false) {
189  std::fill(coeff_.origin(), coeff_.origin()+coeff_.num_elements(), 0.0);
190 
191  set_validity();
192  check_validity();//this may throw
193  };
195  piecewise_polynomial(int n_section,
196  const std::vector<double> &section_edges,
197  const boost::multi_array<T, 2> &coeff) : k_(coeff.shape()[1]-1),
198  n_sections_(section_edges.size() - 1),
199  section_edges_(section_edges),
200  coeff_(coeff), valid_(false) {
201  set_validity();
202  check_validity();//this may throw
203  };
204 
206  piecewise_polynomial<T>& operator=(const piecewise_polynomial<T>& other) {
207  k_ = other.k_;
208  n_sections_ = other.n_sections_;
209  section_edges_ = other.section_edges_;
210  //Should be resized before a copy
211  coeff_.resize(boost::extents[other.coeff_.shape()[0]][other.coeff_.shape()[1]]);
212  coeff_ = other.coeff_;
213  valid_ = other.valid_;
214  return *this;
215  }
216 
218  int order() const {
219  return k_;
220  }
221 
223  int num_sections() const {
224 #ifndef NDEBUG
225  check_validity();
226 #endif
227  return n_sections_;
228  }
229 
231  inline double section_edge(int i) const {
232  assert(i >= 0 && i < section_edges_.size());
233 #ifndef NDEBUG
234  check_validity();
235 #endif
236  return section_edges_[i];
237  }
238 
240  const std::vector<double> &section_edges() const {
241 #ifndef NDEBUG
242  check_validity();
243 #endif
244  return section_edges_;
245  }
246 
248  inline const T& coefficient(int i, int p) const {
249  assert(i >= 0 && i < section_edges_.size());
250  assert(p >= 0 && p <= k_);
251 #ifndef NDEBUG
252  check_validity();
253 #endif
254  return coeff_[i][p];
255  }
256 
258  inline T& coefficient(int i, int p) {
259  assert(i >= 0 && std::size_t(i) < section_edges_.size());
260  assert(p >= 0 && p <= k_);
261 #ifndef NDEBUG
262  check_validity();
263 #endif
264  return coeff_[i][p];
265  }
266 
268  void set_zero() {
269  std::fill(coeff_.origin(), coeff_.origin()+coeff_.num_elements(), 0.0);
270  }
271 
273  inline T compute_value(double x) const {
274 #ifndef NDEBUG
275  check_validity();
276 #endif
277  return compute_value(x, find_section(x));
278  }
279 
281  inline T compute_value(double x, int section) const {
282 #ifndef NDEBUG
283  check_validity();
284 #endif
285  if (x < section_edges_[section] || (x != section_edges_.back() && x >= section_edges_[section + 1])) {
286  throw std::runtime_error("The given x is not in the given section.");
287  }
288 
289  const double dx = x - section_edges_[section];
290  T r = 0.0, x_pow = 1.0;
291  for (int p = 0; p < k_ + 1; ++p) {
292  r += coeff_[section][p] * x_pow;
293  x_pow *= dx;
294  }
295  return r;
296  }
297 
299  int find_section(double x) const {
300 #ifndef NDEBUG
301  check_validity();
302 #endif
303  if (x == section_edges_[0]) {
304  return 0;
305  } else if (x == section_edges_.back()) {
306  return coeff_.size() - 1;
307  }
308 
309  std::vector<double>::const_iterator it =
310  std::upper_bound(section_edges_.begin(), section_edges_.end(), x);
311  --it;
312  return (&(*it) - &(section_edges_[0]));
313  }
314 
316  template<class T2>
317  T overlap(const piecewise_polynomial<T2> &other) const {
318  check_validity();
319  if (section_edges_ != other.section_edges_) {
320  throw std::runtime_error("Computing overlap between piecewise polynomials with different section edges are not supported");
321  }
322  typedef BOOST_TYPEOF(static_cast<T>(1.0)*static_cast<T2>(1.0)) Tr;
323 
324  const int k = this->order();
325  const int k2 = other.order();
326 
327  Tr r = 0.0;
328  std::vector<double> x_min_power(k+k2+2), dx_power(k+k2+2);
329 
330  for (int s = 0; s < n_sections_; ++s) {
331  dx_power[0] = 1.0;
332  const double dx = section_edges_[s + 1] - section_edges_[s];
333  for (std::size_t p = 1; p < dx_power.size(); ++p) {
334  dx_power[p] = dx * dx_power[p - 1];
335  }
336 
337  for (int p = 0; p < k + 1; ++p) {
338  for (int p2 = 0; p2 < k2 + 1; ++p2) {
339  r += detail::outer_product((Tr) coeff_[s][p], (Tr) other.coeff_[s][p2])
340  * dx_power[p + p2 + 1] / (p + p2 + 1.0);
341  }
342  }
343  }
344  return r;
345  }
346 
348  double squared_norm() const {
349  return static_cast<double>(this->overlap(*this));
350  }
351 
353  bool operator==(const piecewise_polynomial<T> &other) const {
354  return (n_sections_ == other.n_sections_) &&
355  (section_edges_ == other.section_edges_) &&
356  (coeff_ == other.coeff_);
357  }
358 
360  void save(alps::hdf5::archive& ar, const std::string& path) const {
361  check_validity();
362  ar[path+"/k"] << k_;
363  ar[path+"/num_sections"] << num_sections();
364  ar[path+"/section_edges"] << section_edges_;
365  ar[path+"/coefficients"] << coeff_;
366  }
367 
369  void load(alps::hdf5::archive& ar, const std::string& path) {
370  ar[path+"/k"] >> k_;
371  ar[path+"/num_sections"] >> n_sections_;
372  ar[path+"/section_edges"] >> section_edges_;
373  ar[path+"/coefficients"] >> coeff_;
374 
375  set_validity();
376  check_validity();
377  }
378 
380  void save(alps::hdf5::archive& ar) const
381  {
382  save(ar, ar.get_context());
383  }
384 
386  void load(alps::hdf5::archive& ar)
387  {
388  load(ar, ar.get_context());
389  }
390 
391 #ifdef ALPS_HAVE_MPI
392  void broadcast(const alps::mpi::communicator& comm, int root)
394  {
395  using alps::mpi::broadcast;
396 
397  broadcast(comm, k_, root);
398  broadcast(comm, n_sections_, root);
399  section_edges_.resize(n_sections_+1);
400  broadcast(comm, &section_edges_[0], n_sections_+1, root);
401 
402  coeff_.resize(boost::extents[n_sections_][k_+1]);
403  broadcast(comm, coeff_.origin(), (k_+1)*n_sections_, root);
404 
405  set_validity();
406  check_validity();
407  }
408 #endif
409 
410  };//class pieacewise_polynomial
411 
413  template<typename T>
416  return detail::do_op(f1, f2, detail::pp_plus<T>());
417  }
418 
420  template<typename T>
423  return detail::do_op(f1, f2, detail::pp_minus<T>());
424  }
425 
427  template<typename T>
429  piecewise_polynomial<T> pp_copy(pp);
431  pp_copy.coeff_.origin(), pp_copy.coeff_.origin() + pp_copy.coeff_.num_elements(),
432  pp_copy.coeff_.origin(), std::bind1st(std::multiplies<T>(), scalar)
433 
434  );
435  return pp_copy;
436  }
437 
439  template<typename T>
440  void orthonormalize(std::vector<piecewise_polynomial<T> > &pps) {
441  typedef piecewise_polynomial<T> pp_type;
442 
443  for (std::size_t l = 0; l < pps.size(); ++l) {
444  pp_type pp_new(pps[l]);
445  for (std::size_t l2 = 0; l2 < l; ++l2) {
446  const T overlap = pps[l2].overlap(pps[l]);
447  pp_new = pp_new - overlap * pps[l2];
448  }
449  double norm = pp_new.overlap(pp_new);
450  pps[l] = (1.0 / std::sqrt(norm)) * pp_new;
451  }
452  }
453  }
454 }
455 
456 #endif //ALPSCORE_PIEACEWISE_POLYNOMIAL_HPP
piecewise_polynomial< T > operator+(const piecewise_polynomial< T > &f1, const piecewise_polynomial< T > &f2)
Add piecewise_polynomial objects.
void load(archive &ar, std::string const &path, T &value, std::vector< std::size_t > chunk=std::vector< std::size_t >(), std::vector< std::size_t >=std::vector< std::size_t >())
Definition: archive.hpp:309
boost::array< T, N > sqrt(boost::array< T, N > arg)
std::enable_if<!is_sequence< T >::value, std::size_t >::type size(T const &)
Definition: size.hpp:20
void broadcast(C const &c, P &p, int r=0)
Definition: api.hpp:56
piecewise_polynomial< T > operator-(const piecewise_polynomial< T > &f1, const piecewise_polynomial< T > &f2)
Substract piecewise_polynomial objects.
const piecewise_polynomial< T > operator*(T scalar, const piecewise_polynomial< T > &pp)
Multiply piecewise_polynomial by a scalar.
bool operator==(const dictionary &lhs, const dictionary &rhs)
Definition: dictionary.hpp:96
mean_result< T > transform(no_prop, const transformer< T > &tf, const InResult &in)
Definition: transform.hpp:27
Encapsulation of an MPI communicator and some communicator-related operations.
Definition: mpi.hpp:111
T norm(T x, T y=T(), T z=T())
void orthonormalize(std::vector< piecewise_polynomial< T > > &pps)
Gram-Schmidt orthonormalization.
int order() const
Order of the polynomial.
int num_sections() const
Number of sections.
std::string get_context() const
Definition: archive.cpp:144
Functions to MPI-broadcast internal data structures.
traits< Acc >::result_type result(const Acc &acc)
Definition: util.hpp:53
void broadcast(const communicator &comm, T *vals, std::size_t count, int root)
Broadcasts array vals of a primitive type T, length count on communicator comm with root root ...
Definition: mpi.hpp:270
T r(T x, T y=T(), T z=T())
const std::vector< double > & section_edges() const
Return a refence to end points.
Class representing a piecewise polynomial and utilities.
void save(archive &ar, std::string const &path, T const &value, std::vector< std::size_t >=std::vector< std::size_t >(), std::vector< std::size_t > chunk=std::vector< std::size_t >(), std::vector< std::size_t >=std::vector< std::size_t >())
Definition: archive.hpp:292