Arithmetic with Matrices

The important thing about matrices in Octave is that the arithmetic operators work as you would expect from math rather than programming.

As long as the matrices and scalars involved in the expression are conformable then the operation will be a matrix operation.

For example:

A=[1,2,3;4,5,6]B=[7,8;9,10;11,12]A*B

will multiply the two matrices together.

If C=10 then

C*A

is a scalar multiplication of each element of A by 10.

The only variation in the way matrices are treated is that you can opt for an element-by-element operation by putting a dot before the operator.

For example

A.*B

performs an element-by-element multiplication of the two matrices and not a matrix multiplication i.e. aijbij.

There are two specifically matrix operations that we need to know about. The single quote performs a complex transpose, e.g.

A=[1,2,3]'

is a column vector. For a real matrix the dash is a simple transpose. If the matrix is complex the dash also takes the complex conjugate (i.e. it is the Hermitian transpose). If you want a simple transpose of a complex matrix then use dot single quote.

The second is the inverse which is more complicated. You can use the inverse function to find the inverse of any square non-singular matrix. For example

A=[1,2;3,4]B=inverse[A]A*B

displays the identity matrix.

There is another way to use the inverse via an operator.

The expression x\y is the left division of y by x and is equivalent to

inverse(x)*y

The advantage of using this notation is that the inverse isn't actually used in the calculation.

The expression x/y is the right division of x by y and it is equivalent to

x*inverse(y)

Again the inverse matrix is never computed and generalized inverses are used if necessary.

Indexing

In an ideal world we would just define some matrices and get on with combining them using matrix arithmetic. In practice matrices are often the wrong shape for the job and we need to get at sub-matrices or even single elements.

This is what indexing is all about - specifying sub-matrices.

The indexing operator is () and if you specify index values for each dimension of a matrix then things work as you would expect . For example

A(1,2)

is the value in row 1 column 2.

You can assign a new value to a single element e.g.

A(1,2)=3

If you supply just one index for a 2D matrix then it is treated as a single 1D column vector obtained by stacking up each column of the original matrix.

To define sub-matrices you need to use more complicated indexing. There are two approaches - vectors of simple indexes or ranges.

Vector Indexes

A vector of indexes just picks out the combined set of elements that each index would pick out. For example:

A([1,2],1)

picks out A(1,1) and A(2,1) and the result is a column vector because you have specified part of a column of the original matrix.

It doesn't matter if the vector of indexes is a row or column vector. That is A([1;2],1) is the same as A([1,2],1).

You can use vector indexes in both index positions. for example:

A([1,2],[1,2])

picks out A(1,1), A(2,1), A(1,2) and A(2,2) which is returned as a 2x2 matrix because this is the "shape" of the sub matrix in the original array.

The most important thing to notice about vector indexes is that they allow you to pick out non-contiguous columns and rows. For example:

A([1,3],[1,2])

also picks out a 2x2 matrix but it takes the intersection of the first and third rows and the first and second columns.

In general if you write something like:

Matrix[ v1, v2]

where v1 and v2 are vectors then this gives a matrix made up of the rows specified by v1 and the columns specified by v2.

You can use variables within vector indexing, for example:

v1=[1,3]v2=[1,2]A(v1,v2)

works and is the same as:

A([1,3],[1,2])

Also:

s=1A([s,3],[1,2])

is allowed.

Notice that any sub-matrix you specify can be retrieved or you can assign a matrix of the same size to it. For example:

A([1,3],[1,2])=[0,0;0,0]

zeros the intersection of the first and third rows and the first and second columns.

Range indexes

Vector indexes work well when you want to select a small number of rows and columns but they are a lot of work if the number increases. For example how would you select the first 1000 elements of column 1? Clearly

A([1,2,3,4....],1)

isn't going to be easy typing.

The solution is to use a range. A range represents a numerical range of value. For example, 1:10 generates the numbers 1 to 10. You can also specify a step size, so for example 1:2:7 (or 1:2:8) generates 1,3,5,7. Using a range you can pick out lots of rows and columns very easily but only if they have a simple numerical pattern.

For example to pick out the first 1000 elements in column 1 you would write:

A(1:1000,1)

You can write

A([1:1000],1)

and get the same 1000 rows but a range is interpreted as a row vector so you don't have to.

If you would like every other row of the matrix you can use:

A([1:2:1000],1)

In general a range is specified as

start:increment:end

and if you leave out the increment it is assumed to be 1 and the range is

start:end

The increment can be negative.

You can also specify a default range as just a : which means the entire possible range. So

A(:,1)

means all the rows and column 1 i.e. all of column 1 and

A(1,:)

means all of the columns and row 1 and finally

A(:,:)

is the same as A.

You can also use A(:) to mean all of the rows and columns returned as a single column vector in column order.

As with vector indexes you can use variables to specify the start, stop and increment values.

You can also assign to the selected sub-matrix if what you are assigning has the same size as the sub-matrix.

Finally you can mix vector and indexing.

If you want to pick out the first 1000 rows and the 1500th row you can write:

A([1:1000,1500],:)

Also notice that an index can occur more than once in both vector, range and mixed indexing. If this happens the column or row is included more than once. This can be used to construct larger matrices from smaller ones - more on this in the next section. For example: