2.4 Indexing and Slicing

Matrices can be indexed using one or two arguments. In single-argument indexing of a matrix A, the index runs from -len(A) to len(A)-1, and is interpreted as an index in the one-dimensional array of coefficients of A in column-major order. Negative indices have the standard Python interpretation: for negative k, A[k] is the same element as A[len(A)+k].

Four different types of one-argument indexing are implemented.

  1. The index can be a single integer. This returns a number, e.g., A[0] is the first element of A.

  2. The index can be an integer matrix. This returns a column matrix: the command "A[matrix([0,1,2,3])]" returns the 4 by 1 matrix consisting of the first four elements of A. The size of the index matrix is ignored: "A[matrix([0,1,2,3], (2,2))]" returns the same 4 by 1 matrix.

  3. The index can be a list of integers. This returns a column matrix, e.g., A[[0,1,2,3]] is the 4 by 1 matrix consisting of elements 0, 1, 2, 3 of A.

  4. The index can be a Python slice. This returns a matrix with one column (possibly 0 by 1, or 1 by 1). For example, A[::2] is the column matrix defined by taking every other element of A, stored in column-major order. A[0:0] is a matrix with size (0,1).
Thus, single-argument indexing returns a scalar (if the index is an integer), or a matrix with one column. This is consistent with the interpretation that single-argument indexing accesses the matrix in column-major order.

Note that an index list or an index matrix are equivalent, but they are both useful, especially when we perform operations on index sets. For example, if I and J are lists then I+J is the concatenated list, and 2*I is I repeated twice. If they are matrices, these operations are interpreted as arithmetic operations. For large index sets, indexing with integer matrices is also faster than indexing with lists.

The following example illustrates one-argument indexing.

>>> from cvxopt.base import matrix 
>>> A = matrix(range(16), (4,4), 'd')
>>> print A
   0.0000e+00   4.0000e+00   8.0000e+00   1.2000e+01
   1.0000e+00   5.0000e+00   9.0000e+00   1.3000e+01
   2.0000e+00   6.0000e+00   1.0000e+01   1.4000e+01
   3.0000e+00   7.0000e+00   1.1000e+01   1.5000e+01
>>> A[4]
4.0
>>> I = matrix([0, 5, 10, 15])
>>> print A[I]      # the diagonal
   0.0000e+00
   5.0000e+00
   1.0000e+01
   1.5000e+01
>>> I = [0,2];  J = [1,3]
>>> print A[2*I+J]  # duplicate I and append J
   0.0000e+00
   2.0000e+00
   0.0000e+00
   2.0000e+00
   1.0000e+00
   3.0000e+00
>>> I = matrix([0, 2]);  J =  matrix([1, 3])
>>> print A[2*I+J]  # multiply I by 2 and add J
   1.0000e+00
   7.0000e+00
>>> print A[4::4]   # get every fourth element skipping the first four  
   4.0000e+00
   8.0000e+00
   1.2000e+01

In two-argument indexing the arguments can be any combinations of the four types listed above. The first argument indexes the rows of the matrix and the second argument indexes the columns. If both indices are scalars, then a scalar is returned. In all other cases, a matrix is returned. We continue the example.

>>> print A[:,1]
   4.0000e+00
   5.0000e+00
   6.0000e+00
   7.0000e+00
>>> J = matrix([0, 2])
>>> print A[J,J]
   0.0000e+00   8.0000e+00
   2.0000e+00   1.0000e+01
>>> print A[:2, -2:]   
   8.0000e+00   1.2000e+01
   9.0000e+00   1.3000e+01

Expressions of the form A[I] or A[I,J] can also appear on the lefthand side of an assignment. The righthand side must be a scalar (i.e., a number or a 1 by 1 dense matrix), a sequence of numbers, or a dense or sparse matrix. If the righthand side is a scalar, it is interpreted as a matrix with identical entries and the dimensions of the lefthand side. If the righthand side is a sequence of numbers (list, tuple, array array, xrange object, ...) its values are interpreted as the coefficients of the lefthand side in column-major order. If the righthand side is a matrix (matrix or spmatrix), it must have the same size as the lefthand side. Sparse matrices are converted to dense in the assignment.

Indexed assignments are only allowed if they do not change the type of the matrix. For example, if A is a matrix with type 'd', then A[I] = B is only permitted if B is an integer, a float, or a matrix of type 'i' or 'd'. If A is an integer matrix, then A[I] = B is only permitted if B is an integer or an integer matrix.

The following example illlustrates indexed assignment.

>>> A = matrix(range(16), (4,4))
>>> A[::2,::2] = matrix([[-1, -2], [-3, -4]])
>>> print A
  -1      4     -3      12
   1      5      9      13
  -2      6     -4      14
   3      7      11     15
>>> A[::5] += 1
>>> print A
   0      4     -3      12
   1      6      9      13
  -2      6     -3      14
   3      7      11     16
>>> A[0,:] = -1, 1, -1, 1
>>> print A
  -1      1     -1      1 
   1      6      9      13
  -2      6     -3      14
   3      7      11     16
>>> A[2:,2:] = xrange(4)
>>> print A
  -1      1     -1      1 
   1      6      9      13
  -2      6      0      2
   3      7      1      3