```python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.modules.utils import _pair
from torch.utils.cpp_extension import load_inline


_cpp_source = r"""
#include <torch/extension.h>

torch::Tensor expand_pool_weights(
    torch::Tensor weight, int64_t pool_h, int64_t pool_w);

torch::Tensor sigmoid_sum_cuda(torch::Tensor input);
"""

_cuda_source = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <c10/cuda/CUDAException.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <algorithm>

__global__ void expand_weights_kernel(
    const float* __restrict__ weight,
    float* __restrict__ expanded,
    int64_t count,
    int kh, int kw,
    int ph, int pw,
    int eh, int ew)
{
    const int64_t index =
        static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
    if (index >= count) return;

    const int x = index % ew;
    const int y = (index / ew) % eh;
    const int64_t channel_pair = index / (eh * ew);

    const int y0 = max(0, y - ph + 1);
    const int y1 = min(kh - 1, y);
    const int x0 = max(0, x - pw + 1);
    const int x1 = min(kw - 1, x);

    float value = 0.0f;
    for (int ky = y0; ky <= y1; ++ky) {
        for (int kx = x0; kx <= x1; ++kx) {
            value += weight[channel_pair * kh * kw + ky * kw + kx];
        }
    }
    expanded[index] = value / static_cast<float>(ph * pw);
}

__device__ __forceinline__ float block_sum(float value)
{
    __shared__ float warp_sums[8];
    const int lane = threadIdx.x & 31;
    const int warp = threadIdx.x >> 5;

    #pragma unroll
    for (int offset = 16; offset > 0; offset >>= 1)
        value += __shfl_down_sync(0xffffffff, value, offset);

    if (lane == 0)
        warp_sums[warp] = value;
    __syncthreads();

    value = (warp == 0) ? warp_sums[lane & 7] : 0.0f;
    if (warp == 0) {
        if (lane >= 8) value = 0.0f;
        #pragma unroll
        for (int offset = 16; offset > 0; offset >>= 1)
            value += __shfl_down_sync(0xffffffff, value, offset);
    }
    return value;
}

__global__ void sigmoid_sum_partial_kernel(
    const float* __restrict__ input,
    float* __restrict__ partial,
    int64_t elements_per_batch,
    int chunks)
{
    const int batch = blockIdx.y;
    const int chunk = blockIdx.x;
    const int64_t start = static_cast<int64_t>(chunk) * 4096;
    const int64_t end = min(start + 4096, elements_per_batch);
    const float* row =
        input + static_cast<int64_t>(batch) * elements_per_batch;

    float value = 0.0f;
    for (int64_t i = start + threadIdx.x; i < end; i += blockDim.x) {
        value += 1.0f / (1.0f + expf(-row[i]));
    }

    value = block_sum(value);
    if (threadIdx.x == 0)
        partial[static_cast<int64_t>(batch) * chunks + chunk] = value;
}

__global__ void finish_sum_kernel(
    const float* __restrict__ partial,
    float* __restrict__ output,
    int chunks)
{
    float value = 0.0f;
    const float* row =
        partial + static_cast<int64_t>(blockIdx.x) * chunks;

    for (int i = threadIdx.x; i < chunks; i += blockDim.x)
        value += row[i];

    value = block_sum(value);
    if (threadIdx.x == 0)
        output[blockIdx.x] = value;
}

torch::Tensor expand_pool_weights(
    torch::Tensor weight, int64_t pool_h, int64_t pool_w)
{
    TORCH_CHECK(weight.is_cuda() && weight.scalar_type() == at::kFloat,
                "Expected CUDA float32 weights");
    TORCH_CHECK(weight.dim() == 4 && weight.is_contiguous(),
                "Expected contiguous convolution weights");
    TORCH_CHECK(pool_h > 0 && pool_w > 0,
                "Pool dimensions must be positive");

    const c10::cuda::CUDAGuard guard(weight.device());
    const int kh = weight.size(2);
    const int kw = weight.size(3);
    const int eh = kh + pool_h - 1;
    const int ew = kw + pool_w - 1;

    auto expanded = torch::empty(
        {weight.size(0), weight.size(1), eh, ew}, weight.options());

    const int64_t count = expanded.numel();
    auto stream = at::cuda::getCurrentCUDAStream();

    expand_weights_kernel<<<(count + 255) / 256, 256, 0, stream>>>(
        weight.data_ptr<float>(), expanded.data_ptr<float>(),
        count, kh, kw, pool_h, pool_w, eh, ew);
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return expanded;
}

torch::Tensor sigmoid_sum_cuda(torch::Tensor input)
{
    TORCH_CHECK(input.is_cuda() && input.scalar_type() == at::kFloat,
                "Expected CUDA float32 input");
    TORCH_CHECK(input.dim() == 4, "Expected a four-dimensional input");

    const c10::cuda::CUDAGuard guard(input.device());
    input = input.contiguous();

    const int64_t batch = input.size(0);
    const int64_t elements =
        input.size(1) * input.size(2) * input.size(3);
    auto output = torch::empty({batch}, input.options());
    if (batch == 0) return output;
    TORCH_CHECK(elements > 0, "Expected nonempty spatial dimensions");

    const int chunks = (elements + 4095) / 4096;
    auto partial = torch::empty({batch, chunks}, input.options());
    auto stream = at::cuda::getCurrentCUDAStream();

    sigmoid_sum_partial_kernel<<<dim3(chunks, batch), 256, 0, stream>>>(
        input.data_ptr<float>(), partial.data_ptr<float>(),
        elements, chunks);
    C10_CUDA_KERNEL_LAUNCH_CHECK();

    finish_sum_kernel<<<batch, 256, 0, stream>>>(
        partial.data_ptr<float>(), output.data_ptr<float>(), chunks);
    C10_CUDA_KERNEL_LAUNCH_CHECK();

    return output;
}
"""

_ops = load_inline(
    name="conv_avgpool_sigmoid_sum_fused_v1",
    cpp_sources=_cpp_source,
    cuda_sources=_cuda_source,
    functions=["expand_pool_weights", "sigmoid_sum_cuda"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(
        self, in_channels, out_channels, kernel_size, pool_kernel_size
    ):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)
        self.avg_pool = nn.AvgPool2d(pool_kernel_size)
        self.pool_size = _pair(pool_kernel_size)

    def forward(self, x):
        if (
            not x.is_cuda
            or x.dtype != torch.float32
            or self.conv.weight.dtype != torch.float32
            or torch.is_autocast_enabled()
            or (
                torch.is_grad_enabled()
                and (
                    x.requires_grad
                    or self.conv.weight.requires_grad
                    or self.conv.bias.requires_grad
                )
            )
        ):
            return torch.sigmoid(self.avg_pool(self.conv(x))).sum(
                dim=(1, 2, 3)
            )

        # Compose convolution and average pooling into one strided
        # convolution, avoiding the full-resolution feature tensor.
        ph, pw = self.pool_size
        weight = _ops.expand_pool_weights(
            self.conv.weight.contiguous(), ph, pw
        )
        pooled = F.conv2d(
            x,
            weight,
            self.conv.bias,
            stride=(ph, pw),
        )
        return _ops.sigmoid_sum_cuda(pooled)
```