# core.reference.coordinate_map

## Module: `core.reference.coordinate_map`

Inheritance diagram for `nipy.core.reference.coordinate_map`:

This module describes two types of mappings:

- CoordinateMap: a general function from a domain to a range, with a possible inverse function;

- AffineTransform: an affine function from a domain to a range, not necessarily of the same dimension, hence not always invertible.

Each of these objects is meant to encapsulate a tuple of (domain, range, function). Each of the mapping objects contain all the details about their domain CoordinateSystem, their range CoordinateSystem and the mapping between them.

### Common API

They are separate classes, neither one inheriting from the other. They do, however, share some parts of an API, each having methods:

- renamed_domain : rename on the coordinates of the domain (returns a new mapping)

- renamed_range : rename the coordinates of the range (returns a new mapping)

- reordered_domain : reorder the coordinates of the domain (returns a new mapping)

- reordered_range : reorder the coordinates of the range (returns a new mapping)

- inverse : when appropriate, return the inverse mapping

These methods are implemented by module level functions of the same name.

They also share some attributes:

- ndims : the dimensions of the domain and range, respectively

- function_domain : CoordinateSystem describing the domain

- function_range : CoordinateSystem describing the range

### Operations on mappings (module level functions)

- compose : Take a sequence of mappings (either CoordinateMaps or AffineTransforms) and return their composition. If they are all AffineTransforms, an AffineTransform is returned. This checks to ensure that domains and ranges of the various mappings agree.

- product : Take a sequence of mappings (either CoordinateMaps or AffineTransforms) and return a new mapping that has domain and range given by the concatenation of their domains and ranges, and the mapping simply concatenates the output of each of the individual mappings. If they are all AffineTransforms, an AffineTransform is returned. If they are all AffineTransforms that are in fact linear (i.e. origin=0) then can is represented as a block matrix with the size of the blocks determined by

- concat : Take a mapping and prepend a coordinate to its domain and range. For mapping `m`, this is the same as `product(AffineTransform.identity('concat'), m)`

## Classes

### `AffineTransform`

class nipy.core.reference.coordinate_map.AffineTransform(function_domain, function_range, affine)

  Bases: `object`

  Class for affine transformation from domain to a range

  This class has an affine attribute, which is a matrix representing the affine transformation in homogeneous coordinates. This matrix is used to evaluate the function, rather than having an explicit function (as is the case for a CoordinateMap).

  Examples

      >>> inp_cs = CoordinateSystem('ijk')
      >>> out_cs = CoordinateSystem('xyz')
      >>> cm = AffineTransform(inp_cs, out_cs, np.diag([1, 2, 3, 1]))
      >>> cm
      AffineTransform(
         function_domain=CoordinateSystem(coord_names=('i', 'j', 'k'), name='', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('x', 'y', 'z'), name='', coord_dtype=float64),
         affine=array([[ 1.,  0.,  0.,  0.],
                       [ 0.,  2.,  0.,  0.],
                       [ 0.,  0.,  3.,  0.],
                       [ 0.,  0.,  0.,  1.]])
      )

      >>> cm.affine
      array([[ 1.,  0.,  0.,  0.],
             [ 0.,  2.,  0.,  0.],
             [ 0.,  0.,  3.,  0.],
             [ 0.,  0.,  0.,  1.]])
      >>> cm([1,1,1])
      array([ 1.,  2.,  3.])
      >>> icm = cm.inverse()
      >>> icm([1,2,3])
      array([ 1.,  1.,  1.])

  __init__(function_domain, function_range, affine)

    Initialize AffineTransform

    Parameters:
      function_domain `CoordinateSystem`

        input coordinates

      function_range `CoordinateSystem`

        output coordinates

      affine array-like

        affine homogeneous coordinate matrix

    Notes

    The dtype of the resulting matrix is determined by finding a safe typecast for the function_domain, function_range and affine.

  affine = array([[3, 0, 0, 0], [0, 4, 0, 0], [0, 0, 5, 0], [0, 0, 0, 1]])
  static from_params(innames, outnames, params, domain_name='', range_name='')

    Create AffineTransform from innames and outnames

    Parameters:
      innames sequence of str or str

        The names of the axes of the domain. If str, then names given by `list(innames)`

      outnames sequence of str or str

        The names of the axes of the range. If str, then names given by `list(outnames)`

      params AffineTransform, array or (array, array)

        An affine function between the domain and range. This can be represented either by a single ndarray (which is interpreted as the representation of the function in homogeneous coordinates) or an (A,b) tuple.

      domain_name str, optional

        Name of domain CoordinateSystem

      range_name str, optional

        Name of range CoordinateSystem

    Returns:
      aff `AffineTransform`

    Notes

    Precondition:

      `len(shape) == len(names)`

    Raises ValueError:

      `if len(shape) != len(names)`

  static from_start_step(innames, outnames, start, step, domain_name='', range_name='')

    New AffineTransform from names, start and step.

    Parameters:
      innames sequence of str or str

        The names of the axes of the domain. If str, then names given by `list(innames)`

      outnames sequence of str or str

        The names of the axes of the range. If str, then names given by `list(outnames)`

      start sequence of float

        Start vector used in constructing affine transformation

      step sequence of float

        Step vector used in constructing affine transformation

      domain_name str, optional

        Name of domain CoordinateSystem

      range_name str, optional

        Name of range CoordinateSystem

    Returns:
      cm CoordinateMap

    Notes

    `len(names) == len(start) == len(step)`

    Examples

        >>> cm = AffineTransform.from_start_step('ijk', 'xyz', [1, 2, 3], [4, 5, 6])
        >>> cm.affine
        array([[ 4.,  0.,  0.,  1.],
               [ 0.,  5.,  0.,  2.],
               [ 0.,  0.,  6.,  3.],
               [ 0.,  0.,  0.,  1.]])

  function_domain = CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64)
  function_range = CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64)
  static identity(coord_names, name='')

    Return an identity coordmap of the given shape

    Parameters:
      coord_names sequence of str or str

        The names of the axes of the domain. If str, then names given by `list(coord_names)`

      name str, optional

        Name of origin of coordinate system

    Returns:
      cm CoordinateMap

        `CoordinateMap` with `CoordinateSystem` domain and an identity transform, with identical domain and range.

    Examples

        >>> cm = AffineTransform.identity('ijk', 'somewhere')
        >>> cm.affine
        array([[ 1.,  0.,  0.,  0.],
               [ 0.,  1.,  0.,  0.],
               [ 0.,  0.,  1.,  0.],
               [ 0.,  0.,  0.,  1.]])
        >>> cm.function_domain
        CoordinateSystem(coord_names=('i', 'j', 'k'), name='somewhere', coord_dtype=float64)
        >>> cm.function_range
        CoordinateSystem(coord_names=('i', 'j', 'k'), name='somewhere', coord_dtype=float64)

  inverse(preserve_dtype=False)

    Return coordinate map with inverse affine transform or None

    Parameters:
      preserve_dtype bool

        If False, return affine mapping from inverting the `affine`. The domain / range dtypes for the inverse may then change as a function of the dtype of the inverted `affine`. If True, try to invert our `affine`, and see if it can be cast to the needed data type, which is `self.function_domain.coord_dtype`. We need this dtype in order for the inverse to preserve the coordinate system dtypes.

    Returns:
      aff_cm_inv `AffineTransform` instance or None

        `AffineTransform` mapping from the range of input self to the domain of input self - the inverse of self. If `self.affine` was not invertible return None. If preserve_dtype is True, and the inverse of `self.affine` cannot be cast to `self.function_domain.coord_dtype`, then return None. Otherwise return `AffineTransform` inverse mapping. If preserve_dtype is False, the domain / range dtypes of the return inverse may well be different from those of the input self.

    Examples

        >>> input_cs = CoordinateSystem('ijk', coord_dtype=np.int_)
        >>> output_cs = CoordinateSystem('xyz', coord_dtype=np.int_)
        >>> affine = np.array([[1,0,0,1],
        ...                    [0,1,0,1],
        ...                    [0,0,1,1],
        ...                    [0,0,0,1]])
        >>> affine_transform = AffineTransform(input_cs, output_cs, affine)
        >>> affine_transform([2,3,4])
        array([3, 4, 5])

    The inverse transform, by default, generates a floating point inverse matrix and therefore floating point output:

        >>> affine_transform_inv = affine_transform.inverse()
        >>> affine_transform_inv([2, 6, 12])
        array([  1.,   5.,  11.])

    You can force it to preserve the coordinate system dtype with the preserve_dtype flag:

        >>> at_inv_preserved = affine_transform.inverse(preserve_dtype=True)
        >>> at_inv_preserved([2, 6, 12])
        array([  1,   5,  11])

    If you preserve_dtype, and there is no inverse affine preserving the dtype, the inverse is None:

        >>> affine2 = affine.copy()
        >>> affine2[0, 0] = 2 # now inverse can't be integer
        >>> aff_t = AffineTransform(input_cs, output_cs, affine2)
        >>> aff_t.inverse(preserve_dtype=True) is None
        True

  ndims = (3, 3)
  renamed_domain(newnames, name='')

    New AffineTransform with function_domain renamed

    Parameters:
      newnames dict

        A dictionary whose keys are integers or are in mapping.function_domain.coord_names and whose values are the new names.

    Returns:
      newmapping AffineTransform

        A new AffineTransform with renamed function_domain.

    Examples

        >>> affine_domain = CoordinateSystem('ijk')
        >>> affine_range = CoordinateSystem('xyz')
        >>> affine_matrix = np.identity(4)
        >>> affine_mapping = AffineTransform(affine_domain, affine_range, affine_matrix)

        >>> new_affine_mapping = affine_mapping.renamed_domain({'i':'phase','k':'freq','j':'slice'})
        >>> new_affine_mapping.function_domain
        CoordinateSystem(coord_names=('phase', 'slice', 'freq'), name='', coord_dtype=float64)

        >>> new_affine_mapping = affine_mapping.renamed_domain({'i':'phase','k':'freq','l':'slice'})
        Traceback (most recent call last):
           ...
        ValueError: no domain coordinate named l

  renamed_range(newnames, name='')

    New AffineTransform with renamed function_domain

    Parameters:
      newnames dict

        A dictionary whose keys are integers or are in mapping.function_range.coord_names and whose values are the new names.

    Returns:
      newmapping AffineTransform

        A new AffineTransform with renamed function_range.

    Examples

        >>> affine_domain = CoordinateSystem('ijk')
        >>> affine_range = CoordinateSystem('xyz')
        >>> affine_matrix = np.identity(4)
        >>> affine_mapping = AffineTransform(affine_domain, affine_range, affine_matrix)

        >>> new_affine_mapping = affine_mapping.renamed_range({'x':'u'})
        >>> new_affine_mapping.function_range
        CoordinateSystem(coord_names=('u', 'y', 'z'), name='', coord_dtype=float64)

        >>> new_affine_mapping = affine_mapping.renamed_range({'w':'u'})
        Traceback (most recent call last):
           ...
        ValueError: no range coordinate named w

  reordered_domain(order=None)

    New AffineTransform with function_domain reordered

    Default behaviour is to reverse the order of the coordinates.

    Parameters:
      order sequence

        Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_domain.coord_names.

    Returns:
      newmapping :AffineTransform

        A new AffineTransform with the coordinates of function_domain reordered.

    Examples

        >>> input_cs = CoordinateSystem('ijk')
        >>> output_cs = CoordinateSystem('xyz')
        >>> cm = AffineTransform(input_cs, output_cs, np.identity(4))
        >>> cm.reordered_domain('ikj').function_domain
        CoordinateSystem(coord_names=('i', 'k', 'j'), name='', coord_dtype=float64)

  reordered_range(order=None)

    New AffineTransform with function_range reordered

    Defaults to reversing the coordinates of function_range.

    Parameters:
      order sequence

        Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_range.coord_names.

    Returns:
      newmapping AffineTransform

        A new AffineTransform with the coordinates of function_range reordered.

    Examples

        >>> input_cs = CoordinateSystem('ijk')
        >>> output_cs = CoordinateSystem('xyz')
        >>> cm = AffineTransform(input_cs, output_cs, np.identity(4))
        >>> cm.reordered_range('xzy').function_range
        CoordinateSystem(coord_names=('x', 'z', 'y'), name='', coord_dtype=float64)
        >>> cm.reordered_range([0,2,1]).function_range.coord_names
        ('x', 'z', 'y')

        >>> newcm = cm.reordered_range('yzx')
        >>> newcm.function_range.coord_names
        ('y', 'z', 'x')

  similar_to(other)

    Does other have similar coordinate systems and same mappings?

    A “similar” coordinate system is one with the same coordinate names and data dtype, but ignoring the coordinate system name.

### `AxisError`

class nipy.core.reference.coordinate_map.AxisError

  Bases: `Exception`

  Error for incorrect axis selection

  __init__(*args, **kwargs)
  add_note(note, /)

    Add a note to the exception

  args
  with_traceback(tb, /)

    Set self.__traceback__ to tb and return self.

### `CoordMapMaker`

class nipy.core.reference.coordinate_map.CoordMapMaker(domain_maker, range_maker)

  Bases: `object`

  Class to create coordinate maps of different dimensions

  __init__(domain_maker, range_maker)

    Create coordinate map maker

    Parameters:
      domain_maker callable

        A coordinate system maker, returning a coordinate system with input argument only `N`, an integer giving the length of the coordinate map.

      range_maker callable

        A coordinate system maker, returning a coordinate system with input argument only `N`, an integer giving the length of the coordinate map.

    Examples

        >>> from nipy.core.reference.coordinate_system import CoordSysMaker
        >>> dmaker = CoordSysMaker('ijkl', 'generic-array')
        >>> rmaker = CoordSysMaker('xyzt', 'generic-scanner')
        >>> cm_maker = CoordMapMaker(dmaker, rmaker)

  affine_maker

    alias of `AffineTransform`

  generic_maker

    alias of `CoordinateMap`

  make_affine(affine, append_zooms=(), append_offsets=())

    Create affine coordinate map

    Parameters:
      affine(M, N) array-like

        Array expressing the affine transformation

      append_zooms scalar or sequence length E

        If scalar, converted to sequence length E==1. Append E entries to the diagonal of affine (see examples)

      append_offsets scalar or sequence length F

        If scalar, converted to sequence length F==1. If F==0, and E!=0, use sequence of zeros length E. Append E entries to the translations (final column) of affine (see examples).

    Returns:
      affmap `AffineTransform` coordinate map

    Examples

        >>> from nipy.core.reference.coordinate_system import CoordSysMaker
        >>> dmaker = CoordSysMaker('ijkl', 'generic-array')
        >>> rmaker = CoordSysMaker('xyzt', 'generic-scanner')
        >>> cm_maker = CoordMapMaker(dmaker, rmaker)
        >>> cm_maker.make_affine(np.diag([2,3,4,1]))
        AffineTransform(
           function_domain=CoordinateSystem(coord_names=('i', 'j', 'k'), name='generic-array', coord_dtype=float64),
           function_range=CoordinateSystem(coord_names=('x', 'y', 'z'), name='generic-scanner', coord_dtype=float64),
           affine=array([[ 2.,  0.,  0.,  0.],
                         [ 0.,  3.,  0.,  0.],
                         [ 0.,  0.,  4.,  0.],
                         [ 0.,  0.,  0.,  1.]])
        )

    We can add extra orthogonal dimensions, by specifying the diagonal elements:

        >>> cm_maker.make_affine(np.diag([2,3,4,1]), 6)
        AffineTransform(
           function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='generic-array', coord_dtype=float64),
           function_range=CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='generic-scanner', coord_dtype=float64),
           affine=array([[ 2.,  0.,  0.,  0.,  0.],
                         [ 0.,  3.,  0.,  0.,  0.],
                         [ 0.,  0.,  4.,  0.,  0.],
                         [ 0.,  0.,  0.,  6.,  0.],
                         [ 0.,  0.,  0.,  0.,  1.]])
        )

    Or the diagonal elements and the offset elements:

        >>> cm_maker.make_affine(np.diag([2,3,4,1]), [6], [9])
        AffineTransform(
           function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='generic-array', coord_dtype=float64),
           function_range=CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='generic-scanner', coord_dtype=float64),
           affine=array([[ 2.,  0.,  0.,  0.,  0.],
                         [ 0.,  3.,  0.,  0.,  0.],
                         [ 0.,  0.,  4.,  0.,  0.],
                         [ 0.,  0.,  0.,  6.,  9.],
                         [ 0.,  0.,  0.,  0.,  1.]])
        )

  make_cmap(domain_N, xform, inv_xform=None)

    Coordinate map with transform function xform

    Parameters:
      domain_N int

        Number of domain coordinates

      xform callable

        Function that transforms points of dimension domain_N

      inv_xform None or callable, optional

        Function, such that `inv_xform(xform(pts))` returns `pts`

    Returns:
      cmap `CoordinateMap`

    Examples

        >>> from nipy.core.reference.coordinate_system import CoordSysMaker
        >>> dmaker = CoordSysMaker('ijkl', 'generic-array')
        >>> rmaker = CoordSysMaker('xyzt', 'generic-scanner')
        >>> cm_maker = CoordMapMaker(dmaker, rmaker)
        >>> cm_maker.make_cmap(4, lambda x : x+1)
        CoordinateMap(
           function_domain=CoordinateSystem(coord_names=('i', 'j', 'k', 'l'), name='generic-array', coord_dtype=float64),
           function_range=CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='generic-scanner', coord_dtype=float64),
           function=<function <lambda> at ...>
          )

### `CoordMapMakerError`

class nipy.core.reference.coordinate_map.CoordMapMakerError

  Bases: `Exception`

  __init__(*args, **kwargs)
  add_note(note, /)

    Add a note to the exception

  args
  with_traceback(tb, /)

    Set self.__traceback__ to tb and return self.

### `CoordinateMap`

class nipy.core.reference.coordinate_map.CoordinateMap(function_domain, function_range, function, inverse_function=None)

  Bases: `object`

  A set of domain and range CoordinateSystems and a function between them.

  For example, the function may represent the mapping of a voxel (the domain of the function) to real space (the range). The function may be an affine or non-affine transformation.

  Attributes:
    function_domain `CoordinateSystem` instance

      The input coordinate system.

    function_range `CoordinateSystem` instance

      The output coordinate system.

    `function` callable

      exp(x, /, out=None, *, where=True, casting=’same_kind’, order=’K’, dtype=None, subok=True[, signature])

    `inverse_function` None or callable

      log(x, /, out=None, *, where=True, casting=’same_kind’, order=’K’, dtype=None, subok=True[, signature])

  Examples

      >>> function_domain = CoordinateSystem('ijk', 'voxels')
      >>> function_range = CoordinateSystem('xyz', 'world')
      >>> mni_orig = np.array([-90.0, -126.0, -72.0])
      >>> function = lambda x: x + mni_orig
      >>> inv_function = lambda x: x - mni_orig
      >>> cm = CoordinateMap(function_domain, function_range, function, inv_function)

  Map the first 3 voxel coordinates, along the x-axis, to mni space:

      >>> x = np.array([[0,0,0], [1,0,0], [2,0,0]])
      >>> cm.function(x)
      array([[ -90., -126.,  -72.],
             [ -89., -126.,  -72.],
             [ -88., -126.,  -72.]])

      >>> x = CoordinateSystem('x')
      >>> y = CoordinateSystem('y')
      >>> m = CoordinateMap(x, y, np.exp, np.log)
      >>> m
      CoordinateMap(
         function_domain=CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64),
         function=<ufunc 'exp'>,
         inverse_function=<ufunc 'log'>
        )
      >>> m.inverse()
      CoordinateMap(
         function_domain=CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64),
         function_range=CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64),
         function=<ufunc 'log'>,
         inverse_function=<ufunc 'exp'>
        )

  __init__(function_domain, function_range, function, inverse_function=None)

    Create a CoordinateMap given function, domain and range.

    Parameters:
      function_domain `CoordinateSystem`

        The input coordinate system.

      function_range `CoordinateSystem`

        The output coordinate system

      function callable

        The function between function_domain and function_range. It should be a callable that accepts arrays of shape (N, function_domain.ndim) and returns arrays of shape (N, function_range.ndim), where N is the number of points for transformation.

      inverse_function None or callable, optional

        The optional inverse of function, with the intention being `x = inverse_function(function(x))`. If the function is affine and invertible, then this is true for all x. The default is None

    Returns:
      coordmap CoordinateMap
  function(x, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature]) = <ufunc 'exp'>
  function_domain = CoordinateSystem(coord_names=('x',), name='', coord_dtype=float64)
  function_range = CoordinateSystem(coord_names=('y',), name='', coord_dtype=float64)
  inverse()

    New CoordinateMap with the functions reversed

  inverse_function(x, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature]) = <ufunc 'log'>
  ndims = (1, 1)
  renamed_domain(newnames, name='')

    New CoordinateMap with function_domain renamed

    Parameters:
      newnames dict

        A dictionary whose keys are integers or are in mapping.function_domain.coord_names and whose values are the new names.

    Returns:
      newmaping CoordinateMap

        A new CoordinateMap with renamed function_domain.

    Examples

        >>> domain = CoordinateSystem('ijk')
        >>> range = CoordinateSystem('xyz')
        >>> cm = CoordinateMap(domain, range, lambda x:x+1)

        >>> new_cm = cm.renamed_domain({'i':'phase','k':'freq','j':'slice'})
        >>> new_cm.function_domain
        CoordinateSystem(coord_names=('phase', 'slice', 'freq'), name='', coord_dtype=float64)

        >>> new_cm = cm.renamed_domain({'i':'phase','k':'freq','l':'slice'})
        Traceback (most recent call last):
           ...
        ValueError: no domain coordinate named l

  renamed_range(newnames, name='')

    New CoordinateMap with function_domain renamed

    Parameters:
      newnames dict

        A dictionary whose keys are integers or are in mapping.function_range.coord_names and whose values are the new names.

    Returns:
      newmapping CoordinateMap

        A new CoordinateMap with renamed function_range.

    Examples

        >>> domain = CoordinateSystem('ijk')
        >>> range = CoordinateSystem('xyz')
        >>> cm = CoordinateMap(domain, range, lambda x:x+1)

        >>> new_cm = cm.renamed_range({'x':'u'})
        >>> new_cm.function_range
        CoordinateSystem(coord_names=('u', 'y', 'z'), name='', coord_dtype=float64)

        >>> new_cm = cm.renamed_range({'w':'u'})
        Traceback (most recent call last):
           ...
        ValueError: no range coordinate named w

  reordered_domain(order=None)

    Create a new CoordinateMap with the coordinates of function_domain reordered. Default behaviour is to reverse the order of the coordinates.

    Parameters:
      order sequence

        Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_domain.coord_names.

    Returns:
      newmapping CoordinateMap

        A new CoordinateMap with the coordinates of function_domain reordered.

    Examples

        >>> input_cs = CoordinateSystem('ijk')
        >>> output_cs = CoordinateSystem('xyz')
        >>> cm = CoordinateMap(input_cs, output_cs, lambda x:x+1)
        >>> cm.reordered_domain('ikj').function_domain
        CoordinateSystem(coord_names=('i', 'k', 'j'), name='', coord_dtype=float64)

  reordered_range(order=None)

    Nnew CoordinateMap with function_range reordered.

    Defaults to reversing the coordinates of function_range.

    Parameters:
      order sequence

        Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_range.coord_names.

    Returns:
      newmapping CoordinateMap

        A new CoordinateMap with the coordinates of function_range reordered.

    Examples

        >>> input_cs = CoordinateSystem('ijk')
        >>> output_cs = CoordinateSystem('xyz')
        >>> cm = CoordinateMap(input_cs, output_cs, lambda x:x+1)
        >>> cm.reordered_range('xzy').function_range
        CoordinateSystem(coord_names=('x', 'z', 'y'), name='', coord_dtype=float64)
        >>> cm.reordered_range([0,2,1]).function_range.coord_names
        ('x', 'z', 'y')

        >>> newcm = cm.reordered_range('yzx')
        >>> newcm.function_range.coord_names
        ('y', 'z', 'x')

  similar_to(other)

    Does other have similar coordinate systems and same mappings?

    A “similar” coordinate system is one with the same coordinate names and data dtype, but ignoring the coordinate system name.

## Functions

nipy.core.reference.coordinate_map.append_io_dim(cm, in_name, out_name, start=0, step=1)

  Append input and output dimension to coordmap

  Parameters:
    cm Affine

      Affine coordinate map instance to which to append dimension

    in_name str

      Name for new input dimension

    out_name str

      Name for new output dimension

    start float, optional

      Offset for transformed values in new dimension

    step float, optional

      Step, or scale factor for transformed values in new dimension

  Returns:
    cm_plus Affine

      New coordinate map with appended dimension

  Examples

  Typical use is creating a 4D coordinate map from a 3D

      >>> cm3d = AffineTransform.from_params('ijk', 'xyz', np.diag([1,2,3,1]))
      >>> cm4d = append_io_dim(cm3d, 'l', 't', 9, 5)
      >>> cm4d.affine
      array([[ 1.,  0.,  0.,  0.,  0.],
             [ 0.,  2.,  0.,  0.,  0.],
             [ 0.,  0.,  3.,  0.,  0.],
             [ 0.,  0.,  0.,  5.,  9.],
             [ 0.,  0.,  0.,  0.,  1.]])

nipy.core.reference.coordinate_map.axmap(coordmap, direction='in2out', fix0=True)

  Return mapping between input and output axes

  Parameters:
    coordmap Affine

      Affine coordinate map instance for which to get axis mappings

    direction{‘in2out’, ‘out2in’, ‘both’}

      direction to find mapping. If ‘in2out’, returned mapping will have keys from the input axis (names and indices) and values of corresponding output axes. If ‘out2in’ the keys will be output axis names, indices and the values will be input axis indices. If both, return both mappings.

    fix0: bool, optional

      Whether to fix potential 0 TR in affine

  Returns:
    map dict or tuple

      - if direction == ‘in2out’ - mapping with keys of input names and input indices, values of output indices. Mapping is to closest matching axis. None means there appears to be no matching axis

      - if direction == ‘out2in’ - mapping with keys of output names and input indices, values of input indices, as above.

      - if direction == ‘both’ - tuple of (input to output mapping, output to input mapping)

nipy.core.reference.coordinate_map.compose(*cmaps)

  Return the composition of two or more CoordinateMaps.

  Parameters:
    cmaps sequence of CoordinateMaps
  Returns:
    cmap `CoordinateMap`

      The resulting CoordinateMap has function_domain == cmaps[-1].function_domain and function_range == cmaps[0].function_range

  Examples

      >>> cmap = AffineTransform.from_params('i', 'x', np.diag([2.,1.]))
      >>> cmapi = cmap.inverse()
      >>> id1 = compose(cmap,cmapi)
      >>> id1.affine
      array([[ 1.,  0.],
             [ 0.,  1.]])

      >>> id2 = compose(cmapi,cmap)
      >>> id1.function_domain.coord_names
      ('x',)
      >>> id2.function_domain.coord_names
      ('i',)

nipy.core.reference.coordinate_map.drop_io_dim(cm, axis_id, fix0=True)

  Drop dimensions axis_id from coordinate map, if orthogonal to others

  If you specify an input dimension, drop that dimension and any corresponding output dimension, as long as all other outputs are orthogonal to dropped input. If you specify an output dimension, drop that dimension and any corresponding input dimension, as long as all other inputs are orthogonal to dropped output.

  Parameters:
    cm class:AffineTransform

      Affine coordinate map instance

    axis_id int or str

      If int, gives index of input axis to drop. If str, gives name of input or output axis to drop. When specifying an input axis: if given input axis does not affect any output axes, just drop input axis. If input axis affects only one output axis, drop both input and corresponding output. Similarly when specifying an output axis. If axis_id is a str, it must be unambiguous - if the named axis exists in both input and output, and they do not correspond, raises a AxisError. See Raises section for checks

    fix0: bool, optional

      Whether to fix potential 0 TR in affine

  Returns:
    cm_redux Affine

      Affine coordinate map with orthogonal input + output dimension dropped

  Raises:
    AxisError: if axis_id is a str and does not match any no input or output

      coordinate names.

    AxisError: if specified axis_id affects more than a single input / output

      axis.

    AxisError: if the named axis_id exists in both input and output, and they

      do not correspond.

  Examples

  Typical use is in getting a 3D coordinate map from 4D

      >>> cm4d = AffineTransform.from_params('ijkl', 'xyzt', np.diag([1,2,3,4,1]))
      >>> cm3d = drop_io_dim(cm4d, 't')
      >>> cm3d.affine
      array([[ 1.,  0.,  0.,  0.],
             [ 0.,  2.,  0.,  0.],
             [ 0.,  0.,  3.,  0.],
             [ 0.,  0.,  0.,  1.]])

nipy.core.reference.coordinate_map.equivalent(mapping1, mapping2)

  A test to see if mapping1 is equal to mapping2 after possibly reordering the domain and range of mapping.

  Parameters:
    mapping1 CoordinateMap or AffineTransform
    mapping2 CoordinateMap or AffineTransform
  Returns:
    are_they_equal bool

  Examples

      >>> ijk = CoordinateSystem('ijk')
      >>> xyz = CoordinateSystem('xyz')
      >>> T = np.random.standard_normal((4,4))
      >>> T[-1] = [0,0,0,1] # otherwise AffineTransform raises
      ...                   # an exception because
      ...                   # it's supposed to represent an
      ...                   # affine transform in homogeneous
      ...                   # coordinates
      >>> A = AffineTransform(ijk, xyz, T)
      >>> B = A.reordered_domain('ikj').reordered_range('xzy')
      >>> C = B.renamed_domain({'i':'slice'})
      >>> equivalent(A, B)
      True
      >>> equivalent(A, C)
      False
      >>> equivalent(B, C)
      False
      >>>
      >>> D = CoordinateMap(ijk, xyz, np.exp)
      >>> equivalent(D, D)
      True
      >>> E = D.reordered_domain('kij').reordered_range('xzy')
      >>> # no non-AffineTransform will ever be
      >>> # equivalent to a reordered version of itself,
      >>> # because their functions don't evaluate as equal
      >>> equivalent(D, E)
      False
      >>> equivalent(E, E)
      True
      >>>
      >>> # This has not changed the order
      >>> # of the axes, so the function is still the same
      >>>
      >>> F = D.reordered_range('xyz').reordered_domain('ijk')
      >>> equivalent(F, D)
      True
      >>> id(F) == id(D)
      False

nipy.core.reference.coordinate_map.input_axis_index(coordmap, axis_id, fix0=True)

  Return input axis index for axis_id

  axis_id can be integer, or a name of an input axis, or it can be the name of an output axis which maps to an input axis.

  Parameters:
    coordmap AffineTransform
    axis_id int or str

      If int, then an index of an input axis. Can be negative, so that -2 refers to the second to last input axis. If a str can be the name of an input axis, or the name of an output axis that should have a corresponding input axis (see Raises section).

    fix0: bool, optional

      Whether to fix potential single 0 on diagonal of affine. This often happens when loading nifti images with TR set to 0.

  Returns:
    inax int

      index of matching input axis. If axis_id is the name of an output axis, then inax will be the input axis that had a ‘best’ match with this output axis. The ‘best’ match algorithm ensures that there can only be one input axis paired with one output axis.

  Raises:
    AxisError: if no matching name found
    AxisError if name exists in both input and output and they do not map to

      each other

    AxisError if name present in output but no matching input

nipy.core.reference.coordinate_map.io_axis_indices(coordmap, axis_id, fix0=True)

  Return input and output axis index for id axis_id in coordmap

  Parameters:
    cm class:AffineTransform

      Affine coordinate map instance

    axis_id int or str

      If int, gives index of input axis. Can be negative, so that -2 refers to the second from last input axis. If str, gives name of input or output axis. If axis_id is a str, it must be unambiguous - if the named axis exists in both input and output, and they do not correspond, raises a AxisError. See Raises section for checks

    fix0: bool, optional

      Whether to fix potential 0 column / row in affine

  Returns:
    in_index None or int

      index of input axis that corresponds to axis_id

    out_index None or int

      index of output axis that corresponds to axis_id

  Raises:
    AxisError: if axis_id is a str and does not match any input or output

      coordinate names.

    AxisError: if the named axis_id exists in both input and output, and they

      do not correspond.

  Examples

      >>> aff = [[0, 1, 0, 10], [1, 0, 0, 11], [0, 0, 1, 12], [0, 0, 0, 1]]
      >>> cmap = AffineTransform('ijk', 'xyz', aff)
      >>> io_axis_indices(cmap, 0)
      (0, 1)
      >>> io_axis_indices(cmap, 1)
      (1, 0)
      >>> io_axis_indices(cmap, -1)
      (2, 2)
      >>> io_axis_indices(cmap, 'j')
      (1, 0)
      >>> io_axis_indices(cmap, 'y')
      (0, 1)

nipy.core.reference.coordinate_map.orth_axes(in_ax, out_ax, affine, allow_zero=True, tol=1e-05)

  True if in_ax related only to out_ax in affine and vice versa

  Parameters:
    in_ax int

      Input axis index

    out_ax int

      Output axis index

    affine array-like

      Affine transformation matrix

    allow_zero bool, optional

      Whether to allow zero in `affine[out_ax, in_ax]`. This means that the two axes are not related, but nor is this pair related to any other part of the affine.

  Returns:
    tf bool

      True if in_ax, out_ax pair are orthogonal to the rest of affine, unless allow_zero is False, in which case require in addition that `affine[out_ax, in_ax] != 0`.

  Examples

      >>> aff = np.eye(4)
      >>> orth_axes(1, 1, aff)
      True
      >>> orth_axes(1, 2, aff)
      False

nipy.core.reference.coordinate_map.product(*cmaps, **kwargs)

  “topological” product of two or more mappings

  The mappings can be either AffineTransforms or CoordinateMaps.

  If they are all AffineTransforms, the result is an AffineTransform, else it is a CoordinateMap.

  Parameters:
    cmaps sequence of CoordinateMaps or AffineTransforms
  Returns:
    cmap `CoordinateMap`

  Examples

      >>> inc1 = AffineTransform.from_params('i', 'x', np.diag([2,1]))
      >>> inc2 = AffineTransform.from_params('j', 'y', np.diag([3,1]))
      >>> inc3 = AffineTransform.from_params('k', 'z', np.diag([4,1]))

      >>> cmap = product(inc1, inc3, inc2)
      >>> cmap.function_domain.coord_names
      ('i', 'k', 'j')
      >>> cmap.function_range.coord_names
      ('x', 'z', 'y')
      >>> cmap.affine
      array([[ 2.,  0.,  0.,  0.],
             [ 0.,  4.,  0.,  0.],
             [ 0.,  0.,  3.,  0.],
             [ 0.,  0.,  0.,  1.]])

      >>> A1 = AffineTransform.from_params('ij', 'xyz', np.array([[2,3,1,0],[3,4,5,0],[7,9,3,1]]).T)
      >>> A2 = AffineTransform.from_params('xyz', 'de', np.array([[8,6,7,4],[1,-1,13,3],[0,0,0,1]]))

      >>> A1.affine
      array([[ 2.,  3.,  7.],
             [ 3.,  4.,  9.],
             [ 1.,  5.,  3.],
             [ 0.,  0.,  1.]])
      >>> A2.affine
      array([[  8.,   6.,   7.,   4.],
             [  1.,  -1.,  13.,   3.],
             [  0.,   0.,   0.,   1.]])

      >>> p=product(A1, A2)
      >>> p.affine
      array([[  2.,   3.,   0.,   0.,   0.,   7.],
             [  3.,   4.,   0.,   0.,   0.,   9.],
             [  1.,   5.,   0.,   0.,   0.,   3.],
             [  0.,   0.,   8.,   6.,   7.,   4.],
             [  0.,   0.,   1.,  -1.,  13.,   3.],
             [  0.,   0.,   0.,   0.,   0.,   1.]])

      >>> np.allclose(p.affine[:3,:2], A1.affine[:3,:2])
      True
      >>> np.allclose(p.affine[:3,-1], A1.affine[:3,-1])
      True
      >>> np.allclose(p.affine[3:5,2:5], A2.affine[:2,:3])
      True
      >>> np.allclose(p.affine[3:5,-1], A2.affine[:2,-1])
      True
      >>>

      >>> A1([3,4])
      array([ 25.,  34.,  26.])
      >>> A2([5,6,7])
      array([ 129.,   93.])
      >>> p([3,4,5,6,7])
      array([  25.,   34.,   26.,  129.,   93.])

nipy.core.reference.coordinate_map.renamed_domain(mapping, newnames, name='')

  New coordmap with the coordinates of function_domain renamed

  Parameters:
    newnames: dict

      A dictionary whose keys are integers or are in mapping.function_range.coord_names and whose values are the new names.

  Returns:
    newmapping CoordinateMap or AffineTransform

      A new mapping with renamed function_domain. If isinstance(mapping, AffineTransform), newmapping is also an AffineTransform. Otherwise, it is a CoordinateMap.

  Examples

      >>> affine_domain = CoordinateSystem('ijk')
      >>> affine_range = CoordinateSystem('xyz')
      >>> affine_matrix = np.identity(4)
      >>> affine_mapping = AffineTransform(affine_domain, affine_range, affine_matrix)

      >>> new_affine_mapping = affine_mapping.renamed_domain({'i':'phase','k':'freq','j':'slice'})
      >>> new_affine_mapping.function_domain
      CoordinateSystem(coord_names=('phase', 'slice', 'freq'), name='', coord_dtype=float64)

      >>> new_affine_mapping = affine_mapping.renamed_domain({'i':'phase','k':'freq','l':'slice'})
      Traceback (most recent call last):
         ...
      ValueError: no domain coordinate named l

nipy.core.reference.coordinate_map.renamed_range(mapping, newnames)

  New coordmap with the coordinates of function_range renamed

  Parameters:
    newnames dict

      A dictionary whose keys are integers or in mapping.function_range.coord_names and whose values are the new names.

  Returns:
    newmapping CoordinateMap or AffineTransform

      A new CoordinateMap with the coordinates of function_range renamed. If isinstance(mapping, AffineTransform), newmapping is also an AffineTransform. Otherwise, it is a CoordinateMap.

  Examples

      >>> affine_domain = CoordinateSystem('ijk')
      >>> affine_range = CoordinateSystem('xyz')
      >>> affine_matrix = np.identity(4)
      >>> affine_mapping = AffineTransform(affine_domain, affine_range, affine_matrix)
      >>> new_affine_mapping = affine_mapping.renamed_range({'x':'u'})
      >>> new_affine_mapping.function_range
      CoordinateSystem(coord_names=('u', 'y', 'z'), name='', coord_dtype=float64)

      >>> new_affine_mapping = affine_mapping.renamed_range({'w':'u'})
      Traceback (most recent call last):
         ...
      ValueError: no range coordinate named w

nipy.core.reference.coordinate_map.reordered_domain(mapping, order=None)

  New coordmap with the coordinates of function_domain reordered

  Default behaviour is to reverse the order of the coordinates.

  Parameters:
    order: sequence

      Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_domain.coord_names.

  Returns:
    newmapping CoordinateMap or AffineTransform

      A new CoordinateMap with the coordinates of function_domain reordered. If isinstance(mapping, AffineTransform), newmapping is also an AffineTransform. Otherwise, it is a CoordinateMap.

  Notes

  If no reordering is to be performed, it returns a copy of mapping.

  Examples

      >>> input_cs = CoordinateSystem('ijk')
      >>> output_cs = CoordinateSystem('xyz')
      >>> cm = AffineTransform(input_cs, output_cs, np.identity(4))
      >>> cm.reordered_domain('ikj').function_domain
      CoordinateSystem(coord_names=('i', 'k', 'j'), name='', coord_dtype=float64)

nipy.core.reference.coordinate_map.reordered_range(mapping, order=None)

  New coordmap with the coordinates of function_range reordered

  Defaults to reversing the coordinates of function_range.

  Parameters:
    order: sequence

      Order to use, defaults to reverse. The elements can be integers, strings or 2-tuples of strings. If they are strings, they should be in mapping.function_range.coord_names.

  Returns:
    newmapping CoordinateMap or AffineTransform

      A new CoordinateMap with the coordinates of function_range reordered. If isinstance(mapping, AffineTransform), newmapping is also an AffineTransform. Otherwise, it is a CoordinateMap.

  Notes

  If no reordering is to be performed, it returns a copy of mapping.

  Examples

      >>> input_cs = CoordinateSystem('ijk')
      >>> output_cs = CoordinateSystem('xyz')
      >>> cm = AffineTransform(input_cs, output_cs, np.identity(4))
      >>> cm.reordered_range('xzy').function_range
      CoordinateSystem(coord_names=('x', 'z', 'y'), name='', coord_dtype=float64)
      >>> cm.reordered_range([0,2,1]).function_range.coord_names
      ('x', 'z', 'y')

      >>> newcm = cm.reordered_range('yzx')
      >>> newcm.function_range.coord_names
      ('y', 'z', 'x')

nipy.core.reference.coordinate_map.shifted_domain_origin(mapping, difference_vector, new_origin)

  Shift the origin of the domain

  Parameters:
    difference_vector array

      Representing the difference shifted_origin-current_origin in the domain’s basis.

  Examples

      >>> A = np.random.standard_normal((5,6))
      >>> A[-1] = [0,0,0,0,0,1]
      >>> affine_transform = AffineTransform(CS('ijklm', 'oldorigin'), CS('xyzt'), A)
      >>> affine_transform.function_domain
      CoordinateSystem(coord_names=('i', 'j', 'k', 'l', 'm'), name='oldorigin', coord_dtype=float64)

  A random change of origin

      >>> difference = np.random.standard_normal(5)

  The same affine transformation with a different origin for its domain

      >>> shifted_affine_transform = shifted_domain_origin(affine_transform, difference, 'neworigin')
      >>> shifted_affine_transform.function_domain
      CoordinateSystem(coord_names=('i', 'j', 'k', 'l', 'm'), name='neworigin', coord_dtype=float64)

  Let’s check that things work

      >>> point_in_old_basis = np.random.standard_normal(5)

  This is the relation ship between coordinates in old and new origins

      >>> np.allclose(shifted_affine_transform(point_in_old_basis), affine_transform(point_in_old_basis+difference))
      True
      >>> np.allclose(shifted_affine_transform(point_in_old_basis-difference), affine_transform(point_in_old_basis))
      True

nipy.core.reference.coordinate_map.shifted_range_origin(mapping, difference_vector, new_origin)

  Shift the origin of the range.

  Parameters:
    difference_vector array

      Representing the difference shifted_origin-current_origin in the range’s basis.

  Examples

      >>> A = np.random.standard_normal((5,6))
      >>> A[-1] = [0,0,0,0,0,1]
      >>> affine_transform = AffineTransform(CS('ijklm'), CS('xyzt', 'oldorigin'), A)
      >>> affine_transform.function_range
      CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='oldorigin', coord_dtype=float64)

  Make a random shift of the origin in the range

      >>> difference = np.random.standard_normal(4)
      >>> shifted_affine_transform = shifted_range_origin(affine_transform, difference, 'neworigin')
      >>> shifted_affine_transform.function_range
      CoordinateSystem(coord_names=('x', 'y', 'z', 't'), name='neworigin', coord_dtype=float64)
      >>>

  Evaluate the transform and verify it does as expected

      >>> point_in_domain = np.random.standard_normal(5)

  Check that things work

      >>> np.allclose(shifted_affine_transform(point_in_domain), affine_transform(point_in_domain) - difference)
      True
      >>> np.allclose(shifted_affine_transform(point_in_domain) + difference, affine_transform(point_in_domain))
      True