-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHCNNPool.cpp
More file actions
130 lines (117 loc) · 4.85 KB
/
Copy pathHCNNPool.cpp
File metadata and controls
130 lines (117 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 David Liptak
#include "HCNNPool.h"
#include "ThreadPool.h"
#include <cstdint>
#include <stdexcept>
#include <string>
namespace hcnn {
// Pool work per element is lighter than conv, so use a higher threshold.
static constexpr int POOL_THREAD_DIM_THRESHOLD = 14;
HCNNPool::HCNNPool(int input_dim, PoolType type)
: input_dim(input_dim), output_dim(input_dim - 1),
input_N(0), output_N(0), type(type) {
// Align with HCNNNetwork: need dim >= 2 to pool; N = 2^dim in signed int.
if (input_dim < 2 || input_dim > 30) {
throw std::runtime_error(
"HCNNPool requires 2 <= input_dim <= 30, got "
+ std::to_string(input_dim));
}
input_N = static_cast<int>(std::uint32_t{1} << input_dim);
output_N = static_cast<int>(std::uint32_t{1} << (input_dim - 1));
}
void HCNNPool::forward(const float* in, float* out, int num_channels,
std::vector<int>* max_indices) const {
if (max_indices && type == PoolType::MAX) {
max_indices->resize(num_channels * output_N);
}
const uint32_t anti_mask = (1u << input_dim) - 1;
const bool use_threads = thread_pool && input_dim >= POOL_THREAD_DIM_THRESHOLD;
auto do_channels = [&](int c_begin, int c_end) {
if (type == PoolType::MAX) {
if (max_indices) {
for (int c = c_begin; c < c_end; ++c) {
const float* chan_in = in + c * input_N;
float* chan_out = out + c * output_N;
int* idx = max_indices->data() + c * output_N;
for (int v = 0; v < output_N; ++v) {
int v_anti = v ^ anti_mask;
if (chan_in[v] >= chan_in[v_anti]) {
chan_out[v] = chan_in[v];
idx[v] = v;
} else {
chan_out[v] = chan_in[v_anti];
idx[v] = v_anti;
}
}
}
} else {
for (int c = c_begin; c < c_end; ++c) {
const float* chan_in = in + c * input_N;
float* chan_out = out + c * output_N;
for (int v = 0; v < output_N; ++v) {
int v_anti = v ^ anti_mask;
chan_out[v] = (chan_in[v] >= chan_in[v_anti])
? chan_in[v] : chan_in[v_anti];
}
}
}
} else {
for (int c = c_begin; c < c_end; ++c) {
const float* chan_in = in + c * input_N;
float* chan_out = out + c * output_N;
for (int v = 0; v < output_N; ++v) {
chan_out[v] = (chan_in[v] + chan_in[v ^ anti_mask]) * 0.5f;
}
}
}
};
if (use_threads) {
thread_pool->ForEach(static_cast<size_t>(num_channels),
[&](size_t, size_t begin, size_t end) {
do_channels(static_cast<int>(begin), static_cast<int>(end));
});
} else {
do_channels(0, num_channels);
}
}
void HCNNPool::backward(const float* grad_out, float* grad_in, int num_channels,
const std::vector<int>* max_indices) const {
const uint32_t anti_mask = (1u << input_dim) - 1;
const bool use_threads = thread_pool && input_dim >= POOL_THREAD_DIM_THRESHOLD;
auto do_channels = [&](int c_begin, int c_end) {
// Zero the output range for these channels
for (int i = c_begin * input_N; i < c_end * input_N; ++i)
grad_in[i] = 0.0f;
if (type == PoolType::MAX) {
if (!max_indices)
throw std::runtime_error("MAX pool backward requires max_indices from forward pass");
for (int c = c_begin; c < c_end; ++c) {
const float* g_out = grad_out + c * output_N;
float* g_in = grad_in + c * input_N;
const int* idx = max_indices->data() + c * output_N;
for (int v = 0; v < output_N; ++v) {
g_in[idx[v]] = g_out[v];
}
}
} else {
for (int c = c_begin; c < c_end; ++c) {
const float* g_out = grad_out + c * output_N;
float* g_in = grad_in + c * input_N;
for (int v = 0; v < output_N; ++v) {
g_in[v] += g_out[v] * 0.5f;
g_in[v ^ anti_mask] += g_out[v] * 0.5f;
}
}
}
};
if (use_threads) {
thread_pool->ForEach(static_cast<size_t>(num_channels),
[&](size_t, size_t begin, size_t end) {
do_channels(static_cast<int>(begin), static_cast<int>(end));
});
} else {
do_channels(0, num_channels);
}
}
} // namespace hcnn