docstr: Set the affine transform (operating in physical space).
  
  Also sets `self.affine_inv` - the inverse of `affine`, or None if
  there is no inverse.
  
  Parameters
  ----------
  affine : array, shape (dim + 1, dim + 1)
      the matrix representing the affine transform operating in
      physical space. The domain and co-domain information
      remains unchanged. If None, then `self` represents the identity
      transformation.
docstr_tok: ["Set","the","affine","transform","(","operating","in","physical","space",")",".","Also","sets","`","self.affine_inv","`","-","the","inverse","of","`","affine","`",",","or","None","if","there","is","no","inverse",".","Parameters","--","--","--","--","--","affine",":","array",",","shape","(","dim","+","1",",","dim","+","1",")","the","matrix","representing","the","affine","transform","operating","in","physical","space",".","The","domain","and","co-domain","information","remains","unchanged",".","If","None",",","then","`","self","`","represents","the","identity","transformation","."]
code: def set_affine(self, affine):
      """Set the affine transform (operating in physical space).
  
      Also sets `self.affine_inv` - the inverse of `affine`, or None if
      there is no inverse.
  
      Parameters
      ----------
      affine : array, shape (dim + 1, dim + 1)
          the matrix representing the affine transform operating in
          physical space. The domain and co-domain information
          remains unchanged. If None, then `self` represents the identity
          transformation.
  
      """
  
      if affine is None:
          self.affine = None
          self.affine_inv = None
          return
  
      try:
          affine = np.array(affine)
      except Exception:
          raise TypeError(
              "Input must be type ndarray, or be convertible" " to one."
          )
  
      if len(affine.shape) != _number_dim_affine_matrix:
          raise AffineInversionError("Affine transform must be 2D")
  
      if not affine.shape[0] == affine.shape[1]:
          raise AffineInversionError(
              "Affine transform must be a square " "matrix"
          )
  
      if not np.all(np.isfinite(affine)):
          raise AffineInvalidValuesError(
              "Affine transform contains invalid" " elements"
          )
  
      # checking on proper augmentation
      # First n-1 columns in last row in matrix contain non-zeros
      if not np.all(affine[-1, :-1] == 0.0):
          raise AffineInvalidValuesError(
              "First {n_1} columns in last row"
              " in matrix contain non-zeros!".format(
                  n_1=affine.shape[0] - 1
              )
          )
  
      # Last row, last column in matrix must be 1.0!
      if affine[-1, -1] != 1.0:
          raise AffineInvalidValuesError(
              "Last row, last column in matrix" " is not 1.0!"
          )
  
      # making a copy to insulate it from changes outside object
      self.affine = affine.copy()
  
      try:
          self.affine_inv = npl.inv(affine)
      except npl.LinAlgError:
          raise AffineInversionError("Affine cannot be inverted")
  
imports: from warnings import warn
  import numpy
  import numpy.linalg
  import scipy.ndimage
  from dipy.core.optimize import Optimizer
  from dipy.core.interpolation import interpolate_scalar_2d, interpolate_scalar_3d
  from dipy.align import vector_fields
  from dipy.align import VerbosityLevels
  from dipy.align.parzenhist import ParzenJointHistogram, sample_domain_regular, compute_parzen_mi
  from dipy.align.imwarp import get_direction_and_spacings, ScaleSpace
  from dipy.align.scalespace import IsotropicScaleSpace
  from dipy.utils.deprecator import deprecated_params
label_desc: Use image node_id 3 for calling the AffineMap obj's underlying member method code with example usage: obj.set_affine(affine) without return types