fn_interp1.hpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au)
  2. // Copyright 2008-2016 National ICT Australia (NICTA)
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // ------------------------------------------------------------------------
  15. //! \addtogroup fn_interp1
  16. //! @{
  17. template<typename eT>
  18. inline
  19. void
  20. interp1_helper_nearest(const Mat<eT>& XG, const Mat<eT>& YG, const Mat<eT>& XI, Mat<eT>& YI, const eT extrap_val)
  21. {
  22. arma_extra_debug_sigprint();
  23. const eT XG_min = XG.min();
  24. const eT XG_max = XG.max();
  25. YI.copy_size(XI);
  26. const eT* XG_mem = XG.memptr();
  27. const eT* YG_mem = YG.memptr();
  28. const eT* XI_mem = XI.memptr();
  29. eT* YI_mem = YI.memptr();
  30. const uword NG = XG.n_elem;
  31. const uword NI = XI.n_elem;
  32. uword best_j = 0;
  33. for(uword i=0; i<NI; ++i)
  34. {
  35. eT best_err = Datum<eT>::inf;
  36. const eT XI_val = XI_mem[i];
  37. if((XI_val < XG_min) || (XI_val > XG_max))
  38. {
  39. YI_mem[i] = extrap_val;
  40. }
  41. else
  42. {
  43. // XG and XI are guaranteed to be sorted in ascending manner,
  44. // so start searching XG from last known optimum position
  45. for(uword j=best_j; j<NG; ++j)
  46. {
  47. const eT tmp = XG_mem[j] - XI_val;
  48. const eT err = (tmp >= eT(0)) ? tmp : -tmp;
  49. if(err >= best_err)
  50. {
  51. // error is going up, so we have found the optimum position
  52. break;
  53. }
  54. else
  55. {
  56. best_err = err;
  57. best_j = j; // remember the optimum position
  58. }
  59. }
  60. YI_mem[i] = YG_mem[best_j];
  61. }
  62. }
  63. }
  64. template<typename eT>
  65. inline
  66. void
  67. interp1_helper_linear(const Mat<eT>& XG, const Mat<eT>& YG, const Mat<eT>& XI, Mat<eT>& YI, const eT extrap_val)
  68. {
  69. arma_extra_debug_sigprint();
  70. const eT XG_min = XG.min();
  71. const eT XG_max = XG.max();
  72. YI.copy_size(XI);
  73. const eT* XG_mem = XG.memptr();
  74. const eT* YG_mem = YG.memptr();
  75. const eT* XI_mem = XI.memptr();
  76. eT* YI_mem = YI.memptr();
  77. const uword NG = XG.n_elem;
  78. const uword NI = XI.n_elem;
  79. uword a_best_j = 0;
  80. uword b_best_j = 0;
  81. for(uword i=0; i<NI; ++i)
  82. {
  83. const eT XI_val = XI_mem[i];
  84. if((XI_val < XG_min) || (XI_val > XG_max))
  85. {
  86. YI_mem[i] = extrap_val;
  87. }
  88. else
  89. {
  90. // XG and XI are guaranteed to be sorted in ascending manner,
  91. // so start searching XG from last known optimum position
  92. eT a_best_err = Datum<eT>::inf;
  93. eT b_best_err = Datum<eT>::inf;
  94. for(uword j=a_best_j; j<NG; ++j)
  95. {
  96. const eT tmp = XG_mem[j] - XI_val;
  97. const eT err = (tmp >= eT(0)) ? tmp : -tmp;
  98. if(err >= a_best_err)
  99. {
  100. break;
  101. }
  102. else
  103. {
  104. a_best_err = err;
  105. a_best_j = j;
  106. }
  107. }
  108. if( (XG_mem[a_best_j] - XI_val) <= eT(0) )
  109. {
  110. // a_best_j is to the left of the interpolated position
  111. b_best_j = ( (a_best_j+1) < NG) ? (a_best_j+1) : a_best_j;
  112. }
  113. else
  114. {
  115. // a_best_j is to the right of the interpolated position
  116. b_best_j = (a_best_j >= 1) ? (a_best_j-1) : a_best_j;
  117. }
  118. b_best_err = std::abs( XG_mem[b_best_j] - XI_val );
  119. if(a_best_j > b_best_j)
  120. {
  121. std::swap(a_best_j, b_best_j );
  122. std::swap(a_best_err, b_best_err);
  123. }
  124. const eT weight = (a_best_err > eT(0)) ? (a_best_err / (a_best_err + b_best_err)) : eT(0);
  125. YI_mem[i] = (eT(1) - weight)*YG_mem[a_best_j] + (weight)*YG_mem[b_best_j];
  126. }
  127. }
  128. }
  129. template<typename eT>
  130. inline
  131. void
  132. interp1_helper(const Mat<eT>& X, const Mat<eT>& Y, const Mat<eT>& XI, Mat<eT>& YI, const uword sig, const eT extrap_val)
  133. {
  134. arma_extra_debug_sigprint();
  135. arma_debug_check( ((X.is_vec() == false) || (Y.is_vec() == false) || (XI.is_vec() == false)), "interp1(): currently only vectors are supported" );
  136. arma_debug_check( (X.n_elem != Y.n_elem), "interp1(): X and Y must have the same number of elements" );
  137. arma_debug_check( (X.n_elem < 2), "interp1(): X must have at least two unique elements" );
  138. // sig = 10: nearest neighbour
  139. // sig = 11: nearest neighbour, assume monotonic increase in X and XI
  140. //
  141. // sig = 20: linear
  142. // sig = 21: linear, assume monotonic increase in X and XI
  143. if(sig == 11) { interp1_helper_nearest(X, Y, XI, YI, extrap_val); return; }
  144. if(sig == 21) { interp1_helper_linear (X, Y, XI, YI, extrap_val); return; }
  145. uvec X_indices;
  146. try { X_indices = find_unique(X,false); } catch(...) { }
  147. // NOTE: find_unique(X,false) provides indices of elements sorted in ascending order
  148. // NOTE: find_unique(X,false) will reset X_indices if X has NaN
  149. const uword N_subset = X_indices.n_elem;
  150. arma_debug_check( (N_subset < 2), "interp1(): X must have at least two unique elements" );
  151. Mat<eT> X_sanitised(N_subset,1);
  152. Mat<eT> Y_sanitised(N_subset,1);
  153. eT* X_sanitised_mem = X_sanitised.memptr();
  154. eT* Y_sanitised_mem = Y_sanitised.memptr();
  155. const eT* X_mem = X.memptr();
  156. const eT* Y_mem = Y.memptr();
  157. const uword* X_indices_mem = X_indices.memptr();
  158. for(uword i=0; i<N_subset; ++i)
  159. {
  160. const uword j = X_indices_mem[i];
  161. X_sanitised_mem[i] = X_mem[j];
  162. Y_sanitised_mem[i] = Y_mem[j];
  163. }
  164. Mat<eT> XI_tmp;
  165. uvec XI_indices;
  166. const bool XI_is_sorted = XI.is_sorted();
  167. if(XI_is_sorted == false)
  168. {
  169. XI_indices = sort_index(XI);
  170. const uword N = XI.n_elem;
  171. XI_tmp.copy_size(XI);
  172. const uword* XI_indices_mem = XI_indices.memptr();
  173. const eT* XI_mem = XI.memptr();
  174. eT* XI_tmp_mem = XI_tmp.memptr();
  175. for(uword i=0; i<N; ++i)
  176. {
  177. XI_tmp_mem[i] = XI_mem[ XI_indices_mem[i] ];
  178. }
  179. }
  180. const Mat<eT>& XI_sorted = (XI_is_sorted) ? XI : XI_tmp;
  181. if(sig == 10) { interp1_helper_nearest(X_sanitised, Y_sanitised, XI_sorted, YI, extrap_val); }
  182. else if(sig == 20) { interp1_helper_linear (X_sanitised, Y_sanitised, XI_sorted, YI, extrap_val); }
  183. if( (XI_is_sorted == false) && (YI.n_elem > 0) )
  184. {
  185. Mat<eT> YI_unsorted;
  186. YI_unsorted.copy_size(YI);
  187. const eT* YI_mem = YI.memptr();
  188. eT* YI_unsorted_mem = YI_unsorted.memptr();
  189. const uword N = XI_sorted.n_elem;
  190. const uword* XI_indices_mem = XI_indices.memptr();
  191. for(uword i=0; i<N; ++i)
  192. {
  193. YI_unsorted_mem[ XI_indices_mem[i] ] = YI_mem[i];
  194. }
  195. YI.steal_mem(YI_unsorted);
  196. }
  197. }
  198. template<typename T1, typename T2, typename T3>
  199. inline
  200. typename
  201. enable_if2
  202. <
  203. is_real<typename T1::elem_type>::value,
  204. void
  205. >::result
  206. interp1
  207. (
  208. const Base<typename T1::elem_type, T1>& X,
  209. const Base<typename T1::elem_type, T2>& Y,
  210. const Base<typename T1::elem_type, T3>& XI,
  211. Mat<typename T1::elem_type>& YI,
  212. const char* method = "linear",
  213. const typename T1::elem_type extrap_val = Datum<typename T1::elem_type>::nan
  214. )
  215. {
  216. arma_extra_debug_sigprint();
  217. typedef typename T1::elem_type eT;
  218. uword sig = 0;
  219. if(method != NULL )
  220. if(method[0] != char(0))
  221. if(method[1] != char(0))
  222. {
  223. const char c1 = method[0];
  224. const char c2 = method[1];
  225. if(c1 == 'n') { sig = 10; } // nearest neighbour
  226. else if(c1 == 'l') { sig = 20; } // linear
  227. else
  228. {
  229. if( (c1 == '*') && (c2 == 'n') ) { sig = 11; } // nearest neighour, assume monotonic increase in X and XI
  230. if( (c1 == '*') && (c2 == 'l') ) { sig = 21; } // linear, assume monotonic increase in X and XI
  231. }
  232. }
  233. arma_debug_check( (sig == 0), "interp1(): unsupported interpolation type" );
  234. const quasi_unwrap<T1> X_tmp( X.get_ref());
  235. const quasi_unwrap<T2> Y_tmp( Y.get_ref());
  236. const quasi_unwrap<T3> XI_tmp(XI.get_ref());
  237. if( X_tmp.is_alias(YI) || Y_tmp.is_alias(YI) || XI_tmp.is_alias(YI) )
  238. {
  239. Mat<eT> tmp;
  240. interp1_helper(X_tmp.M, Y_tmp.M, XI_tmp.M, tmp, sig, extrap_val);
  241. YI.steal_mem(tmp);
  242. }
  243. else
  244. {
  245. interp1_helper(X_tmp.M, Y_tmp.M, XI_tmp.M, YI, sig, extrap_val);
  246. }
  247. }
  248. //! @}