Expression Templates Library (ETL)
upsample.hpp
1 //=======================================================================
2 // Copyright (c) 2014-2023 Baptiste Wicht
3 // Distributed under the terms of the MIT License.
4 // (See accompanying file LICENSE or copy at
5 // http://opensource.org/licenses/MIT)
6 //=======================================================================
7 
8 #pragma once
9 
10 namespace etl::impl::standard {
11 
15 struct upsample_2d {
24  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, typename A, typename M>
25  static void upsample_block_2d(A&& in, M& m, size_t j, size_t k) {
26  auto value = in(j, k);
27 
28  // Slow path for cells with padding
29  if constexpr (P1 || P2) {
30  if (cpp_unlikely(j < P1 || k < P2 || j >= etl::dim<0>(in) - P1 || k >= etl::dim<1>(in) - P2)) {
31  const size_t base_j = j * S1 - P1;
32  const size_t base_k = k * S2 - P2;
33 
34  for (size_t jj = 0; jj < C1; ++jj) {
35  for (size_t kk = 0; kk < C2; ++kk) {
36  if (base_j + jj < etl::dim<0>(m) && base_k + kk < etl::dim<1>(m)) {
37  if constexpr (S1 == C1 && S2 == C2) {
38  m(base_j + jj, base_k + kk) = value;
39  } else {
40  m(base_j + jj, base_k + kk) += value;
41  }
42  }
43  }
44  }
45 
46  return;
47  }
48  }
49 
50  for (size_t jj = 0; jj < C1; ++jj) {
51  for (size_t kk = 0; kk < C2; ++kk) {
52  if constexpr (S1 == C1 && S2 == C2) {
53  m(j * S1 - P1 + jj, k * S2 - P2 + kk) = value;
54  } else {
55  m(j * S1 - P1 + jj, k * S2 - P2 + kk) += value;
56  }
57  }
58  }
59  }
60 
69  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, typename A, typename M>
70  static void upsample_block_3d(A&& in, M& m, size_t q, size_t j, size_t k) {
71  auto value = in(q, j, k);
72 
73  // Slow path for cells with padding
74  if constexpr (P1 || P2) {
75  if (cpp_unlikely(j < P1 || k < P2 || j >= etl::dim<1>(in) - P1 || k >= etl::dim<2>(in) - P2)) {
76  const size_t base_j = j * S1 - P1;
77  const size_t base_k = k * S2 - P2;
78 
79  for (size_t jj = 0; jj < C1; ++jj) {
80  for (size_t kk = 0; kk < C2; ++kk) {
81  if (base_j + jj < etl::dim<1>(m) && base_k + kk < etl::dim<2>(m)) {
82  if constexpr (S1 == C1 && S2 == C2) {
83  m(q, base_j + jj, base_k + kk) = value;
84  } else {
85  m(q, base_j + jj, base_k + kk) += value;
86  }
87  }
88  }
89  }
90 
91  return;
92  }
93  }
94 
95  for (size_t jj = 0; jj < C1; ++jj) {
96  for (size_t kk = 0; kk < C2; ++kk) {
97  if constexpr (S1 == C1 && S2 == C2) {
98  m(q, j * S1 - P1 + jj, k * S2 - P2 + kk) = value;
99  } else {
100  m(q, j * S1 - P1 + jj, k * S2 - P2 + kk) += value;
101  }
102  }
103  }
104  }
105 
114  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, typename A, typename M>
115  static void upsample_block_4d(A&& in, M& m, size_t p, size_t q, size_t j, size_t k) {
116  auto value = in(p, q, j, k);
117 
118  // Slow path for cells with padding
119  if constexpr (P1 || P2) {
120  if (cpp_unlikely(j < P1 || k < P2 || j >= etl::dim<2>(in) - P1 || k >= etl::dim<3>(in) - P2)) {
121  const size_t base_j = j * S1 - P1;
122  const size_t base_k = k * S2 - P2;
123 
124  for (size_t jj = 0; jj < C1; ++jj) {
125  for (size_t kk = 0; kk < C2; ++kk) {
126  if (base_j + jj < etl::dim<2>(m) && base_k + kk < etl::dim<3>(m)) {
127  if constexpr (S1 == C1 && S2 == C2) {
128  m(p, q, base_j + jj, base_k + kk) = value;
129  } else {
130  m(p, q, base_j + jj, base_k + kk) += value;
131  }
132  }
133  }
134  }
135 
136  return;
137  }
138  }
139 
140  for (size_t jj = 0; jj < C1; ++jj) {
141  for (size_t kk = 0; kk < C2; ++kk) {
142  if constexpr (S1 == C1 && S2 == C2) {
143  m(p, q, j * S1 - P1 + jj, k * S2 - P2 + kk) = value;
144  } else {
145  m(p, q, j * S1 - P1 + jj, k * S2 - P2 + kk) += value;
146  }
147  }
148  }
149  }
150 
159  template <typename A, typename M>
160  static void upsample_block_2d(A&& in, M& m, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
161  auto value = in(j, k);
162 
163  // Slow path for cells with padding
164  if (cpp_unlikely(p1 || p2)) {
165  if (cpp_unlikely(j < p1 || k < p2 || j >= etl::dim<0>(in) - p1 || k >= etl::dim<1>(in) - p2)) {
166  const size_t base_j = j * s1 - p1;
167  const size_t base_k = k * s2 - p2;
168 
169  for (size_t jj = 0; jj < c1; ++jj) {
170  for (size_t kk = 0; kk < c2; ++kk) {
171  if (base_j + jj < etl::dim<0>(m) && base_k + kk < etl::dim<1>(m)) {
172  if (s1 == c1 && s2 == c2) {
173  m(base_j + jj, base_k + kk) = value;
174  } else {
175  m(base_j + jj, base_k + kk) += value;
176  }
177  }
178  }
179  }
180 
181  return;
182  }
183  }
184 
185  if (s1 == c1 && s2 == c2) {
186  for (size_t jj = 0; jj < c1; ++jj) {
187  for (size_t kk = 0; kk < c2; ++kk) {
188  m(j * s1 - p1 + jj, k * s2 - p2 + kk) = value;
189  }
190  }
191  } else {
192  for (size_t jj = 0; jj < c1; ++jj) {
193  for (size_t kk = 0; kk < c2; ++kk) {
194  m(j * s1 - p1 + jj, k * s2 - p2 + kk) += value;
195  }
196  }
197  }
198  }
199 
208  template <typename A, typename M>
209  static void upsample_block_3d(A&& in, M& m, size_t q, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
210  auto value = in(q, j, k);
211 
212  // Slow path for cells with padding
213  if (cpp_unlikely(p1 || p2)) {
214  if (cpp_unlikely(j < p1 || k < p2 || j >= etl::dim<1>(in) - p1 || k >= etl::dim<2>(in) - p2)) {
215  const size_t base_j = j * s1 - p1;
216  const size_t base_k = k * s2 - p2;
217 
218  for (size_t jj = 0; jj < c1; ++jj) {
219  for (size_t kk = 0; kk < c2; ++kk) {
220  if (base_j + jj < etl::dim<1>(m) && base_k + kk < etl::dim<2>(m)) {
221  if (s1 == c1 && s2 == c2) {
222  m(q, base_j + jj, base_k + kk) = value;
223  } else {
224  m(q, base_j + jj, base_k + kk) += value;
225  }
226  }
227  }
228  }
229 
230  return;
231  }
232  }
233 
234  if (s1 == c1 && s2 == c2) {
235  for (size_t jj = 0; jj < c1; ++jj) {
236  for (size_t kk = 0; kk < c2; ++kk) {
237  m(q, j * s1 - p1 + jj, k * s2 - p2 + kk) = value;
238  }
239  }
240  } else {
241  for (size_t jj = 0; jj < c1; ++jj) {
242  for (size_t kk = 0; kk < c2; ++kk) {
243  m(q, j * s1 - p1 + jj, k * s2 - p2 + kk) += value;
244  }
245  }
246  }
247  }
248 
257  template <typename A, typename M>
258  static void upsample_block_4d(A&& in, M& m, size_t p, size_t q, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
259  auto value = in(p, q, j, k);
260 
261  // Slow path for cells with padding
262  if (cpp_unlikely(p1 || p2)) {
263  if (cpp_unlikely(j < p1 || k < p2 || j >= etl::dim<2>(in) - p1 || k >= etl::dim<3>(in) - p2)) {
264  const size_t base_j = j * s1 - p1;
265  const size_t base_k = k * s2 - p2;
266 
267  for (size_t jj = 0; jj < c1; ++jj) {
268  for (size_t kk = 0; kk < c2; ++kk) {
269  if (base_j + jj < etl::dim<2>(m) && base_k + kk < etl::dim<3>(m)) {
270  if (s1 == c1 && s2 == c2) {
271  m(p, q, base_j + jj, base_k + kk) = value;
272  } else {
273  m(p, q, base_j + jj, base_k + kk) += value;
274  }
275  }
276  }
277  }
278 
279  return;
280  }
281  }
282 
283  if (s1 == c1 && s2 == c2) {
284  for (size_t jj = 0; jj < c1; ++jj) {
285  for (size_t kk = 0; kk < c2; ++kk) {
286  m(p, q, j * s1 - p1 + jj, k * s2 - p2 + kk) = value;
287  }
288  }
289  } else {
290  for (size_t jj = 0; jj < c1; ++jj) {
291  for (size_t kk = 0; kk < c2; ++kk) {
292  m(p, q, j * s1 - p1 + jj, k * s2 - p2 + kk) += value;
293  }
294  }
295  }
296  }
297 
298  // 2D handling
299 
307  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, etl_2d A, typename M>
308  static void apply(A&& in, M&& m) {
309  if (S1 != C1 || S2 != C2) {
310  m = 0;
311  }
312 
313  for (size_t j = 0; j < etl::dim<0>(in); ++j) {
314  for (size_t k = 0; k < etl::dim<1>(in); ++k) {
315  upsample_block_2d<C1, C2, S1, S2, P1, P2>(in, m, j, k);
316  }
317  }
318  }
319 
327  template <etl_2d A, typename M>
328  static void apply(A&& in, M&& m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
329  if (s1 != c1 || s2 != c2) {
330  m = 0;
331  }
332 
333  for (size_t j = 0; j < etl::dim<0>(in); ++j) {
334  for (size_t k = 0; k < etl::dim<1>(in); ++k) {
335  upsample_block_2d(in, m, j, k, c1, c2, s1, s2, p1, p2);
336  }
337  }
338  }
339 
340  // 3D handling
341 
349  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, etl_3d A, typename M>
350  static void apply(A&& in, M&& m) {
351  if (S1 != C1 || S2 != C2) {
352  m = 0;
353  }
354 
355  // GPU/CPU Synchronization must not be done in parallel
358 
359  auto batch_fun = [&](const size_t first, const size_t last) {
360  for (size_t q = first; q < last; ++q) {
361  for (size_t j = 0; j < etl::dim<1>(in); ++j) {
362  for (size_t k = 0; k < etl::dim<2>(in); ++k) {
363  upsample_block_3d<C1, C2, S1, S2, P1, P2>(in, m, q, j, k);
364  }
365  }
366  }
367  };
368 
369  const size_t N = etl::dim<0>(in);
370 
371  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
372  }
373 
381  template <etl_3d A, typename M>
382  static void apply(A&& in, M&& m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
383  if (s1 != c1 || s2 != c2) {
384  m = 0;
385  }
386 
387  // GPU/CPU Synchronization must not be done in parallel
390 
391  auto batch_fun = [&](const size_t first, const size_t last) {
392  for (size_t q = first; q < last; ++q) {
393  for (size_t j = 0; j < etl::dim<1>(in); ++j) {
394  for (size_t k = 0; k < etl::dim<2>(in); ++k) {
395  upsample_block_3d(in, m, q, j, k, c1, c2, s1, s2, p1, p2);
396  }
397  }
398  }
399  };
400 
401  const size_t N = etl::dim<0>(in);
402 
403  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
404  }
405 
406  // 4D handling
407 
415  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, etl_4d A, typename M>
416  static void apply(A&& in, M&& m) {
417  if (S1 != C1 || S2 != C2) {
418  m = 0;
419  }
420 
421  // GPU/CPU Synchronization must not be done in parallel
424 
425  auto batch_fun = [&](const size_t first, const size_t last) {
426  for (size_t p = first; p < last; ++p) {
427  for (size_t q = 0; q < etl::dim<1>(in); ++q) {
428  for (size_t j = 0; j < etl::dim<2>(in); ++j) {
429  for (size_t k = 0; k < etl::dim<3>(in); ++k) {
430  upsample_block_4d<C1, C2, S1, S2, P1, P2>(in, m, p, q, j, k);
431  }
432  }
433  }
434  }
435  };
436 
437  const size_t N = etl::dim<0>(in);
438 
439  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
440  }
441 
449  template <etl_4d A, typename M>
450  static void apply(A&& in, M&& m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
451  if (s1 != c1 || s2 != c2) {
452  m = 0;
453  }
454 
455  // GPU/CPU Synchronization must not be done in parallel
458 
459  auto batch_fun = [&](const size_t first, const size_t last) {
460  for (size_t p = first; p < last; ++p) {
461  for (size_t q = 0; q < etl::dim<1>(in); ++q) {
462  for (size_t j = 0; j < etl::dim<2>(in); ++j) {
463  for (size_t k = 0; k < etl::dim<3>(in); ++k) {
464  upsample_block_4d(in, m, p, q, j, k, c1, c2, s1, s2, p1, p2);
465  }
466  }
467  }
468  }
469  };
470 
471  const size_t N = etl::dim<0>(in);
472 
473  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
474  }
475 
476  // Deep Handling
477 
485  template <size_t C1, size_t C2, size_t S1, size_t S2, size_t P1, size_t P2, etl_5d_and_plus A, typename M>
486  static void apply(A&& in, M& m) {
487  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
488  apply<C1, C2, S1, S2, P1, P2>(in(i), m(i));
489  }
490  }
491 
499  template <etl_5d_and_plus A, typename M>
500  static void apply(A&& in, M& m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2) {
501  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
502  apply(in(i), m(i), c1, c2, s1, s2, p1, p2);
503  }
504  }
505 };
506 
510 struct upsample_3d {
522  template <size_t C1, size_t C2, size_t C3, typename A, typename M>
523  static void upsample_block_3d(A&& in, M& m, size_t i, size_t j, size_t k) {
524  auto value = in(i, j, k);
525 
526  for (size_t ii = 0; ii < C1; ++ii) {
527  for (size_t jj = 0; jj < C2; ++jj) {
528  for (size_t kk = 0; kk < C3; ++kk) {
529  m(i * C1 + ii, j * C2 + jj, k * C3 + kk) = value;
530  }
531  }
532  }
533  }
534 
546  template <size_t C1, size_t C2, size_t C3, typename A, typename M>
547  static void upsample_block_4d(A&& in, M& m, size_t q, size_t i, size_t j, size_t k) {
548  auto value = in(q, i, j, k);
549 
550  for (size_t ii = 0; ii < C1; ++ii) {
551  for (size_t jj = 0; jj < C2; ++jj) {
552  for (size_t kk = 0; kk < C3; ++kk) {
553  m(q, i * C1 + ii, j * C2 + jj, k * C3 + kk) = value;
554  }
555  }
556  }
557  }
558 
570  template <typename A, typename M>
571  static void upsample_block_3d(A&& in, M& m, size_t i, size_t j, size_t k, size_t c1, size_t c2, size_t c3) {
572  auto value = in(i, j, k);
573 
574  for (size_t ii = 0; ii < c1; ++ii) {
575  for (size_t jj = 0; jj < c2; ++jj) {
576  for (size_t kk = 0; kk < c3; ++kk) {
577  m(i * c1 + ii, j * c2 + jj, k * c3 + kk) = value;
578  }
579  }
580  }
581  }
582 
594  template <typename A, typename M>
595  static void upsample_block_4d(A&& in, M& m, size_t q, size_t i, size_t j, size_t k, size_t c1, size_t c2, size_t c3) {
596  auto value = in(q, i, j, k);
597 
598  for (size_t ii = 0; ii < c1; ++ii) {
599  for (size_t jj = 0; jj < c2; ++jj) {
600  for (size_t kk = 0; kk < c3; ++kk) {
601  m(q, i * c1 + ii, j * c2 + jj, k * c3 + kk) = value;
602  }
603  }
604  }
605  }
606 
607  // 3D Handling
608 
617  template <size_t C1, size_t C2, size_t C3, etl_3d A, typename M>
618  static void apply(A&& in, M&& m) {
619  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
620  for (size_t j = 0; j < etl::dim<1>(in); ++j) {
621  for (size_t k = 0; k < etl::dim<2>(in); ++k) {
622  upsample_block_3d<C1, C2, C3>(in, m, i, j, k);
623  }
624  }
625  }
626  }
627 
636  template <etl_3d A, typename M>
637  static void apply(A&& in, M&& m, size_t c1, size_t c2, size_t c3) {
638  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
639  for (size_t j = 0; j < etl::dim<1>(in); ++j) {
640  for (size_t k = 0; k < etl::dim<2>(in); ++k) {
641  upsample_block_3d(in, m, i, j, k, c1, c2, c3);
642  }
643  }
644  }
645  }
646 
647  // 4D Handling
648 
657  template <size_t C1, size_t C2, size_t C3, etl_4d A, typename M>
658  static void apply(A&& in, M&& m) {
659  // GPU/CPU Synchronization must not be done in parallel
662 
663  auto batch_fun = [&](const size_t first, const size_t last) {
664  for (size_t q = first; q < last; ++q) {
665  for (size_t i = 0; i < etl::dim<1>(in); ++i) {
666  for (size_t j = 0; j < etl::dim<2>(in); ++j) {
667  for (size_t k = 0; k < etl::dim<3>(in); ++k) {
668  upsample_block_4d<C1, C2, C3>(in, m, q, i, j, k);
669  }
670  }
671  }
672  }
673  };
674 
675  const size_t N = etl::dim<0>(in);
676 
677  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
678  }
679 
688  template <etl_4d A, typename M>
689  static void apply(A&& in, M&& m, size_t c1, size_t c2, size_t c3) {
690  // GPU/CPU Synchronization must not be done in parallel
693 
694  auto batch_fun = [&](const size_t first, const size_t last) {
695  for (size_t q = first; q < last; ++q) {
696  for (size_t i = 0; i < etl::dim<1>(in); ++i) {
697  for (size_t j = 0; j < etl::dim<2>(in); ++j) {
698  for (size_t k = 0; k < etl::dim<3>(in); ++k) {
699  upsample_block_4d(in, m, q, i, j, k, c1, c2, c3);
700  }
701  }
702  }
703  }
704  };
705 
706  const size_t N = etl::dim<0>(in);
707 
708  engine_dispatch_1d_serial(batch_fun, 0, N, 2UL);
709  }
710 
711  // Deep Handling
712 
721  template <size_t C1, size_t C2, size_t C3, etl_5d_and_plus A, typename M>
722  static void apply(A&& in, M& m) {
723  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
724  apply<C1, C2, C3>(in(i), m(i));
725  }
726  }
727 
736  template <etl_5d_and_plus A, typename M>
737  static void apply(A&& in, M& m, size_t c1, size_t c2, size_t c3) {
738  for (size_t i = 0; i < etl::dim<0>(in); ++i) {
739  apply(in(i), m(i), c1, c2, c3);
740  }
741  }
742 };
743 
744 } //end of namespace etl::impl::standard
static void upsample_block_4d(A &&in, M &m, size_t q, size_t i, size_t j, size_t k, size_t c1, size_t c2, size_t c3)
Upsample a block of the sub expression.
Definition: upsample.hpp:595
static void apply(A &&in, M &&m)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:308
static void upsample_block_2d(A &&in, M &m, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2)
Upsample a block of the sub expression.
Definition: upsample.hpp:160
static void apply(A &&in, M &m, size_t c1, size_t c2, size_t c3)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:737
void engine_dispatch_1d_serial(Functor &&functor, size_t first, size_t last, size_t threshold, [[maybe_unused]] size_t n_threads=etl::threads)
Dispatch the elements of a range to a functor in a parallel manner, using the global thread engine...
Definition: parallel_support.hpp:734
Functor for 3D Upsampling.
Definition: upsample.hpp:510
static void upsample_block_4d(A &&in, M &m, size_t p, size_t q, size_t j, size_t k)
Upsample a block of the sub expression.
Definition: upsample.hpp:115
Definition: prob_pooling.hpp:10
static void upsample_block_4d(A &&in, M &m, size_t q, size_t i, size_t j, size_t k)
Upsample a block of the sub expression.
Definition: upsample.hpp:547
static void upsample_block_2d(A &&in, M &m, size_t j, size_t k)
Upsample a block of the sub expression.
Definition: upsample.hpp:25
static void apply(A &&in, M &&m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:328
static void upsample_block_3d(A &&in, M &m, size_t i, size_t j, size_t k, size_t c1, size_t c2, size_t c3)
Upsample a block of the sub expression.
Definition: upsample.hpp:571
static void upsample_block_3d(A &&in, M &m, size_t i, size_t j, size_t k)
Upsample a block of the sub expression.
Definition: upsample.hpp:523
static void upsample_block_3d(A &&in, M &m, size_t q, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2)
Upsample a block of the sub expression.
Definition: upsample.hpp:209
static void apply(A &&in, M &m)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:486
static void apply(A &&in, M &&m, size_t c1, size_t c2, size_t c3)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:637
static void upsample_block_3d(A &&in, M &m, size_t q, size_t j, size_t k)
Upsample a block of the sub expression.
Definition: upsample.hpp:70
void safe_ensure_cpu_up_to_date(E &&expr)
Ensure that the CPU is up to date.
Definition: helpers.hpp:278
static void apply(A &&in, M &&m)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:618
Functor for 2D Upsampling.
Definition: upsample.hpp:15
static void apply(A &&in, M &m, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:500
static void apply(A &&in, M &m)
Apply the functor on sub and store the result in m.
Definition: upsample.hpp:722
static void upsample_block_4d(A &&in, M &m, size_t p, size_t q, size_t j, size_t k, size_t c1, size_t c2, size_t s1, size_t s2, size_t p1, size_t p2)
Upsample a block of the sub expression.
Definition: upsample.hpp:258