class_docstr: A Transform3d object encapsulates a batch of N 3D transformations, and knows
  how to transform points and normal vectors. Suppose that t is a Transform3d;
  then we can do the following:
  
  .. code-block:: python
  
      N = len(t)
      points = torch.randn(N, P, 3)
      normals = torch.randn(N, P, 3)
      points_transformed = t.transform_points(points)    # => (N, P, 3)
      normals_transformed = t.transform_normals(normals)  # => (N, P, 3)
  
  
  BROADCASTING
  Transform3d objects supports broadcasting. Suppose that t1 and tN are
  Transform3d objects with len(t1) == 1 and len(tN) == N respectively. Then we
  can broadcast transforms like this:
  
  .. code-block:: python
  
      t1.transform_points(torch.randn(P, 3))     # => (P, 3)
      t1.transform_points(torch.randn(1, P, 3))  # => (1, P, 3)
      t1.transform_points(torch.randn(M, P, 3))  # => (M, P, 3)
      tN.transform_points(torch.randn(P, 3))     # => (N, P, 3)
      tN.transform_points(torch.randn(1, P, 3))  # => (N, P, 3)
  
  
  COMBINING TRANSFORMS
  Transform3d objects can be combined in two ways: composing and stacking.
  Composing is function composition. Given Transform3d objects t1, t2, t3,
  the following all compute the same thing:
  
  .. code-block:: python
  
      y1 = t3.transform_points(t2.transform_points(t1.transform_points(x)))
      y2 = t1.compose(t2).compose(t3).transform_points(x)
      y3 = t1.compose(t2, t3).transform_points(x)
  
  
  Composing transforms should broadcast.
  
  .. code-block:: python
  
      if len(t1) == 1 and len(t2) == N, then len(t1.compose(t2)) == N.
  
  We can also stack a sequence of Transform3d objects, which represents
  composition along the batch dimension; then the following should compute the
  same thing.
  
  .. code-block:: python
  
      N, M = len(tN), len(tM)
      xN = torch.randn(N, P, 3)
      xM = torch.randn(M, P, 3)
      y1 = torch.cat([tN.transform_points(xN), tM.transform_points(xM)], dim=0)
      y2 = tN.stack(tM).transform_points(torch.cat([xN, xM], dim=0))
  
  BUILDING TRANSFORMS
  We provide convenience methods for easily building Transform3d objects
  as compositions of basic transforms.
  
  .. code-block:: python
  
      # Scale by 0.5, then translate by (1, 2, 3)
      t1 = Transform3d().scale(0.5).translate(1, 2, 3)
  
      # Scale each axis by a different amount, then translate, then scale
      t2 = Transform3d().scale(1, 3, 3).translate(2, 3, 1).scale(2.0)
  
      t3 = t1.compose(t2)
      tN = t1.stack(t3, t3)
  
  
  BACKPROP THROUGH TRANSFORMS
  When building transforms, we can also parameterize them by Torch tensors;
  in this case we can backprop through the construction and application of
  Transform objects, so they could be learned via gradient descent or
  predicted by a neural network.
  
  .. code-block:: python
  
      s1_params = torch.randn(N, requires_grad=True)
      t_params = torch.randn(N, 3, requires_grad=True)
      s2_params = torch.randn(N, 3, requires_grad=True)
  
      t = Transform3d().scale(s1_params).translate(t_params).scale(s2_params)
      x = torch.randn(N, 3)
      y = t.transform_points(x)
      loss = compute_loss(y)
      loss.backward()
  
      with torch.no_grad():
          s1_params -= lr * s1_params.grad
          t_params -= lr * t_params.grad
          s2_params -= lr * s2_params.grad
  
  CONVENTIONS
  We adopt a right-hand coordinate system, meaning that rotation about an axis
  with a positive angle results in a counter clockwise rotation.
  
  This class assumes that transformations are applied on inputs which
  are row vectors. The internal representation of the Nx4x4 transformation
  matrix is of the form:
  
  .. code-block:: python
  
      M = [
              [Rxx, Ryx, Rzx, 0],
              [Rxy, Ryy, Rzy, 0],
              [Rxz, Ryz, Rzz, 0],
              [Tx,  Ty,  Tz,  1],
          ]
  
  To apply the transformation to points, which are row vectors, the latter are
  converted to homogeneous (4D) coordinates and right-multiplied by the M matrix:
  
  .. code-block:: python
  
      points = [[0, 1, 2]]  # (1 x 3) xyz coordinates of a point
      [transformed_points, 1] ∝ [points, 1] @ M
class_docstr_tok: ["A","Transform3d","object","encapsulates","a","batch","of","N","3D","transformations",",","and","knows","how","to","transform","points","and","normal","vectors",".","Suppose","that","t","is","a","Transform3d",";","then","we","can","do","the","following",":","..","code-block",":",":","python","N","=","len","(","t",")","points","=","torch.randn","(","N",",","P",",","3",")","normals","=","torch.randn","(","N",",","P",",","3",")","points_transformed","=","t.transform_points","(","points",")","#","=",">","(","N",",","P",",","3",")","normals_transformed","=","t.transform_normals","(","normals",")","#","=",">","(","N",",","P",",","3",")","BROADCASTING","Transform3d","objects","supports","broadcasting",".","Suppose","that","t1","and","tN","are","Transform3d","objects","with","len","(","t1",")","==","1","and","len","(","tN",")","==","N","respectively",".","Then","we","can","broadcast","transforms","like","this",":","..","code-block",":",":","python","t1.transform_points","(","torch.randn","(","P",",","3",")",")","#","=",">","(","P",",","3",")","t1.transform_points","(","torch.randn","(","1",",","P",",","3",")",")","#","=",">","(","1",",","P",",","3",")","t1.transform_points","(","torch.randn","(","M",",","P",",","3",")",")","#","=",">","(","M",",","P",",","3",")","tN.transform_points","(","torch.randn","(","P",",","3",")",")","#","=",">","(","N",",","P",",","3",")","tN.transform_points","(","torch.randn","(","1",",","P",",","3",")",")","#","=",">","(","N",",","P",",","3",")","COMBINING","TRANSFORMS","Transform3d","objects","can","be","combined","in","two","ways",":","composing","and","stacking",".","Composing","is","function","composition",".","Given","Transform3d","objects","t1",",","t2",",","t3",",","the","following","all","compute","the","same","thing",":","..","code-block",":",":","python","y1","=","t3.transform_points","(","t2.transform_points","(","t1.transform_points","(","x",")",")",")","y2","=","t1.compose","(","t2",")",".compose","(","t3",")",".transform_points","(","x",")","y3","=","t1.compose","(","t2",",","t3",")",".transform_points","(","x",")","Composing","transforms","should","broadcast",".","..","code-block",":",":","python","if","len","(","t1",")","==","1","and","len","(","t2",")","==","N",",","then","len","(","t1.compose","(","t2",")",")","==","N.","We","can","also","stack","a","sequence","of","Transform3d","objects",",","which","represents","composition","along","the","batch","dimension",";","then","the","following","should","compute","the","same","thing",".","..","code-block",":",":","python","N",",","M","=","len","(","tN",")",",","len","(","tM",")","xN","=","torch.randn","(","N",",","P",",","3",")","xM","=","torch.randn","(","M",",","P",",","3",")","y1","=","torch.cat","(","[","tN.transform_points","(","xN",")",",","tM.transform_points","(","xM",")","]",",","dim=0",")","y2","=","tN.stack","(","tM",")",".transform_points","(","torch.cat","(","[","xN",",","xM","]",",","dim=0",")",")","BUILDING","TRANSFORMS","We","provide","convenience","methods","for","easily","building","Transform3d","objects","as","compositions","of","basic","transforms",".","..","code-block",":",":","python","#","Scale","by","0.5",",","then","translate","by","(","1",",","2",",","3",")","t1","=","Transform3d","(",")",".scale","(","0.5",")",".translate","(","1",",","2",",","3",")","#","Scale","each","axis","by","a","different","amount",",","then","translate",",","then","scale","t2","=","Transform3d","(",")",".scale","(","1",",","3",",","3",")",".translate","(","2",",","3",",","1",")",".scale","(","2.0",")","t3","=","t1.compose","(","t2",")","tN","=","t1.stack","(","t3",",","t3",")","BACKPROP","THROUGH","TRANSFORMS","When","building","transforms",",","we","can","also","parameterize","them","by","Torch","tensors",";","in","this","case","we","can","backprop","through","the","construction","and","application","of","Transform","objects",",","so","they","could","be","learned","via","gradient","descent","or","predicted","by","a","neural","network",".","..","code-block",":",":","python","s1_params","=","torch.randn","(","N",",","requires_grad=True",")","t_params","=","torch.randn","(","N",",","3",",","requires_grad=True",")","s2_params","=","torch.randn","(","N",",","3",",","requires_grad=True",")","t","=","Transform3d","(",")",".scale","(","s1_params",")",".translate","(","t_params",")",".scale","(","s2_params",")","x","=","torch.randn","(","N",",","3",")","y","=","t.transform_points","(","x",")","loss","=","compute_loss","(","y",")","loss.backward","(",")","with","torch.no_grad","(",")",":","s1_params","-=","lr","*","s1_params.grad","t_params","-=","lr","*","t_params.grad","s2_params","-=","lr","*","s2_params.grad","CONVENTIONS","We","adopt","a","right-hand","coordinate","system",",","meaning","that","rotation","about","an","axis","with","a","positive","angle","results","in","a","counter","clockwise","rotation",".","This","class","assumes","that","transformations","are","applied","on","inputs","which","are","row","vectors",".","The","internal","representation","of","the","Nx4x4","transformation","matrix","is","of","the","form",":","..","code-block",":",":","python","M","=","[","[","Rxx",",","Ryx",",","Rzx",",","0","]",",","[","Rxy",",","Ryy",",","Rzy",",","0","]",",","[","Rxz",",","Ryz",",","Rzz",",","0","]",",","[","Tx",",","Ty",",","Tz",",","1","]",",","]","To","apply","the","transformation","to","points",",","which","are","row","vectors",",","the","latter","are","converted","to","homogeneous","(","4D",")","coordinates","and","right-multiplied","by","the","M","matrix",":","..","code-block",":",":","python","points","=","[","[","0",",","1",",","2","]","]","#","(","1","x","3",")","xyz","coordinates","of","a","point","[","transformed_points",",","1","]","\u221d","[","points",",","1","]","@","M"]
docstr: Returns a new Transform3d object that represents an inverse of the
  current transformation.
  
  Args:
      invert_composed:
          - True: First compose the list of stored transformations
            and then apply inverse to the result. This is
            potentially slower for classes of transformations
            with inverses that can be computed efficiently
            (e.g. rotations and translations).
          - False: Invert the individual stored transformations
            independently without composing them.
  
  Returns:
      A new Transform3d object containing the inverse of the original
      transformation.
docstr_tok: ["Returns","a","new","Transform3d","object","that","represents","an","inverse","of","the","current","transformation",".","Args",":","invert_composed",":","-","True",":","First","compose","the","list","of","stored","transformations","and","then","apply","inverse","to","the","result",".","This","is","potentially","slower","for","classes","of","transformations","with","inverses","that","can","be","computed","efficiently","(","e.g",".","rotations","and","translations",")",".","-","False",":","Invert","the","individual","stored","transformations","independently","without","composing","them",".","Returns",":","A","new","Transform3d","object","containing","the","inverse","of","the","original","transformation","."]
code: def inverse(self, invert_composed: bool = False) -> "Transform3d":
      """
      Returns a new Transform3d object that represents an inverse of the
      current transformation.
  
      Args:
          invert_composed:
              - True: First compose the list of stored transformations
                and then apply inverse to the result. This is
                potentially slower for classes of transformations
                with inverses that can be computed efficiently
                (e.g. rotations and translations).
              - False: Invert the individual stored transformations
                independently without composing them.
  
      Returns:
          A new Transform3d object containing the inverse of the original
          transformation.
      """
  
      tinv = Transform3d(dtype=self.dtype, device=self.device)
  
      if invert_composed:
          # first compose then invert
          tinv._matrix = torch.inverse(self.get_matrix())
      else:
          # self._get_matrix_inverse() implements efficient inverse
          # of self._matrix
          i_matrix = self._get_matrix_inverse()
  
          # 2 cases:
          if len(self._transforms) > 0:
              # a) Either we have a non-empty list of transforms:
              # Here we take self._matrix and append its inverse at the
              # end of the reverted _transforms list. After composing
              # the transformations with get_matrix(), this correctly
              # right-multiplies by the inverse of self._matrix
              # at the end of the composition.
              tinv._transforms = [
                  t.inverse() for t in reversed(self._transforms)
              ]
              last = Transform3d(dtype=self.dtype, device=self.device)
              last._matrix = i_matrix
              tinv._transforms.append(last)
          else:
              # b) Or there are no stored transformations
              # we just set inverted matrix
              tinv._matrix = i_matrix
  
      return tinv
  
imports: import math
  import os
  import warnings
  from typing import List, Optional, Union
  import torch
  from ..common.datatypes import Device, get_device, make_device
  from ..common.workaround import _safe_det_3x3
  from .rotation_conversions import _axis_angle_rotation
  from .se3 import se3_log_map
label_desc: Use image node_id 8 for calling the Transform3d obj's underlying member method code with example usage: obj.inverse(invert_composed) and returns: tinv