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();
}
}
}