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


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

torch::Tensor fused_forward_cuda(
    torch::Tensor input,
    torch::Tensor weight,
    torch::Tensor bias,
    torch::Tensor gamma,
    torch::Tensor beta,
    torch::Tensor multiplier,
    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>
#include <cublas_v2.h>

__device__ __forceinline__ float sum8(float value) {
    value += __shfl_xor_sync(0xffffffffu, value, 4, 8);
    value += __shfl_xor_sync(0xffffffffu, value, 2, 8);
    value += __shfl_xor_sync(0xffffffffu, value, 1, 8);
    return value;
}

__device__ __forceinline__ float activate(
    float centered,
    float inv_std,
    float gamma,
    float beta,
    float multiplier)
{
    const float normalized = (centered * inv_std) * gamma + beta;
    const float swish = normalized / (1.0f + __expf(-normalized));
    const float weighted = swish * multiplier;
    return weighted / (1.0f + __expf(-weighted));
}

__global__ void bias_groupnorm32_swish_kernel(
    const float4* __restrict__ input,
    const float4* __restrict__ bias,
    const float4* __restrict__ gamma,
    const float4* __restrict__ beta,
    const float4* __restrict__ multiplier,
    float4* __restrict__ output,
    int64_t vector_count,
    int channel_vectors,
    float eps)
{
    const int64_t index =
        static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
    const bool valid = index < vector_count;
    const int channel = static_cast<int>(index % channel_vectors);

    float4 value = make_float4(0.f, 0.f, 0.f, 0.f);
    if (valid) {
        value = input[index];
        const float4 b = bias[channel];
        value.x += b.x;
        value.y += b.y;
        value.z += b.z;
        value.w += b.w;
    }

    const float mean =
        sum8((value.x + value.y) + (value.z + value.w)) * 0.03125f;

    value.x -= mean;
    value.y -= mean;
    value.z -= mean;
    value.w -= mean;

    const float squares =
        (value.x * value.x + value.y * value.y) +
        (value.z * value.z + value.w * value.w);
    const float inv_std = rsqrtf(sum8(squares) * 0.03125f + eps);

    if (valid) {
        const float4 g = gamma[channel];
        const float4 b = beta[channel];
        const float4 m = multiplier[channel];

        float4 result;
        result.x = activate(value.x, inv_std, g.x, b.x, m.x);
        result.y = activate(value.y, inv_std, g.y, b.y, m.y);
        result.z = activate(value.z, inv_std, g.z, b.z, m.z);
        result.w = activate(value.w, inv_std, g.w, b.w, m.w);
        output[index] = result;
    }
}

torch::Tensor fused_forward_cuda(
    torch::Tensor input,
    torch::Tensor weight,
    torch::Tensor bias,
    torch::Tensor gamma,
    torch::Tensor beta,
    torch::Tensor multiplier,
    double eps)
{
    TORCH_CHECK(input.is_cuda() && input.scalar_type() == at::kFloat);
    const c10::cuda::CUDAGuard guard(input.device());

    TORCH_CHECK(input.dim() == 2 && input.is_contiguous());
    TORCH_CHECK(weight.dim() == 2);
    const int64_t batch64 = input.size(0);
    const int64_t features64 = input.size(1);
    const int64_t channels64 = weight.size(0);

    TORCH_CHECK(features64 > 0 && weight.size(1) == features64);
    TORCH_CHECK(channels64 > 0 && channels64 % 32 == 0);
    TORCH_CHECK(batch64 <= INT_MAX && features64 <= INT_MAX &&
                channels64 <= INT_MAX);

    for (const auto& tensor : {weight, bias, gamma, beta, multiplier}) {
        TORCH_CHECK(tensor.device() == input.device());
        TORCH_CHECK(tensor.scalar_type() == at::kFloat);
        TORCH_CHECK(tensor.is_contiguous());
    }
    TORCH_CHECK(bias.numel() == channels64);
    TORCH_CHECK(gamma.numel() == channels64);
    TORCH_CHECK(beta.numel() == channels64);
    TORCH_CHECK(multiplier.numel() == channels64);

    const int batch = static_cast<int>(batch64);
    const int features = static_cast<int>(features64);
    const int channels = static_cast<int>(channels64);

    auto output = torch::empty({batch64, channels64}, input.options());
    if (batch == 0)
        return output;

    auto intermediate = torch::empty_like(output);
    const auto stream = at::cuda::getCurrentCUDAStream();
    auto handle = at::cuda::getCurrentCUDABlasHandle();

    cublasMath_t original_math;
    TORCH_CHECK(
        cublasGetMathMode(handle, &original_math) == CUBLAS_STATUS_SUCCESS,
        "Could not read cuBLAS math mode");

    TORCH_CHECK(
        cublasSetMathMode(handle, CUBLAS_TF32_TENSOR_OP_MATH) ==
            CUBLAS_STATUS_SUCCESS,
        "Could not enable TF32 tensor cores");

    const float alpha = 1.0f;
    const float zero = 0.0f;

    // Row-major X @ W.T is column-major W.T @ X.
    const cublasStatus_t gemm_status = cublasGemmEx(
        handle,
        CUBLAS_OP_T,
        CUBLAS_OP_N,
        channels,
        batch,
        features,
        &alpha,
        weight.data_ptr<float>(),
        CUDA_R_32F,
        features,
        input.data_ptr<float>(),
        CUDA_R_32F,
        features,
        &zero,
        intermediate.data_ptr<float>(),
        CUDA_R_32F,
        channels,
        CUBLAS_COMPUTE_32F_FAST_TF32,
        CUBLAS_GEMM_DEFAULT_TENSOR_OP);

    const cublasStatus_t restore_status =
        cublasSetMathMode(handle, original_math);

    TORCH_CHECK(
        gemm_status == CUBLAS_STATUS_SUCCESS,
        "TF32 GEMM failed: ", static_cast<int>(gemm_status));
    TORCH_CHECK(
        restore_status == CUBLAS_STATUS_SUCCESS,
        "Could not restore cuBLAS math mode");

    constexpr int threads = 256;
    const int64_t vector_count = output.numel() / 4;
    const int blocks =
        static_cast<int>((vector_count + threads - 1) / threads);

    bias_groupnorm32_swish_kernel<<<blocks, threads, 0, stream>>>(
        reinterpret_cast<const float4*>(intermediate.data_ptr<float>()),
        reinterpret_cast<const float4*>(bias.data_ptr<float>()),
        reinterpret_cast<const float4*>(gamma.data_ptr<float>()),
        reinterpret_cast<const float4*>(beta.data_ptr<float>()),
        reinterpret_cast<const float4*>(multiplier.data_ptr<float>()),
        reinterpret_cast<float4*>(output.data_ptr<float>()),
        vector_count,
        channels / 4,
        static_cast<float>(eps));

    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return output;
}
"""

_fused_ops = load_inline(
    name="tf32_gemm_bias_groupnorm_double_swish_v5",
    cpp_sources=_cpp_source,
    cuda_sources=_cuda_source,
    functions=["fused_forward_cuda"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    extra_ldflags=["-lcublas"],
    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):
        if (
            x.is_cuda
            and x.dtype == torch.float32
            and x.ndim == 2
            and x.shape[1] == self.gemm.in_features
            and self.gemm.in_features > 0
            and self.gemm.out_features // self.group_norm.num_groups == 32
            and self.gemm.weight.dtype == torch.float32
            and self.gemm.bias.dtype == torch.float32
            and self.group_norm.weight.dtype == torch.float32
            and self.group_norm.bias.dtype == torch.float32
            and self.multiply_weight.dtype == torch.float32
            and self.multiply_weight.shape == (self.gemm.out_features,)
            and not torch.is_autocast_enabled()
        ):
            return _fused_ops.fused_forward_cuda(
                x.contiguous(),
                self.gemm.weight.contiguous(),
                self.gemm.bias.contiguous(),
                self.group_norm.weight.contiguous(),
                self.group_norm.bias.contiguous(),
                self.multiply_weight.contiguous(),
                self.group_norm.eps,
            )

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