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


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

torch::Tensor fused_groupnorm_swish_cuda(
    torch::Tensor x,
    torch::Tensor gamma,
    torch::Tensor beta,
    torch::Tensor multiply_weight,
    double eps);
"""

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

__device__ __forceinline__ float warp_sum(float value) {
    #pragma unroll
    for (int offset = 16; offset > 0; offset >>= 1) {
        value += __shfl_down_sync(0xffffffffu, value, offset);
    }
    return __shfl_sync(0xffffffffu, value, 0);
}

__global__ void fused_groupnorm_swish_kernel(
    const float* __restrict__ x,
    const float* __restrict__ gamma,
    const float* __restrict__ beta,
    const float* __restrict__ multiply_weight,
    float* __restrict__ output,
    int64_t total_groups,
    int channels,
    float eps)
{
    const int lane = threadIdx.x & 31;
    const int64_t group =
        static_cast<int64_t>(blockIdx.x) * 8 + (threadIdx.x >> 5);

    // Every lane of a warp takes the same branch.
    if (group >= total_groups) {
        return;
    }

    const int64_t index = group * 32 + lane;
    const int channel = static_cast<int>(index % channels);
    const float value = x[index];

    const float mean = warp_sum(value) * (1.0f / 32.0f);
    const float centered = value - mean;
    const float variance =
        warp_sum(centered * centered) * (1.0f / 32.0f);
    const float normalized = centered * rsqrtf(variance + eps);
    const float affine = normalized * gamma[channel] + beta[channel];

    const float first_swish = affine / (1.0f + expf(-affine));
    const float multiplied = first_swish * multiply_weight[channel];
    output[index] = multiplied / (1.0f + expf(-multiplied));
}

torch::Tensor fused_groupnorm_swish_cuda(
    torch::Tensor x,
    torch::Tensor gamma,
    torch::Tensor beta,
    torch::Tensor multiply_weight,
    double eps)
{
    TORCH_CHECK(x.is_cuda(), "x must be a CUDA tensor");
    TORCH_CHECK(x.dim() == 2, "x must have two dimensions");
    TORCH_CHECK(x.scalar_type() == at::kFloat && x.is_contiguous(),
                "x must be contiguous float32");
    TORCH_CHECK(x.size(1) > 0 && x.size(1) % 32 == 0,
                "channels must be a positive multiple of 32");

    for (const auto& parameter : {gamma, beta, multiply_weight}) {
        TORCH_CHECK(parameter.device() == x.device(),
                    "all tensors must be on the same device");
        TORCH_CHECK(parameter.scalar_type() == at::kFloat &&
                    parameter.is_contiguous(),
                    "parameters must be contiguous float32");
        TORCH_CHECK(parameter.numel() == x.size(1),
                    "parameters must contain one value per channel");
    }

    const c10::cuda::CUDAGuard device_guard(x.device());
    auto output = torch::empty_like(x);
    const int64_t total_groups = x.numel() / 32;

    if (total_groups != 0) {
        const auto stream = at::cuda::getCurrentCUDAStream();
        fused_groupnorm_swish_kernel<<<
            static_cast<unsigned int>((total_groups + 7) / 8),
            256, 0, stream.stream()>>>(
                x.data_ptr<float>(),
                gamma.data_ptr<float>(),
                beta.data_ptr<float>(),
                multiply_weight.data_ptr<float>(),
                output.data_ptr<float>(),
                total_groups,
                static_cast<int>(x.size(1)),
                static_cast<float>(eps));
        C10_CUDA_KERNEL_LAUNCH_CHECK();
    }

    return output;
}
"""

_fused_ops = load_inline(
    name="fused_groupnorm32_double_swish",
    cpp_sources=_cpp_source,
    cuda_sources=_cuda_source,
    functions=["fused_groupnorm_swish_cuda"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3", "--fmad=false"],
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(
        self, in_features, out_features, num_groups, multiply_weight_shape
    ):
        super().__init__()
        self.gemm = nn.Linear(in_features, out_features)
        self.group_norm = nn.GroupNorm(num_groups, out_features)
        self.multiply_weight = nn.Parameter(torch.randn(multiply_weight_shape))

    def forward(self, x):
        x = self.gemm(x)

        parameters = (
            self.group_norm.weight,
            self.group_norm.bias,
            self.multiply_weight,
        )
        if (
            x.is_cuda
            and x.dtype == torch.float32
            and x.dim() == 2
            and x.is_contiguous()
            and x.shape[1] == self.group_norm.num_groups * 32
            and self.multiply_weight.shape == (x.shape[1],)
            and all(
                p.dtype == torch.float32
                and p.device == x.device
                and p.is_contiguous()
                for p in parameters
            )
        ):
            return _fused_ops.fused_groupnorm_swish_cuda(
                x,
                self.group_norm.weight,
                self.group_norm.bias,
                self.multiply_weight,
                self.group_norm.eps,
            )

        x = self.group_norm(x)
        x = x * torch.sigmoid(x)
        x = x * self.multiply_weight
        return x * torch.sigmoid(x)
```