import torch
from torch import nn
from torch.nn import functional as F


class InvConv2d(nn.Module):

    def __init__(self, in_channel):
        """
        a flow contains the equivalent of a permutation that reverses the ordering of the channels
        replact the fixed permutation with a (learned) invertible 1x1 conv, weigth matrix is initialized as a random rotation matrix
        Note: 1x1 conv with equal number of input and output channels --> generalization of a permutation operation
        :param in_channel:
        """
        super(InvConv2d, self).__init__()
        weight = torch.randn(in_channel, in_channel)
        q, _ = torch.qr(weight)
        weight = q.unsqueeze(2).unsqueeze(3)
        self.weight = nn.Parameter(weight)

    def forward(self, input):
        _, _, height, width = input.shape
        out = F.conv2d(input, self.weight)
        logdet = height * width * torch.slogdet(self.weight.squeeze().double()
            )[1].float()
        return out, logdet

    def reverse(self, output):
        return F.conv2d(output, self.weight.squeeze().inverse().unsqueeze(2
            ).unsqueeze(3))


def get_inputs():
    return [torch.rand([4, 4, 4, 4])]


def get_init_inputs():
    return [[], {'in_channel': 4}]

import torch
from torch._inductor.select_algorithm import extern_kernels
import triton
import triton.language as tl
from torch._inductor.runtime.triton_heuristics import grid
from torch._C import _cuda_getCurrentRawStream as get_raw_stream
from torch import nn
from torch.nn import functional as F
assert_size_stride = torch._C._dynamo.guards.assert_size_stride
empty_strided_cuda = torch._C._dynamo.guards._empty_strided_cuda


@triton.jit
def triton_poi_fused_convolution_0(in_ptr0, out_ptr0, ynumel, xnumel,
    YBLOCK: tl.constexpr, XBLOCK: tl.constexpr):
    ynumel = 4
    xnumel = 4
    yoffset = tl.program_id(1) * YBLOCK
    yindex = yoffset + tl.arange(0, YBLOCK)[None, :]
    ymask = yindex < ynumel
    xoffset = tl.program_id(0) * XBLOCK
    xindex = xoffset + tl.arange(0, XBLOCK)[:, None]
    xmask = xindex < xnumel
    x1 = xindex
    y0 = yindex
    tmp0 = tl.load(in_ptr0 + (y0 + 4 * x1), xmask & ymask)
    tl.store(out_ptr0 + (x1 + 4 * y0), tmp0, xmask & ymask)


@triton.jit
def triton_poi_fused__to_copy_1(in_ptr0, out_ptr0, xnumel, XBLOCK: tl.constexpr
    ):
    xnumel = 16
    xoffset = tl.program_id(0) * XBLOCK
    xindex = xoffset + tl.arange(0, XBLOCK)[:]
    xmask = xindex < xnumel
    x0 = xindex
    tmp0 = tl.load(in_ptr0 + x0, xmask)
    tmp1 = tmp0.to(tl.float64)
    tl.store(out_ptr0 + x0, tmp1, xmask)


@triton.jit
def triton_poi_fused__to_copy_mul_2(in_ptr0, out_ptr0, xnumel, XBLOCK: tl.
    constexpr):
    xoffset = tl.program_id(0) * XBLOCK
    xoffset + tl.arange(0, XBLOCK)[:]
    tl.full([XBLOCK], True, tl.int1)
    tmp0 = tl.load(in_ptr0 + 0)
    tmp1 = tl.broadcast_to(tmp0, [XBLOCK])
    tmp2 = tmp1.to(tl.float32)
    tmp3 = 16.0
    tmp4 = tmp2 * tmp3
    tl.store(out_ptr0 + tl.full([XBLOCK], 0, tl.int32), tmp4, None)


def call(args):
    primals_1, primals_2 = args
    args.clear()
    assert_size_stride(primals_1, (4, 4, 4, 4), (64, 16, 4, 1))
    assert_size_stride(primals_2, (4, 4, 1, 1), (1, 4, 1, 1))
    with torch.cuda._DeviceGuard(0):
        torch.cuda.set_device(0)
        buf0 = empty_strided_cuda((4, 4, 1, 1), (4, 1, 1, 1), torch.float32)
        get_raw_stream(0)
        triton_poi_fused_convolution_0[grid(4, 4)](primals_2, buf0, 4, 4,
            XBLOCK=4, YBLOCK=4, num_warps=1, num_stages=1)
        buf1 = extern_kernels.convolution(primals_1, buf0, stride=(1, 1),
            padding=(0, 0), dilation=(1, 1), transposed=False,
            output_padding=(0, 0), groups=1, bias=None)
        assert_size_stride(buf1, (4, 4, 4, 4), (64, 16, 4, 1))
        del buf0
        buf2 = empty_strided_cuda((4, 4), (1, 4), torch.float64)
        triton_poi_fused__to_copy_1[grid(16)](primals_2, buf2, 16, XBLOCK=
            16, num_warps=1, num_stages=1)
        buf3 = torch.ops.aten._linalg_slogdet.default(buf2)
        del buf2
        buf5 = buf3[1]
        buf6 = buf3[2]
        buf7 = buf3[3]
        del buf3
        buf8 = empty_strided_cuda((), (), torch.float32)
        triton_poi_fused__to_copy_mul_2[grid(1)](buf5, buf8, 1, XBLOCK=1,
            num_warps=1, num_stages=1)
        del buf5
    return buf1, buf8, primals_1, primals_2, buf6, buf7


class InvConv2dNew(nn.Module):

    def __init__(self, in_channel):
        """
        a flow contains the equivalent of a permutation that reverses the ordering of the channels
        replact the fixed permutation with a (learned) invertible 1x1 conv, weigth matrix is initialized as a random rotation matrix
        Note: 1x1 conv with equal number of input and output channels --> generalization of a permutation operation
        :param in_channel:
        """
        super(InvConv2dNew, self).__init__()
        weight = torch.randn(in_channel, in_channel)
        q, _ = torch.qr(weight)
        weight = q.unsqueeze(2).unsqueeze(3)
        self.weight = nn.Parameter(weight)

    def reverse(self, output):
        return F.conv2d(output, self.weight.squeeze().inverse().unsqueeze(2
            ).unsqueeze(3))

    def forward(self, input_0):
        primals_2 = self.weight
        primals_1 = input_0
        output = call([primals_1, primals_2])
        return output[0], output[1]
