Untitled

mail@pastecode.io avatar
unknown
python
23 days ago
7.3 kB
3
Indexable
Never
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F


################################################################
# fourier layer
################################################################

class SpectralConv2d_fast(nn.Module):
    def __init__(self, in_channels, out_channels, modes1, modes2):
        super(SpectralConv2d_fast, self).__init__()

        """
        2D Fourier layer. It does FFT, linear transform, and Inverse FFT.    
        """

        self.in_channels = in_channels
        self.out_channels = out_channels
        self.modes1 = modes1 #Number of Fourier modes to multiply, at most floor(N/2) + 1
        self.modes2 = modes2

        self.scale = (1 / (in_channels * out_channels))
        self.weights1 = nn.Parameter(self.scale * torch.rand(1, out_channels, self.modes1, self.modes2, dtype=torch.cfloat))
        self.weights2 = nn.Parameter(self.scale * torch.rand(1, out_channels, self.modes1, self.modes2, dtype=torch.cfloat))

    # Complex multiplication
    def compl_mul2d(self, input, weights):
        # (batch, in_channel, x,y ), (in_channel, out_channel, x,y) -> (batch, out_channel, x,y)
        return torch.einsum("bixy,ioxy->boxy", input, weights)

    def forward(self, x):
        batchsize = x.shape[0]
        #Compute Fourier coeffcients up to factor of e^(- something constant)
        x_ft = torch.fft.rfft2(x)

        # Multiply relevant Fourier modes
        out_ft = torch.zeros(batchsize, self.out_channels,  x.size(-2), x.size(-1)//2 + 1, dtype=torch.cfloat, device=x.device)
        out_ft[:, :, :self.modes1, :self.modes2] = \
            self.compl_mul2d(x_ft[:, :, :self.modes1, :self.modes2], self.weights1)
        out_ft[:, :, -self.modes1:, :self.modes2] = \
            self.compl_mul2d(x_ft[:, :, -self.modes1:, :self.modes2], self.weights2)

        # out_ft = self.ca(out_ft.abs()) * out_ft
        # out_ft = self.sa(out_ft.abs()) * out_ft

        #Return to physical space
        x = torch.fft.irfft2(out_ft, s=(x.size(-2), x.size(-1)))
        return x

class FNO2d(nn.Module):
    def __init__(self, modes, width, in_dim, out_dim=2):
        super(FNO2d, self).__init__()
        
        """
        The overall network. It contains 4 layers of the Fourier layer.
        1. Lift the input to the desire channel dimension by self.fc0 .
        2. 4 layers of the integral operators u' = (W + K)(u).
            W defined by self.w; K defined by self.conv .
        3. Project from the channel space to the output space by self.fc1 and self.fc2 .
        
        input: the solution of the previous 10 timesteps + 2 locations (u(t-10, x, y), ..., u(t-1, x, y),  x, y)
        input shape: (batchsize, x=64, y=64, c=12)
        output: the solution of the next timestep
        output shape: (batchsize, x=64, y=64, c=1)
        """

        self.modes = modes
        self.width = width
        self.padding = 2 # pad the domain if input is non-periodic
        self.conv_begin_0 = nn.Conv2d(in_dim+2, self.width, 1)  # GoPro dataset: RGB 3 channels
        self.conv_begin_1 = nn.Conv2d(self.width, self.width, 1)
        # input channel is 12: the solution of the previous 10 timesteps + 2 locations (u(t-10, x, y), ..., u(t-1, x, y),  x, y)
        self.conv_end1 = nn.Conv2d(self.width, self.width, 1)
        self.conv_end2 = nn.Conv2d(self.width, 2, 1)
        self.prelu_begin = nn.PReLU(self.width)
        self.prelu_end = nn.PReLU(self.width)
        self.lsc = nn.Conv2d(self.width, self.width, 1)

        self.scales_per_block = [1,1,2,2,2,4,4,4]
        self.share_block = [True,True,True,True,True,False,False,False]
        self.num_per_block = [2,2,2,2,2,2,2,2]
        assert len(self.scales_per_block) == len(self.share_block) and len(self.scales_per_block) == len(self.num_per_block)

        self.SConv2d_list = []
        self.w_list = []
        self.prelu_list = []
        self.ssc_list = []
        for i in range(len(self.scales_per_block)):
            print("building scales", i)
            if self.share_block[i]:
                print("\tshared params", end=' ')
                print(self.scales_per_block[i])
                self.SConv2d_list.append(SpectralConv2d_fast(self.width, self.width, self.modes//self.scales_per_block[i], self.modes//self.scales_per_block[i]))
                self.w_list.append(nn.Conv2d(self.width, self.width, 1))
                self.prelu_list.append(nn.PReLU(self.width))
            else:
                print("\tnot shared params", end=' ')
                for _ in range(self.num_per_block[i]):
                    print(self.scales_per_block[i], end=' ')
                    self.SConv2d_list.append(SpectralConv2d_fast(self.width, self.width, self.modes//self.scales_per_block[i], self.modes//self.scales_per_block[i]))
                    self.w_list.append(nn.Conv2d(self.width, self.width, 1))
                    self.prelu_list.append(nn.PReLU(self.width))
                print()
            self.ssc_list.append(nn.Conv2d(self.width, self.width, 1))
        self.SConv2d_list = nn.ModuleList(self.SConv2d_list)
        self.w_list = nn.ModuleList(self.w_list)
        self.prelu_list = nn.ModuleList(self.prelu_list)
        self.ssc_list = nn.ModuleList(self.ssc_list)
        

    def forward(self, x):
        grid = self.get_grid(x.shape, x.device)
        x = torch.cat((x, grid), dim=1)
        # x = x.permute(0, 3, 1, 2)
        x = self.conv_begin_0(x)
        x = self.prelu_begin(x)
        x = self.conv_begin_1(x)
        x_0 = x
        x_s = x_0
        x_t = x_0
        

        pointer = 0
        for i in range(len(self.scales_per_block)):
            # print(i)
            if self.share_block[i]:
                for _ in range(self.num_per_block[i]):
                    # print("\t",pointer)
                    x = self.SConv2d_list[pointer](x) + self.w_list[pointer](x)
                    x = self.prelu_list[pointer](x)
                    x = x + x_t
                    x_t = x
                pointer += 1
            else:
                for _ in range(self.num_per_block[i]):
                    # print("\t",pointer)
                    x = self.SConv2d_list[pointer](x) + self.w_list[pointer](x)
                    x = self.prelu_list[pointer](x)
                    x = x + x_t
                    x_t = x
                    pointer += 1
            x = self.ssc_list[i](x)
            x = x + x_s
            x_s = x
            x_t = x_s


        x = self.lsc(x)
        x = x + x_0

        x = self.conv_end1(x)
        x = self.prelu_end(x)
        x = self.conv_end2(x)
        # x = x.permute(0, 2, 3, 1)
        return x

    def get_grid(self, shape, device):
        # return [N, C, H, W] grids
        n, c, size_x, size_y = shape
        gridx = torch.tensor(np.linspace(0, 1, size_x), dtype=torch.float)
        gridx = gridx.reshape(1, 1, size_x, 1).repeat([n, 1, 1, size_y])
        gridy = torch.tensor(np.linspace(0, 1, size_y), dtype=torch.float)
        gridy = gridy.reshape(1, 1, 1, size_y).repeat([n, 1, size_x, 1])
        return torch.cat((gridx, gridy), dim=1).to(device)
Leave a Comment