Octave is powerful programming language that lets you quickly prototype mathematical algorithms. Octave is very similar to MathWorks’ MATLAB.
Everything is a Matrix
When starting out with Octave it is best to think of everything as matrix, because that’s really how Octave sees it. So, the following statement:
octave> x = 5
x = 5
Would create a 1x1 matrix that stores the value of 5. Don’t think about it as a single value, think about it as a matrix. To further show this, run the "whos" command that prints out the defined variables:
octave> who
Variables in the current scope:
Attr Name Size Bytes Class
==== ==== ==== ===== =====
x 1x1 8 double
Total is 1 element using 8 bytes
Since everything is a matrix, let’s go ahead and create a bigger one:
octave> x = [ 1 2; 3 4 ]
x =
1 2
3 4
The semicolon above signifies the end of a row, and the spaces separate the values in the array. Now if you use the whos command, x is a 2x2 matrix.
Octave also has some short cuts for creating common arrays. Say you wanted to create an array that stored the numbers from 1 to 10, you could do it like this:
octave> x = 1:10
x =
1 2 3 4 5 6 7 8 9 10
Where the first number specifies where to start and the last number specifies where to stop; both are inclusive. There is also an optional middle parameter that specifies the spacing between entries:
octave> x = 1:2:10
x =
1 3 5 7 9
This is a quick and easy way to create a matrix.
Indexing
You can also access elements of a matrix using an index. Note: the index of the first element is 1.
octave> x = 1:10;
octave> x(3)
ans = 3
The index into the matrix is specified in parenthesis after the variable name. The other interesting part of this code same is that you can suppress the echoing of the variable by adding a semicolon to the end of the line. That’s why the “x = 1:10;” line does not produce any output.
Matrices with multiple dimensions work in the same way:
octave> x = [ 1 2; 3 4 ];
octave> x(2,2)
ans = 4
Now, Octave has a couple of tricks up its sleeves. The first is that you can access the last item in the matrix by using the word “end”:
octave> x = 1:10;
octave> x(end)
ans = 10
You can even do something like this:
octave> x = 1:10;
octave> x(end-1)
ans = 9
Also, remember that everything is best thought of as a matrix. So, the index that you pass in isn’t just a value, it’s a matrix. That means that you can do things like this:
octave> x = 1:10;
octave> x(2:4)
ans =
2 3 4
This makes manipulating matrices much easier. If you don’t specify a starting index or a stopping index, it will grab the whole row:
octave> x = [ 1 2; 3 4 ];
octave> x(2,:)
ans =
3 4
Or the whole column:
octave> x = [ 1 2; 3 4 ];
octave> x(:,2)
ans =
2
4
There is one more interesting aspect of indexing in Octave. You can always use a single index to access an element in a multi-dimensional array:
octave> x = [ 1 2; 3 4 ];
octave> x(4)
ans = 4
Octave internally stores everything in column major order. What this means is that you start at the first element in the upper left hand corner and go down the first column to get the next element. Once the column is exhausted, then you move to the next column. So, that explains this:
octave> x = [ 1 2; 3 4 ];
octave> x(2)
ans = 3
Instead of going right to left, Octave goes from top to bottom. This is very important to remember when you are using a built in function like sum:
octave> x = [ 1 2; 3 4 ];
octave> sum(x)
ans =
4 6
Sum adds up the columns of the matrix, because it – like most things in Octave – works on the columns of the matrix first.
Operations
Remember that everything in Octave is a matrix, so this behavior shouldn’t surprise you:
octave> x = [ 1 2; 3 4 ];
octave> y = [ 4 5; 6 7 ];
octave> x*y
ans =
16 19
36 43
That’s because the asterisk means matrix multiplication. In the simple situation where both x and y are 1x1 matrices, it doesn’t change anything, but when they are 2x2 matrices it does. If what you really wanted was the first element in x multiplied by the first element in y and so on, that’s what Octave calls element-wise multiplication. To do that, just add a period before the asterisk:
octave> x = [ 1 2; 3 4 ];
octave> y = [ 4 5; 6 7 ];
octave> x.*y
ans =
4 10
18 28
There is no such distinction for addition and subtraction.
To raise a number to a power, use the carrot symbol:
octave> x = [ 1 2; 3 4];
octave> x^2
ans =
7 10
15 22
Remember that everything is a matrix, so if you want to square every element of the matrix, you need to add a period:
octave> x = [ 1 2; 3 4];
octave> x.^2
ans =
1 4
9 16
Another common operation is taking the transpose of a matrix:
octave> x = [ 1 2; 3 4 ]
octave> transpose(x)
ans =
1 3
2 4
There is also a short cut for that – a period followed by an apostrophe:
octave> x = [ 1 2; 3 4 ]
octave> x.’
ans =
1 3
2 4
A common misconception is that the apostrophe means transpose. What it really means is conjugate transpose, which is the same if all of the values in your matrix are real.
Looping
Another way to do the element-wise operations above would be to write a simple for loop:
octave> x = 1:5;
octave> y = 3:7;
octave> for(idx = 1:length(x)) z(idx) = x(idx) * y(idx); end
octave> z
z =
3 8 15 24 35
There are a couple of new things here, so let’s go through them. The first is the for loop. It defines a new variable idx and assigns it to every element in the matrix to the right, one at a time. The value on the right side of the equals sign (within the for loop parenthesis), just creates a matrix with the values from one to five:
octave> 1:length(x)
ans =
1 2 3 4 5
The inside of the loop, does the element-wise multiplication, and the “end” marks the end of the logic to be included within the loop.
Also, notice that I used idx as the variable name and not just the letter i. This is because the letter i is used for the imaginary number in Octave. Overriding this and making it a variable is very bad idea. This will only lead to frustration if you have to use imaginary numbers down the line. So, do yourself a favor and pick a different variable name. (Same thing goes with the letter j by the way, but this is usually less of an issue.)
For our 2x2 matrix, the loop would look something like this:
octave> clear all
octave> x = [ 1 2; 3 4 ];
octave> y = [ 4 5; 6 7 ];
octave> for(row = 1:size(x,1)) for(col = 1:size(x,2)) z(row, col) = x(row, col) * y(row, col); end; end;
octave> z
z =
4 10
18 28
There are two new things here. First is the usage of “clear all”. This deletes all of the variables that are currently defined. It is usually a good idea to put this at the top of any script that you write, so that previous attempts don’t interfere with the current attempt. The usage of the size function is also new. That tells you the length of a given dimension. Other than that, the code is pretty much the same.
Performance
Octave gets a bad reputation for having poor performance. This doesn’t have to be the case. Octave can be fast, but it takes a little work, and it isn’t completely obvious at first glance.
There are two keys to getting good performance out of Octave. The first is to avoid for loops. The for loop examples above could easily be replaced by an element-wise multiplication, which will be much faster. Doing a comparison for a 10,000 x 10,000 matrix, the element-wise multiplication took 13.87 seconds and the for loop took 3,782 seconds. Now, you don’t have to remove every for loop, just the ones that are doing the majority of the work. Start with the inner loop and work your way out.
Another trick to minimizing for loops is to using matrices as indices to do repetitive tasks. Even if the contents of the for loop has to be broken up into multiple matrix indexed statements, it will execute much faster. The reason is that octave is an interpreted language. This means that for every statement it encounters, it has to parse and compile that line into something that can be executed on the computer. With a for loop, it parses that line of code many, many times. By using matrix indices, Octave only has to compile the line once. It’s true that it will still do the for loop internally, but it is much faster when it does it internally.
The second trick is to pre-allocate all matrices. So, in the example above:
octave> x = 1:5;
octave> y = 3:7;
octave> for(idx = 1:length(x)) z(idx) = x(idx) * y(idx); end
octave> z
z =
3 8 15 24 35
The z matrix is built one element at a time. This means that before the loop executes, the z variable doesn’t exist. After the first iteration, the z variable is a 1x1 matrix with the value 3 stored in it. After the second iteration, the z variable is a 1x2 matrix with the values 3 and 8 stored in it. And so on. This successive growing of the z matrix is problematic because the matrix has to be stored contiguously in memory. So, on the first pass, it only has to find a spot big enough to store one value. On the second pass, it has to extend that memory space to be two values. Sometimes this is possible without moving where it is stored, sometimes it is not. Moving it takes additional time. By stating the size of the matrix up front with the zero command, we can reserve enough space to store the whole matrix, so that it doesn’t have to be shuffled around in memory:
octave> x = 1:5;
octave> y = 3:7;
octave> z = zeros(1, 5);
octave> for(idx = 1:length(x)) z(idx) = x(idx) * y(idx); end
octave> z
z =
3 8 15 24 35
Plotting
This is another one of Octave’s strengths. The ability to quickly and easily plot your data. Take an example like this:
octave> x = -3:0.1:3;
octave> y = e.^(-x.^2);
octave> plot(x,y)
Just looking at the data in the variable y, it would be difficult to see its shape, but it is very clear when plotted:
When plotting, a function that comes in handy is “linspace”. It creates a matrix of points that are linearly spaced apart from one another:
octave:48> linspace(1, 3, 5)
ans =
1.0000 1.5000 2.0000 2.5000 3.0000
The first number is the value to start at, the second is the number to end at, and the third is the number of points to generate. This is very helpful for generating graphs because it lets you quickly and easily change the number of data points to plot.
Getting Help
The help built into Octave is really good. To get information about a command just type help and then the name of the command:
help linspace
Improving your skills and comfort in Octave can be difficult because it usually takes a good deal of knowledge about the mathematical function that you are trying to use. As an example, using a command like “fft” is really easy, but understanding what is going on is much more challenging. At least after this short introduction, you should have a solid foundation to build upon.
Top Comments