import graph.Axis; import graph.DataSet; import graph.G2Dint; import graph.Markers; import java.applet.Applet; import java.awt.Button; import java.awt.Checkbox; import java.awt.Color; import java.awt.Label; import java.awt.Panel; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import Jama.Matrix; /** * RandomJacobi.java, a class designed to run the Jacobi algorithm on * a random symmetric matrix and show the results in a graph as a Java * applet. *

* @author admin */ public class RandomJacobi extends Applet implements Runnable { /** The object used to graph the results **/ private G2Dint graph = new G2Dint(); /** The x axis of the graph object **/ private Axis xaxis; /** The yaxis of the graph object **/ private Axis yaxis; /** The data set for the actual results **/ private DataSet data = new DataSet(); /** The data set for the theoretical bound results **/ private DataSet theorydata = new DataSet(); /** The location of the markers definitions for the graph **/ private URL markerURL; /** The error used as the stopping condition for Jacobi **/ private double stop_condition; /** The dimension of the square matrix **/ private int mSize; /** The matrix being run throught the Jacobi algorithm **/ private Matrix A; /** The diagnolized matrix **/ private Matrix D; /** Denotes whethe Jacobi is being run **/ private boolean running; /** Whether or not to sort in the algorithm **/ private boolean sorting; /** Reference to this object for anonymous action listeners **/ private RandomJacobi thisObj; /** The Off(A) numbers for each iteration **/ private double[] offs; /** Precision for showing the matrices **/ private int digits; /** The natural log of 9/10 **/ private final static double LN_NINETEN = -0.10536051565782630122750098083931; /** The text field for the matrix size **/ private TextField mszfld; /** The text field for the stop value **/ private TextField mfld; /** The display for the original matrix **/ private TextArea origDisp; /** The display for the diagnol matrix **/ private TextArea diagDisp; /** The checkbox to toggle sorting **/ private Checkbox sortbox; /** The display precision field **/ private TextField digt; /** Display for number of steps **/ private Label steps; /** The string used for the steps to diagnolization label **/ private final static String STPLBL = "Steps to diagnolization: "; /** Denotes whether it is the first time running. **/ private boolean first; /** * Initialize the applet. */ public void init() { super.init(); running = false; sorting = true; stop_condition = 10e-9; mSize = 5; digits = 3; setLayout(null); setSize(680, 360); setBackground(Color.WHITE); steps = new Label(); steps.setBounds(320, 260, 170, 25); add(steps); thisObj = this; initOptions(); initMatrixDisplays(); generateMatrix(); first = true; if (!running) { running = true; new Thread(thisObj).start(); } first = false; init2(); } /** * Initialized the user options. */ private void initOptions() { Panel options = new Panel(); options.setBounds(100, 300, 500, 70); options.setLayout(null); add(options); sortbox = new Checkbox("Sorting", true); sortbox.setBounds(260, 33, 70, 25); options.add(sortbox); Label mszlbl = new Label("Matrix Size:"); mszlbl.setBounds(5, 33, 65, 25); options.add(mszlbl); mszfld = new TextField("5"); mszfld.setBounds(70, 33, 25, 25); options.add(mszfld); Label mlbl = new Label("Stop Value:"); mlbl.setBounds(103, 33, 65, 25); options.add(mlbl); mfld = new TextField("10^-9"); mfld.setBounds(170, 33, 65, 25); options.add(mfld); Label digtlbl = new Label("Display Precision:"); digtlbl.setBounds(330, 33, 100, 25); options.add(digtlbl); digt = new TextField("3"); digt.setBounds(434, 33, 25, 25); options.add(digt); Button gen = new Button("Generate Random Symmetric Matrix"); gen.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { generateMatrix(); } }); gen.setBounds(35, 5, 220, 25); options.add(gen); Button solve = new Button("Run Jacobi Algorithm"); solve.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { if (!running) { running = true; new Thread(thisObj).start(); } } }); solve.setBounds(275, 5, 160, 25); options.add(solve); } /** * Initializes the matrix displays. */ private void initMatrixDisplays() { Label origlbl = new Label("Original Matrix"); origlbl.setBounds(5, 2, 100, 20); add(origlbl); origDisp = new TextArea(); origDisp.setBounds(5, 25, 265, 115); origDisp.setEditable(false); add(origDisp); Label diaglbl = new Label("Diagnolized Matrix"); diaglbl.setBounds(5, 155, 110, 20); add(diaglbl); diagDisp = new TextArea(); diagDisp.setEditable(false); diagDisp.setBounds(5, 178, 265, 115); add(diagDisp); } /** * Initializes the graph. */ public void init2() { try { markerURL = new URL(getDocumentBase(),"marker.txt"); graph.setMarkers(new Markers(markerURL)); } catch(Exception e) { System.out.println("Failed to create Marker URL!"); } xaxis = graph.createXAxis(); xaxis.setTitleText("X"); yaxis = graph.createYAxis(); yaxis.setTitleText("ln(Off(B)) & x*ln(9/10)+ln(Off(A))"); data.linestyle = 1; data.linecolor = new Color(100, 100, 255); data.marker = 3; data.markerscale = 1; data.markercolor = new Color(0,0,255); data.legend(100,194,"Results"); data.legendColor(Color.black); theorydata.linestyle = 1; theorydata.linecolor = Color.RED; theorydata.marker = 2; theorydata.markerscale = 1; theorydata.markercolor = new Color(255,0,0); theorydata.legend(100,210,"Theoretical Bound"); theorydata.legendColor(Color.black); graph.attachDataSet(data); graph.attachDataSet(theorydata); xaxis.attachDataSet(data); xaxis.attachDataSet(theorydata); yaxis.attachDataSet(data); yaxis.attachDataSet(theorydata); graph.setDataBackground(new Color(255,200,175)); graph.setBounds(260, 5, 425, 285); add(graph); plot(); } /** * Plots the data points for the theoretical bound and the actual * results on the graph and displays it. */ public void plot() { if (offs != null) { double[] d = new double[offs.length * 2]; int j = 0; for (int i = 0; i < offs.length - 1; i++) { j += 2; d[j] = i; d[j + 1] = Math.log(offs[i]); } double[] thePoints = new double[offs.length * 2]; j = 0; for (int i = 0; i < offs.length - 1; i++) { j += 2; thePoints[j] = i; thePoints[j + 1] = (i * LN_NINETEN) + Math.log(offs[0]); } data.deleteData(); theorydata.deleteData(); try { data.append(d, d.length / 2); theorydata.append(thePoints, thePoints.length / 2); xaxis.attachDataSet(data); xaxis.attachDataSet(theorydata); yaxis.attachDataSet(data); yaxis.attachDataSet(theorydata); graph.attachDataSet(data); graph.attachDataSet(theorydata); } catch(Exception e) { this.showStatus("Error while appending data!"); System.out.println("Error while appending data!"); return; } graph.repaint(); } } /** * Generates a random Matrix of the specified size etc */ private void generateMatrix() { // get matrix size mSize = Integer.parseInt(mszfld.getText()); // generate random mSizexmSize symmetric matrix A = D = Matrix.random(mSize, mSize); // make A symmetric for (int m = 0; m < mSize; m++) { for (int n = m + 1; n < mSize; n++) { A.set(n, m, A.get(m, n)); } } //A.print(5, 4); // UPDATE all displays graph.detachDataSets(); graph.repaint(); steps.setText(""); origDisp.setText(prettyPrint(A)); // blank diagnolized view diagDisp.setText("Run Jacobi Algorithm to find " + "the \ndiagnolized matrix and its graph."); } /** * Returns a String of the given Matrix in a formatted * represenation. *

* @param Z the matrix * @return string displaying the matrix */ private String prettyPrint(Matrix Z) { // grab precision digits = Integer.parseInt(digt.getText()); // use the Matrix print method to a bytearray to grab // the formatted ouput for displaying in GUI ByteArrayOutputStream byout = new ByteArrayOutputStream(); PrintWriter pout = new PrintWriter(byout); Z.print(pout, digits + 7, digits); pout.flush(); String patternStr = "\n "; String replaceStr = "\n "; Pattern pattern = Pattern.compile(patternStr); Matcher matcher = pattern.matcher(byout.toString()); String rtString = matcher.replaceAll(replaceStr); patternStr = "\n -"; replaceStr = "\n-"; pattern = Pattern.compile(patternStr); matcher = pattern.matcher(rtString); rtString = matcher.replaceAll(replaceStr); rtString = rtString.replaceFirst("\n",""); return rtString; } /** * Parses the stop condition text field for a double number. */ private void parseStopCondition() { // get error String error = mfld.getText(); int loc = error.indexOf("^"); if (loc > -1) { String[] nums = new String[]{error.substring(0, loc), error.substring(loc + 1)}; stop_condition = Math.pow(Double.parseDouble(nums[0]), Double.parseDouble(nums[1])); } else { stop_condition = Double.parseDouble(error); } } /** * Generates a random matrix and runs the Jacobi algorithm * on it until the stopping condition is met. */ public void run() { parseStopCondition(); sorting = sortbox.getState(); // run Jacobi algorithm on matrix runJacobi(); // show the results diagDisp.setText(prettyPrint(D)); // display number of iterations steps.setText(STPLBL + (offs.length - 1)); // graph the results if(!first) { plot(); } running = false; } /** * Runs the Jacobi algorithm to its completeion. */ public void runJacobi() { // set matrix D equal to A D = A; // create the list to store the Off values List offlist = new ArrayList(); // run iterations int[] largestlocation = new int[]{1, 0}; while(true) { // Check Off(D) // sum the squares of the off-diagnols double off = 0, largestoff = 0, temp; // calculate Off(D) and find the largest off element for (int i = 0; i < mSize; i++) { for (int j = 0; j < mSize; j++) { if (i != j) { temp = Math.abs(D.get(i, j)); if (sorting) { // check if the current entry is // larger than previously larger entry if (temp > largestoff) { largestoff = temp; // save location of this entry largestlocation[0] = i; largestlocation[1] = j; } } off += Math.pow(temp, 2); } } } // add new off to offlist offlist.add(new Double(off)); // check that off is <= 10^-9 if (off <= stop_condition) { break; } int x, y; // get the inner 2x2 matrix containing the largest entry // or the next systematic matrix if (!sorting) { // last column, increment row, reset column if (largestlocation[0] == (mSize - 1)) { largestlocation[1] += 1; largestlocation[0] = largestlocation[1] + 1; } else { largestlocation[0] += 1; } // if column has hit end, got back to beginning if (largestlocation[1] == (mSize - 1)) { largestlocation = new int[]{1, 0}; } } x = largestlocation[0]; y = largestlocation[1]; double[][] bvals = new double[2][2]; // by symmetric bvals[0][1] = bvals[1][0] = D.get(x, y); bvals[0][0] = D.get(x, x); bvals[1][1] = D.get(y, y); Matrix B = new Matrix(bvals); //B.print(5, 4); // diagnolize B and get the V B = B.eig().getV(); //B.print(5, 3); // create the Givens rotation matrix Matrix G = Matrix.identity(mSize, mSize); G.set(x, x, B.get(0, 0)); G.set(x, y, B.get(0, 1)); G.set(y, x, B.get(1, 0)); G.set(y, y, B.get(1, 1)); //G.print(5, 4); // produce the more diagnolized D D = (G.transpose()).times(D).times(G); //A.print(5, 4); } // end while loop // convert the list to an array of doubles to return int sz = offlist.size(); // there should always be one number, 0 if diagnol offs = new double[sz]; for (int p = 0; p < sz; p++) { offs[p] = ((Double) offlist.get(p)).doubleValue(); } } }