import graph.Axis;
import graph.DataSet;
import graph.G2Dint;
import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import Jama.Matrix;
import Jama.SingularValueDecomposition;
/**
* VisualSVD.java, a Java applet to allow a visual understanding of what
* the singular value decomposition says about the linear transformation
* described by a 2x2 matrix A.
*
*
* @author Andrew Trusty
*/
public class VisualSVD extends Applet {
/** The object used to graph the results **/
private G2Dint graph;
/** The x axis of the graph object **/
private Axis xaxis;
/** The yaxis of the graph object **/
private Axis yaxis;
/** The object used to graph the results **/
private G2Dint tgraph;
/** The x axis of the graph object **/
private Axis txaxis;
/** The yaxis of the graph object **/
private Axis tyaxis;
/** The data set for the actual results **/
private DataSet unitData;
/** The data set for the theoretical bound results **/
private DataSet transformData;
/** The unit circle points storage array **/
private double[] unitPoints;
/** The number of points to plot to form the graph **/
private final static int PLOT_POINTS = 1000;
/** The increment number for plotting points to form the graph **/
private final static double increment = (Math.PI * 2) / PLOT_POINTS;
/** The textfields used to display the matrix A **/
private TextField A_a;
private TextField A_b;
private TextField A_c;
private TextField A_d;
/** The textfields used to display the matrix V **/
private TextField V_a;
private TextField V_b;
private TextField V_c;
private TextField V_d;
/** The textfields used to display the matrix D **/
private TextField D_a;
private TextField D_b;
private TextField D_c;
private TextField D_d;
/** The textfields used to display the matrix U **/
private TextField U_a;
private TextField U_b;
private TextField U_c;
private TextField U_d;
/** The user inputed matrix A **/
private Matrix A;
/** The diagnolized matrix found by the SVD of A **/
private Matrix D;
/** The matrix V found by the SVD of A **/
private Matrix V;
/** Initialize all the components **/
public void init() {
setSize(525, 280);
setBackground(Color.WHITE);
setLayout(null);
A = new Matrix(new double[][]{{1, 0}, {0, 1}});
V = new Matrix(new double[][]{{1, 0}, {0, 1}});
Label uc = new Label("Unit Circle");
uc.setBounds(110, 3, 80, 16);
add(uc);
Label lt = new Label("Linear Transformation by A");
lt.setBounds(325, 3, 150, 16);
add(lt);
Panel topPanel = new Panel(null);
Panel input = initAinput();
input.setBounds(5, 0, 350, 83);
topPanel.add(input);
Button calc = new Button("Calculate SVD & Graph");
calc.setBounds(363, 47, 150, 25);
topPanel.add(calc);
topPanel.setBounds(5, 198, 515, 85);
add(topPanel);
// calculate the unit circle points
double theta = 0;
unitPoints = new double[PLOT_POINTS * 2];
for (int i = 0; i < (PLOT_POINTS * 2); i+=2) {
unitPoints[i] = Math.cos(theta);
unitPoints[i + 1] = Math.sin(theta);
theta += increment;
}
unitData = new DataSet();
try {
unitData.append(unitPoints, PLOT_POINTS);
} catch (Exception e) {
System.err.println("Unable to append unitPoints data");
}
Panel graphPanel = new Panel(new GridLayout(1, 2));
// setup graphs
graph = new G2Dint();
xaxis = graph.createXAxis();
xaxis.setTitleText("X");
yaxis = graph.createYAxis();
yaxis.setTitleText("Y");
xaxis.setManualRange(true);
yaxis.setManualRange(true);
// attach unitData to its graph
graph.attachDataSet(unitData);
xaxis.attachDataSet(unitData);
yaxis.attachDataSet(unitData);
tgraph = new G2Dint();
tgraph.disable(); // disable it so it cannot be selected etc
// and wont accept input which could make strange side-effects
// happen
transformData = new DataSet();
txaxis = tgraph.createXAxis();
txaxis.setTitleText("X");
tyaxis = tgraph.createYAxis();
tyaxis.setTitleText("Y");
txaxis.setManualRange(true);
tyaxis.setManualRange(true);
// attach unitData to its graph
tgraph.attachDataSet(unitData);
txaxis.attachDataSet(unitData);
tyaxis.attachDataSet(unitData);
graph.setDataBackground(new Color(160,212,207));
graph.gridcolor = new Color(80, 170, 161);
graph.zerocolor = new Color(134, 248, 237);
graph.setBounds(0, 0, 260, 220);
graphPanel.add(graph);
tgraph.setDataBackground(new Color(160,212,207));
tgraph.gridcolor = new Color(80, 170, 161);
tgraph.zerocolor = new Color(134, 248, 237);
tgraph.setBounds(260, 0, 260, 220);
graphPanel.add(tgraph);
graphPanel.setBounds(-10, 3, 520, 220);
add(graphPanel);
graph.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
graph.line = true;
}
public void mouseReleased(MouseEvent e) {
//graph.line = false;
}
});
graph.addMouseMotionListener(new MouseMotionListener(){
public void mouseDragged(MouseEvent e) {
double[] pt = graph.getClosestPoint(e.getX(), e.getY());
// graph the line on the unit circle
graph.drawLine(pt[0], pt[1]);
// transform the point and draw it on the transfomed graph
double[][] tpts = A.times(new Matrix(
new double[][]{{pt[0]}, {pt[1]}})).getArray();
tgraph.drawLine(tpts[0][0], tpts[1][0]);
}
public void mouseMoved(MouseEvent e) {
// do nothing //
}
});
calc.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
tgraph.line = false;
graph.line = false;
parseA();
plotTransform();
double[] pt;
// check if the transformation is the identity, if so
// just show the unit vectors
if (A.get(0, 0) == 1 && A.get(0,1) == 0
&& A.get(1,0) == 0 && A.get(1,1) == 1) {
double unit = Math.sqrt(2) / 2;
pt = new double[]{unit , unit};
tgraph.drawVector(pt[0], pt[1]);
tgraph.drawVector2(-pt[0], pt[1]);
} else {
pt = tgraph.getClosestPointCoords(0, 0);
tgraph.drawVector(pt[0], pt[1]);
pt = tgraph.getMostFarPointCoords(0, 0);
tgraph.drawVector2(pt[0], pt[1]);
}
graph.repaint();
tgraph.repaint();
}
});
}
/**
* Initialize the input fields for the matrix A.
*/
public Panel initAinput() {
Panel Apanel = new Panel(new GridLayout(2, 5));
Label Vlbl = new Label("Matrix V");
Apanel.add(Vlbl);
Label Dlbl = new Label("Matrix D");
Apanel.add(Dlbl);
Label Ulbl = new Label("Matrix U^t");
Apanel.add(Ulbl);
Label empty = new Label("");
Apanel.add(empty);
Label Albl = new Label("Matrix A");
Apanel.add(Albl);
Panel VentryPanel = new Panel(new GridLayout(2, 2));
V_a = new TextField("");
V_a.setEditable(false);
VentryPanel.add(V_a);
V_b = new TextField("");
V_b.setEditable(false);
VentryPanel.add(V_b);
V_c = new TextField("");
V_c.setEditable(false);
VentryPanel.add(V_c);
V_d = new TextField("");
V_d.setEditable(false);
VentryPanel.add(V_d);
Apanel.add(VentryPanel);
Panel DentryPanel = new Panel(new GridLayout(2, 2));
D_a = new TextField("");
D_a.setEditable(false);
DentryPanel.add(D_a);
D_b = new TextField("");
D_b.setEditable(false);
DentryPanel.add(D_b);
D_c = new TextField("");
D_c.setEditable(false);
DentryPanel.add(D_c);
D_d = new TextField("");
D_d.setEditable(false);
DentryPanel.add(D_d);
Apanel.add(DentryPanel);
Panel UentryPanel = new Panel(new GridLayout(2, 2));
U_a = new TextField("");
U_a.setEditable(false);
UentryPanel.add(U_a);
U_b = new TextField("");
U_b.setEditable(false);
UentryPanel.add(U_b);
U_c = new TextField("");
U_c.setEditable(false);
UentryPanel.add(U_c);
U_d = new TextField("");
U_d.setEditable(false);
UentryPanel.add(U_d);
Apanel.add(UentryPanel);
Label eql = new Label(" = ");
Apanel.add(eql);
Panel AentryPanel = new Panel(new GridLayout(2, 2));
A_a = new TextField("1");
AentryPanel.add(A_a);
A_b = new TextField("0");
AentryPanel.add(A_b);
A_c = new TextField("0");
AentryPanel.add(A_c);
A_d = new TextField("1");
AentryPanel.add(A_d);
Apanel.add(AentryPanel);
return Apanel;
}
/**
* Parse the text fields and create the Matrix A from their values.
*/
public void parseA() {
double a = Double.parseDouble(A_a.getText());
double b = Double.parseDouble(A_b.getText());
double c = Double.parseDouble(A_c.getText());
double d = Double.parseDouble(A_d.getText());
A = new Matrix(new double[][]{{a, b}, {c, d}});
//A.print(3, 4);
}
/**
* Calculates the transformed points for the matrix A, and plots
* them on the transform graph.
*/
public void plotTransform() {
// calculate the transformed circle points
double theta = 0;
double[] transformPoints = new double[PLOT_POINTS * 2];
for (int i = 0; i < (PLOT_POINTS * 2); i+=2) {
Matrix B = A.times(new Matrix(new double[][]{{unitPoints[i]},
{unitPoints[i + 1]}}));
transformPoints[i] = B.get(0, 0);
transformPoints[i + 1] = B.get(1, 0);
}
txaxis.detachAll();
tyaxis.detachAll();
tgraph.detachDataSets();
transformData.deleteData();
try {
transformData.append(transformPoints, PLOT_POINTS);
} catch (Exception e) {
System.err.println("Unable to append unitPoints data");
}
tgraph.attachDataSet(transformData);
txaxis.attachDataSet(transformData);
tyaxis.attachDataSet(transformData);
double xmax = txaxis.getDataMax();
double ymax = tyaxis.getDataMax();
double max = (xmax > ymax) ? xmax : ymax;
txaxis.maximum = max;
txaxis.minimum = -max;
tyaxis.maximum = max;
tyaxis.minimum = -max;
tgraph.repaint();
SingularValueDecomposition Asvd = A.svd();
Matrix U = Asvd.getV().transpose(); // strange V is U..
D = Asvd.getS();
V = Asvd.getU();
//(V.times(D).times(U)).print(4, 2);
V_a.setText("" + V.get(0, 0));
V_b.setText("" + V.get(0, 1));
V_c.setText("" + V.get(1, 0));
V_d.setText("" + V.get(1, 1));
D_a.setText("" + D.get(0, 0));
D_b.setText("" + D.get(0, 1));
D_c.setText("" + D.get(1, 0));
D_d.setText("" + D.get(1, 1));
U_a.setText("" + U.get(0, 0));
U_b.setText("" + U.get(0, 1));
U_c.setText("" + U.get(1, 0));
U_d.setText("" + U.get(1, 1));
}
}