This chapter introduces LME(TM) (Lightweight Math Engine), the interpreter for numeric computing used by Sysquake, and shows you how to perform basic computations. It supposes you can type commands to a command-line interface. You are invited to type the examples as you read this tutorial and to experiment on your own. For a more systematic description of LME, please consult the LME Reference chapter.
In the examples below, we assume that LME displays a prompt >. This is not the case for all applications. You should never type it yourself. Enter what follows the prompt on the same line, hit the Return key (or tap the Eval or Execute button), and observe the result.
LME interprets what you type at the command prompt and displays the result unless you end the command with a semicolon. Simple expressions follow the syntactic rules of many programming languages.
> 2+3*4 ans = 14 > 2+3/4 ans = 2.75
As you can see, the evaluation order follows the usual rules which state that the multiplication (denoted with a star) and division (slash) have a higher priority than the addition and subtraction. You can change this order with parenthesis:
> (2+3)*4 ans = 20
The result of expressions is automatically assigned to variable ans (more about variables later), which you can reuse in the next expression:
> 3*ans ans = 60
Power is represented by the ^ symbol:
> 2^5 ans = 32
LME has many mathematical functions. Trigonometric functions assume that angles are expressed in radians, and sqrt denotes the square root.
> sin(pi/4) * sqrt(2) ans = 1
In many computer languages, the square root is defined only for nonnegative
arguments. However, it is extremely useful to extend the set of numbers to remove
this limitation. One defines
> 2+3*j ans = 2+3j > 3j+2 ans = 2+3j
Many functions accept complex numbers as argument, and return a complex result when the input requires it even if it is real:
> sqrt(-2) ans = 0+1.4142i > exp(3+2j) ans = -8.3585+18.2637j > log(-8.3585+18.2637j) ans = 3+2j
To get the real or imaginary part of a complex number, use the functions real or imag, respectively:
> real(2+3j) ans = 2 > imag(2+3j) ans = 3
Complex numbers can be seen as vectors in a plane. Then addition and subtraction of complex numbers correspond to the same operations applied to the vectors. The absolute value of a complex number, also called its magnitude, is the length of the vector:
> abs(3+4j) ans = 5 > sqrt(3^2+4^2) ans = 5
The argument of a complex number is the angle between the x axis ("real axis") and the vector, counterclockwise. It is calculated by the angle function.
> angle(2+3j) ans = 0.9828
The last function specific to complex numbers we will mention here is conj, which calculates the conjugate of a complex number. The conjugate is simply the original number where the sign of the imaginary part is changed.
> conj(2+3j) ans = 2-3j
Real numbers are also complex numbers, with a null imaginary part; hence
> abs(3) ans = 3 > conj(3) ans = 3 > angle(3) ans = 0 > angle(-3) ans = 3.1416
Vectors and Matrices
LME manipulates vectors and matrices as easily as scalars. To define a matrix, enclose its contents in square brackets and use commas to separate elements on the same row and semicolons to separate the rows themselves:
> [1,2;5,3] ans = 1 2 5 3
Column vectors are matrices with one column, and row vectors are matrices with one row. You can also use the colon operator to build a row vector by specifying the start and end values, and optionally the step value. Note that the end value is included only if the range is a multiple of the step. Negative steps are allowed.
> 1:5 ans = 1 2 3 4 5 > 0:0.2:1 ans = 0 0.2 0.4 0.6 0.8 1 > 0:-0.3:1 ans = 0 -0.3 -0.6 -0.9
There are functions to create special matrices. The zeros, ones, rand, and randn functions create matrices full of zeros, ones, random numbers uniformly distributed between 0 and 1, and random numbers normally distributed with a mean of 0 and a standard deviation of 1, respectively. The eye function creates an identity matrix, i.e. a matrix with ones on the main diagonal and zeros elsewhere. All of these functions can take one scalar argument n to create a square n-by-n matrix, or two arguments m and n to create an m-by-n matrix.
> zeros(3) ans = 0 0 0 0 0 0 0 0 0 > ones(2,3) ans = 1 1 1 1 1 1 > rand(2) ans = 0.1386 0.9274 0.3912 0.8219 > randn(2) ans = 0.2931 1.2931 -2.3011 0.9841 > eye(3) ans = 1 0 0 0 1 0 0 0 1 > eye(2,3) ans = 1 0 0 0 1 0
You can use most scalar functions with matrices; functions are applied to each element.
> sin([1;2]) ans = 0.8415 0.9093
There are also functions which are specific to matrices. For example, det calculates the determinant of a square matrix:
> det([1,2;5,3]) ans = -7
Arithmetic operations can also be applied to matrices, with their usual mathematical behavior. Additions and subtractions are performed on each element. The multiplication symbol * is used for the product of two matrices or a scalar and a matrix.
> [1,2;3,4] * [2;7] ans = 16 34
The division symbol / denotes the multiplication by the inverse of the
right argument (which must be a square matrix). To multiply by the inverse of the left
argument, use the symbol \. This is handy to solve a set of linear equations.
For example, to find the values of
> [1,2;3,4] \ [2;7] ans = 3 -0.5
> [1,2;3,4] * [2,1;5,3] ans = 12 7 26 15 > [1,2;3,4] .* [2,1;5,3] ans = 2 2 15 12
Some functions change the order of elements. The transpose operator (tick) reverses the columns and the rows:
> [1,2;3,4;5,6]' ans = 1 3 5 2 4 6
When applied to complex matrices, the complex conjugate transpose is obtained. Use dot-tick if you just want to reverse the rows and columns. The flipud function flips a matrix upside-down, and fliplr flips a matrix left-right.
> flipud([1,2;3,4]) ans = 3 4 1 2 > fliplr([1,2;3,4]) ans = 2 1 4 3
To sort the elements of each column of a matrix, or the elements of a row vector, use the sort function:
> sort([2,4,8,7,1,3]) ans = 1 2 3 4 7 8
To get the size of a matrix, you can use the size function, which gives you both the number of rows and the number of columns unless you specify which of them you want in the optional second argument:
> size(rand(13,17)) ans = 13 17 > size(rand(13,17), 1) ans = 13 > size(rand(13,17), 2) ans = 17
LME handles mostly numeric values. Therefore, it cannot differentiate functions
Adding two polynomials would be like adding the coefficient vectors if they had the same size; in the general case, however, you had better use the function addpol, which can also be used for subtraction:
> addpol([1,2],[3,7]) ans = 4 9 > addpol([1,2],[2,4,5]) ans = 2 5 7 > addpol([1,2],-[2,4,5]) ans = -2 -3 -3
Multiplication of polynomials corresponds to convolution (no need to understand what it means here) of the coefficient vectors.
> conv([1,2],[2,4,5]) ans = 2 8 13 10
You type strings by delimiting them with single quotes:
> 'Hello, World!' ans = Hello, World!
If you want single quotes in a string, double them:
> 'Easy, isn''t it?' ans = Easy, isn't it?
Some control characters have a special representation. For example, the line feed, used in LME as an end-of-line character, is \n:
> 'Hello,\nWorld!' ans = Hello, World!
Strings are actually matrices of characters. You can use commas and semicolons to build larger strings:
> ['a','bc';'de','f'] ans = abc def
You can store the result of an expression into what is called a variable. You can have as many variables as you want and the memory permits. Each variable has a name to retrieve the value it contains. You can change the value of a variable as often as you want.
> a = 3; > a + 5 ans = 8 > a = 4; > a + 5 ans = 9
Note that a command terminated by a semicolon does not display its result. To see the result, remove the semicolon, or use a comma if you have several commands on the same line. Implicit assignment to variable ans is not performed when you assign to another variable or when you just display the contents of a variable.
> a = 3 a = 3 > a = 7, b = 3 + 2 * a a = 7 b = 17
Loops and Conditional Execution
To repeat the execution of some commands, you can use either a for/end block or a while/end block. With for, you use a variable as a counter:
> for i=1:3;i,end i = 1 i = 2 i = 3
With while, the commands are repeated as long as some expression is true:
> i = 1; while i < 10; i = 2 * i, end i = 2 i = 4 i = 8
You can choose to execute some commands only if a condition holds true :
> if 2 < 3;'ok',else;'amazing...',end ans = ok
LME permits you to extend its set of functions with your own. This is convenient not only when you want to perform the same computation on different values, but also to make you code clearer by dividing the whole task in smaller blocks and giving names to them. To define a new function, you have to write its code in a file; you cannot do it from the command line. In Sysquake, put them in a function block.
Functions begin with a header which specifies its name, its input arguments (parameters which are provided by the calling expression) and its output arguments (result of the function). The input and output arguments are optional. The function header is followed by the code which is executed when the function is called. This code can use arguments like any other variables.
We will first define a function without any argument, which just displays a magic square, the sum of each line, and the sum of each column:
function magicsum3 magic_3 = magic(3) sum_of_each_line = sum(magic_3, 2) sum_of_each_column = sum(magic_3, 1)
You can call the function just by typing its name in the command line:
> magicsum3 magic_3 = 8 1 6 3 5 7 4 9 2 sum_of_each_line = 15 15 15 sum_of_each_column = 15 15 15
This function is limited to a single size. For more generality, let us add an input argument:
function magicsum(n) magc = magic(n) sum_of_each_line = sum(magc, 2) sum_of_each_column = sum(magc, 1)
When you call this function, add an argument:
> magicsum(2) magc = 1 3 4 2 sum_of_each_line = 4 6 sum_of_each_column = 5 5
Note that since there is no 2-by-2 magic square, magic(2) gives something else... Finally, let us define a function which returns the sum of each line and the sum of each column:
function (sum_of_each_line, sum_of_each_column) = magicSum(n) magc = magic(n); sum_of_each_line = sum(magc, 2); sum_of_each_column = sum(magc, 1);
Since we can obtain the result by other means, we have added semicolons after each statement to suppress any output. Note the uppercase S in the function name: for LME, this function is different from the previous one. To retrieve the results, use the same syntax:
> (sl, sc) = magicSum(3) sl = 15 15 15 sc = 15 15 15
You do not have to retrieve all the output arguments. To get only the first one, just type
> sl = magicSum(3) sl = 15 15 15
When you retrieve only one output argument, you can use it directly in an expression:
> magicSum(3) + 3 ans = 18 18 18
One of the important benefits of defining function is that the variables have a limited scope. Using a variable inside the function does not make it available from the outside; thus, you can use common names (such as x and y) without worrying about whether they are used in some other part of your whole program. For instance, let us use one of the variables of magicSum:
> magc = 77 magc = 77 > magicSum(3) + magc ans = 92 92 92 > magc magc = 77
Local and Global Variables
When a value is assigned to a variable which has never been referenced, a new variable is created. It is visible only in the current context: the base workspace for assignments made from the command-line interface, or the current function invocation for functions. The variable is discarded when the function returns to its caller.
Variables can also be declared to be global, i.e. to survive the end of the function and to support sharing among several functions and the base workspace. Global variables are declared with keyword global:
global x global y z
A global variable is unique if its name is unique, even if it is declared in several functions.
In the following example, we define functions which implement a queue which contains scalar numbers. The queue is stored in a global variable named QUEUE. Elements are added at the front of the vector with function queueput, and retrieved from the end of the vector with function queueget.
function queueput(x) global QUEUE; QUEUE = [x, QUEUE]; function x = queueget global QUEUE; x = QUEUE(end); QUEUE(end) = ;
Both functions must declare QUEUE as global; otherwise, the variable would be local, even if there exists also a global variable defined elsewhere. The first time a global variable is defined, its value is set to the empty matrix . In our case, there is no need to initialized it to another value.
Here is how these functions can be used.
> queueput(1); > queueget ans = 1 > queueput(123); > queueput(2+3j); > queueget ans = 123 > queueget ans = 2 + 3j
To observe the value of QUEUE from the command-line interface, QUEUE must be declared global there. If a local variable QUEUE already exists, it is discarded.
> global QUEUE > QUEUE QUEUE =  > queueput(25); > queueput(17); > QUEUE QUEUE = 17 25