Tutorial 8 Topics Matrix Methods o The Matrix Class o Matrices addition method o Matrices multiplication method Linear Systems Queues and Stacks o java.util library o The Stack class o Java collection framework o Queue interface Matrix Methods In this section, we will discuss some of the Matrix class methods from the lecture. The Matrix Class The Matrix class is a Java class we created to represent and manipulate matrices. It is used most often to prepare matrices for use in solving linear systems. It has: § A 2-D array with m rows by n columns private double[ ][ ] data = new double[m][n]; int nrows = data.length; int ncols = data[0].length; § Methods for matrix operations such as addition, subtraction, multiplication Public Matrix addMatrices (Matrix b) Public Matrix multMatrices (Matrix b) Public void print (); Matrix Addition Two matrices (a & b) can be added only if they have the same number of rows and columns. The result is a new matrix. The core of the addition process is: c[i][j] = a[i][j] + b[i][j]; Here is the code Public Matrix addMatrices (Matrix b) { //The result matix Matrix result = new Matrix(nrows, ncols); //Add only if they have the same number of rows and cols if (b.nrows == nrows && b.ncols == ncols) { for (int i=0; i<nrows; i++) for (int j=0; j<ncols; j++) result.data[i][j] = data[i][j] + b.data[i][j]; } return result; } Matrix Multiplication Two matrices can be multiplied only if they have a.ncols = b.nrow. The result is a new matrix. The core of the multiplication process is: For example: c[0][0] = a[0][0]* b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0] + …. + a[0][ncols]*b[nrows][0]; Here is the code Public Matrix multMatrices (Matrix b) { //The result matix has a.nrows by b.ncols Matrix result = new Matrix(nrows, b.ncols); //Multiply only if a.ncols = b.nrow if (b.nrows == ncols) { for (int i=0; i<nrows; i++) for (int j=0; j<b.ncols; j++) for (int k=0; k<ncols; k++) result.data[i][j]+= data[i][k] * b.data[k][j]; } return result; } Linear Systems Simultaneous equations arise in many engineering problems: they are a system of n equations with n unknowns. Matrices are often used to solve linear systems. The formulation is shown below: AX=B where A is the following matrix, X = (x1, x2, ..., xn) B = (b1, b2, ..., bn)' For example: 2X0 + 3X1 + 5X2 = 10 X0 + 2X1 + 4X2 = 5 4X0 + 7X1 + 3X2 = 15 A is a 3 by 3 matrix: 2 3 5 1 2 4 4 7 3 X is a 3 by 1 matrix: X0 X1 X2 B is a 3 by 1 matrix: 10 5 15 The lecture notes walked you through the process of how we derived the Java programs to solve a linear system. Here, we will show you how to use it. Basically, there are three steps: 1. Create the left-side matrix a 2. Create the right-side matrix b 3. Plug a and b in the gaussian method of the Gauss class Lets follow the steps to solve the above problem: 1. Create the left-side matrix a (To keep the code simple, we assume all arrays in the matrices are public) Matrix a = new Matrix(3,3); a.data[0][0] = 2.0; a.data[0][1] = 3.0; a.data[0][2] = 5.0; a.data[1][0] = 1.0; a.data[1][0] = 2.0; a.data[1][2] = 4.0; a.data[2][0] = 4.0; a.data[2][1] = 7.0; a.data[2][2] = 3.0; 2. Create the right-side matrix b Matrix b = new Matrix(3,1); b.data[0][0] = 10.0; b.data[1][0] = 5.0; b.data[2][0] = 15.0; 3. Plug a and b in the gaussian method of the Gauss class Matrix result = new Matrix(3,1); GaussMain.gaussian (a, b, result); //Print the solution result.print(); Here is the result X0 = 6.0 X1 = -1.5 X2 = 0.5 As you can see, solving a linear system using the Gauss class is incredibly simple. If you are interested in learning about how we derived the gaussian method, please see the appendix Queues and Stacks A data structure is a systematic way of organizing and accessing data; we have already seen a number of simple data structures in this course (e.g., arrays and vectors). This section (and the next tutorial) will show you how to implement some of the traditional data structures (Queue, Stack, Linked List, and Tree) using Java. java.util Library Before the release of the Java 2 platform, the java.util library supplied a small set of classes for the most useful data structures, such as Vector, Stack, and Hashtable: The Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can grow or shrink as needed to accommodate adding and removing items after the Vector has been created. The Stack class represents a last-in-first-out (LIFO) stack of objects. It provides a push method to add an object at the top of the stack and a pop method to remove it from the top. The Hashtable class stores objects as a key-value pairs which can be randomly accessed using the key. Stack Class A stack is a container of objects that are inserted and removed according to the last-in first-out (LIFO) principle. An example of this principle is a can of tennis balls. The last ball inserted in the can is the first one that will be taken out. The java.util.Stack class contains the following methods: void push(Object item) pushes an item onto the stack. Parameters: item the item to be added Object pop() pops and returns the top item of the stack. Don't call this method if the stack is empty. Object peek() returns the top of the stack without popping it. Don't call this method if the stack is empty. In the following example, we simulate the tennis ball can using two classes: TennisBall and TennisBallCan. First, lets define a simple TennisBall class which contains only 1 attribute: color. class TennisBall{ String color; TennisBall (String color){ this.color = color; } } String getColor() { return color; } Next, let's define the TennisBallCan class which uses a Stack to organize its balls. import java.util.*; import TennisBall; class TennisBallCan { public static void main (String args[]) { //Create 3 objects of TennisBall TennisBall t1 = new TennisBall ("Red") ; TennisBall t2 = new TennisBall ("Green"); TennisBall t3 = new TennisBall ("Yellow") Stack can = new Stack(); //Push the balls into the can System.out.println can.push (t1); System.out.println can.push (t2) ; System.out.println can.push (t3); System.out.println (t1.getColor() + " ball in"); (t2.getColor() + " ball in"); (t3.getColor() + " ball in"); ("Size of the can: " + can.size()); //Take a peek of the last ball System.out.println ("Peek of the last ball: color = " + ((TennisBall)can.peek()).getColor()); System.out.println ("Size of the can after the peek: " + can.size()); //Pop the balls for (int i=0; i<3; i++) System.out.println (((TennisBall)can.pop()).getColor() + " ball out" ); } } Here is the result Red ball in Green ball in Yellow ball in Size of the can: 3 Peek of the last ball:Yellow Size of the can after the peek: 3 Yellow ball out Green ball out Red ball out Size of the can after the pop: 0 The Java Collections Framework Introduced with the JavaTM 2 platform, the Java Collections Framework provides a well-designed set of interfaces and classes for storing and manipulating groups of data as a single unit - a collection. The framework provides a convenient API to many of the abstract data types: maps, sets, lists, trees, arrays, hashtables and other collections. Because of their object-oriented design, the Java classes in the Collections Framework encapsulate both the data structures and the algorithms associated with these abstractions. As is common for modern data structure libraries, the Java collection library separates interfaces and implementations. Next, we will look at that separation with a familiar data structure, the queue. The Java library does not supply a queue class or interface, but it is nevertheless a good example to introduce the basic concepts. Queue Interface A queue interface specifies that you can add elements at the tail end of the queue, remove them at the head, and find out how many elements are in the queue. You use a queue when you need to collect objects and retrieve them in a "first in, first out" fashion. The queue interface should perform the following operations: interface Queue{ void add(Object obj); Object remove(); int size(); } Note that the interface tells you nothing about how the queue is implemented. Here is an example of how one can implement the Queue interface: class TennisBallQueue implements Queue { public void add(Object obj) { . . . } public Object remove() { . . . } public int size() { . . . } } NOTE: If you do need a queue, you can simply use the LinkedList class in java.util library. Among other methods, LinkedList provides the following "queue-like" operations: void addLast(Object o) Appends the given element to the end of this list. Object removeFirst() Removes and returns the first element from this list. We will discuss LinkedList next week. 4) Tutorial Problems 1. Matrix Methods Here are three matrices: A, B, and C 1 2 3 A= 5 6 7 9 4 5 3 2 4 B= 2 1 6 4 10 20 3 C= 5 6 What is the result of (A-B)*C 2. Linear Systems Solve the following linear system for X, Y, and Z. X+Y+Z=8 X+Y-Z=4 X-Y=4 3. Stack I modified the TennisBallCan example and added the following actions in my program: Create an empty can Move all balls from one can to another Balls in the new can should have the reverse order of those in the original can 5) Appendix: Simultaneous Linear Equations Simultaneous equations arise in many engineering problems: a system of n equations with n unknowns. The matrix formulation of of these linear systems is shown below: AX=B, where A is the following matrix, and X = (x1, x2, ..., xn)', B = (b1, b2, ..., bn)' The basic steps in solving simultaneous equations are the following. Triangularization The set of equations need to be converted a form so that only the upper triangular element remains. This process is called triangularization.The triangularized set of equations is shown below. a11 * x1 + a12 * x2 + a13 * x3 +.... + a1n * xn = b1 0 * x1 + a'22 * x2 + a'23 * x3 + .... + a'2n * xn = b'2 0 * x1 + 0 * x2 + a'33 * x3 +.... + a'3n * xn = b'3 ....... 0 * x1 + 0 * x2 + 0 * x3 +.... + a'nn * xn = b'n The idea behind triangularization is very simple. We know that if we multiply both sides of an equation, the equation remains the same. Our aim is to make the elements below the diagonal to be zero. So if we start with the first column then we need to make all the elements except the first element equal zero. Also we do not want to change the equations. Lets consider the bottom row first, if we multiply the bottom row with a11 and then divide by an1 and then subtract from this row the first row we have made the first element equal to zero. We do this for all the columns and rows until we get the triangular matrix. The code is given below. private static void forward_solve(Matrix q) { int i, j, k, maxr, n; double t, pivot; n= q.getNumRows(); for (i=0; i < n; i++) { // Find row w/max element in this maxr= i; // column, at or below diagonal for (j= i+1; j < n; j++) if (Math.abs(q.getElement(j,i)) > Math.abs(q.getElement(maxr,i))) maxr= j; if (maxr != i) // If row not current row, swap for (k=i; k <= n; k++) { t= q.getElement(i,k); // t= q(i,k) q.setElement(i,k, q.getElement(maxr, k)); // q(i,k)= q(maxr, k) q.setElement(maxr, k, t); // q(maxr, k)= t } for (j= i+1; j <n; j++) // Calculate pivot ratio { pivot= q.getElement(j,i)/q.getElement(i,i); // q(j,i)/q(i,i) for (k= n; k >=i; k--) q.setElement(j, k, q.getElement(j,k)-q.getElement(i,k)*pivot); // q(j,k) -= q(i,k)*pivot; // Update row j below diag } } } Back-substitution Once the triangular matrix is obtained the solution is obtained by a process known as back-substitution. We start from the bottom most row. Since the matrix is triangular we have only one non zero element in the bottom most row. So the value of xn can be readly obtained, xn can be computed as b'n/a'nn, then xn-1 = ( b'n-1 - a'n-1,n * xn ) / a'n-1,n-1. And so on to x1. The code for backsubstitution is given below: private static void back_solve(Matrix q) // Back substitution function { // starting at row n-1 int j, k, n; double t; // t- temporary n= q.getNumRows(); for (j=n-1; j >=0; j--) // Start at last row { t= 0.0; for (k= j+1; k < n; k++) // t += q(j,k)* q(k,n) t += q.getElement(j,k)* q.getElement(k,n); q.setElement(j, n, (q.getElement(j, n) -t)/q.getElement(j,j)); // q(j, n)= (q(j, n) -t)/q(j,j); } } Partial-pivoting implementation of Gaussian elimination The implementation gathers up all we have been saying so far: public static void gaussian(Matrix a, Matrix b, Matrix x) { int i, j, n; n= a.getNumRows(); // Number of unknowns Matrix q= new Matrix(n, n+1); for (i=0; i < n; i++) { for (j=0; j < n; j++) // Form q matrix q.setElement(i, j, a.getElement(i, j)); // q(i,j)= a(i,j) q.setElement(i, n, b.getElement(i, 0)); // q(i,n)= b(i,0) } forward_solve(q); // Do Gaussian elimination back_solve(q); // Perform back substitution } for (i=0; i<n; i++) x.setElement(i, 0, q.getElement(i, n)); // x(i,0)= q(i,n) We can make this method part of the Matrix class.