diff --git a/build.xml b/build.xml
index 6bc44ff..ca0b6de 100755
--- a/build.xml
+++ b/build.xml
@@ -191,6 +191,7 @@
+
diff --git a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java
new file mode 100644
index 0000000..2793bbc
--- /dev/null
+++ b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java
@@ -0,0 +1,560 @@
+/*
+ * 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 .
+ */
+
+package infodynamics.demos.autoanalysis;
+
+import infodynamics.measures.continuous.ConditionalMutualInfoMultiVariateCommon;
+import infodynamics.measures.continuous.TransferEntropyCalculator;
+import infodynamics.measures.continuous.gaussian.TransferEntropyCalculatorGaussian;
+import infodynamics.measures.continuous.kernel.TransferEntropyCalculatorKernel;
+import infodynamics.measures.continuous.kraskov.ConditionalMutualInfoCalculatorMultiVariateKraskov;
+import infodynamics.measures.continuous.kraskov.TransferEntropyCalculatorKraskov;
+import infodynamics.utils.ArrayFileReader;
+import infodynamics.utils.MatrixUtils;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JComboBox;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JFileChooser;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.table.AbstractTableModel;
+
+import java.awt.EventQueue;
+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.io.File;
+import java.util.HashMap;
+import java.util.Vector;
+
+public class AutoAnalyser extends JFrame implements ActionListener, DocumentListener {
+
+ /**
+ * Need serialVersionUID to be serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ // Set options for the TE Calculator type:
+ public final static String CALC_TYPE_DISCRETE = "Discrete";
+ public final static String CALC_TYPE_GAUSSIAN = "Gaussian";
+ public final static String CALC_TYPE_KRASKOV = "Kraskov";
+ public final static String CALC_TYPE_KERNEL = "Kernel";
+ protected String[] calcTypes = {
+ CALC_TYPE_DISCRETE, CALC_TYPE_GAUSSIAN,
+ CALC_TYPE_KRASKOV, CALC_TYPE_KERNEL};
+ protected String[] unitsForEachCalc = {"bits", "nats", "nats", "bits"};
+
+ // Store the properties for each calculator
+ protected String[] commonContProperties = {
+ TransferEntropyCalculator.K_PROP_NAME,
+
+ };
+ protected String[] gaussianProperties = {
+ TransferEntropyCalculator.K_TAU_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.L_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.L_TAU_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.DELAY_PROP_NAME, // Not common to Kernel
+ };
+ protected String[] kernelProperties = {
+ TransferEntropyCalculatorKernel.KERNEL_WIDTH_PROP_NAME,
+ TransferEntropyCalculatorKernel.DYN_CORR_EXCL_TIME_NAME,
+ TransferEntropyCalculatorKernel.NORMALISE_PROP_NAME,
+ };
+ protected String[] kraskovProperties = {
+ TransferEntropyCalculator.K_TAU_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.L_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.L_TAU_PROP_NAME, // Not common to Kernel
+ TransferEntropyCalculator.DELAY_PROP_NAME, // Not common to Kernel
+ ConditionalMutualInfoMultiVariateCommon.PROP_NORMALISE,
+ ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K,
+ ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE,
+ ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME,
+ ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE,
+ ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS,
+ TransferEntropyCalculatorKraskov.PROP_KRASKOV_ALG_NUM,
+ TransferEntropyCalculatorKraskov.PROP_AUTO_EMBED_METHOD,
+ TransferEntropyCalculatorKraskov.PROP_K_SEARCH_MAX,
+ TransferEntropyCalculatorKraskov.PROP_TAU_SEARCH_MAX,
+ TransferEntropyCalculatorKraskov.PROP_RAGWITZ_NUM_NNS
+ };
+
+ protected JButton computeButton;
+ protected JButton openDataButton;
+ // Stores the current data file
+ protected File dataFile = null;
+ // Combo box for selecting the calculator type
+ protected JComboBox calcTypeComboBox;
+ // Displays the current data file
+ protected JTextField dataFileTextField;
+ // Descriptor of the data:
+ protected JLabel dataFileDescriptorLabel;
+ // The data we're working with
+ protected double[][] data = null;
+ // Number of rows and columns of the data:
+ protected int dataRows = 0;
+ protected int dataColumns = 0;
+ // Source column field
+ protected JTextField sourceColTextField;
+ // Destination column field
+ protected JTextField destColTextField;
+ // Table for the properties
+ protected JTable propertiesTable;
+ // Table model (local class) for the table
+ protected PropertiesTableModel propertiesTableModel;
+ // Panel for the table
+ protected JPanel propertiesPanel;
+ // Names of the properties
+ protected Vector fieldNames;
+ // Values of the properties
+ protected HashMap propertyValues;
+ // Results text
+ protected JLabel resultsLabel;
+
+ /**
+ * Constructor to generate the application windows
+ */
+ public AutoAnalyser() {
+
+ ImageIcon icon = new ImageIcon("../../JIDT-logo.png"); // Location for distributions
+ if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
+ // Try the alternative image location for SVN checkouts
+ icon = new ImageIcon("../../web/JIDT-logo.png");
+ }
+ setIconImage(icon.getImage());
+
+ GridBagLayout gridbag = new GridBagLayout();
+ GridBagConstraints c = new GridBagConstraints();
+ setLayout(gridbag);
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1.0;
+
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(500,500);
+ setTitle("JIDT Transfer Entropy Auto-Analyser");
+ // Centre in the middle of the screen
+ setLocationRelativeTo(null);
+
+ // Create a panel for the calc type:
+ JPanel calcTypePanel = new JPanel();
+ JLabel calcTypeLabel = new JLabel("TE Calculator Type:");
+ calcTypeComboBox = (JComboBox) new JComboBox(calcTypes);
+ calcTypeComboBox.setSelectedIndex(2); // Select Kraskov by default
+ calcTypeComboBox.addActionListener(this);
+ calcTypePanel.add(calcTypeLabel);
+ calcTypePanel.add(calcTypeComboBox);
+
+ JPanel dataFileChooserPanel = new JPanel();
+ JLabel fileLabel = new JLabel("Data file:");
+ dataFileTextField = new JTextField(30);
+ dataFileTextField.setEnabled(false);
+ dataFileChooserPanel.add(fileLabel);
+ dataFileChooserPanel.add(dataFileTextField);
+ // Button to open data file
+ openDataButton = new JButton("Select");
+ openDataButton.addActionListener(this);
+ dataFileChooserPanel.add(openDataButton);
+
+ // From column:
+ JPanel sourceChooserPanel = new JPanel();
+ JLabel sourceLabel = new JLabel("Source column (first is 0):");
+ sourceColTextField = new JTextField(5);
+ sourceColTextField.setEnabled(true);
+ sourceColTextField.setText("0");
+ sourceColTextField.getDocument().addDocumentListener(this);
+ sourceChooserPanel.add(sourceLabel);
+ sourceChooserPanel.add(sourceColTextField);
+ // To column:
+ JPanel destChooserPanel = new JPanel();
+ JLabel destLabel = new JLabel("Destination column (first is 0):");
+ destColTextField = new JTextField(5);
+ destColTextField.setEnabled(true);
+ destColTextField.setText("1");
+ destColTextField.addActionListener(this);
+ destChooserPanel.add(destLabel);
+ destChooserPanel.add(destColTextField);
+
+
+ // Description about the data
+ JPanel dataFileDescriptorPanel = new JPanel();
+ dataFileDescriptorLabel = new JLabel("No data file selected yet ...");
+ dataFileDescriptorPanel.add(dataFileDescriptorLabel);
+
+ // TODO Need to set an appropriate width here
+ propertiesPanel = new JPanel();
+ putCalcPropertiesInTable();
+ propertiesTableModel = new PropertiesTableModel();
+ propertiesTable = new JTable(propertiesTableModel);
+ // Make sure any properties are saved when the compute button is clicked
+ propertiesTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
+ propertiesPanel.add(propertiesTable);
+
+ // Button to compute
+ computeButton = new JButton("Compute");
+ computeButton.addActionListener(this);
+
+ // Results label
+ JPanel resultsPanel = new JPanel();
+ resultsLabel = new JLabel(" ");
+ resultsPanel.add(resultsLabel);
+
+
+ // 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);
+ c.gridwidth = 1;
+ c.gridx = 0;
+ add(calcTypePanel, c);
+ add(dataFileChooserPanel, c);
+ add(dataFileDescriptorPanel, c);
+ add(sourceChooserPanel, c);
+ add(destChooserPanel, c);
+ add(propertiesPanel, c);
+ add(computeButton, c);
+ c.gridwidth = GridBagConstraints.REMAINDER; //end row
+ c.anchor = GridBagConstraints.PAGE_START; // top of cell
+ add(resultsPanel, c);
+ // TODO Add the JIDT logo somewhere
+ // TODO Add a panel for auto generated code.
+
+ setVisible(true);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ resultsLabel.setText(""); // Clear text for now if anything changes
+ if (e.getSource() == computeButton) {
+ computeTE();
+ } 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");
+ }
+ // 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(System.getProperty("user.dir"));
+ } 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:
+ ArrayFileReader afr = new ArrayFileReader(dataFile);
+ try {
+ 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());
+ dataFileDescriptorLabel.setText("Invalid data file, please load another");
+ data = null;
+ }
+ }
+ // Else do nothing
+ }
+
+ protected void computeTE() {
+
+ System.out.println("Compute button pressed ...");
+ if (data == null) {
+ JOptionPane.showMessageDialog(this, "No valid data source selected");
+ return;
+ }
+ int sourceColumn = Integer.parseInt(sourceColTextField.getText());
+ int destColumn = Integer.parseInt(destColTextField.getText());
+ if ((sourceColumn < 0) || (sourceColumn >= dataColumns)) {
+ JOptionPane.showMessageDialog(this,
+ String.format("Source column must be between 0 and %d for this data set",
+ dataColumns-1));
+ return;
+ }
+ if ((destColumn < 0) || (destColumn >= dataColumns)) {
+ JOptionPane.showMessageDialog(this,
+ String.format("Destination column must be between 0 and %d for this data set",
+ dataColumns-1));
+ return;
+ }
+
+ String selectedCalcType = (String)
+ calcTypeComboBox.getSelectedItem();
+ String units = unitsForEachCalc[calcTypeComboBox.getSelectedIndex()];
+
+ StringBuffer code = new StringBuffer();
+ code.append("\tString dataFile = \"" + dataFile.getAbsolutePath() + "\";\n");
+ code.append("\tArrayFileReader afr = new ArrayFileReader(dataFile);\n");
+ code.append("\tdouble[][] data = afr.getDouble2DMatrix();\n");
+
+ // Create a Kraskov TE calculator:
+ TransferEntropyCalculator teCalc;
+ code.append("\tTransferEntropyCalculator teCalc;\n");
+
+ try{
+ // Construct an instance of the selected calculator:
+ if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
+ throw new Exception("Not implemented yet");
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
+ teCalc = new TransferEntropyCalculatorGaussian();
+ code.append("\tteCalc = new TransferEntropyCalculatorGaussian();\n");
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV)) {
+ teCalc = new TransferEntropyCalculatorKraskov();
+ code.append("\tteCalc = new TransferEntropyCalculatorKraskov();\n");
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) {
+ teCalc = new TransferEntropyCalculatorKernel();
+ code.append("\tteCalc = new TransferEntropyCalculatorKernel();\n");
+ } else {
+ throw new Exception("No recognised calculator selected: " +
+ selectedCalcType);
+ }
+
+ // Set properties
+ for (String propName : fieldNames) {
+ String propValue = null;
+ 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(teCalc.getProperty(propName))) {
+ // We need to set this property:
+ teCalc.setProperty(propName, propValue);
+ code.append("\tteCalc.setProperty(\"" + propName + "\", \"" +
+ propValue + "\");\n");
+ }
+ }
+
+ teCalc.initialise();
+ code.append("\tteCalc.initialise();\n");
+ // Compute TE
+ teCalc.setObservations(
+ MatrixUtils.selectColumn(data, sourceColumn),
+ MatrixUtils.selectColumn(data, destColumn));
+ code.append("\tteCalc.setObservations(\n" +
+ "\t\tMatrixUtils.selectColumn(data, " + sourceColumn + "),\n" +
+ "\t\tMatrixUtils.selectColumn(data, " + destColumn + "));\n");
+ double teResult = teCalc.computeAverageLocalOfObservations();
+ code.append("\tdouble teResult = teCalc.computeAverageLocalOfObservations();\n");
+ String resultsPrefixString = String.format("TE_%s(col_%d -> col_%d) = ",
+ selectedCalcType, sourceColumn, destColumn);
+ resultsLabel.setText(String.format(resultsPrefixString + "%.4f %s", teResult, units));
+ code.append("\tSystem.out.printf(\"" + resultsPrefixString + "%.4f " + units + "\\n\", teResult);\n");
+ System.out.println("Code to generate this result:");
+ System.out.print(code);
+ } catch (Exception ex) {
+ ex.printStackTrace(System.err);
+ JOptionPane.showMessageDialog(this,
+ ex.getMessage());
+ resultsLabel.setText("Calculation failed, please see console");
+ }
+
+ }
+
+ 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 = fieldNames.get(rowIndex);
+ propertyValues.put(propName, (String) aValue);
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 2;
+ }
+
+ @Override
+ public int getRowCount() {
+ return fieldNames.size();
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ String propName = fieldNames.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();
+ @SuppressWarnings("rawtypes")
+ Class teCalcClass = null;
+ String[] classSpecificProperties = null;
+ TransferEntropyCalculator teCalc = null;
+ try {
+ if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
+ throw new Exception("Not implemented yet");
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
+ teCalcClass = TransferEntropyCalculatorGaussian.class;
+ teCalc = new TransferEntropyCalculatorGaussian();
+ classSpecificProperties = gaussianProperties;
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV)) {
+ teCalcClass = TransferEntropyCalculatorKraskov.class;
+ teCalc = new TransferEntropyCalculatorKraskov();
+ classSpecificProperties = kraskovProperties;
+ } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) {
+ teCalcClass = TransferEntropyCalculatorKernel.class;
+ teCalc = new TransferEntropyCalculatorKernel();
+ classSpecificProperties = kernelProperties;
+ } else {
+ throw new Exception("No recognised calculator selected: " +
+ selectedCalcType);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace(System.err);
+ JOptionPane.showMessageDialog(this,
+ ex.getMessage());
+ resultsLabel.setText("Cannot load requested calculator");
+ }
+
+ // Now get all of the possible properties for this class:
+ fieldNames = new Vector();
+ Vector fieldDescriptions = new Vector();
+ for (int i = 0; i < commonContProperties.length; i++) {
+ fieldNames.add(commonContProperties[i]);
+ System.out.println("Adding property name " + commonContProperties[i]);
+ }
+ for (int i = 0; i < classSpecificProperties.length; i++) {
+ fieldNames.add(classSpecificProperties[i]);
+ System.out.println("Adding property name " + classSpecificProperties[i]);
+ }
+ // Now extract the default values for all of these properties:
+ propertyValues = new HashMap();
+ for (String propName : fieldNames) {
+ String defaultPropValue = null;
+ try {
+ defaultPropValue = teCalc.getProperty(propName);
+ } catch (Exception ex) {
+ ex.printStackTrace(System.err);
+ JOptionPane.showMessageDialog(this,
+ ex.getMessage());
+ propertyValues.put(propName, "Cannot find a value");
+ }
+ propertyValues.put(propName, defaultPropValue);
+ }
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ new AutoAnalyser();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ // Source or dest col number updated
+ resultsLabel.setText(""); // Clear text for now if anything changes
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ // Source or dest col number updated
+ resultsLabel.setText(""); // Clear text for now if anything changes
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ // Source or dest col number updated
+ resultsLabel.setText(""); // Clear text for now if anything changes
+ }
+}