Virtual Testbed
Ship dynamics simulator for extreme conditions
arma.cc
1 #include <algorithm>
2 #include <chrono>
3 #include <cstring>
4 #include <sstream>
5 #include <sstream>
6 #include <stdexcept>
7 #include <string>
8 
9 #include <vtestbed/config/real_type.hh>
10 #include <vtestbed/core/arma.hh>
11 #include <vtestbed/core/convolution.hh>
12 #include <vtestbed/core/debug.hh>
13 #include <vtestbed/base/for_loop.hh>
14 #include <vtestbed/core/polynomial.hh>
15 #include <vtestbed/profile/profile.hh>
16 
17 //#define VTB_DEBUG_ARMA
18 
19 namespace {
20 
21  template <class T, int N>
22  inline blitz::Array<T,N>
23  exponential_decay(
24  const vtb::core::Grid<T,N>& grid,
25  const blitz::TinyVector<T,N>& alpha
26  ) {
27  const auto& delta = grid.delta();
28  return generate(
29  grid,
30  [&grid,&delta,alpha] (const blitz::TinyVector<int,N>& idx) {
31  using blitz::abs;
32  using blitz::sum;
33  using std::abs;
34  using std::exp;
35  return exp(-(sum(abs(grid(idx, delta) * alpha))));
36  }
37  );
38  }
39 
40  template <class T, int N>
41  inline void
42  exponential_decay(
43  blitz::Array<T,N>& surface,
44  const vtb::core::Grid<T,N>& grid,
45  const blitz::TinyVector<T,N>& alpha
46  ) {
47  using blitz::variance;
48  using std::isfinite;
49  using std::sqrt;
50  const T old_variance = variance(surface);
51  surface *= exponential_decay(grid, alpha);
52  const T new_variance = variance(surface);
53  const T a = sqrt(old_variance / new_variance);
54  if (!isfinite(a)) {
55  throw std::runtime_error{"too large exponential decay"};
56  }
57  surface *= a;
58  }
59 
60 }
61 
62 template <class T, int N>
63 void
64 vtb::core::validate_process(blitz::Array<T,N> phi) {
65  VTB_PROFILE_BLOCK(__func__);
68  const int n = phi.numElements();
69  Polynomial<T> characteristic_polynomial(n);
70  std::copy_n(phi.data(), n, characteristic_polynomial.begin());
71  characteristic_polynomial.coefficients() =
72  -characteristic_polynomial.coefficients();
73  characteristic_polynomial.coefficients(0) = 1;
75  int num_bad_roots = num_roots_inside_unit_disk(characteristic_polynomial);
76  if (num_bad_roots > 0) {
77  throw Invalid_ARMA_process(num_bad_roots);
78  }
79 }
80 
81 template <class T, int N>
82 blitz::Array<T,N>
83 vtb::core::auto_covariance(const blitz::Array<T,N>& rhs) {
84  VTB_PROFILE_BLOCK(__func__);
85  typedef std::complex<T> C;
86  using blitz::abs;
87  using blitz::pow2;
88  using blitz::real;
89  Chirp_Z_transform<C,N> fft(rhs.shape());
90  blitz::Array<C,N> tmp(rhs.shape());
91  tmp = rhs;
92  fft.forward(tmp);
93  tmp = pow2(abs(tmp));
94  fft.forward(tmp);
95  const int n = rhs.numElements();
96  return blitz::Array<T,N>(real(tmp) / n / n);
97 }
98 
99 template <class T, int N>
100 blitz::Array<T,N>
101 vtb::core::covariance(
102  blitz::Array<T,N> lhs,
103  blitz::Array<T,N> rhs,
104  Chirp_Z_transform<std::complex<T>,N>& fft
105 ) {
106  VTB_PROFILE_BLOCK(__func__);
107  typedef std::complex<T> C;
108  using namespace blitz;
109  if (!all(lhs.shape() == rhs.shape())) {
110  throw std::invalid_argument{"covariance: bad shape"};
111  }
112  blitz::Array<C,N> f1(lhs.shape());
113  f1 = lhs;
114  fft.forward(f1);
115  blitz::Array<C,N> f2(rhs.shape());
116  f2 = rhs;
117  fft.forward(f2);
118  f1 = conj(f1) * f2;
119  fft.backward(f1);
120  const int n = rhs.numElements();
121  return blitz::Array<T,N>(real(f1) / n / n);
122 }
123 
124 
125 template <class T, int N>
126 void
128  typedef std::chrono::high_resolution_clock clock_type;
129  std::random_device dev;
130  std::seed_seq seq{{
131  clock_type::now().time_since_epoch().count(),
132  clock_type::rep(dev())
133  }};
134  this->_prng.seed(seq);
135 }
136 
137 template <class T, int N>
138 void
140  array_type result
141 ) {
142  VTB_PROFILE_BLOCK(__func__);
143  const T stdev = std::sqrt(this->white_noise_variance());
144  std::normal_distribution<T> normal(T{0}, stdev);
145  for (auto& x : result) { x = normal(this->_prng); }
146 }
147 
148 template <class T, int N>
149 void
151  if (this->has_acf_generator()) {
152  grid_type grid = this->_acf_generator->acf_grid();
153  array_type surface(grid.shape());
154  this->_acf_generator->generate(grid, surface);
155  #if defined(VTB_DEBUG_ARMA)
156  dbg::write_animated_surface("base_surface", surface);
157  #endif
158  exponential_decay(surface, grid, this->decay());
159  #if defined(VTB_DEBUG_ARMA)
160  dbg::write_animated_surface("base_surface_exp", surface);
161  #endif
162  auto tmp = auto_covariance(surface);
163  tmp.resizeAndPreserve(tmp.shape()/2);
164  #if defined(VTB_DEBUG_ARMA)
165  dbg::write_animated_surface("acf", tmp);
166  #endif
167  this->_acf.reference(tmp);
168  }
169  this->_coefficients.reference(this->_solver->solve(this->acf()));
170  #if defined(VTB_DEBUG_ARMA)
171  dbg::write_animated_surface("coefficients", this->_coefficients);
172  #endif
173  validate_process(this->_coefficients);
174  this->_whitenoisevariance = this->_solver->white_noise_variance();
175  if (this->_whitenoisevariance <= T{0}) {
176  std::cerr << "white noise variance = " << _whitenoisevariance << std::endl;
177  throw std::out_of_range("white noise variance <= 0");
178  }
179 }
180 
181 template <class T, int N>
182 void
184  const grid_type& grid_txy,
185  array_type& result
186 ) {
187  typedef blitz::TinyVector<int,N> index;
188  const auto& phi = this->coefficients();
189  auto& buffer = this->_buffer;
190  auto& offset = this->_offset;
191  if (buffer.extent(1) != grid_txy.extent(1) ||
192  buffer.extent(2) != grid_txy.extent(2) ||
193  buffer.extent(0) < phi.extent(0) + grid_txy.extent(0)) {
194  buffer.resize(
195  std::max(phi.extent(0) + 1, phi.extent(0) + grid_txy.extent(0)),
196  grid_txy.extent(1),
197  grid_txy.extent(2)
198  );
199  buffer = 0;
200  offset = 0;
201  }
202  const auto nt = grid_txy.extent(0);
203  if (offset + nt >= buffer.extent(0)) {
204  int shift = offset + nt - buffer.extent(0);
205  int slice_size = buffer.extent(1)*buffer.extent(2);
206  std::memmove(
207  buffer.data(),
208  buffer.data() + shift*slice_size,
209  (offset-shift)*slice_size*sizeof(T)
210  );
211  offset -= shift;
212  }
213  index r0{offset,0,0};
214  index r1{offset+grid_txy.extent(0), buffer.extent(1), buffer.extent(2)};
215  blitz::RectDomain<N> r{r0,r1-1};
216  this->generate_white_noise(buffer(r));
217  for_loop<N>(
218  r,
219  [&] (const index& idx) {
220  T sum{0};
221  index shape{blitz::min(idx+1, phi.shape())};
222  for_loop<N>(
223  shape,
224  [&] (const index& idx2) {
225  sum += phi(idx2) * buffer(index{idx-idx2});
226  }
227  );
228  buffer(idx) += sum;
229  }
230  );
231  result = buffer(r);
232  offset += nt;
233  auto m = blitz::minmax(result);
234  std::clog << "m.min=" << m.min << std::endl;
235  std::clog << "m.max=" << m.max << std::endl;
236 }
237 
238 template <class T, int N>
239 void
241  const grid_type& grid,
242  array_type& result
243 ) {
244  this->generate_white_noise(result);
245  typedef std::complex<T> C;
246  typedef blitz::Array<C,N> carray_type;
247  using blitz::real;
248  const auto& theta = this->coefficients();
249  carray_type signal(result.shape()), kernel(theta.shape());
250  signal = result;
251  kernel = theta;
252  kernel(0,0,0) = -1;
253  kernel = -kernel;
254  Convolution<C,N> convolve(result.shape(), theta.shape());
255  result = real(convolve(signal, kernel));
256 }
257 
258 template void
259 vtb::core::validate_process(blitz::Array<VTB_REAL_TYPE,3> phi);
260 
261 template blitz::Array<VTB_REAL_TYPE,3>
262 vtb::core::auto_covariance(const blitz::Array<VTB_REAL_TYPE,3>& rhs);
263 
264 template blitz::Array<VTB_REAL_TYPE,1>
265 vtb::core::covariance(
266  blitz::Array<VTB_REAL_TYPE,1> lhs,
267  blitz::Array<VTB_REAL_TYPE,1> rhs,
268  Chirp_Z_transform<std::complex<VTB_REAL_TYPE>,1>& fft
269 );
270 
271 template blitz::Array<VTB_REAL_TYPE,3>
272 vtb::core::covariance(
273  blitz::Array<VTB_REAL_TYPE,3> lhs,
274  blitz::Array<VTB_REAL_TYPE,3> rhs,
275  Chirp_Z_transform<std::complex<VTB_REAL_TYPE>,3>& fft
276 );
277 
281 
282 #if defined(VTB_REAL_TYPE_FLOAT)
285 #endif
T exp(T... args)
T generate(T... args)
Based on autoregerssive model.
Definition: arma.hh:267
void validate_process(blitz::Array< T, N > phi)
Check AR (MA) process stationarity (invertibility).
Definition: arma.cc:64
T variance(const Array< T, N > &rhs)
Sample variance.
Definition: blitz.hh:458
New and missing Blitz++ functions.
Definition: blitz.hh:16
blitz::Array< T, N > auto_covariance(const blitz::Array< T, N > &rhs)
Computes autocovariance function of three-dimensional field.
Definition: arma.cc:83
const T delta(int i) const
The size of the patch (interval) for dimension i.
Definition: core/grid.hh:239
A region defined by start and end index and lower and upper bound for each dimension.
Definition: core/grid.hh:25
T isfinite(T... args)
T signal(T... args)
Based on moving-average model.
Definition: arma.hh:301
int num_roots_inside_unit_disk(Polynomial< T > p)
Finds the number of polynomial roots inside the unit disk.
Definition: polynomial.hh:286
T sqrt(T... args)