jidt/java/source/infodynamics/demos/autoanalysis/AutoAnalyser.java

2009 lines
79 KiB
Java

/*
* Java Information Dynamics Toolkit (JIDT)
* Copyright (C) 2015, Joseph T. Lizier
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package infodynamics.demos.autoanalysis;
import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous;
import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete;
import infodynamics.utils.AnalyticMeasurementDistribution;
import infodynamics.utils.AnalyticNullDistributionComputer;
import infodynamics.utils.ArrayFileReader;
import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.EmpiricalNullDistributionComputer;
import infodynamics.utils.MatrixUtils;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JComboBox;
import javax.swing.JCheckBox;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JFileChooser;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ToolTipManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Vector;
/**
* This abstract class provides a GUI to build a simple calculation,
* and supply the code to execute it.
* Child classes fix this to a TE or MI calculation.
*
*
* @author Joseph Lizier
*
*/
public abstract class AutoAnalyser extends JFrame
implements ActionListener, DocumentListener, MouseListener, ChangeListener {
/**
* Need serialVersionUID to be serializable
*/
private static final long serialVersionUID = 1L;
// Set options for the Calculator type:
public final static String CALC_TYPE_DISCRETE = "Discrete";
public final static String CALC_TYPE_BINNED = "Binned";
// Child classes must define options for other calculator types
// and initialise the calcTypes array and unitsForEachCalc array
protected String[] calcTypes;
protected String[] unitsForEachCalc;
// Set additional general options for the Calculator type:
public final static String CALC_TYPE_GAUSSIAN = "Gaussian";
public final static String CALC_TYPE_KRASKOV = "Kraskov (KSG)";
public final static String CALC_TYPE_KERNEL = "Kernel";
public final static String CALC_TYPE_KOZ_LEO = "Kozachenko-Leonenko";
// Properties for each calculator
protected static final String DISCRETE_PROPNAME_BASE = "base";
protected String[] discreteProperties; // Children to initialise
protected String[] discretePropertyDefaultValues; // Children to initialise
protected String[] discretePropertyDescriptions; // Children to initialise
protected String[][] discretePropertyValueChoices; // Children to initialise
// Common property names for all continuous calculators:
protected String[] commonContPropertyNames;
protected String[] commonContPropertiesFieldNames;
protected String[] commonContPropertyDescriptions;
protected String[][] commonContPropertyValueChoices;
// Children can define properties for specific continuous
// calculators
// Number of variables that this measure operates on (1 to 3)
protected int numVariables = 1;
// Labels for each of the fields to enter the column number for the variables.
// Should be overridden by the child classes.
protected String[] variableColNumLabels = null;
protected boolean[] disableVariableColTextFieldsForAllCombos = null;
protected int indentsForAllCombos = 1;
// Store which calculator type we're using:
@SuppressWarnings("rawtypes")
protected Class calcClass = null;
@SuppressWarnings("rawtypes")
protected Class abstractContinuousClass = null;
@SuppressWarnings("rawtypes")
protected Class discreteClass = null;
protected String measureAcronym = null;
// String to describe the relationship between the variables
// being measured here, with format tokens %%d in order for
// each variable. To be assigned by the child class.
protected String variableRelationshipFormatString = null;
protected JButton computeButton;
protected JButton openDataButton;
// Stores the current data file
protected File dataFile = null;
// Combo box for selecting the calculator type
protected JComboBox<String> calcTypeComboBox;
// Displays the current data file
protected JTextField dataFileTextField;
// Descriptor of the data:
protected JLabel dataFileDescriptorLabel;
// The continuous data we're working with
protected double[][] data = null;
// The discrete data we're working with
protected int[][] dataDiscrete = null;
// Number of rows and columns of the data:
protected int dataRows = 0;
protected int dataColumns = 0;
// Boolean for whether we include the all combinations checkbox and label on the GUI
// (default false). Child classes can alter this in makeSpecificInitialisations()
protected boolean useAllCombosCheckBox = false;
// Which word should be used to describe combinations for this measure? "pairs", "variables", etc?
// Child classes should override this.
protected String wordForCombinations = null;
// CheckBox for "all variables?" OR "all pairs?"
protected JCheckBox allCombosCheckBox;
// Variable text fields
protected JTextField[] variableColTextFields;
// Boolean for whether we include the statistical significance checkbox and label on the GUI
// (default false). Child classes can alter this in makeSpecificInitialisations()
protected boolean useStatSigCheckBox = false;
// CheckBox for statistical significance
protected JCheckBox statSigCheckBox;
// CheckBox for statistical significance analytically
protected JCheckBox statSigAnalyticCheckBox;
// Number of permutations to use for statistical significance
protected int numPermutationsToCheck = 100;
// Table for the properties
protected JTable propertiesTable;
// Default editor for the property values
protected TableCellEditor propertiesDefaultEditor;
// Table model (local class) for the table
protected PropertiesTableModel propertiesTableModel;
// Names of the properties
protected Vector<String> propertyNames;
// Names of the fields for the properties
protected Vector<String> propertyFieldNames;
// Descriptions of the fields for the properties
protected Vector<String> propertyDescriptions;
// Lists of drop-down options for the properties
protected Vector<String[]> propertyValueChoices;
// Values of the properties
protected HashMap<String,String> propertyValues;
// CheckBox for "Compute result?"
protected JCheckBox computeResultCheckBox;
// Results text
protected JLabel resultsLabel;
// Text area for the generated Java code
protected JTextArea javaCodeTextArea;
// Text area for the generated Python code
protected JTextArea pythonCodeTextArea;
// Text area for the generated Matlab code
protected JTextArea matlabCodeTextArea;
protected String codeDefaultText = " ... Awaiting new parameter selection (press compute) ...";
protected String buttonTextCodeAndCompute = "Generate code and Compute";
protected String buttonTextCodeOnly = "Generate code";
protected String appletTitle;
// Specifies the location of the demos/AutoAnalyser/ directory.
// Is set in the constructor (either by argument or by assuming
// this is the same as a the working directory).
protected String pathToAutoAnalyserDir = "";
// Main JIDT git/distribution folder, inferred from pathToAutoAnalyserDir
protected String jidtFolder = "";
public class TextAreaWithImage extends JTextArea {
/**
* Default serialVersionUID
*/
private static final long serialVersionUID = 1L;
/**
* Image for the background of the text area
*/
protected Image image;
protected boolean rescaled = false;
protected int xOffset = 0, yOffset = 0;
public TextAreaWithImage(String defaultText, Image image) {
super(defaultText);
setOpaque(false); // I think this is set by default
this.image = image;
}
@Override
protected void paintComponent(Graphics g) {
if (!rescaled) {
int width = getWidth();
int height = getHeight();
if (width < height) {
image = image.getScaledInstance(width, -1, 0);
// TODO The following doesn't work because the image height is
// not returned.
// yOffset = height - image.getHeight(null)/2;
} else {
image = image.getScaledInstance(-1, height, 0);
// The following doesn't work because the image height is
// not returned.
// xOffset = width - image.getWidth(this)/2;
}
rescaled = true;
}
// Hacking an offset in because I haven't worked out how to
// access the current width/height. This link might have a solution:
// http://stackoverflow.com/questions/26386422/how-to-set-background-image-to-a-jtextarea-in-a-jpanel
// g.drawImage(image,xOffset,yOffset,null);
g.drawImage(image,50,0,null);
super.paintComponent(g);
}
}
/**
* Constructor to generate the application windows
*/
public AutoAnalyser() {
// Assume our working directory is the AutoAnalyser directory:
this(System.getProperty("user.dir") + "/");
}
/**
* Constructor to generate the application windows
*/
public AutoAnalyser(String pathToAutoAnalyserDir) {
this.pathToAutoAnalyserDir = pathToAutoAnalyserDir;
System.out.println("Working directory is: " +
System.getProperty("user.dir"));
System.out.println("Starting with path to AutoAnalyser folder: " +
pathToAutoAnalyserDir);
jidtFolder = pathToAutoAnalyserDir + "../../";
makeSpecificInitialisations();
// Build the swing applet
ImageIcon icon = new ImageIcon(jidtFolder + "JIDT-logo.png"); // Location for distributions
if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
// Try the alternative image location for git checkouts
icon = new ImageIcon(jidtFolder + "web/JIDT-logo.png");
}
setIconImage(icon.getImage());
Image watermarkImage = (new ImageIcon(pathToAutoAnalyserDir + "JIDT-logo-watermark.png")).getImage();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1100,680);
setTitle(appletTitle);
// Centre in the middle of the screen
setLocationRelativeTo(null);
// Create the fields for the calc type:
JLabel calcTypeLabel = new JLabel("Calculator Type:");
calcTypeLabel.setToolTipText("Select estimator type. \"Discrete\" is for discrete or pre-binned data; all others for continuous data.");
calcTypeLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
calcTypeComboBox = (JComboBox<String>) new JComboBox<String>(calcTypes);
calcTypeComboBox.setSelectedIndex(3); // Select 2nd continuous estimator by default
calcTypeComboBox.addActionListener(this);
// Don't set an empty border as it becomes clickable as well,
// we'll use an empty label instead
//calcTypeComboBox.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
// Create the fields for the data file
JLabel fileLabel = new JLabel("Data file:");
fileLabel.setToolTipText("Must be a text file of samples or time-series values with time/sample increasing in rows, and variables along columns");
dataFileTextField = new JTextField(30);
dataFileTextField.setEnabled(false);
dataFileTextField.addMouseListener(this);
// Don't set border around this text field as it doesn't look right
// Button to open data file
openDataButton = new JButton("Select");
openDataButton.addActionListener(this);
// Description about the data
dataFileDescriptorLabel = new JLabel("No data file selected yet ...");
dataFileDescriptorLabel.setHorizontalAlignment(JLabel.RIGHT);
dataFileDescriptorLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
// We define the all combos checkbox, but we won't add it later
// if it's not used for this measure type.
allCombosCheckBox = new JCheckBox("All " + wordForCombinations + "?");
allCombosCheckBox.setToolTipText("Compute for all column " + wordForCombinations + "?");
allCombosCheckBox.addChangeListener(this);
variableColTextFields = new JTextField[numVariables];
JLabel[] labels = new JLabel[numVariables];
for (int i = 0; i < numVariables; i++) {
labels[i] = new JLabel(variableColNumLabels[i] + " column:");
labels[i].setToolTipText("First column is 0.");
// This border was only done on the 2nd variable before, so may need
// to restrict its use:
labels[i].setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
variableColTextFields[i] = new JTextField(5);
variableColTextFields[i].setEnabled(true);
variableColTextFields[i].setText(Integer.toString(i));
// Must add document listener, not add action listener
variableColTextFields[i].getDocument().addDocumentListener(this);
}
// We define the CheckBox for statistical signficance,
// but we won't add it later if it's not used for this measure type.
statSigCheckBox = new JCheckBox("Add stat. signif.?");
statSigCheckBox.setToolTipText("Compute the null surrogate distribution under the null hypothesis that source and target are not related?");
statSigCheckBox.addChangeListener(this);
statSigAnalyticCheckBox = new JCheckBox("analytically?");
statSigAnalyticCheckBox.setToolTipText("Compute the null surrogate distribution analytically?");
statSigAnalyticCheckBox.addChangeListener(this);
statSigAnalyticCheckBox.setEnabled(false);
JLabel dummyLabel1 = new JLabel(" ");
dummyLabel1.setSize(10, 10);
JLabel dummyLabel2 = new JLabel(" ");
dummyLabel2.setSize(10, 10);
JLabel dummyLabel3 = new JLabel(" ");
dummyLabel3.setSize(10, 10);
putCalcPropertiesInTable();
propertiesTableModel = new PropertiesTableModel();
propertiesTable = new TableWithToolTip(propertiesTableModel);
// Get the default editor for the properties values:
propertiesDefaultEditor = propertiesTable.getDefaultEditor(
propertiesTable.getColumnClass(1));
System.out.println("Default properties editor is " + propertiesDefaultEditor.getClass().getName());
// Make sure any properties are saved when the compute button is clicked
propertiesTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
Font headerFont = propertiesTable.getTableHeader().getFont();
propertiesTable.getTableHeader().setFont(headerFont.deriveFont(Font.BOLD));
TableColumn valueColumn = propertiesTable.getColumn("Property value");
valueColumn.setMinWidth(170);
valueColumn.setMaxWidth(170);
JScrollPane propsTableScrollPane = new JScrollPane(propertiesTable);
// Set up for ~18 rows maximum (the +6 is exact to fit all props
// for Kraskov TE in without scrollbar)
Dimension d = propertiesTable.getPreferredSize();
int rowHeight = propertiesTable.getRowHeight();
propsTableScrollPane.setPreferredSize(
new Dimension(d.width,rowHeight*17+6));
propsTableScrollPane.setMinimumSize(
new Dimension(d.width,rowHeight*17+6));
System.out.println("Row height was " + rowHeight);
// Checkbox for compute result
computeResultCheckBox = new JCheckBox("Compute result?");
computeResultCheckBox.setToolTipText("Compute result or only generate code?");
computeResultCheckBox.setSelected(true);
computeResultCheckBox.addChangeListener(this);
// Button to compute
computeButton = new JButton(buttonTextCodeAndCompute);
computeButton.addActionListener(this);
// Results label
resultsLabel = new JLabel(" ");
// Code panel
JPanel codePanel = new JPanel();
codePanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Generated code"),
BorderFactory.createEmptyBorder(5,5,5,5)));
JTabbedPane codeTabbedPane = new JTabbedPane();
// Generate Java code text area
javaCodeTextArea = new TextAreaWithImage(codeDefaultText, watermarkImage);
javaCodeTextArea.setOpaque(false);
javaCodeTextArea.setEditable(false);
javaCodeTextArea.setBorder(BorderFactory.createCompoundBorder(
javaCodeTextArea.getBorder(),
BorderFactory.createEmptyBorder(5,5,5,5)));
//codeTextArea.setLineWrap(true); // This makes it all unreadable
//codeTextArea.setWrapStyleWord(true);
JScrollPane javaAreaScrollPane = new JScrollPane(javaCodeTextArea);
javaAreaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
javaAreaScrollPane.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
int codeTextAreaWidth = 560;
int codeTextAreaHeight = 530;
Dimension codeTextAreaDimension =
new Dimension(codeTextAreaWidth, codeTextAreaHeight);
javaAreaScrollPane.setPreferredSize(codeTextAreaDimension);
javaAreaScrollPane.setMinimumSize(codeTextAreaDimension);
javaAreaScrollPane.setMaximumSize(codeTextAreaDimension);
codeTabbedPane.addTab("Java", javaAreaScrollPane);
// Generate Python code text area
pythonCodeTextArea = new TextAreaWithImage(codeDefaultText, watermarkImage);
pythonCodeTextArea.setOpaque(false);
pythonCodeTextArea.setEditable(false);
pythonCodeTextArea.setBorder(BorderFactory.createCompoundBorder(
pythonCodeTextArea.getBorder(),
BorderFactory.createEmptyBorder(5,5,5,5)));
JScrollPane pythonAreaScrollPane = new JScrollPane(pythonCodeTextArea);
pythonAreaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
pythonAreaScrollPane.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
pythonAreaScrollPane.setPreferredSize(codeTextAreaDimension);
pythonAreaScrollPane.setMinimumSize(codeTextAreaDimension);
pythonAreaScrollPane.setMaximumSize(codeTextAreaDimension);
codeTabbedPane.addTab("Python", pythonAreaScrollPane);
// Generate Matlab code text area
matlabCodeTextArea = new TextAreaWithImage(codeDefaultText, watermarkImage);
matlabCodeTextArea.setOpaque(false);
matlabCodeTextArea.setEditable(false);
matlabCodeTextArea.setBorder(BorderFactory.createCompoundBorder(
matlabCodeTextArea.getBorder(),
BorderFactory.createEmptyBorder(5,5,5,5)));
JScrollPane matlabAreaScrollPane = new JScrollPane(matlabCodeTextArea);
matlabAreaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
matlabAreaScrollPane.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
matlabAreaScrollPane.setPreferredSize(codeTextAreaDimension);
matlabAreaScrollPane.setMinimumSize(codeTextAreaDimension);
matlabAreaScrollPane.setMaximumSize(codeTextAreaDimension);
codeTabbedPane.addTab("Matlab", matlabAreaScrollPane);
// Now add the tabbed pane to the panel
codePanel.add(codeTabbedPane);
codePanel.setSize(codeTextAreaWidth+10, codeTextAreaHeight+10);
// Add all the components in:
/*
add(calcTypePanel, BorderLayout.NORTH);
add(dataFileChooserPanel, BorderLayout.EAST);
add(dataFileDescriptorPanel, BorderLayout.WEST);
add(computeButton, BorderLayout.SOUTH);
*/
// gridbag.setConstraints(calcTypePanel, c);
// add(calcTypePanel);
JPanel paramsPanel = new JPanel();
paramsPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Calculation parameters"),
BorderFactory.createEmptyBorder(5,5,5,5)));
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
paramsPanel.setLayout(gridbag);
c.anchor = GridBagConstraints.EAST; // Not sure what I put EAST for?
// Add the CalcType label and combobox
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
paramsPanel.add(calcTypeLabel, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(calcTypeComboBox, c);
// Add dummy label for spacing
paramsPanel.add(dummyLabel3, c);
// Add the data file chooser fields
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
paramsPanel.add(fileLabel, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(dataFileTextField, c);
c.gridx = 1;
paramsPanel.add(openDataButton, c);
c.gridx = -1; // Reset to no indication
paramsPanel.add(dataFileDescriptorLabel, c);
if (useAllCombosCheckBox) {
// Add the all combos label and checkbox
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.gridx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(allCombosCheckBox, c);
c.gridx = -1; // reset to no indication
}
// Add the column number selectors
for (int i = 0; i < numVariables; i++) {
// Add the column selector for variable i
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
paramsPanel.add(labels[i], c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(variableColTextFields[i], c);
}
if (useStatSigCheckBox) {
// Add the CheckBoxes for statistical significance checks:
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
paramsPanel.add(statSigCheckBox, c);
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(statSigAnalyticCheckBox, c);
}
// Add dummy label for spacing
paramsPanel.add(dummyLabel1, c);
// Add the properties table
paramsPanel.add(propsTableScrollPane, c);
// Add dummy label for spacing
paramsPanel.add(dummyLabel2, c);
// Add the "compute result" checkbox:
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
paramsPanel.add(computeResultCheckBox);
// Add the compute button
c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
paramsPanel.add(computeButton, c);
// Status panel
JPanel statusPanel = new JPanel();
statusPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Status"),
BorderFactory.createEmptyBorder(5,5,5,5)));
statusPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
// Add the results text
statusPanel.add(resultsLabel);
// Add all panels into the frame with Border layout
add(paramsPanel, BorderLayout.WEST);
add(codePanel, BorderLayout.EAST);
add(statusPanel, BorderLayout.SOUTH);
setVisible(true);
// The default tool tip delay before dismissing was too short to read these, so
// I'm setting it to 30 sec.
ToolTipManager.sharedInstance().setDismissDelay(30000);
// Try to force the watermark images to come up
javaCodeTextArea.repaint();
pythonCodeTextArea.repaint();
pythonCodeTextArea.repaint();
}
/**
* Child classes over-ride this to set strings etc specifically for their measure type
*/
protected abstract void makeSpecificInitialisations();
@Override
public void actionPerformed(ActionEvent e) {
// Clear text fields for now if anything changes
resultsLabel.setText(" ");
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
if (e.getSource() == computeButton) {
compute();
} else if (e.getSource() == openDataButton) {
selectFileAction();
} else if (e.getSource() == calcTypeComboBox) {
putCalcPropertiesInTable();
propertiesTableModel.fireTableDataChanged(); // Alerts to refresh the table contents
System.out.println("Added properties for new calculator");
if (statSigCheckBox.isSelected()) {
// We might allow the analytic box to be checked if the selected
// calculator allows this:
if (calcImplementsAnalyticStatSig()) {
statSigAnalyticCheckBox.setEnabled(true);
} else {
statSigAnalyticCheckBox.setSelected(false);
statSigAnalyticCheckBox.setEnabled(false);
}
} else {
statSigAnalyticCheckBox.setSelected(false);
statSigAnalyticCheckBox.setEnabled(false);
}
}
// Else nothing extra to do
}
public void repaint() {
System.out.println("repainting ...");
super.repaint();
}
protected void selectFileAction() {
System.out.println("Open data button pressed ...");
// Give user a choice of file, starting from the currently selected
// one
JFileChooser dataFileChooser;
if (dataFile == null) {
dataFileChooser = new JFileChooser(
pathToAutoAnalyserDir + "../data/");
System.out.println("Choosing data from: " + pathToAutoAnalyserDir + "../data/");
} else {
dataFileChooser = new JFileChooser(dataFile);
}
int returnVal = dataFileChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
// File selection was approved:
dataFile = dataFileChooser.getSelectedFile();
dataFileTextField.setText(dataFile.getAbsolutePath());
System.out.println("Data file selected: " + dataFile.getAbsolutePath());
// Now load the file in to check it:
loadData(false); // Assume is doubles for the moment
}
// Else do nothing
}
protected void loadData(boolean isInts) {
try {
ArrayFileReader afr = new ArrayFileReader(dataFile);
if (isInts) {
dataDiscrete = afr.getInt2DMatrix();
dataRows = dataDiscrete.length;
if (dataRows > 0) {
dataColumns = dataDiscrete[0].length;
} else {
dataColumns = 0;
}
} else {
data = afr.getDouble2DMatrix();
dataRows = data.length;
if (dataRows > 0) {
dataColumns = data[0].length;
} else {
dataColumns = 0;
}
}
dataFileDescriptorLabel.setText(
String.format("Valid data file with %d rows and %d columns",
dataRows, dataColumns));
System.out.printf("Read in data with %d rows and %d columns\n",
dataRows, dataColumns);
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this, ex.getMessage() +
(isInts ? ".\nFor Discrete calculator make sure you load a file with integer data!" : ""));
dataFileDescriptorLabel.setText("Invalid data file, please load another");
if (isInts) {
dataDiscrete = null;
} else {
data = null;
}
}
}
protected void compute() {
System.out.println("Compute button pressed ...");
resultsLabel.setText("Computing ...");
String selectedCalcType = (String)
calcTypeComboBox.getSelectedItem();
String units = unitsForEachCalc[calcTypeComboBox.getSelectedIndex()];
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
// Try to read the data file as ints now:
loadData(true);
if (dataDiscrete == null) {
// An error message will have been shown by loadData()
resultsLabel.setText(" ");
return;
}
} else if (data == null) {
// We need a file of continuous data but no file has been selected
JOptionPane.showMessageDialog(this, "No valid data source selected");
resultsLabel.setText(" ");
return;
}
int[] singleCalcColumns = new int[numVariables];
Vector<int[]> variableCombinations = new Vector<int[]>();
try {
if (!allCombosCheckBox.isSelected()) {
// we're doing a single combination
for (int i = 0; i < numVariables; i++) {
singleCalcColumns[i] = Integer.parseInt(variableColTextFields[i].getText());
if ((singleCalcColumns[i] < 0) || (singleCalcColumns[i] >= dataColumns)) {
JOptionPane.showMessageDialog(this,
String.format("%s column must be between 0 and %d for this data set",
variableColNumLabels[i], dataColumns-1));
resultsLabel.setText(" ");
return;
}
}
}
if (computeResultCheckBox.isSelected()) {
// Only need variableCombinations filled out if we're computing
if (allCombosCheckBox.isSelected()) {
// We're doing all combinations
fillOutAllCombinations(variableCombinations);
} else {
variableCombinations.add(singleCalcColumns);
}
}
} catch (Exception e) {
// Catches number format exception, and column number out of bounds
JOptionPane.showMessageDialog(this,
e.getMessage());
resultsLabel.setText("Cannot parse a column number from input: " + e.getMessage());
return;
}
// Generate headers:
// 1. Java
StringBuffer javaCode = new StringBuffer();
javaCode.append("package infodynamics.demos.autoanalysis;\n\n");
javaCode.append("import infodynamics.utils.ArrayFileReader;\n");
if (statSigCheckBox.isSelected()) {
if (statSigAnalyticCheckBox.isSelected()) {
javaCode.append("import infodynamics.utils.AnalyticMeasurementDistribution;\n");
} else {
javaCode.append("import infodynamics.utils.EmpiricalMeasurementDistribution;\n");
}
}
javaCode.append("import infodynamics.utils.MatrixUtils;\n\n");
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
// Cover all children:
javaCode.append("import infodynamics.measures.discrete.*;\n");
} else {
// Cover the calculator and any common conditional MI classes
// used for property names
javaCode.append("import infodynamics.measures.continuous.*;\n");
}
// Work out full path of the jar and code utilities:
String jarLocation, pythonDemosLocation, matlabDemosLocation;
try {
File jarLocationFile = new File(jidtFolder + "infodynamics.jar");
jarLocation = jarLocationFile.getCanonicalPath().replace("\\", "\\\\");
File pythonDemosLocationFile = new File(pathToAutoAnalyserDir + "../python");
pythonDemosLocation = pythonDemosLocationFile.getCanonicalPath().replace("\\", "\\\\");
File matlabDemosLocationFile = new File(pathToAutoAnalyserDir + "../octave");
matlabDemosLocation = matlabDemosLocationFile.getCanonicalPath().replace("\\", "\\\\");
} catch (IOException ioex) {
JOptionPane.showMessageDialog(this,
ioex.getMessage());
resultsLabel.setText("Cannot find jar and Matlab/Python utility locations: " + ioex.getMessage());
return;
}
// 2. Python
StringBuffer pythonCode = new StringBuffer();
pythonCode.append("from jpype import *\n");
pythonCode.append("import numpy\n");
pythonCode.append("import sys\n");
pythonCode.append("# Our python data file readers are a bit of a hack, python users will do better on this:\n");
pythonCode.append("sys.path.append(\"" + pythonDemosLocation + "\")\n");
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
pythonCode.append("import readIntsFile\n\n");
} else {
pythonCode.append("import readFloatsFile\n\n");
}
pythonCode.append("# Add JIDT jar library to the path\n");
pythonCode.append("jarLocation = \"" + jarLocation + "\"\n");
pythonCode.append("# Start the JVM (add the \"-Xmx\" option with say 1024M if you get crashes due to not enough memory space)\n");
pythonCode.append("startJVM(getDefaultJVMPath(), \"-ea\", \"-Djava.class.path=\" + jarLocation, convertStrings=True)\n\n");
// 3. Matlab:
StringBuffer matlabCode = new StringBuffer();
matlabCode.append("% Add JIDT jar library to the path, and disable warnings that it's already there:\n");
matlabCode.append("warning('off','MATLAB:Java:DuplicateClass');\n");
matlabCode.append("javaaddpath('" + jarLocation + "');\n");
matlabCode.append("% Add utilities to the path\n");
matlabCode.append("addpath('" + matlabDemosLocation + "');\n\n");
try{
// Create both discrete and continuous calculators to make
// following code simpler:
InfoMeasureCalculatorContinuous calcContinuous = null;
InfoMeasureCalculatorDiscrete calcDiscrete = null;
// Construct an instance of the selected calculator:
String javaConstructorLine = null;
String pythonPreConstructorLine = null;
String matlabConstructorLine = null;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
// Defer our processing for this to below ...
} else {
calcContinuous = assignCalcObjectContinuous(selectedCalcType);
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
// Cover the calculator and any references to conditional MI calculator properties
javaCode.append("import infodynamics.measures.continuous.gaussian.*;\n");
javaConstructorLine = " calc = new " + calcContinuous.getClass().getSimpleName() + "();\n";
pythonPreConstructorLine = "calcClass = JPackage(\"infodynamics.measures.continuous.gaussian\")." +
calcContinuous.getClass().getSimpleName() + "\n";
matlabConstructorLine = "calc = javaObject('infodynamics.measures.continuous.gaussian." +
calcContinuous.getClass().getSimpleName() + "');\n";
} else if (selectedCalcType.startsWith(CALC_TYPE_KRASKOV)) {
// The if statement will work for both MI Kraskov calculators
// Cover the calculator and any references to conditional MI calculator properties
javaCode.append("import infodynamics.measures.continuous.kraskov.*;\n");
javaConstructorLine = " calc = new " + calcContinuous.getClass().getSimpleName() + "();\n";
pythonPreConstructorLine = "calcClass = JPackage(\"infodynamics.measures.continuous.kraskov\")." +
calcContinuous.getClass().getSimpleName() + "\n";
matlabConstructorLine = "calc = javaObject('infodynamics.measures.continuous.kraskov." +
calcContinuous.getClass().getSimpleName() + "');\n";
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) {
// Cover the calculator and any references to conditional MI calculator properties
javaCode.append("import infodynamics.measures.continuous.kernel.*;\n");
javaConstructorLine = " calc = new " + calcContinuous.getClass().getSimpleName() + "();\n";
pythonPreConstructorLine = "calcClass = JPackage(\"infodynamics.measures.continuous.kernel\")." +
calcContinuous.getClass().getSimpleName() + "\n";
matlabConstructorLine = "calc = javaObject('infodynamics.measures.continuous.kernel." +
calcContinuous.getClass().getSimpleName() + "');\n";
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KOZ_LEO)) {
// Cover the calculator and any references to other classes
javaCode.append("import infodynamics.measures.continuous.kozachenko.*;\n");
javaConstructorLine = " calc = new " + calcContinuous.getClass().getSimpleName() + "();\n";
pythonPreConstructorLine = "calcClass = JPackage(\"infodynamics.measures.continuous.kozachenko\")." +
calcContinuous.getClass().getSimpleName() + "\n";
matlabConstructorLine = "calc = javaObject('infodynamics.measures.continuous.kozachenko." +
calcContinuous.getClass().getSimpleName() + "');\n";
} else {
throw new Exception("No recognised calculator selected: " +
selectedCalcType);
}
}
javaCode.append("\npublic class GeneratedCalculator {\n\n");
javaCode.append(" public static void main(String[] args) throws Exception {\n\n");
// Code to read in data:
String loadDataComment = "0. Load/prepare the data:\n";
String filenameAsEscapedString = dataFile.getAbsolutePath().replace("\\", "\\\\");
// 1. Java
javaCode.append(" // " + loadDataComment);
javaCode.append(" String dataFile = \"" + filenameAsEscapedString + "\";\n");
javaCode.append(" ArrayFileReader afr = new ArrayFileReader(dataFile);\n");
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
javaCode.append(" int[][] data = afr.getInt2DMatrix();\n");
if (! allCombosCheckBox.isSelected()) {
for (int i=0; i < numVariables; i++) {
javaCode.append(" int[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.selectColumn(data, " + singleCalcColumns[i] + ");\n");
}
javaCode.append("\n");
}
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
javaCode.append(" double[][] data = afr.getDouble2DMatrix();\n");
if (! allCombosCheckBox.isSelected()) {
for (int i=0; i < numVariables; i++) {
javaCode.append(" int[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.discretise(\n " +
"MatrixUtils.selectColumn(data, " + singleCalcColumns[i] + "), " +
propertyValues.get(DISCRETE_PROPNAME_BASE) + ");\n");
}
javaCode.append("\n");
}
} else {
javaCode.append(" double[][] data = afr.getDouble2DMatrix();\n");
if (! allCombosCheckBox.isSelected()) {
for (int i=0; i < numVariables; i++) {
javaCode.append(" double[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.selectColumn(data, " + singleCalcColumns[i] + ");\n");
}
javaCode.append("\n");
}
}
// 2. Python
pythonCode.append("# " + loadDataComment);
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
pythonCode.append("dataRaw = readIntsFile.readIntsFile(\"" + filenameAsEscapedString + "\")\n");
} else {
pythonCode.append("dataRaw = readFloatsFile.readFloatsFile(\"" + filenameAsEscapedString + "\")\n");
}
pythonCode.append("# As numpy array:\n");
pythonCode.append("data = numpy.array(dataRaw)\n");
if (! allCombosCheckBox.isSelected()) {
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
for (int i=0; i < numVariables; i++) {
pythonCode.append(variableColNumLabels[i].toLowerCase() + " = JArray(JInt, 1)(data[:," +
singleCalcColumns[i] + "].tolist())\n");
}
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
pythonCode.append("mUtils = JPackage('infodynamics.utils').MatrixUtils\n");
for (int i=0; i < numVariables; i++) {
pythonCode.append(variableColNumLabels[i].toLowerCase() + " = mUtils.discretise(JArray(JDouble, 1)(data[:," +
singleCalcColumns[i] + "].tolist()), " + propertyValues.get(DISCRETE_PROPNAME_BASE) + ")\n");
}
} else {
for (int i=0; i < numVariables; i++) {
pythonCode.append(variableColNumLabels[i].toLowerCase() + " = JArray(JDouble, 1)(data[:," +
singleCalcColumns[i] + "].tolist())\n");
}
}
pythonCode.append("\n");
}
// 3. Matlab
matlabCode.append("% " + loadDataComment);
matlabCode.append("data = load('" + filenameAsEscapedString + "');\n");
if (! allCombosCheckBox.isSelected()) {
matlabCode.append("% Column indices start from 1 in Matlab:\n");
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
for (int i=0; i < numVariables; i++) {
matlabCode.append(variableColNumLabels[i].toLowerCase() + " = octaveToJavaIntArray(data(:," +
(singleCalcColumns[i]+1) + "));\n");
}
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
matlabCode.append("mUtils = javaObject('infodynamics.utils.MatrixUtils');\n");
for (int i=0; i < numVariables; i++) {
matlabCode.append(variableColNumLabels[i].toLowerCase() + " = mUtils.discretise(octaveToJavaDoubleArray(data(:," +
(singleCalcColumns[i]+1) + ")), " + propertyValues.get(DISCRETE_PROPNAME_BASE) + ");\n");
}
} else {
for (int i=0; i < numVariables; i++) {
matlabCode.append(variableColNumLabels[i].toLowerCase() + " = octaveToJavaDoubleArray(data(:," +
(singleCalcColumns[i]+1) + "));\n");
}
}
matlabCode.append("\n");
}
// Construct the calculator and set relevant properties:
String constructComment = "1. Construct the calculator:\n";
javaCode.append(" // " + constructComment);
pythonCode.append("# " + constructComment);
matlabCode.append("% " + constructComment);
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
DiscreteCalcAndArguments dcaa = assignCalcObjectDiscrete();
if (dcaa == null) {
return;
}
calcDiscrete = dcaa.calc;
int base = dcaa.base;
String args = dcaa.arguments;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
// Now check that the data is ok:
// (no need to do this for binned)
int minInData = MatrixUtils.min(dataDiscrete);
int maxInData = MatrixUtils.max(dataDiscrete);
if ((minInData < 0) || (maxInData >= base)) {
JOptionPane.showMessageDialog(this,
"Values in data file (in range " + minInData +
":" + maxInData + ") lie outside the expected range 0:" +
(base-1) + " for base " + base);
resultsLabel.setText(" ");
return;
}
}
// 1. Java
javaCode.append(" " + calcDiscrete.getClass().getSimpleName() + " calc\n");
javaCode.append(" = new " + calcDiscrete.getClass().getSimpleName() +
"(" + args + ");\n");
// 2. Python
pythonCode.append("calcClass = JPackage(\"infodynamics.measures.discrete\")." +
calcDiscrete.getClass().getSimpleName() + "\n");
pythonCode.append("calc = calcClass(" + args + ")\n");
// 3. Matlab
matlabCode.append("calc = javaObject('infodynamics.measures.discrete." +
calcDiscrete.getClass().getSimpleName() + "', " +
args + ");\n");
String setPropertiesComment = "2. No other properties to set for discrete calculators.\n";
javaCode.append(" // " + setPropertiesComment);
pythonCode.append("# " + setPropertiesComment);
matlabCode.append("% " + setPropertiesComment);
} else {
// Construct the calculator:
// 1. Java
// If we wanted to create as superclass:
// javaCode.append(" " + abstractContinuousClass.getSimpleName() + " calc;\n");
// But that gives problems when we use interfaces such as AnalyticNullDistributionComputer,
// so we'll store it as the instantiated class:
javaCode.append(" " + calcContinuous.getClass().getSimpleName() + " calc;\n");
javaCode.append(javaConstructorLine);
// 2. Python
pythonCode.append(pythonPreConstructorLine);
pythonCode.append("calc = calcClass()\n");
// 3. Matlab
matlabCode.append(matlabConstructorLine);
// Set properties
String setPropertiesComment = "2. Set any properties to non-default values:\n";
javaCode.append(" // " + setPropertiesComment);
pythonCode.append("# " + setPropertiesComment);
matlabCode.append("% " + setPropertiesComment);
int i = 0;
boolean anyPropValueChanged = false;
for (String propName : propertyNames) {
String propValue = null;
String propFieldName = propertyFieldNames.get(i++);
try {
propValue = propertyValues.get(propName);
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this,
ex.getMessage());
resultsLabel.setText("Cannot find a value for property " + propName);
}
// Check whether this property value is different to the default for
// this calculator. This is more for generating the minimal code.
if (!propValue.equalsIgnoreCase(calcContinuous.getProperty(propName))) {
// We need to set this property:
anyPropValueChanged = true;
calcContinuous.setProperty(propName, propValue);
// 1. Java Code -- use full field name here
javaCode.append(" calc.setProperty(" + propFieldName +
",\n \"" +
propValue + "\");\n");
// 2. Python code
pythonCode.append("calc.setProperty(\"" + propName + "\", \"" +
propValue + "\")\n");
// 3. Matlab code
matlabCode.append("calc.setProperty('" + propName + "', '" +
propValue + "');\n");
}
}
if (!anyPropValueChanged) {
String noPropValueChangedComment = "No properties were set to non-default values\n";
javaCode.append(" // " + noPropValueChangedComment);
pythonCode.append("# " + noPropValueChangedComment);
matlabCode.append("% " + noPropValueChangedComment);
}
}
String javaPrefix = " ";
String pythonPrefix = "";
String matlabPrefix = "";
StringBuffer extraFormatTerms = new StringBuffer();
if (allCombosCheckBox.isSelected()) {
// We're doing all combinations.
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
// Set up the use of utilities for binning if required
pythonCode.append("mUtils = JPackage('infodynamics.utils').MatrixUtils\n");
matlabCode.append("mUtils = javaObject('infodynamics.utils.MatrixUtils');\n");
}
// Set up the loops for these:
String[] columnVariables = setUpLoopsForAllCombos(javaCode, pythonCode, matlabCode);
// Get the indents right from here on,
// and prepare a string of the column labels for
// each variable, to be used in later formatting.
for (int i = 0; i < indentsForAllCombos; i++) {
javaPrefix += " ";
pythonPrefix += " ";
matlabPrefix += "\t";
}
for (int i = 0; i < numVariables; i++) {
extraFormatTerms.append(columnVariables[i] + ", ");
}
// Read in the data for these columns for all pairs
matlabCode.append(matlabPrefix + "% Column indices start from 1 in Matlab:\n");
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
for (int i = 0; i < numVariables; i++) {
javaCode.append(javaPrefix + "int[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.selectColumn(data, " + columnVariables[i] + ");\n");
matlabCode.append(matlabPrefix + variableColNumLabels[i].toLowerCase() +
" = octaveToJavaIntArray(data(:, " + columnVariables[i] + "));\n");
pythonCode.append(pythonPrefix + variableColNumLabels[i].toLowerCase() +
" = JArray(JInt, 1)(data[:, " + columnVariables[i] + "].tolist())\n");
}
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
for (int i = 0; i < numVariables; i++) {
javaCode.append(javaPrefix + "int[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.discretise(\n" + javaPrefix + " " +
"MatrixUtils.selectColumn(data, " + columnVariables[i] + "), " +
propertyValues.get(DISCRETE_PROPNAME_BASE) + ");\n");
matlabCode.append(matlabPrefix + variableColNumLabels[i].toLowerCase() +
" = mUtils.discretise(octaveToJavaDoubleArray(data(:," +
columnVariables[i] + ")), " + propertyValues.get(DISCRETE_PROPNAME_BASE) + ");\n");
pythonCode.append(pythonPrefix + variableColNumLabels[i].toLowerCase() +
" = mUtils.discretise(JArray(JDouble, 1)(data[:," +
columnVariables[i] + "].tolist()), " + propertyValues.get(DISCRETE_PROPNAME_BASE) + ")\n");
}
} else {
// Continuous data
for (int i = 0; i < numVariables; i++) {
javaCode.append(javaPrefix + "double[] " + variableColNumLabels[i].toLowerCase() +
" = MatrixUtils.selectColumn(data, " + columnVariables[i] + ");\n");
matlabCode.append(matlabPrefix + variableColNumLabels[i].toLowerCase() +
" = octaveToJavaDoubleArray(data(:, " + columnVariables[i] + "));\n");
pythonCode.append(pythonPrefix + variableColNumLabels[i].toLowerCase() +
" = JArray(JDouble, 1)(data[:, " + columnVariables[i] + "].tolist())\n");
}
}
javaCode.append("\n");
matlabCode.append("\n");
pythonCode.append("\n");
} else {
// For a single pair, we don't need to set up loops, and
// we've already read in the data to source and dest variables above
}
// Initialise
String initialiseComment = "3. Initialise the calculator for (re-)use:\n";
javaCode.append(javaPrefix + "// " + initialiseComment);
javaCode.append(javaPrefix + "calc.initialise();\n");
pythonCode.append(pythonPrefix + "# " + initialiseComment);
pythonCode.append(pythonPrefix + "calc.initialise()\n");
matlabCode.append(matlabPrefix + "% " + initialiseComment);
matlabCode.append(matlabPrefix + "calc.initialise();\n");
// Set observations
String supplyDataComment = "4. Supply the sample data:\n";
String setObservationsMethod;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
setObservationsMethod = "addObservations";
} else {
setObservationsMethod = "setObservations";
}
StringBuffer methodArguments = new StringBuffer();
for (int i = 0; i < numVariables; i++) {
methodArguments.append(variableColNumLabels[i].toLowerCase());
if (i < numVariables - 1) {
// There are more variables to come:
methodArguments.append(", ");
}
}
// 1. Java
javaCode.append(javaPrefix + "// " + supplyDataComment);
javaCode.append(javaPrefix + "calc." + setObservationsMethod + "(" + methodArguments + ");\n");
// 2. Python
pythonCode.append(pythonPrefix + "# " + supplyDataComment);
pythonCode.append(pythonPrefix + "calc." + setObservationsMethod + pythonSetObsSuffix() + "(" + methodArguments + ")\n");
// 3. Matlab
matlabCode.append(matlabPrefix + "% " + supplyDataComment);
matlabCode.append(matlabPrefix + "calc." + setObservationsMethod + "(" + methodArguments + ");\n");
// Compute the result
String computeComment = "5. Compute the estimate:\n";
javaCode.append(javaPrefix + "// " + computeComment);
javaCode.append(javaPrefix + "double result = calc.computeAverageLocalOfObservations();\n");
pythonCode.append(pythonPrefix + "# " + computeComment);
pythonCode.append(pythonPrefix + "result = calc.computeAverageLocalOfObservations()\n");
matlabCode.append(matlabPrefix + "% " + computeComment);
matlabCode.append(matlabPrefix + "result = calc.computeAverageLocalOfObservations();\n");
String resultsPrefixString;
if (allCombosCheckBox.isSelected()) {
resultsPrefixString = String.format(measureAcronym + "_%s(%s) = ",
selectedCalcType, variableRelationshipFormatString);
} else {
resultsPrefixString = String.format(measureAcronym + "_%s(%s) = ",
selectedCalcType,
formatStringWithColumnNumbers(
variableRelationshipFormatString,
singleCalcColumns));
}
String resultsSuffixString = "";
String statSigFormatTerms = "";
if (statSigCheckBox.isSelected()) {
// Compute statistical significance
String statSigComment = "6. Compute the (statistical significance via) null distribution";
String javaReturnClass, numPermutationsToPrint;
if (statSigAnalyticCheckBox.isSelected()) {
javaReturnClass = "AnalyticMeasurementDistribution";
numPermutationsToPrint = ""; // Missing argument will signal analytic calculation
statSigComment = statSigComment + " analytically:\n";
} else {
javaReturnClass = "EmpiricalMeasurementDistribution";
numPermutationsToPrint = Integer.toString(numPermutationsToCheck);
statSigComment = statSigComment + " empirically (e.g. with 100 permutations):\n";
}
javaCode.append(javaPrefix + "// " + statSigComment);
javaCode.append(javaPrefix + javaReturnClass + " measDist = calc.computeSignificance(" + numPermutationsToPrint + ");\n");
pythonCode.append(pythonPrefix + "# " + statSigComment);
pythonCode.append(pythonPrefix + "measDist = calc.computeSignificance(" + numPermutationsToPrint + ")\n");
matlabCode.append(matlabPrefix + "% " + statSigComment);
matlabCode.append(matlabPrefix + "measDist = calc.computeSignificance(" + numPermutationsToPrint + ");\n");
if (statSigAnalyticCheckBox.isSelected()) {
resultsSuffixString = " (analytic p(surrogate > measured)=%.5f)";
statSigFormatTerms = ", measDist.pValue";
} else {
resultsSuffixString = " (null: %.4f +/- %.4f std dev.; p(surrogate > measured)=%.5f from %d surrogates)";
statSigFormatTerms = String.format(", measDist.getMeanOfDistribution(), measDist.getStdOfDistribution(), measDist.pValue, %d",
numPermutationsToCheck);
}
}
// And generate code to write the results and finalise:
// 1. Java
javaCode.append("\n" + javaPrefix + "System.out.printf(\"" + resultsPrefixString +
"%.4f " + units + resultsSuffixString + "\\n\",\n " + javaPrefix +
extraFormatTerms + "result" + statSigFormatTerms + ");\n");
// 2. Python
pythonCode.append("\n" + pythonPrefix + "print(\"" + resultsPrefixString +
"%.4f " + units + resultsSuffixString + "\" %\\\n " + pythonPrefix + "(" +
extraFormatTerms + "result" + statSigFormatTerms + "))\n");
// 3. Matlab
matlabCode.append("\n" + matlabPrefix + "fprintf('" + resultsPrefixString +
"%.4f " + units + resultsSuffixString + "\\n', ...\n\t" + matlabPrefix +
extraFormatTerms + "result" + statSigFormatTerms + ");\n");
if (allCombosCheckBox.isSelected()) {
// We're doing all combos
// Finalise the loops in the code:
finaliseLoopsForAllCombos(javaCode, pythonCode, matlabCode);
// And tell the user to see results in the console:
resultsLabel.setText("See console for all pairs calculation results");
if (!(selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED))) {
System.out.println("Property values not read back into GUI for all pairs calculation");
}
}
// Finalise the Java code:
javaCode.append(" }\n");
javaCode.append("}\n\n");
// Now set the code in the panel for the user
javaCodeTextArea.setText(javaCode.toString());
javaCodeTextArea.setCaretPosition(0); // Pull focus to the top
pythonCodeTextArea.setText(pythonCode.toString());
pythonCodeTextArea.setCaretPosition(0); // Pull focus to the top
matlabCodeTextArea.setText(matlabCode.toString());
matlabCodeTextArea.setCaretPosition(0); // Pull focus to the top
// Now write the code to a file
String resultsSuffix = "";
try {
// 1. Java
FileWriter codeFileWriter = new FileWriter(pathToAutoAnalyserDir + "../java/infodynamics/demos/autoanalysis/GeneratedCalculator.java");
codeFileWriter.write(javaCode.toString());
codeFileWriter.close();
// 2. Python
codeFileWriter = new FileWriter(pathToAutoAnalyserDir + "GeneratedCalculator.py");
codeFileWriter.write(pythonCode.toString());
codeFileWriter.close();
// 3. Matlab
codeFileWriter = new FileWriter(pathToAutoAnalyserDir + "GeneratedCalculator.m");
codeFileWriter.write(matlabCode.toString());
codeFileWriter.close();
} catch (IOException ioe) {
// We were unable to write to the files -- most likely
// reason is that the jar was moved from the distribution
// and cannot find the folders to write to.
// We still want to perform the calculation though, so
// save an error message to display later and confinue.
ioe.printStackTrace(System.err);
resultsSuffix = " (Error writing generated code files though, see console)";
}
if (computeResultCheckBox.isSelected()) {
// Now make our own calculations here:
// TODO make the calculation in a separate thread, and have a
// button to cancel the calculation whilst it's running
for (int[] columnCombo : variableCombinations) {
if (allCombosCheckBox.isSelected() && skipColumnCombo(columnCombo)) {
System.out.print("Skipping column combo ");
MatrixUtils.printArray(System.out, columnCombo);
continue;
}
// Initialise
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
calcDiscrete.initialise();
} else {
calcContinuous.initialise();
}
setObservations(calcDiscrete, calcContinuous, columnCombo);
// Compute the result:
double result;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
result = calcDiscrete.computeAverageLocalOfObservations();
} else {
result = calcContinuous.computeAverageLocalOfObservations();
}
String resultsSuffixStringFormatted = "";
if (statSigCheckBox.isSelected()) {
// Check statistical significance:
if (statSigAnalyticCheckBox.isSelected()) {
// analytic check of statistical significance:
AnalyticNullDistributionComputer analyticCalc;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
analyticCalc =
(AnalyticNullDistributionComputer) calcDiscrete;
} else {
analyticCalc =
(AnalyticNullDistributionComputer) calcContinuous;
}
AnalyticMeasurementDistribution measDist =
analyticCalc.computeSignificance();
resultsSuffixStringFormatted = String.format(
resultsSuffixString,
measDist.pValue);
} else {
// permutation test of statistical significance:
EmpiricalMeasurementDistribution measDist;
EmpiricalNullDistributionComputer empiricalNullDistCalc;
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
empiricalNullDistCalc =
(EmpiricalNullDistributionComputer) calcDiscrete;
} else {
empiricalNullDistCalc =
(EmpiricalNullDistributionComputer) calcContinuous;
}
measDist = empiricalNullDistCalc.computeSignificance(numPermutationsToCheck);
resultsSuffixStringFormatted = String.format(
resultsSuffixString,
measDist.getMeanOfDistribution(),
measDist.getStdOfDistribution(),
measDist.pValue,
numPermutationsToCheck);
}
}
String resultsText;
if (allCombosCheckBox.isSelected()) {
// We're doing all pairs
// OLD:
// resultsText = String.format(
// resultsPrefixString + "%.4f %s" + resultsSuffixStringFormatted,
// sourceColumn, destColumn, result, units);
// NEW:
resultsText = String.format(formatStringWithColumnNumbers(
resultsPrefixString + "%%.4f %%s" + resultsSuffixStringFormatted,
columnCombo), result, units);
} else {
resultsText = String.format(
resultsPrefixString + "%.4f %s" + resultsSuffixStringFormatted,
result, units);
resultsLabel.setText(resultsText + resultsSuffix);
}
// Write the results to the console:
System.out.println(resultsText);
}
if ((!allCombosCheckBox.isSelected()) &&
!(selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
(selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)))) {
// Read the current property values back out (in case of
// automated parameter assignment)
for (String propName : propertyNames) {
String propValue = null;
try {
propValue = calcContinuous.getProperty(propName);
propertyValues.put(propName, propValue);
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this,
ex.getMessage());
resultsLabel.setText("Cannot find a value for property " + propName);
}
}
propertiesTableModel.fireTableDataChanged(); // Alerts to refresh the table contents
}
} else {
resultsLabel.setText("Code generated (no computation performed)");
}
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this,
ex.getMessage());
resultsLabel.setText("Calculation failed, please see console");
}
}
/**
* Method to allow child to fill out what all of the variable combinations would be that
* would be evaluated by this measure
*
* @param variableCombinations
*/
protected abstract void fillOutAllCombinations(Vector<int[]> variableCombinations) throws Exception;
/**
* Method to allow child classes to set up the loops over all combinations of
* columns
*
* @param javaCode
* @param pythonCode
* @param matlabCode
* @return A string array with the names used for the variables iterating over
* each column
*/
protected abstract String[] setUpLoopsForAllCombos(StringBuffer javaCode,
StringBuffer pythonCode, StringBuffer matlabCode);
/**
* Method to allow child classes to finalise the loops over all combinations of
* columns
*
* @param javaCode
* @param pythonCode
* @param matlabCode
*/
protected abstract void finaliseLoopsForAllCombos(StringBuffer javaCode,
StringBuffer pythonCode, StringBuffer matlabCode);
/**
* Method to allow child classes to format a string with
* an array of ints for the column numbers (since the number
* of these is variable, it's easier if the child classes do this)
*
* @param columnNumbers array of column numbers used to identify the variables.
* @return
*/
protected abstract String formatStringWithColumnNumbers(String formatStr, int[] columnNumbers);
/**
* Check whether this measure type should skip this particular
* combination of columns or not
* (e.g. for a pairwise measurement we do want to skip
* where the two columns are equal).
*
* @param columnCombo
* @return
*/
protected abstract boolean skipColumnCombo(int[] columnCombo);
/**
* Method to assign and initialise our continuous calculator class
* @throws Exception
*/
protected abstract InfoMeasureCalculatorContinuous assignCalcObjectContinuous(String selectedCalcType) throws Exception;
/**
* Method to assign and initialise our discrete calculator class
* @throws Exception
*/
protected abstract DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception;
/**
* Structure used for return calculator and arguments from constructing
* the discrete calculator
*
* @author Joseph Lizier
*
*/
protected class DiscreteCalcAndArguments {
InfoMeasureCalculatorDiscrete calc;
int base;
String arguments;
DiscreteCalcAndArguments(InfoMeasureCalculatorDiscrete calc, int base,
String arguments) {
this.calc = calc;
this.base = base;
this.arguments = arguments;
}
}
/**
* Method to set the observations on the underlying calculator
*
* @param calcDiscrete
* @param calcContinuous
* @param columnCombo int array for the columns of data being computed with.
* @throws Exception
*/
protected abstract void setObservations(InfoMeasureCalculatorDiscrete calcDiscrete,
InfoMeasureCalculatorContinuous calcContinuous,
int[] columnCombo) throws Exception;
/**
* Method to return any suffix if required on the set/addObservations method for python calls
*
* @return
*/
protected String pythonSetObsSuffix() {
return "";
}
/**
* Extends JTable to add ToolTipText to the property names
*
* @author joseph
*
*/
protected class TableWithToolTip extends JTable {
/**
* Default serialVersionUID
*/
private static final long serialVersionUID = 1L;
public TableWithToolTip(AbstractTableModel atm) {
super(atm);
}
public Component prepareRenderer(TableCellRenderer renderer,
int rowIndex, int vColIndex) {
Component c = super.prepareRenderer(renderer, rowIndex, vColIndex);
if (c instanceof JComponent) {
if (vColIndex == 0) {
JComponent jc = (JComponent)c;
try {
String toolTipText;
String selectedCalcType = (String)
calcTypeComboBox.getSelectedItem();
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
toolTipText = "<html>" + propertyNames.get(rowIndex) + ": " +
propertyDescriptions.get(rowIndex) + "</html>";
} else {
toolTipText = "<html>" + propertyFieldNames.get(rowIndex) + ": " +
propertyDescriptions.get(rowIndex) + "</html>";
}
jc.setToolTipText(toolTipText);
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Catch if the row number was outside our array of descriptions (e.g. empty row)
System.out.println("prepareRenderer: Row number " + rowIndex +
" was outside our array of names/fieldnames/descriptions");
}
}
}
return c;
}
/* Alternative method (this over-rides the above if both are in place)
public String getToolTipText(MouseEvent event) {
Point point = event.getPoint();
int rowIndex = rowAtPoint(point);
int columnIndex = columnAtPoint(point);
if (columnIndex == 0) {
try {
return propertyFieldNames.get(rowIndex) + ": " + propertyDescriptions.get(rowIndex);
} catch (ArrayIndexOutOfBoundsException aioobe) {
// Catch if the row number was outside our array of descriptions (e.g. empty row)
return null;
}
} else {
// No tool tip for the property values
return null;
}
}
*/
/**
* Use this method to set combo box options for editing cells
*/
@Override
public TableCellEditor getCellEditor(int row, int column) {
if ((propertyValueChoices.get(row) != null) && (column == 1)) {
// We need to construct a combo box for the selection for this property:
try {
JComboBox<String> paramChoiceComboBox = new JComboBox<String>();
// Set font to not bold and one size less than the default (to fit better)
Font font = paramChoiceComboBox.getFont();
paramChoiceComboBox.setFont(font.deriveFont(Font.PLAIN, font.getSize()-1));
String[] choices = propertyValueChoices.get(row);
for (int c = 0; c < choices.length; c++) {
paramChoiceComboBox.addItem(choices[c]);
}
return new DefaultCellEditor(paramChoiceComboBox);
} catch (Exception e) {
e.printStackTrace();
// But now allow this to be handled by the default cell editor
}
}
// I think the following would do the default behaviour:
// return this.getDefaultEditor(this.getColumnClass(column));
// however it should be safer to just allow the parent to handle:
return super.getCellEditor(row, column);
}
/**
* This method allows us to keep a combo box visible when
* the property value is no longer selected.
* Adapted from answer at https://stackoverflow.com/questions/30744524/how-to-make-the-jcombobox-dropdown-always-visible-in-a-jtable
*/
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
if ((propertyValueChoices.get(row) != null) && (column == 1)) {
try {
return new TableCellRenderer() {
JComboBox<String> box = new JComboBox<String>();
int defaultFontSize = box.getFont().getSize();
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
// Set font to not bold and one size less than the default (to fit better)
Font font = box.getFont();
box.setFont(font.deriveFont(Font.PLAIN, defaultFontSize-1));
// Now empty all items out and just put the value we currently have.
// (This is only for displaying, the editor will override with available values if
// user wants to edit).
box.removeAllItems();
box.addItem(value.toString());
return box;
}
};
} catch (Exception e) {
e.printStackTrace();
// But now allow this to be handled by the default cell renderer
}
}
return super.getCellRenderer(row, column);
}
}
protected class PropertiesTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
public PropertiesTableModel() {
super();
}
@Override
public String getColumnName(int column) {
if (column == 0) {
return "Property name";
} else {
return "Property value";
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return false;
}
return true;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return;
}
// Else we've changed a property value -- which one?
String propName = propertyNames.get(rowIndex);
propertyValues.put(propName, (String) aValue);
// And clear the result and code panels because of this change:
resultsLabel.setText(" "); // Clear text for now if anything changes
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return propertyNames.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
String propName = propertyNames.get(rowIndex);
if (columnIndex == 0) {
return propName;
} else {
return propertyValues.get(propName);
}
}
}
public void putCalcPropertiesInTable() {
System.out.println("Getting calc properties");
String selectedCalcType = (String)
calcTypeComboBox.getSelectedItem();
CalcProperties calcProperties = null;
try {
calcProperties = assignCalcProperties(selectedCalcType);
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this,
ex.getMessage());
resultsLabel.setText("Cannot load requested calculator");
}
String[] classSpecificPropertyNames =
calcProperties.classSpecificPropertyNames;
String[] classSpecificPropertiesFieldNames =
calcProperties.classSpecificPropertiesFieldNames;
String[] classSpecificPropertyDescriptions =
calcProperties.classSpecificPropertyDescriptions;
String[][] classSpecificPropertyValueChoices =
calcProperties.classSpecificPropertyValueChoices;
calcClass = calcProperties.calcClass;
Object calc = calcProperties.calc;
// Now get all of the possible properties for this class:
propertyNames = new Vector<String>();
propertyFieldNames = new Vector<String>();
propertyDescriptions = new Vector<String>();
propertyValueChoices = new Vector<String[]>();
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
// Simply add the properties for this estimator, along
// with their default values
int i = 0;
propertyValues = new HashMap<String,String>();
for (String propName : discreteProperties) {
String propertyDescription = discretePropertyDescriptions[i];
String defaultPropertyValue = discretePropertyDefaultValues[i];
String[] propertyValueChoiceSet = discretePropertyValueChoices[i];
i++;
propertyNames.add(propName);
propertyDescriptions.add(propertyDescription);
propertyValues.put(propName, defaultPropertyValue);
propertyValueChoices.add(propertyValueChoiceSet);
System.out.println("Adding property name " + propName);
}
} else {
// First for the common properties
int i = 0;
for (String fieldName : commonContPropertiesFieldNames) {
String propName = commonContPropertyNames[i];
String propertyDescription = commonContPropertyDescriptions[i];
String[] propertyValueChoiceSet = commonContPropertyValueChoices[i];
i++;
System.out.println("Adding property name " +
abstractContinuousClass.getSimpleName() + "." + fieldName +
" = \"" + propName + "\"");
propertyFieldNames.add(abstractContinuousClass.getSimpleName() + "." + fieldName);
propertyNames.add(propName);
propertyDescriptions.add(propertyDescription);
propertyValueChoices.add(propertyValueChoiceSet);
}
// Then for the specific estimator types
i = 0;
for (String fieldName : classSpecificPropertiesFieldNames) {
String propName = classSpecificPropertyNames[i];
String propertyDescription = classSpecificPropertyDescriptions[i];
String[] propertyValueChoiceSet = classSpecificPropertyValueChoices[i];
i++;
propertyNames.add(propName);
propertyDescriptions.add(propertyDescription);
propertyValueChoices.add(propertyValueChoiceSet);
if (fieldName.contains(".")) {
System.out.println("Adding property name " + fieldName +
" = \"" + propName + "\"");
propertyFieldNames.add(fieldName);
} else {
System.out.println("Adding property name " + calcClass.getSimpleName() +
"." + fieldName + " = \"" + propName + "\"");
propertyFieldNames.add(calcClass.getSimpleName() + "." + fieldName);
}
}
// Now extract the default values for all of these properties:
propertyValues = new HashMap<String,String>();
for (String propName : propertyNames) {
try {
InfoMeasureCalculatorContinuous calcCont =
(InfoMeasureCalculatorContinuous) calc;
String propValue = calcCont.getProperty(propName);
propertyValues.put(propName, propValue);
} catch (Exception ex) {
ex.printStackTrace(System.err);
JOptionPane.showMessageDialog(this,
ex.getMessage());
propertyValues.put(propName, "Cannot find a value");
}
}
}
}
/**
* Structure to return calculator properties
*
* @author Joseph Lizier
*
*/
protected class CalcProperties {
// An default instantiation of the required calculator object
// for continuous data (this is not used for discrete)
InfoMeasureCalculatorContinuous calc;
// Class of the required calculator
@SuppressWarnings("rawtypes")
Class calcClass;
String[] classSpecificPropertyNames;
String[] classSpecificPropertiesFieldNames;
String[] classSpecificPropertyDescriptions;
String[][] classSpecificPropertyValueChoices;
}
/**
* Assign the property details for the calculator in use.
* Should be overridden by child classes, though they
* can refer here to handle discrete calculators.
*
* @param selectedCalcType String name of the calculator
* @return
* @throws Exception
*/
protected CalcProperties assignCalcProperties(String selectedCalcType)
throws Exception {
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE) ||
selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
CalcProperties calcProperties = new CalcProperties();
calcProperties.calcClass = discreteClass;
calcProperties.calc = null; // Not used
calcProperties.classSpecificPropertyNames = discreteProperties;
calcProperties.classSpecificPropertiesFieldNames = null; // Not used
calcProperties.classSpecificPropertyDescriptions = discretePropertyDescriptions;
calcProperties.classSpecificPropertyValueChoices = discretePropertyValueChoices;
// TODO Later can add binning method to the properties for
// binned calculator here.
return calcProperties;
} else {
// We don't handle any other calculators here
return null;
}
}
@Override
public void changedUpdate(DocumentEvent e) {
// Source or dest col number updated
resultsLabel.setText(" "); // Clear text for now if anything changes
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
}
@Override
public void insertUpdate(DocumentEvent e) {
// Source or dest col number updated
resultsLabel.setText(" "); // Clear text for now if anything changes
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
}
@Override
public void removeUpdate(DocumentEvent e) {
// Source or dest col number updated
resultsLabel.setText(" "); // Clear text for now if anything changes
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
}
@Override
public void mouseClicked(MouseEvent me) {
// User clicked on the data file JLabel
// Clear text fields for now if anything changes
resultsLabel.setText(" ");
javaCodeTextArea.setText(codeDefaultText);
pythonCodeTextArea.setText(codeDefaultText);
matlabCodeTextArea.setText(codeDefaultText);
if (me.getSource() == dataFileTextField) {
selectFileAction();
}
}
@Override
public void mouseEntered(MouseEvent me) {
// Do nothing
}
@Override
public void mouseExited(MouseEvent me) {
// Do nothing
}
@Override
public void mousePressed(MouseEvent me) {
// Do nothing
}
@Override
public void mouseReleased(MouseEvent me) {
// Do nothing
}
@Override
public void stateChanged(ChangeEvent e) {
// I'm not sure how to check which checkbox changed,
// so we'll just act on potential changes from both.
// This won't cause a problem here
if (e.getSource() == allCombosCheckBox) {
// "All pairs" checkbox -- update in case changed:
if (allCombosCheckBox.isSelected()) {
for (int i = 0; i < numVariables; i++) {
if (disableVariableColTextFieldsForAllCombos[i]) {
variableColTextFields[i].setEnabled(false);
}
}
} else {
for (int i = 0; i < numVariables; i++) {
variableColTextFields[i].setEnabled(true);
}
}
} else if (e.getSource() == statSigCheckBox) {
System.out.println("StatSigCheckBox state changed to " + statSigCheckBox.isSelected());
// statSigCheckBox -- update statSigAnalyticCheckBox in case changed:
if (statSigCheckBox.isSelected()) {
// We might allow the analytic box to be checked if the selected
// calculator allows this:
if (calcImplementsAnalyticStatSig()) {
statSigAnalyticCheckBox.setEnabled(true);
} else {
statSigAnalyticCheckBox.setSelected(false);
statSigAnalyticCheckBox.setEnabled(false);
}
} else {
statSigAnalyticCheckBox.setSelected(false);
statSigAnalyticCheckBox.setEnabled(false);
}
} else if (e.getSource() == computeResultCheckBox) {
// Compute checkbox -- update in case changed:
if (computeResultCheckBox.isSelected()) {
computeButton.setText(buttonTextCodeAndCompute);
} else {
computeButton.setText(buttonTextCodeOnly);
}
}
}
/**
* Checks whether the current calculator class implements the
* AnalyticNullDistributionComputer interface
*
* @return
*/
protected boolean calcImplementsAnalyticStatSig() {
Class<?> currentClass = calcClass;
while (currentClass != null) {
// This getInterfaces call doesn't seem to return the interfaces
// implemented by the superclass, so we need to go up the tree
// to check all of those. That seems odd, surely there is a simpler way?
Class<?>[] interfacesOfThisClass = currentClass.getInterfaces();
for (Class<?> theInterface : interfacesOfThisClass) {
if (theInterface == AnalyticNullDistributionComputer.class) {
return true;
}
}
currentClass = currentClass.getSuperclass();
}
return false;
}
}