Exact AvgPool2d encoding in NCET¶
This note explains how NCET can represent a fixed PyTorch AvgPool2d
operation as an exact sparse linear equality and propagate interval bounds
through it.
Logic: 1. Output index: \((c_o,i,j)\) 2. Kernel position: \((r,t)\) 3. 1 and 2 together determine the corresponding input index to be weighted: \((c_i, h, w)\) with \(h=iS_h+r-P_h\) and \(w=jS_w+t-P_w\). Note that \(c_o = c_i\). 4. Meanwhile, \(h\) and \(w\) must satisfy \(0\le h<H_{\mathrm{in}}\) and \(0\le w<W_{\mathrm{in}}\); \(r\) and \(t\) must satisfy \(0\le r<K_h\) and \(0\le t<K_w\). 5. Then for all valid input indices by varying \(r\) and \(t\) belong to the output position \((i,j)\), the coefficient is \(\frac{1}{D_{i,j}}\). 6. Transform the output and input indices into the position of the vectorized output and input tensors, which are also the row and column indices of the sparse matrix \(A\).
1. Main idea¶
Average pooling computes a weighted sum of the input elements in each pooling
window. The weights are fixed, nonnegative averaging coefficients. Therefore,
AvgPool2d is a linear operator:
Here:
- \(X\) and \(Y\) are the input and output tensors;
- \(A\) is a sparse matrix containing the pooling connections and averaging coefficients;
- \(\operatorname{vec}_C\) flattens a tensor in C-order: channel, row, and then column.
This equality is exact. AvgPool2d requires continuous output variables but
no binary variables.
2. Tensor shapes¶
NCET represents one sample without a batch dimension:
Average pooling operates independently on every channel. It changes the spatial dimensions but does not mix channels.
Let:
- \((K_h,K_w)\) be the kernel size;
- \((S_h,S_w)\) be the stride;
- \((P_h,P_w)\) be the padding.
For ceil_mode=False, the output dimensions are
PyTorch FX shape propagation determines these shapes before GraphIR and the optimization backend are constructed.
3. Pooling-window coordinates¶
Consider output element \(Y_{c,i,j}\). For kernel position \((r,t)\), the corresponding coordinate in the original, unpadded input is
where
Only coordinates satisfying
refer to input tensor elements. Coordinates outside this range are padding positions whose values are fixed to zero.
Define the valid kernel positions for output location \((i,j)\) as
4. Scalar AvgPool2d formula¶
When count_include_pad=True, padding zeros are included in the denominator:
The padding terms do not appear in the sum because their values are zero, but they are still counted by the denominator \(K_hK_w\).
When count_include_pad=False, only valid input elements are counted:
It is convenient to define
Then both cases share the formula
5. C-order flattening¶
The input element \((c,h,w)\) becomes column index
The output element \((c,i,j)\) becomes row index
Therefore,
6. Constructing the sparse matrix¶
The pooling matrix has shape
To state each coefficient precisely, use \(c_o\) for an output channel and \(c_i\) for an input channel. Matrix entries are defined only for valid output and input tensor coordinates:
Within these coordinate domains, each coefficient is
The two window-membership conditions are equivalent to
They test whether input position \((h,w)\) belongs to the pooling window for
output position \((i,j)\). The condition \(c_i=c_o\) expresses that average
pooling does not mix channels. The explicit input-size restrictions exclude
padding coordinates: padding positions have fixed value zero and therefore do
not receive columns in \(A\). When count_include_pad=True, those omitted zero
positions still contribute to \(D_{i,j}\); when it is False, they contribute
to neither the matrix nor the divisor.
The scalar and matrix forms are connected by
Each row of \(A\) therefore represents one output element. Its nonzero columns identify the valid input elements in that element's pooling window.
7. Small example without padding¶
Suppose X.shape == (1, 3, 3), the kernel is 2 x 2, stride is one, padding
is zero, and count_include_pad=True. The flattened input and output are
The matrix is
For example, the first row gives
8. Effect of padding on the denominator¶
Consider a 2 x 2 kernel at a boundary where its window contains one valid
input value \(x\) and three padding zeros.
For count_include_pad=True,
For count_include_pad=False,
Thus, padding coordinates never become optimization variables. They only affect the denominator when padding is included in the average.
9. Interval bound propagation¶
All entries of \(A\) are nonnegative:
Consequently, AvgPool2d is monotone in every input element. Given input bounds
the exact interval propagation rule is
Equivalently, the same AvgPool2d operation can be applied independently to the lower and upper tensors:
Unlike a general affine operator with negative coefficients, there is no need to split the matrix into positive and negative parts.
10. Exact optimization constraint¶
The backend flattens the three-dimensional CVXPY expressions only for applying the sparse matrix:
input_vector = cp.reshape(input_value, (input_value.size,), order="C")
output_vector = cp.reshape(output_value, (output_value.size,), order="C")
constraint = output_vector == matrix @ input_vector
This adds the exact equality
The reshape operations create views of existing expressions, not additional decision variables. AvgPool2d introduces one continuous output tensor and no binary variables.
11. Current NCET support boundary¶
The exact formulation supports:
- per-sample tensors with shape
(C, H, W); - integer or two-dimensional kernel size, stride, and padding;
ceil_mode=False;- both values of
count_include_pad; divisor_override=None.
Supporting ceil_mode=True requires matching PyTorch's additional boundary
window rules. A non-None divisor_override changes \(D_{i,j}\) to the supplied
constant. The frontend explicitly rejects these two cases.