text: """
  Copyright (c) Facebook, Inc. and its affiliates.
  
  This source code is licensed under the MIT license found in the
  LICENSE file in the root directory of this source tree.
  """
  
  import numpy as np
  import torch
  
  
  def to_tensor(data):
      """
      Convert numpy array to PyTorch tensor.
      
      For complex arrays, the real and imaginary parts are stacked along the last
      dimension.
  
      Args:
          data (np.array): Input numpy array.
  
      Returns:
          torch.Tensor: PyTorch version of data.
      """
      if np.iscomplexobj(data):
          data = np.stack((data.real, data.imag), axis=-1)
  
      return torch.from_numpy(data)
  
  
  def tensor_to_complex_np(data):
      """
      Converts a complex torch tensor to numpy array.
  
      Args:
          data (torch.Tensor): Input data to be converted to numpy.
  
      Returns:
          np.array: Complex numpy version of data.
      """
      data = data.numpy()
  
      return data[..., 0] + 1j * data[..., 1]
  
  
  def apply_mask(data, mask_func, seed=None, padding=None):
      """
      Subsample given k-space by multiplying with a mask.
  
      Args:
          data (torch.Tensor): The input k-space data. This should have at least 3 dimensions, where
              dimensions -3 and -2 are the spatial dimensions, and the final dimension has size
              2 (for complex values).
          mask_func (callable): A function that takes a shape (tuple of ints) and a random
              number seed and returns a mask.
          seed (int or 1-d array_like, optional): Seed for the random number generator.
  
      Returns:
          (tuple): tuple containing:
              masked data (torch.Tensor): Subsampled k-space data
              mask (torch.Tensor): The generated mask
      """
      shape = np.array(data.shape)
      shape[:-3] = 1
      mask = mask_func(shape, seed)
      if padding is not None:
          mask[:, :, : padding[0]] = 0
          mask[:, :, padding[1] :] = 0  # padding value inclusive on right of zeros
  
      masked_data = data * mask + 0.0  # the + 0.0 removes the sign of the zeros
  
      return masked_data, mask
  
  
  def mask_center(x, mask_from, mask_to):
      mask = torch.zeros_like(x)
      mask[:, :, :, mask_from:mask_to] = x[:, :, :, mask_from:mask_to]
  
      return mask
  
  
  def center_crop(data, shape):
      """
      Apply a center crop to the input real image or batch of real images.
  
      Args:
          data (torch.Tensor): The input tensor to be center cropped. It should
              have at least 2 dimensions and the cropping is applied along the
              last two dimensions.
          shape (int, int): The output shape. The shape should be smaller than
              the corresponding dimensions of data.
  
      Returns:
          torch.Tensor: The center cropped image.
      """
      assert 0 < shape[0] <= data.shape[-2]
      assert 0 < shape[1] <= data.shape[-1]
  
      w_from = (data.shape[-2] - shape[0]) // 2
      h_from = (data.shape[-1] - shape[1]) // 2
      w_to = w_from + shape[0]
      h_to = h_from + shape[1]
  
      return data[..., w_from:w_to, h_from:h_to]
  
  
  def complex_center_crop(data, shape):
      """
      Apply a center crop to the input image or batch of complex images.
  
      Args:
          data (torch.Tensor): The complex input tensor to be center cropped. It
              should have at least 3 dimensions and the cropping is applied along
              dimensions -3 and -2 and the last dimensions should have a size of
              2.
          shape (int): The output shape. The shape should be smaller than
              the corresponding dimensions of data.
  
      Returns:
          torch.Tensor: The center cropped image
      """
      assert 0 < shape[0] <= data.shape[-3]
      assert 0 < shape[1] <= data.shape[-2]
  
      w_from = (data.shape[-3] - shape[0]) // 2
      h_from = (data.shape[-2] - shape[1]) // 2
      w_to = w_from + shape[0]
      h_to = h_from + shape[1]
  
      return data[..., w_from:w_to, h_from:h_to, :]
  
  
  def center_crop_to_smallest(x, y):
      """
      Apply a center crop on the larger image to the size of the smaller.
  
      The minimum is taken over dim=-1 and dim=-2. If x is smaller than y at
      dim=-1 and y is smaller than x at dim=-2, then the returned dimension will
      be a mixture of the two.
      
      Args:
          x (torch.Tensor): The first image.
          y (torch.Tensor): The second image
  
      Returns:
          tuple: tuple of tensors x and y, each cropped to the minimim size.
      """
      smallest_width = min(x.shape[-1], y.shape[-1])
      smallest_height = min(x.shape[-2], y.shape[-2])
      x = center_crop(x, (smallest_height, smallest_width))
      y = center_crop(y, (smallest_height, smallest_width))
  
      return x, y
  
  
  def normalize(data, mean, stddev, eps=0.0):
      """
      Normalize the given tensor.
  
      Applies the formula (data - mean) / (stddev + eps).
  
      Args:
          data (torch.Tensor): Input data to be normalized.
          mean (float): Mean value.
          stddev (float): Standard deviation.
          eps (float, default=0.0): Added to stddev to prevent dividing by zero.
  
      Returns:
          torch.Tensor: Normalized tensor
      """
      return (data - mean) / (stddev + eps)
  
  
  def normalize_instance(data, eps=0.0):
      """
      Normalize the given tensor  with instance norm/
  
      Applies the formula (data - mean) / (stddev + eps), where mean and stddev
      are computed from the data itself.
  
      Args:
          data (torch.Tensor): Input data to be normalized
          eps (float): Added to stddev to prevent dividing by zero
  
      Returns:
          torch.Tensor: Normalized tensor
      """
      mean = data.mean()
      std = data.std()
  
      return normalize(data, mean, std, eps), mean, std
  