Professor: Alan G. Isaac
Last modified: 2013 Nov 12

Gauss: Some Basics

This handout covers some basic features of GAUSS(TM), the matrix programming language and system produced by Aptech Systems. See Marc Nerlove's GAUSS Basics for more details. Read the GAUSS manual for more advanced features. I assume that GAUSS is already properly installed.

See Basic GAUSS for many more details.

To indicate a key to be pressed, I will sometimes use brackets. For example, [Enter] indicates that you should press the Enter key. If two keys are indicated in a bracket as [Alt-F2], press [Alt] and [F2] keys simultaneously.

Getting Started

Starting GAUSS

These instructions assume you have the Windows version of GAUSS, but a similar approach will work on other platforms. Double click the GAUSS icon to start GAUSS. You will be in the command mode of GAUSS, where you can interact immediately with the GAUSS interpreter. You will see the GAUSS prompt: in the latest Windows version the standard prompt is ». In the command mode, you can execute GAUSS commands interactively or run GAUSS programs stored in ASCII files.

Expressions

Often we want to compute a value from an expression. The simplest expressions are literal, such as 5.0 or "string1". We can use operators to combine expressions into more complex expressions, such as the sum 5.0+17.5. We often work with values that have been associated with variable names, in which case our sum might look like x+y. An expression can combine values, operators, and functions.

Statements

Unlike an expression, a statement does not return a value. This does not mean it has no effects. Consider a print statement: its effect is to print to the screen (or possibly to a file) a representation of the value of an expression. For example, print 5.0+17.5;.

Assignment statements allow us to associate values with variable names. E.g, x=5.0 or z="string1". Expressions can be used to produce the value on the right hand side of an assignment statement.

Flow control statements, such as conditionals and loops, are discussed below.

Exiting GAUSS

In the Windows version, exit in any of the standard Windows fashions.

Using the GAUSS Editor

Start GAUSS. As usual, you see the command window when GAUSS starts up. From the menu pick File/New. An editing window opens. In this window enter the single line:

print "test";
From the menu pick File/SaveAs and save the file as g:\temp.gau. (Caution: some versions of GAUSS will not prompt you before overwriting an existing file! Also note that we use the .gau extension rather than the more traditional .prg extension, since the latter is used by many other applications.) From the menu pick File/Run; in the dialog box that opens, enter your file name and click Open. Return to the command window to see the results of running your first program. Exit GAUSS.

Comment: you can use any ASCII editor to create your GAUSS program files. Although the GAUSS editor has some program specific features, I recommend using your favorite editor (as long as it is reasonably powerful—ideally if will have syntax highlighting). You can edit the gauss.cfg file to tell GAUSS you wish to use an external editor.

Preparing an Output File

It is often a good idea to keep track of your GAUSS sessions with an output file. The statement
output file=a:\myout.out;
designates a file (a:\myout.out, in this case) that will hold the results of all your print statements. Open your output file to begin appending new text to it with the statement
output on;
Or, to first discard the current contents and then open the file to begin writing to it, use the statement
output reset;
Before you can print out or edit your output file, you have to close the output file with the statement
output off;
(The statement end; also closes the output file.) If you run a program that does not reach this statement because of errors, you must issue this command from the COMMAND mode to close the output file before you can examine it. You can open and close an output file as many times as you want in a single session.

Comment: You will usually want to choose a decent monospaced font, such as CourierNew, to view your GAUSS output. (My favorite is AndaleMono.) In GAUSS for Windows, this can be set for the screen output by choosing Font, Select, CourierNew from the menu. When viewing the output file, you will want to make a similar selection for the screen display of your editor.
You can use the locate, csrlin, and csrcol commands to control screen output. The locate positions the cursor on the screen, while csrlin and csrcol return the current cursor position. For example, to print something at the 10th line, 40th column on the screen, execute the statement locate(10,40); right before the print command. (This actually prints to the screen buffer. To make sure it displays immediately, you can use print/flush.)

Start GAUSS. At the GAUSS prompt, type
output file=a:\temp.out on;[Enter]
to open an output file. Then type
print 36*2;[Enter]
x=36; print x*2;[Enter]
output off;[Enter]
Finally, exit GAUSS. Use a text editor to look at the output file and see what was captured there. Every version of Windows should include the text editor NotePad, which will be adequate for now. (If you are on campus, TextPad and Vim are available as EagleNet applications.)

Basic Syntax

This section lists some of the general rules of syntax for GAUSS programs.

Case

GAUSS is case insenstive: it does not distinguish between upper and lower case except inside double quotes.

Basic Numeric Operators

The basic numeric operators for simple arithmetic in GAUSS are + for addition, - for subtraction, * for multiplication, / for division, and ^ for exponentiation. Finally, = is the assignment operator. For example, the statement
Y=X*Z;
assigns to product of X and Z to Y.

The binary operator % is used for modulo division. Given two integers X and Y, the expression X%Y returns the remainder from the division of X by Y.

The unary operator ! is used to compute factorials. Given an whole number x, the expression x! returns the factorial of x. Recall that 0! is 1, and that the factorials of larger numbers are produced recursively: (n+1)!=(n+1)*n!.

Printing Basics

If you have assigned values to X and Y, the statement
print X Y;
will print these values to the screen. Note that the print command interprets spacing as separating different objects to be printed. (Don't forget this!) If you have turned on an output file, these values will also be written to the output file. You can print unassigned strings by using double quotes:
print "This will be printed.";
Comment: You can use ? instead of print:
? X Y;
Furthermore, the print command can generally be omitted altogether from a print statement. However, we will use it explicitly in this handout.

Statements

A GAUSS program is a sequence of statements. Statements in a GAUSS program must end with a semicolon.

Comments

In addition to a sequence of statements, a GAUSS program should contain explanatory comments. Comments can be placed inside /* and */, which can nest other comments. Comments can also be placed inside @ and @, which cannot nest other comments. It is wise to fully comment your programs.

Start GAUSS. At the GAUSS prompt, type
output file=g:\temp.out reset;[Enter]
to open g:\temp.out for overwriting. Then type
print "Example with a comment.";[Enter]
Y=2;Z=36;X=Y*Z;[Enter]
/* Comment: note irrelevance of capitalization. */[Enter]
print X Y Z; print x y z;[Enter]
output off;[Enter]
Finally, exit GAUSS. Use a text editor to look at the output file and see what was captured there. Note that your comment was not printed to the screen or to the output file.

Programs

GAUSS programs are commented collections of statements stored together in ASCII files. You can create and edit these files using the text editor of your choice.

Editing a File with GAUSS

GAUSS includes a full screen editor. To use the GAUSS editor from the GAUSS prompt, type
edit filename.ext;[Enter]
to edit a file named filename.ext, for example. (This can be a new file.) You will be in the EDIT mode of the GAUSS editor.

As usual, you can move around the file using the arrow keys and the [PgDn] and [PgUp] keys. You can edit the file by deleting characters using the [Del] or [Backspace] keys and by typing in new characters. Find other editing tools by looking at the Help menu. (In the DOS version, type [Alt-H].)

To save the file click Save in the Windows version. Then close the file by pressing [Alt-F][c]. In the DOS version, press [F1] to save the file and exit the editor. Or you can exit the GAUSS editor without saving by pressing [Alt-X]Q.

Running a Stored Program from the COMMAND Mode

You can determine your current directory with the command ``cdir(0)``. The current directory is also calle the working directory. When you enter ``run *filename*``, GAUSS will first search for *filename* in the current directory. You can change your current directory to *dirname* with the command ``chdir dirname``. (On Windows, GAUSS accepts single backslashes as separators in ``dirname``.)

At the GAUSS prompt, type
run filename.ext;[Enter]
to run a the program in a file named filename.ext, for example.

new; and end; Statements

It is usually a good idea to make the first line of every program
new;
and it never hurts to make the last line of every program
end;
If you use the new command, it must be the first line in your program. This is particularly useful if you are working at the command prompt, running a program repeatedly as you work on it. The new command deletes all the symbols you have have previously defined from GAUSS's memory, so that you can run your program "afresh". (Otherwise you may not notice that you are depending on symbols that you were defined in a previous version of your program but are no longer defined in the current version.)

Assuming you are working on your G: drive, start GAUSS and at the command line type edit a:\temp.src;. Enter the following text in your new program file:
new;
X=36;
Y=X*2;
print Y;
end;
Save and run this program. Then exit GAUSS.

Program Flow

Often we need to control program flow. The two basic ways of doing this are branching and loops. See the manual for a more detailed discussion.

Relational Operators

There are six basic relational operators: EQ (equal), NE (not equal), GT (greater than), LT (less than), GE (greater than or equal), and LE (less than or equal). Each has an alternative symbolic form: ==, /=, >, <, >=, <=. The basic relational operators return a scalar: either a zero (false) or one (true). The return is true only if the comparison is true for each element of the objects compared. To take an example from the GAUSS manual: x/=y returns a one only if each element of x does not equal the corresponding element of y. Since a comparisons to determine the failure of matrix equality should return a one if any element of x does not equal the corresponding element of y, such comparisons must be coded as not x==y instead.

Before using relational operators for flow control, it is a good idea to read The Perils of Floating Point. Bottom line: comparisons between integers will generally give you what you expect, while comparions to floating point numbers may well surprise you.

For byte by byte comparisons of character data or strings, precede symbolic version of the relational operator with a dollar sign. E.g., $>=. For element by element comparisons, precede the relational operator with a period. E.g., .>= or .$>=.

There is an element-by-element version of each relational operator, formed by preceding the operator with a dot. For example
let p = 1 0; print (p .ge p');
will return a matrix of zeroes and ones.

Branching

Conditional branching is initiated by an if statement and ended by an endif; statement. For example, your program might contain the statements
if x>100; y=1; endif;
The if statement tests a condition, and then GAUSS executes that following statement(s) only if the condition tests true. In this case, your program sets y=1 only if x>100.

More complex conditional branching can be controlled with elseif and else; statements. GAUSS also supports unconditional branching with goto and gosub statements. (Comment: it is seldom good programming practice to use goto.) See the manual for details.

Loops

GAUSS supports three looping constructs: for, do while, and do until.

for

This section discusses the for construct, which is the fastest of the three. A for loop requires a name for a counter (say, ctr), along with a start value, and stop value, and a stepsize. (The counter is local to the loop.) Place the statements to be executed in the loop between the statement for ctr(startval,stopval,stepsize); and the statement endfor;. So, for example, to print the numbers 1 through 10, we could say
for ctr(1,10,1); print ctr; endfor;

Note: up to GAUSS 3.2.26, you need a workaround to pass the counter to a procedure.

Use a for loop to write a GAUSS procedure that has two input arguments, a square matrix and an integer power, and one output argument, the matrix raised to that power.
Hint: do not use the GAUSS code x^n, because the GAUSS exponentiation operator is element-by-element.

do while

This section discusses the do while construct, which is the most common of the three looping constructs. A do while loop begins with a test: the loop body is only executed if the test is passed, and it is repeatedly executed until the test fails. Ordinarily you will define a counter and use it for your test. Place the statements to be executed in the loop between the statement do while test; and the statement enddo;. So, for example, to print the numbers 1 through 10, we could say

ctr = 1;
do while ctr <= 10:
  print ctr;
  ctr = ctr + 1;
endo;  

Language Peculiarities

There is no built-in way to remove specified rows or columns. If you want to remove rows 2, 3, and 5 from matrix ``X``, do the following.

todelete = {2, 3, 5};
mask = zeros(rows(X),1);
mask[todelete] = 1
Y = delif(X, mask)

There is no built-in permutation function. The following works (but is slow).

proc (1) = permutations(x);
  local n,p,xi,subx;
  n = rows(x)*cols(x);
  if n==1;
    retp(x);
  endif;
  x = reshape(x,n,1);
  p = {};
  for i (1,n,1);
    xi = x[i] * ones((n-1)!,1);
    subx = delif(x, seqa(1,1,n).==i);
    p = p | (xi ~ permute(subx));
  endfor;
  retp(p);
endp;

Functions and Procedures

Often our programs involve code that we want to use over and over again. In GAUSS we do this with functions and procedures.

Functions

You can create functions with the fn command. GAUSS functions must be created with a single statement and return a single value. You can call a GAUSS function just about anything you like. For example, let us define a simple function that we will call areafn that calculates the area of a rectangle from its length and width. At the GAUSS prompt type
fn areafn(len,w)=len*w;[Enter]
Note that there is only one semicolon on that line: the declaration and definition always take place in a single statement. When you give GAUSS the command fn, you are saying ``this statement declares a function''. The first thing you must do after that command is provide a name for your function. In the example, we provide the name areafn, but you can pick almost anything. Then you must indicate what arguments it will take. In the example, we write (len,w), which tells GAUSS that the function will take two arguments, which we will call len and w in the function definition. (When you actually used the function, as we will in a moment, you of course put in numbers or objects that have been assigned numbers as their values.) Lastly, you offer GAUSS the actual definition of the function, which you state in terms of the variables that you have chosen to represent your arguments. In the example, we write len*w, using the variables len and w that we chose to represent the arguments. (These variables do not interact with the rest of the program, which might even have variables of the same name; we say the variables in the function definition are local variables.) Now test your new function: at the GAUSS prompt type
print areafn(5,3);[Enter]
Now let's test your understanding of what you have done. What do you expect to get when you tell GAUSS the following.
x=50; y=30; w=3; len=5;[Enter]
print areafn(x,y); print areafn(w,y);[Enter]

Are your expectation matched by reality?

Caution: Functions Are Compiled!

GAUSS will compile all your functions and procedures before executing your program. This has an important implication: putting a function in a certain location in your program does not imply that its code will be executed at that point in your program. (In fact, it is good practice to place all of your functions and procedures in a separate section at the very end of your program.) In particular, if you use the same function name twice, the last definition will generally apply for your entire program. You can ask GAUSS to warn you about this by pressing ctrl-w (or turning changing declare_warnings on in the gauss.cfg file).

Passing Variables

Generally we will not be providing numbers directly to a procedure but will be assigning numeric values to variable names and providing these variable names as the arguments. Let us continue working with our new areafn function. At the GAUSS prompt type
length=5; width=3;[Enter]
Now we will pass these to our function.
print areafn(length,width);[Enter]
Passing variables to procedures works exactly the same way.

Procedures

Since GAUSS functions are limited, we work more often with procedures. Procedures definitions must include three parts (in this order): a statement beginning with proc that declares the procedure, the body of the procedure, and an endp; statement that ends the procedure definition. Start the GAUSS editor and define a new procedure areaproc by typing the following statements in a new program file.


proc (0) = areaproc(l,w);
print "The area of the rectangle is:" l*w;
endp;

Save and close your new program file. To compile your new procedure, just run your program. Then you can test your new procedure by executing it from the GAUSS prompt. E.g.,
areaproc(36,2);[Enter]

Output from a Procedure

When you look at the procedure areaproc, the presence of (0) = in the declaration statement may seem mysterious. This just says that the procedure has zero ``output arguments'': while it does something (i.e., prints something to the screen), it does not return any values that might be used elsewhere in your program. Usually we want to use some of the values calculated by our procedures. GAUSS allows any number of items to be returned from a procedure by means of an retp statement. The retp statement causes your program to ``return'' from the procedure: it is required unless your procedure has no output arguments. (We use the terms ``item returned'' and ``output argument'' synonymously.)

Let us modify areaproc to return the the length and with that were passed to it along with the area we are calculating. Open your program file and make two changes: i. change the number of items returned to 3, and ii. change the program body to an retp statement that returns these items.


proc (3) = areaproc(l,w);
retp(l,w,l*w);
endp;

Note that the items to be returned are separated by commas. Save, close, and run your program, and then test it at the GAUSS prompt by typing
{length,width,area}=areaproc(36,2); print area;[Enter] Note that the assignments take place to comma-separated names within braces. We assigned the returned value l*w to the variable area and then printed the value of area. (You can of course use pretty much any names you wish.)

Local Variables

Often we will want to use variables in our procedures that do not (and should not) affect other parts of our program. We say we want these variables to be local. In fact we have already encountered local variables: procedure and function argument names are automatically declared as local variables. We have the option of declaring additional local variables in the procedure body. These can be declared anytime before they are used, but it is often good practice place the declaration of local variables as the first statement in the procedure body. We will illustrate this by once again changing areaproc. Open your program file and make three changes: i. declare the local variable a, ii. assign the product l*w to the local variable a, and iii. return a instead of l*w.


proc (3) = areaproc(l,w);
 local a;
 a=l*w;
retp(l,w,a);
endp;

Save, close, and run your program, and then test it at the GAUSS prompt by typing
print areaproc(36,2);[Enter]
Now type the following: print a;[Enter]. What happens? Why?

Start GAUSS. At the prompt, write a function that calculates the area of a triangle from its base and height. Test it.
Next open a new file, say G:\temp.src, and write a procedure that does the following: calculate the area of circle with diameter d and assign it to a local variable ac, calculate the area of a square with side d and assign it to a local variable as, print out the results, and return the two areas as two output arguments from your procedure.

Hint: when writing a procedure, write the code for the procedure body first, and when it works, ``wrap'' it in a procedure (by adding the procedure declaration, local variables declaration, return statement, and endp; statement.).

Passing Functions and Procedures

Sometimes we want to write a procedure that can take a function as an argument. For example, we may want to write a procedure that produces the gradient of any function passed to it. In GAUSS we accomplish this by passing a pointer to the function. A pointer to a function is produced by prepending an ampersand to the function name. Here is a simple example that passes a function with two arguments.


proc (1) = df(&fpass,x,y);
  local fpass:fn;
retp(fpass(x+1,y+1)-fpass(x,y));
endp;

Note that the procedure declaration says we will pass a pointer, and the local statement tells us that the local name fpass points to a function. Type this procedure and our areafn function (above) into a program file. Save, close, and run the program. Then test your program by typing at the GAUSS prompt:
y=df(&areafn,36,2); print y;[Enter]

Passing procedures works exactly the same way, except that the local declaration is proc rather than fn.

Write a procedure that takes a function and two scalars as arguments and uses these to return two items: the changes in the value of the function that results from a unit increment in each of the two arguments.

How to Write a Procedure

When you write a procedure, you are writing code to accomplish something specific and then 'wrapping' it in a procedure declaration. So break this into two steps.

Step 1: write the code that accomplishes what you want to do.

IMPORTANT: Write you code a line at a time when possible, testing it after each line you write. Do not accumulate a bunch of untested code, or you will have troulbe debugging it.

Step 2: wrap your code in a procedure declaration.

For example, suppose I want to write code to compute n!.

First I write the most basic code for some value that you can easily check. Let us compute 4!.

nfac = 1;
for i(1,4,1);
  nfac = i * nfac;
endfor;
print nfac;

Run that code and make sure it works. Next generalize it slightly: make n a variable.

n=4;
nfac = 1;
for i(1,n,1);
  nfac = i * nfac;
endfor;
print nfac;

Run that code and make sure it works. Check it by trying a couple different values of n. If it runs correctly, you are ready to put it inside a procedure body.

First look at all the variables you used: n and nfac. (You can disregard any for loop counter, like i in this example.) Divide these up into variables that will be arguments for your procedure (n in this case) and those that need to be declared local to the procedure (nfac in this case). This means your procedure definition will look like this:

proc (1) = factorial(n);
  local nfac;
  @ ... do stuff here ... @
retp(nfac);
endp;

Finally you are ready to place your working code into your procedure definition.

proc (1) = factorial(n);
  local nfac;
  nfac = 1;
  for i(1,n,1);
    nfac = i * nfac;
  endfor;
retp(nfac);
endp;

As always, make sure it is running correctly by trying some simple values for n. Also, once your code works for the basic cases you care about, you may consider trying to refine it. (For example, by checking for bad input values.)

Matrices

Matrices are the basic data type of GAUSS. (Scalars and vectors are just special cases of the basic type.) Speedy matrix operations are a core strength of this programming language. The simplest way to create a matrix is to assign the elements directly to a variable. For example, at the GAUSS prompt type
x = {2 4, 6 8};[Enter]
to create a two by two matrix of numeric values. The values are entered in row-major order (i.e., row by row). Note the use curly braces and of the comma to separate rows.

A second very closely related way to create the same matrix is to use the let command.
let x[2,2] = 2 4 6 8;
The row and column dimensions can be given in the brackets, and the elements are again entered in row-major order (i.e, row by row). (The let command can also be optionally used in example above with the braces, but not in the following example using the concatenation operators.)

As a third way to create the same matrix, you can instead use the concatenation operators: ~ is horizontal concatenation (``next to'') and | is vertical concatenation (``on top of''). We can produce the same matrix by typing
x = 2~4|6~8;
This third method has an advantage over the other two: variables can be concatenated (assuming they represent conformable matrices). and you can perform operations as well. For example,

x11=2;
x = x11~4|6~8/3; 

To see your matrix, type
print x;[Enter]

Once you have created a matrix or two, you can create new matrices by assignment. To continue the example of this section, at the GAUSS prompt type
y=inv(x);[Enter]
to assign to y the inverse matrix of x, which of course is also a two by two matrix of numeric values. Let's check to see if x*y produces and identity matrix, as it should. At the GAUSS prompt type
print x*y;[Enter]

Reading and Storing Data

We will often want to create matrices from data files. Suppose you have a file on the A: drive that contains N observations and is named mydata.dat. (This file should contain space-delimited, tab-delimited, or comma-delimited ASCII text.) At the GAUSS prompt type
load X[]=a:\mydata.dat;
This reads data stored in the ASCII file into an N×1 matrix.

Once you have read the data into an N×1 matrix, you will usually want to reshape it using the reshape command. For example,
X = reshape(X,r,c}
will turn X into an r×c matrix, where the data will be read in a row at a time.1 What if you have more or fewer data points than your chosen matrix size accommodates? If the matrix is too small, some of the data will be discarded. If the matrix is too large, all the data will be loaded and then GAUSS will return to the first element to keep loading data until the matrix is full.
Comment: the command
load X[r,c]=a:\mydata.dat;
will perform the load and reshape operations all at once, which can be convenient if you are certain about what will be loaded. But it is generally a good idea to load the data, check how much data was loaded, and then reshape to your desired matrix dimensions. The number of rows in a matrix M can be determined as rows(M):
print rows(M);

If you have already created a GAUSS matrix, then you can easily save it for use in another session.
save a:\somename=M;
stores the matrix named M on disk in a specially formatted file named somename.fmt. You can now read this data into a matrix whenever you wish. For example,
load X=a:\somename; will create a matrix named X that is identical to the originally stored matrix M.

Create an ASCII file of numeric ``data''. Write a program that reads this data, checks how much was loaded, and reshapes it into a matrix of desired dimensions.

Operators for Matrix Manipulations

Assignment operator:

Assignments are done with one equal sign.
Y=3;
X=Y;

assigns the value 3 to 1 x 1 matrix Y and then assigns this value in Y to the 1 x 1 matrix X.

Familiar Operators:

The operators +, -, and * can be used according to standard rules of matrix algebra. For example,
Y=X*Z;
performs matrix multiplication when X and Z are conformable in the sense of matrix algebra.
Comment: be aware that although these operators behave as expected for conformable matrices, they return a well defined value even in many non-conformable cases. See the discussion of element-by-element operators, below.

Element by Element Operators:

Notice how the standard matrix operations addition and subtraction operate in element-by-element fashion: if X is m × n and Y is m × n, then X and Y are conformable for these operations. For example,
Z=X+Y;
produces a matrix Z whose i,j-th element Z[i,j]=X[i,j]+Y[i,j]. This element-by-element behavior is extended in GAUSS to multiplication, division, and exponentiation. For example, the Hadamard product is produced quite simply using GAUSS's element-by-element multiplication operator: given two matrices of identical size, say X and Y, then X .* Y yields the matrix whose i,j-th element is X[i,j]*Y[i,j].

The element-by-element operators are the following:

Element-by-element conformability in GAUSS is quite powerful: it includes the standard case but is much more extensive. For example, a matrix has element-by-element conformability with any matrix that has the same number of rows or columns. If X is a r×c matrix and Y is a r×1 column vector, then
Z=X+Y;
will yield an r×c matrix such that to each row of X is added the element in the corresponding row of Y (so that Z[i,j]=X[i,j]+Y[i]). Similarly, if X is a r×c matrix and Y is a 1×c row vector, then
Z=X .* Y;
will yield an r×c matrix such that Z[i,j]=X[i,j]*Y[j]. The element-by-element operators allow you to perform such operations very easily.

Division

The operator / can be used with matrices, but be careful. Consider the statement X=Y/Z;. If Y or Z is a scalar, then the division operation will be element-by-element. However, when the variables are both matrices then GAUSS will compute a generalized inverse. The result is X = (Z'Z)-1Z'Y, which is the same as X = Z-1Y when Z is invertible. Since the division operator behaves in an unfamiliar way, it is a common source of programming errors.

Indexing operator:

Brackets [] are used to index elements in matrices and vectors.
Y[1]=X[3,3];
assigns 3-3 element of the matrix X to the first element of the vector Y. Commas are used to separate row indices from column indices. A vector can take a single index.

Period:

Dots are used in brackets to signify "all rows" or "all columns".
Y=X[.,3];
assigns the third column of X to a column vector Y.

Colon:

A colon is used within brackets to create a continuous range of indices.
Y=X[1:5,.];

Transpose operator:

' transposes matrices. For complex matrices this computes the complex conjugate. There is also dotted version (``.'``) that produces a bookkeeping transpose.

It is a GAUSS idiom to use the transpose operator to produce the complex conjugate of a complex scalar. E.g., ``let x=1+2i;print x';``

Vertical concatenation:

| is used to concatenate two matrices vertically (i.e.,create a new matrix by vertically stacking two matrices). E.g.,
Z=X|Y;

Horizontal concatenation:

~ is used to concatenate two matrices horizontally (i.e., create a new matrix by horizontally juxtaposing two matrices). E.g.,
Z=X~Y;

Numerical Gradient

GAUSS provides the command gradp() for the computation of numerical derivatives. The two input values are a pointer to a function, which must take a single input argument, and a column vector of initial inputs for the function. The output is the gradient of the function. The use is illustrated below.


// SOME DEMONSTRATIONS OF gradp

//DEMONSTRATION FOR REAL-VALUED UNIVARIATE FUNCTION
//First define the function: f1:R->R.";
fn f1(x)=x^2;
// The gradient shd be 1 × 1 
// (We'll evaluate at x=2.)
print gradp(&f1,2);

//DEMONSTRATION FOR REAL-VALUED BIVARIATE FUNCTION
//First define the function: f2:R2->R
fn f2(x)=x[1]^2+x[2]^2;
// The gradient shd be 1 × 2
// (We'll evaluate at x'=[2,1].)
print gradp(&f2,2|1);

//DEMONSTRATION FOR A SYSTEM OF FUNCTIONS
"Define a procedure f3:R4->R2.";
proc (1) = f3(in);
        local x,y,alpha1,alpha2,out1,out2;
        @use indexing to get elements@
        x=in[1];
        y=in[2];
        alpha1=in[3];
        alpha2=in[4];
        @this time we have a 2x1 output@
        out1=x^3/y^2 -2*alpha2*alpha1;
        out2=alpha1^3/x^2 + 5*alpha2/y;
retp(out1|out2);
endp;
"The gradient shd be 2x4.";
"(We'll evaluate at in'=[2,1,2,1].)";
print gradp(&f3,2|1|2|1);


Note that a our numerical gradient function, gradp, does not return a function. It returns that value of the gradient at a particular point, which you supply.

Language Oddities

Newly created objects may not be indexed before being assigned. For example, ``seqa(1,1,12)[2:4]`` raises an error, but ``y=seqa(1,1,12); y[2:4]`` works fine.

Basic Graphics

The GAUSS graphics library graphs data that you have stored in matrices. GAUSS offers extensive control in the production graphs (see chapters 10 & 11 of volume 1 of the manual).

In order to use the GAUSS graphics library, you must include the statement
library pgraph;
in your program file. It is also generally a good idea to reset the graphics global variables to their default values with the statement
graphset;
Here is a simple example.


library pgraph;           @activate the pgraph library@
graphset;                 @reset graphics defaults@
degrees = seqa(1,1,360);  @column of numbers: 1..360@
radians = pi*degrees/180; @convert degrees to radians@
sinr = sin(radians);      @column of 360 numbers@
xy(radians,sinr);         @xy plot of sinr against radians@

Now let us plot two series over the same domain. Just use horizontal concatenation to make a matrix of values, one column for each relation. (See the last line of the next example.) We will also spruce up our graph a bit.


library pgraph;                @activate the pgraph library@
graphset;                      @reset graphics defaults@
degrees = seqa(1,1,360);       @column of numbers: 1..360@
radians = pi*degrees/180;      @convert degrees to radians@
sinr = sin(radians);           @column of 360 numbers@
cosr = cos(radians);           @column of 360 numbers@
title("Sine and Cosine")       @add a title@
xlabel("Radians")              @label the horizontal axis@
_pmcolor = 0|0|0|0|0|0|0|0|15; @set graph background to white@
_pltype = 6;                   @set all lines to solid@
xy(radians,sinr~cosr);         @xy plot of sinr and cosr against radians@

You will note that the resulting graphs look a bit truncated, because GAUSS allows the abscissa to increment all the way to seven radians while we are only plotting values up to 2*pi radians. You can fix that with the xtics command. Just include the following statement in your program before xy command.
xtics(0,2*pi,pi,pi/2);

When your program draws more than one graph, the default for GAUSS is that only the last one is accessible in the TKF file viewer. GAUSS draws each over the previous one. (Note that you can use this for animation!) To get GAUSS to tile the graph windows, put at the top of your command file:
call setvwrmode("many");

Final Observation

Now you will want to read the first three chapters of volume one of the GAUSS manual, which cover the language basics in more detail, and chapter 10, which discusses the graphics library.

Appendix

Miscellaneous Useful Commands

To clear the command window, use ``cls``.

You can use ``error`` to create a user-defined error code. You can use ``errorlog`` to log error message.

You should insure against unwanted inputs to your functions and procedures. You must decide how to handle such inputs. To report the problem, you can print a warning, using the GAUSS error logging facilities. GAUSS provides the errorlog statement for this purpose. You can then return the value computed with the bad input, return a missing value representation, or stop the program. The right choice depends on your application.

GAUSS handles complex numbers. For example, you can produce the complex number 2-3i with the code i = sqrt(-1);x=2-3*i;. Note that GAUSS does not set the value of i by default; and you can pick any variable you want for this purpose. You can test whether a variable has an imaginary part with the iscplx command. If you want to turn off the automatic generation of complex numbers by the sqrt, ln, and log command, set sysstate(8,0).

Logical Operators

GAUSS provides logical operators for complement (not), conjunction (and), disjunction (or), exclusive disjunction (xor), and equivalence (eqv). A non-zero input value is treated as `true'. The operators return a 1 for `true' and a 0 for `false', using standard truth tables.

There is an element-by-element version of each logical operator, formed by preceding the operator with a dot. For example
let p = 1 0; print p.or(.not p);
will return a column vector of ones.

Useful Functions:
Mathematical And Statistical

abs(x) matrix: absolute value of x, element by element.
exp(x) matrix: natural base to powers given x, element by element.
ln(x) matrix: natural log of x, element by element.
log(x) matrix: log base 10 of x, element by element.
sqrt(x) matrix: square root of x, element by element.
cols(x) scalar, the number of columns in x.
rows(x) scalar, the number of rows in x.
meanc(x) column vector: mean of each column of x.
stdc(x) column vector: standard deviation of each column of x.
sumc(x) column vector: column sum for each column of x.
vcx(x) matrix: variance-covariance matrix for the columns of x.
corrx(x) matrix: correlation matrix for the columns of x.

Useful Functions:
Generating Matrices

eye(n) n×n identity matrix.
ones(r,c) r×c matrix of ones.
zeros(r,c) r×c matrix of zeroes.
seqa(s,i,n) n×1 matrix: a sequence of n numbers starting with s and with additive increment i.
seqm(s,i,n) n×1 matrix: a sequence of n numbers starting with s and with multiplicative increment i.
rndn(r,c) r×c matrix of independent draws from the standard normal distribution.
rndu(r,c) r×c matrix of independent draws from the uniform distribution.

Basic Matrix Operations

inv(x) matrix: inverse of x
invpd(x) matrix: inverse of x, which must be symmetric and positive definite
moment(x, flag) matrix: x'x. The flag instructs GAUSS what to do about missing values: ignore them (flag=0) or delete them (flag=1 or 2). Using moment(x, flag) can be much quicker than simply calculating x'x : GAUSS uses the symmetry of the result to avoid unnecessary computation.
det(x) scalar: determinant of x
detl scalar: last determinant. Returns the last determinant created by one of the standard matrix decomposition functions (det, inv, invpd, solpd, chol, crout, croutp). Be careful not to lose track of the last matrix passed to the decomposition routines if you use this.
rank(x) scalar: rank of x
diag(x) column vector: diagonal elements of x
diagrv(x,v) matrix: x, with its diagonal elements replaced by the elements of the column vector v
delif(x,v) matrix: x, with those rows deleted that correspond to a one in the column vector v
selif(x,v) matrix: x, with those rows deleted that correspond to a zero in the column vector v
reshape(x,r,c) r×c matrix: new matrix is filled in row major fashion from x until full. If x has fewer than r×c elements, then x is repeatedly reread until the new matrix is filled.
rows(x) scalar: rows of x
cols(x) scalar: columns of x
maxc(x) vector: maximum element from each column of x
minc(x) vector: minimum element from each column of x
sumc(x) vector: sum of elements from each column of x
Explain why maxc(maxc(x)), minc(minc(x)), and sumc(sumc(x)) return the largest value in x, the smallest value in x, and the total sum of the elements in x.

Answers to Selected Exercises

Matrix Exponential
The simplest procedure produces a specified natural-number power (n) of any square matrix (x).

proc (1) = pow(x,n);
  local xn;
  xn=x;
  for i(2,n,1); xn=xn*x; endfor;
retp(xn);
endp;

It is also inefficient because it does not "divide and conquer". The following procedure illustrates the use of a recursive "divide and conquer" strategy to produce a specified integer power (n) of any square matrix (x).

proc (1) = pow(x,n);
  local xn,rx;
  xn=eye(rows(x));
  if(n gt 0);
    xn=pow(x,int(n/2));  @divide and conquer@
    xn=xn*xn;
    if(n%2); xn=x*xn; endif;
  endif; 
retp(xn);
endp;

But there are still better ways to do this. Just to get started, we might want to avoid the expense of recursion. Perhaps we can allow integer powers. We might also want some error checking: e.g., suppose x is not square or n is not an integer.

/**********************  PROC pow  *****************************
**   author: Alan G. Isaac
**   last update: 22 May 2005
**   FORMAT
**        y = pow(x,n)
**   INPUT
**        x  - KxK square matrix
**        n - scalar, integer power for x
**   OUTPUT
**          y -     K x K matrix square matrix: x to power n
**   GLOBAL VARIABLES: none
**   Comments: iterative version
**********************************************************************/
proc (1) = pow(x,n);
  local xn, sq, absn;
  if trunc(n) ne n; "n must be an integer."; stop; endif;
  if cols(x) ne rows(x); "x must be square."; stop; endif;
  xn=1; sq=x; absn=abs(n);
  do while(absn);
    print absn;
    if(absn%2);
      xn=xn*sq;
    endif;
    absn=floor(absn/2);
    if(absn); sq=sq*sq; endif;
  endo;
  if(n lt 0); xn=inv(xn); endif;
retp(xn);
endp;


Note that GAUSS's reshape function thus resembles that of SciPy but differs from that of Matlab, which enters data in column major order.


© 2005 Alan G. Isaac. Some rights reserved.
This work is licensed under a Creative Commons Attribution License.