/*
* -------------------------------------------------------------------------
* $Id: Basis.java,v 1.3 2004/05/26 18:00:47 estewart Exp $
* -------------------------------------------------------------------------
* Copyright (c) 2002 Visual Numerics Inc. All Rights Reserved.
*
* This software is confidential information which is proprietary to
* and a trade secret of Visual Numerics, Inc. Use, duplication or
* disclosure is subject to the terms of an appropriate license
* agreement.
*
* VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
* NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
* ITS DERIVATIVES.
* --------------------------------------------------------------------------
*/
package com.imsl.demo.Basis;
import com.imsl.stat.*;
import com.imsl.math.*;
import com.imsl.chart.*;
import javax.swing.*;
/**
* Basis creates a Chart where the user can select data to fit with several
* different user basis functions.
*
*@author Ed Stewart
*@created September 25, 2002
*/
public class Basis extends JFrameChart {
private JComboBox jComboBoxUser;
private JComboBox jComboBoxFunc;
private JTextArea displayText;
private JLabel jLabelSNC;
private JScrollBar jScrollCoef;
private Chart chart;
private AxisXY axis, axis2;
private Data lineUser, lineFunc;
private UserBasisRegression ubr;
private final int npoints = 100;
private final int defCoef = 4;
private final double[] xrange = {0.0, 10.0};
private final double[] yrange = {-15.0, 15.0};
private String textFunc, textUser;
private double x[], y[], f[], rand[];
/**
* Constructor for the Basis object sets default values and calls methods
* to create the chart, draw the graph and build the user interface.
*
*@param exitOnClose If false, remove the WindowListener installed by
* JFrameChart.
*/
private Basis(boolean exitOnClose) {
if (!exitOnClose) {
Object l[] = getListeners(java.awt.event.WindowListener.class);
for (int k = 0; k < l.length; k++) {
removeWindowListener((java.awt.event.WindowListener) l[k]);
}
}
com.imsl.demo.gallery.Describe des = new com.imsl.demo.gallery.Describe(this, "/com/imsl/demo/Basis/Basis.html");
des.show();
java.awt.Dimension dess = des.getSize();
java.awt.Dimension ss = getToolkit().getScreenSize();
int h = Math.min(ss.width/2, ss.height-dess.height-32);
int w = (int)(h/0.8);
setSize(w, h);
setLocation(ss.width-dess.width, dess.height);
setTitle("User Basis Regression");
// Set default values
final double pi = Physical.constant("PI").doubleValue();
int ncoef = defCoef;
double[] coef = new double[ncoef];
x = new double[npoints];
rand = new double[npoints];
Random r = new Random();
for (int i = 0; i < npoints; i++) {
x[i] = 3 * pi * (double) i / npoints;
rand[i] = r.nextDouble();
}
createChart();
drawGraph();
initComponents();
}
/** Spawns the Basis class by calling show() */
public static void main(String args[]) {
boolean exitOnClose = true;
if (args.length > 0 && args[0].equals("-noexit")) {
exitOnClose = false;
}
new Basis(exitOnClose).show();
}
/** Pop up a dialog with the coefficients. */
public void detailButtonActionPerformed(java.awt.event.ActionEvent event) {
int width = 35;
java.text.MessageFormat mf = new java.text.MessageFormat(textUser);
JPanel cPanel = new JPanel(new java.awt.BorderLayout());
JTextArea tArea = new JTextArea(" ", 2, width);
Object kargs[] = {new String("k")};
String kusr = mf.format(kargs);
tArea.setText("Leading coefficients of " + kusr + " to fit " + textFunc);
int ncoef = jScrollCoef.getValue();
JTextArea cArea = new JTextArea(" ", ncoef+1, width);
StringBuffer sb = new StringBuffer("\n");
double[] coef = new double[ncoef];
coef = ubr.getCoefficients();
java.text.DecimalFormat fmt = new java.text.DecimalFormat("##0.0000");
for (int i = 0; i < ncoef; i++) {
sb.append("Coefficient #" + (i+1) + " = " + fmt.format(coef[i]) + "\n");
}
cArea.setText(sb.toString());
cArea.setEditable(false);
cArea.setBackground(cPanel.getBackground());
JTextArea eArea = new JTextArea(" ", 2, width);
sb = new StringBuffer("Analytical equation of the estimate:\ny = ");
boolean addText = false;
int addLine = 0;
java.text.DecimalFormat shortfmt = new java.text.DecimalFormat("##0.####");
for (int i = 0; i < ncoef; i++) {
Object args[] = {new Integer(i+1)};
String usr = mf.format(args);
String z = fmt.format(JMath.abs(coef[i]));
if (z.compareTo("0.0000") > 0) {
if (addText) {
sb.append(" + ");
}
if (sb.length() > (width*(addLine+1))*2) {
sb.append("\n ");
addLine++;
}
if (textUser.indexOf(" ") == -1) {
sb.append(shortfmt.format(coef[i]) + "*" + usr);
} else {
sb.append(shortfmt.format(coef[i]) + "*[" + usr +"]");
}
addText = true;
}
}
if (!addText) {
if (textUser.indexOf(" ") == -1) {
sb.append("0 * " + kusr);
} else {
sb.append("0 * [" + kusr +"]");
}
}
eArea.setText(sb.toString());
cPanel.add(tArea, java.awt.BorderLayout.NORTH);
cPanel.add(cArea, java.awt.BorderLayout.CENTER);
cPanel.add(eArea, java.awt.BorderLayout.SOUTH);
JOptionPane.showMessageDialog(this, cPanel, "Coefficients", JOptionPane.INFORMATION_MESSAGE);
}
private String getDescription() {
StringBuffer sb = new StringBuffer();
sb.append("Analytical Function: " + textFunc);
java.text.MessageFormat mf = new java.text.MessageFormat(textUser);
Object kargs[] = {new String("k")};
String kusr = mf.format(kargs);
sb.append("\nUser Basis Function: " + kusr);
return sb.toString();
}
private void createChart() {
// Chart
chart = getChart();
axis = new AxisXY(chart);
axis.setViewport(0.05, 0.98, 0.05, 0.95);
chart.getLegend().setPaint(true);
chart.getLegend().setViewport(0.70, 0.90, 0.10, 0.05);
// Set x-axis propteries
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow(xrange);
axis.getAxisX().setDensity(2);
axis.getAxisX().setNumber(11);
axis.getAxisX().setTickLength(-1);
axis.getAxisX().setTextFormat(new java.text.DecimalFormat("#0"));
// Set y-axis properties
axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisY().setWindow(yrange);
axis.getAxisY().setDensity(2);
axis.getAxisY().setNumber(7);
axis.getAxisY().setTextFormat(new java.text.DecimalFormat("##0"));
axis.getAxisY().setTickLength(-1);
// Add another AxisXY for the boxed look
axis2 = new AxisXY(chart);
axis2.setViewport(0.05, 0.98, 0.05, 0.95);
axis2.getAxisX().setType(AxisXY.AXIS_X_TOP);
axis2.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis2.getAxisX().setWindow(xrange);
axis2.getAxisX().setDensity(2);
axis2.getAxisX().setNumber(11);
axis2.getAxisX().setTickLength(-1);
axis2.getAxisX().setTextColor(java.awt.Color.white);
axis2.getAxisY().setType(AxisXY.AXIS_Y_RIGHT);
axis2.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis2.getAxisY().setWindow(yrange);
axis2.getAxisY().setDensity(2);
axis2.getAxisY().setNumber(11);
axis2.setCross(xrange[1], yrange[1]);
axis2.getAxisY().setTickLength(-1);
axis2.getAxisY().setTextColor(java.awt.Color.white);
}
private void initComponents() {
java.awt.Container cp = getContentPane();
JPanel msgPanel = new JPanel();
displayText = new JTextArea(getDescription(), 2, 20);
displayText.setEditable(false);
JButton detailButton = new JButton("Show Coefficients");
detailButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
detailButtonActionPerformed(evt);
}
});
msgPanel.add(displayText);
msgPanel.add(detailButton);
cp.add(msgPanel, java.awt.BorderLayout.NORTH);
JPanel southPanel = new JPanel();
// Function selection ComboBox
Label labelFn[] = {
new Label("Function 1 (sin)", 0),
new Label("Function 2 (cos)", 1),
new Label("Function 3 (sin*cos)", 2),
new Label("Function 4 (sin*Random)", 3)
};
jComboBoxFunc = new JComboBox();
for (int i = 0; i < labelFn.length; i++) {
jComboBoxFunc.addItem(labelFn[i]);
}
jComboBoxFunc.setSelectedIndex(0);
jComboBoxFunc.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jComboBoxFuncActionPerformed(evt);
}
});
// User Basis Function selection ComboBox
Label labelUs[] = {
new Label("sin(kx)", 0),
new Label("cos(kx)", 1),
new Label("sin(kx) * cos(kx)", 2),
new Label("sin(kx) + cos(kx)", 3)
//new Label("sinh(kx)", 4),
//new Label("cosh(kx)", 5)
};
jComboBoxUser = new JComboBox();
for (int i = 0; i < labelUs.length; i++) {
jComboBoxUser.addItem(labelUs[i]);
}
jComboBoxUser.setSelectedIndex(0);
jComboBoxUser.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jComboBoxUserActionPerformed(evt);
}
});
jLabelSNC = new JLabel();
jLabelSNC.setText("# Coefficients = " + defCoef);
jScrollCoef = new JScrollBar(java.awt.Scrollbar.HORIZONTAL);
jScrollCoef.setValues(defCoef, 1, 1, 9);
jScrollCoef.setBlockIncrement(1);
jScrollCoef.setPreferredSize(new java.awt.Dimension(100,15));
jScrollCoef.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
public void adjustmentValueChanged(java.awt.event.AdjustmentEvent avt) {
jScrollCoefValueChanged(avt);
}
});
// Add components to the panel
southPanel.add(jComboBoxFunc);
southPanel.add(jComboBoxUser);
southPanel.add(jLabelSNC);
southPanel.add(jScrollCoef);
cp.add(southPanel, java.awt.BorderLayout.SOUTH);
}
/** Draw the graph in the chart. The input data and the Basis Regression
* computed from the coefficients are plotted.
*/
private void drawGraph() {
// Get number of coefficients to use
int ncoef = defCoef;
if (jScrollCoef != null) {
ncoef = jScrollCoef.getValue();
}
// Defined the UserBasis here
int val = 0;
if (jComboBoxUser != null) {
val = ((Label) jComboBoxUser.getSelectedItem()).index;
}
switch (val) {
case 0: /* Sin */
ubr = new UserBasisRegression(new BasisSin(), ncoef, false);
textUser = "sin({0}x)";
break;
case 1: /* Cos */
ubr = new UserBasisRegression(new BasisCos(), ncoef, false);
textUser = "cos({0}x)";
break;
case 2: /* Prod */
ubr = new UserBasisRegression(new BasisProd(), ncoef, false);
textUser = "sin({0}x) * cos({0}x)";
break;
case 3: /* Sum */
ubr = new UserBasisRegression(new BasisSum(), ncoef, false);
textUser = "sin({0}x) + cos({0}x)";
break;
case 4: /* Sinh */
ubr = new UserBasisRegression(new BasisSinh(), ncoef, false);
textUser = "sinh({0}x)";
break;
case 5: /* Cosh */
ubr = new UserBasisRegression(new BasisCosh(), ncoef, false);
textUser = "cosh({0}x)";
break;
}
// Draw the function
f = computeFunc(x); // computeFunc updates the ubr
lineFunc = new Data(axis, x, f);
lineFunc.setLineColor(java.awt.Color.blue);
lineFunc.setLineWidth(2.0);
lineFunc.setTitle("Analytical Function");
// Draw the estime
y = computeUser(x); // computeUser retrives coefs from ubr
lineUser = new Data(axis, x, y);
lineUser.setLineColor(java.awt.Color.red);
lineUser.setLineWidth(0.5);
lineUser.setLineDashPattern(Data.DASH_PATTERN_DOT);
lineUser.setTitle("User Basis Regression");
}
/** Compute the line for the function to plot
*
*@param xx The independent values
*@return The dependent values for the input function f(x)
*/
private double[] computeFunc(double[] xx) {
double[] ff = new double[xx.length];
int val = 0;
if (jComboBoxFunc != null) {
val = ((Label) jComboBoxFunc.getSelectedItem()).index;
}
switch (val) {
case 0: /* function 1 */
for (int i = 0; i < npoints; i++) {
ff[i] = JMath.sin(xx[i]) + 7.0 * JMath.sin(3.0 * xx[i]);
ubr.update(xx[i], ff[i], 1.0);
}
textFunc = "sin(x) + 7sin(3x)";
break;
case 1: /* function 2 */
for (int i = 0; i < npoints; i++) {
ff[i] = JMath.cos(xx[i]) + 7.0 * JMath.cos(3.0 * xx[i]);
ubr.update(xx[i], ff[i], 1.0);
}
textFunc = "cos(x) + 7cos(3x)";
break;
case 2: /* function 3 */
for (int i = 0; i < npoints; i++) {
ff[i] = 10.0 * JMath.cos(xx[i]) * JMath.sin(xx[i]);
ubr.update(xx[i], ff[i], 1.0);
}
textFunc = "10 * cos(x) * sin(x)";
break;
case 3: /* function 4 */
for (int i = 0; i < npoints; i++) {
ff[i] = 8.0 * JMath.sin(2.0 * xx[i]) * rand[i];
ubr.update(xx[i], ff[i], 1.0);
}
textFunc = "8sin(2x) * Uniform Random [0,1]";
break;
//case 4: /* file */
/* An original part of the demo. It would be easy to read
* in your own file to extend the functionality of this
* demonstration
textFunc = "file y.dat";
java.io.File file = new java.io.File("y.dat");
try {
java.io.BufferedReader br = new java.io.BufferedReader(
new java.io.InputStreamReader(
new java.io.BufferedInputStream(
new java.io.FileInputStream(file))));
for (int i = 0; i < npoints; i++) {
String s = br.readLine();
try {
ff[i] = Double.valueOf(s.trim()).doubleValue();
} catch (NumberFormatException nfe) {
System.out.println("NumberFormatException: " + nfe.getMessage());
}
}
br.close();
} catch (java.io.IOException e) {
System.err.println("FileIO: " + e);
}
break;
**/
}
return ff;
}
/** Compute the user basis function estimate to plot
*
*@param xx The independent values
*@return The dependent values for the user basis function y(x)
*/
private double[] computeUser(double[] xx) {
double[] yy = new double[xx.length];
int ncoef = defCoef;
if (jScrollCoef != null) {
ncoef = jScrollCoef.getValue();
}
double[] coef = new double[ncoef];
coef = ubr.getCoefficients();
int val = 0;
if (jComboBoxUser != null) {
val = ((Label) jComboBoxUser.getSelectedItem()).index;
}
for (int i = 0; i < npoints; i++) {
double sum = 0.0;
double tmp = 0.0;
for (int j = 0; j < ncoef; j++) {
switch (val) {
case 0: /* Sin */
tmp = JMath.sin((double) (j + 1) * x[i]);
break;
case 1: /* Cos */
tmp = JMath.cos((double) (j + 1) * x[i]);
break;
case 2: /* Prod */
tmp = JMath.sin((double) (j + 1) * x[i]) * JMath.cos((double) (j + 1) * x[i]);
break;
case 3: /* Sum */
tmp = JMath.sin((double) (j + 1) * x[i]) * JMath.sin((double) (j + 1) * x[i]);
break;
case 4: /* Sinh */
tmp = Hyperbolic.sinh((double) (j + 1) * x[i]);
break;
case 5: /* Cosh */
tmp = Hyperbolic.sinh((double) (j + 1) * x[i]);
break;
}
sum = sum + coef[j] * tmp;
}
yy[i] = sum;
}
return yy;
}
/** Update the chart by removing all the Data objects, then calling
* drawGraph() and repaint(). Finally update the displayText.
*/
private void update() {
if (lineFunc != null) {
lineFunc.remove();
}
if (lineUser != null) {
lineUser.remove();
}
drawGraph();
repaint();
displayText.setText(getDescription());
}
/** Update when a new function is selected */
private void jComboBoxFuncActionPerformed(java.awt.event.ActionEvent evt) {
update();
}
/** Update when a new user basis is selected */
private void jComboBoxUserActionPerformed(java.awt.event.ActionEvent evt) {
update();
}
/** Adjusting scrollbar changes the number of coefficients, so update() */
private void jScrollCoefValueChanged(java.awt.event.AdjustmentEvent event) {
jLabelSNC.setText("# Coefficients = " + jScrollCoef.getValue());
update();
}
/** Label objects are used in conjunction with the JComboBox. */
private static class Label {
String label;
int index;
Label(String label, int index) {
this.label = label;
this.index = index;
}
public String toString() {
return label;
}
}
/** Basis functions are below this point
* Each function is best summarized by these docs where
* f is the basis function to use (e.g., JMath.sin())
*
* Compute f(kx)
*
*@param index value of k-1
*@param x independent value
*@return f(k*x)
*/
/** Sine basis function. */
class BasisSin implements RegressionBasis {
public double basis(int index, double x) {
return JMath.sin((index + 1) * x);
}
}
/** Cosine basis function. */
class BasisCos implements RegressionBasis {
public double basis(int index, double x) {
return JMath.cos((index + 1) * x);
}
}
/** Product of sine and cosine basis function. */
class BasisProd implements RegressionBasis {
public double basis(int index, double x) {
return JMath.cos((index + 1) * x) * JMath.sin((index + 1) * x);
}
}
/** Sum of sine and cosine basis function*/
class BasisSum implements RegressionBasis {
public double basis(int index, double x) {
return JMath.cos((index + 1) * x) + JMath.sin((index + 1) * x);
}
}
/** Exponential basis function. */
class BasisExp implements RegressionBasis {
public double basis(int index, double x) {
return JMath.exp((index + 1) * x);
}
}
/** Natural log basis function. */
class BasisLog implements RegressionBasis {
public double basis(int index, double x) {
return JMath.log((index + 1) * x);
}
}
/** Hyperbolic sine basis function. */
class BasisSinh implements RegressionBasis {
public double basis(int index, double x) {
return Hyperbolic.sinh((index + 1) * x);
}
}
/** Hyperbolic cosine basis function. */
class BasisCosh implements RegressionBasis {
public double basis(int index, double x) {
return Hyperbolic.cosh((index + 1) * x);
}
}
}