import torch
import torch.nn as nn
import torch.nn.functional as F
import math

def module_fn(x, params, groups, min_value, max_value, dropout_p, training):
    conv_weight = params["conv_weight"]
    conv_bias = params["conv_bias"]
    norm_weight = params["norm_weight"]
    norm_bias = params["norm_bias"]

    x = F.conv3d(x, conv_weight, conv_bias)
    x = F.group_norm(x, groups, norm_weight, norm_bias)
    x = torch.min(x, torch.tensor(min_value))
    x = torch.clamp(x, min=min_value, max=max_value)
    if training:
        x = F.dropout(x, p=dropout_p)
    return x

class Model(nn.Module):
    """
    Model that performs a 3D convolution, applies Group Normalization, minimum, clamp, and dropout.
    """
    def __init__(self, in_channels, out_channels, kernel_size, groups, min_value, max_value, dropout_p):
        super(Model, self).__init__()
        self.conv_weight = nn.Parameter(torch.empty(out_channels, in_channels, kernel_size, kernel_size, kernel_size))
        self.conv_bias = nn.Parameter(torch.empty(out_channels))
        self.norm_weight = nn.Parameter(torch.empty(out_channels))
        self.norm_bias = nn.Parameter(torch.empty(out_channels))
        self.groups = groups
        self.min_value = min_value
        self.max_value = max_value
        self.dropout_p = dropout_p

        # Initialize parameters
        nn.init.kaiming_uniform_(self.conv_weight, a=math.sqrt(5))
        fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.conv_weight)
        bound = 1 / math.sqrt(fan_in)
        nn.init.uniform_(self.conv_bias, -bound, bound)
        nn.init.ones_(self.norm_weight)
        nn.init.zeros_(self.norm_bias)

        self.params = nn.ParameterDict()
        self.params["conv_weight"] = self.conv_weight
        self.params["conv_bias"] = self.conv_bias
        self.params["norm_weight"] = self.norm_weight
        self.params["norm_bias"] = self.norm_bias

    def forward(self, x, fn=module_fn):
        return fn(x, self.params, self.groups, self.min_value, self.max_value, self.dropout_p, False)

batch_size = 128
in_channels = 3
out_channels = 16
depth, height, width = 16, 32, 32
kernel_size = 3
groups = 8
min_value = 0.0
max_value = 1.0
dropout_p = 0.2

def get_inputs():
    return [torch.randn(batch_size, in_channels, depth, height, width)]

def get_init_inputs():
    return [in_channels, out_channels, kernel_size, groups, min_value, max_value, dropout_p]