docstr: Create 2 pointclouds object and associated padded points/normals tensors by
  starting from lists. The clouds and tensors have the same data. The
  leaf nodes for the clouds are a list of tensors. The padded tensor can be
  used directly as a leaf node.
docstr_tok: ["Create","2","pointclouds","object","and","associated","padded","points\/normals","tensors","by","starting","from","lists",".","The","clouds","and","tensors","have","the","same","data",".","The","leaf","nodes","for","the","clouds","are","a","list","of","tensors",".","The","padded","tensor","can","be","used","directly","as","a","leaf","node","."]
code: def init_pointclouds(
      N,
      P1,
      P2,
      device,
      requires_grad: bool = True,
      allow_empty: bool = True,
  ):
      """
      Create 2 pointclouds object and associated padded points/normals tensors by
      starting from lists. The clouds and tensors have the same data. The
      leaf nodes for the clouds are a list of tensors. The padded tensor can be
      used directly as a leaf node.
      """
      low = 0 if allow_empty else 1
      p1_lengths = torch.randint(
          low, P1, size=(N,), dtype=torch.int64, device=device
      )
      p2_lengths = torch.randint(
          low, P2, size=(N,), dtype=torch.int64, device=device
      )
      P1 = p1_lengths.max().item()
      P2 = p2_lengths.max().item()
      weights = torch.rand((N,), dtype=torch.float32, device=device)
  
      # list of points and normals tensors
      p1 = torch.rand((N, P1, 3), dtype=torch.float32, device=device)
      p2 = torch.rand((N, P2, 3), dtype=torch.float32, device=device)
      n1 = torch.rand((N, P1, 3), dtype=torch.float32, device=device)
      n2 = torch.rand((N, P2, 3), dtype=torch.float32, device=device)
      n1 /= n1.norm(dim=-1, p=2, keepdim=True)
      n2 /= n2.norm(dim=-1, p=2, keepdim=True)
  
      p1_list = []
      p2_list = []
      n1_list = []
      n2_list = []
      for i in range(N):
          l1 = p1_lengths[i]
          l2 = p2_lengths[i]
          p1_list.append(p1[i, :l1].clone())
          p2_list.append(p2[i, :l2].clone())
          n1_list.append(n1[i, :l1].clone())
          n2_list.append(n2[i, :l2].clone())
  
      # Set requires_grad for all tensors in the lists and
      # padded tensors.
      if requires_grad:
          for p in (
              p2_list + p1_list + n1_list + n2_list + [p1, p2, n1, n2]
          ):
              p.requires_grad = True
  
      # Create pointclouds objects
      cloud1 = Pointclouds(points=p1_list, normals=n1_list)
      cloud2 = Pointclouds(points=p2_list, normals=n2_list)
  
      # Return pointclouds objects and padded tensors
      return points_normals(
          p1_lengths=p1_lengths,
          p2_lengths=p2_lengths,
          cloud1=cloud1,
          cloud2=cloud2,
          p1=p1,
          p2=p2,
          n1=n1,
          n2=n2,
          weights=weights,
      )
  
imports: import unittest
  from collections import namedtuple
  import numpy
  import torch
  import torch.nn.functional
  from pytorch3d.loss import chamfer_distance
  from pytorch3d.structures.pointclouds import Pointclouds
  from .common_testing import get_random_cuda_device, TestCaseMixin
label_desc: Use image node_id 2 for calling the TestChamfer obj's underlying member method code with example usage: obj.init_pointclouds(N, P1, P2, device, requires_grad, allow_empty) and returns: points_normals