diff --git a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java index c7dfbeb..54edbcf 100644 --- a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java +++ b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyser.java @@ -18,12 +18,13 @@ package infodynamics.demos.autoanalysis; -import infodynamics.measures.continuous.ChannelCalculatorCommon; -import infodynamics.measures.discrete.ChannelCalculatorDiscrete; +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; @@ -89,13 +90,16 @@ public abstract class AutoAnalyser extends JFrame // Set options for the Calculator type: public final static String CALC_TYPE_DISCRETE = "Discrete"; + // 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"; - protected String[] calcTypes = { - CALC_TYPE_DISCRETE, CALC_TYPE_GAUSSIAN, - CALC_TYPE_KRASKOV, CALC_TYPE_KERNEL}; - protected String[] unitsForEachCalc = {"bits", "nats", "nats", "bits"}; + public final static String CALC_TYPE_KOZ_LEO = "Kozachenko-Leonenko"; // Properties for each calculator protected static final String DISCRETE_PROPNAME_BASE = "base"; @@ -107,16 +111,15 @@ public abstract class AutoAnalyser extends JFrame protected String[] commonContPropertyNames; protected String[] commonContPropertiesFieldNames; protected String[] commonContPropertyDescriptions; - protected String[] gaussianProperties; - protected String[] gaussianPropertiesFieldNames; - protected String[] gaussianPropertyDescriptions; - protected String[] kernelProperties; - protected String[] kernelPropertiesFieldNames; - protected String[] kernelPropertyDescriptions; - protected String[] kraskovProperties; - protected String[] kraskovPropertiesFieldNames; - protected String[] kraskovPropertyDescriptions; + // 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; + // Store which calculator type we're using: @SuppressWarnings("rawtypes") protected Class calcClass = null; @@ -128,6 +131,11 @@ public abstract class AutoAnalyser extends JFrame 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 @@ -145,12 +153,19 @@ public abstract class AutoAnalyser extends JFrame // 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; - // CheckBox for "all pairs?" - protected JCheckBox allPairsCheckBox; + // 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 @@ -252,7 +267,7 @@ public abstract class AutoAnalyser extends JFrame Image watermarkImage = (new ImageIcon("JIDT-logo-watermark.png")).getImage(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setSize(1100,640); + setSize(1100,670); setTitle(appletTitle); // Centre in the middle of the screen setLocationRelativeTo(null); @@ -262,7 +277,7 @@ public abstract class AutoAnalyser extends JFrame 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) new JComboBox(calcTypes); - calcTypeComboBox.setSelectedIndex(2); // Select Kraskov by default + calcTypeComboBox.setSelectedIndex(2); // 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 @@ -270,7 +285,7 @@ public abstract class AutoAnalyser extends JFrame // Create the fields for the data file JLabel fileLabel = new JLabel("Data file:"); - fileLabel.setToolTipText("Must be a text file of time-series values with time increasing in rows, and variables along columns"); + 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); @@ -283,29 +298,29 @@ public abstract class AutoAnalyser extends JFrame dataFileDescriptorLabel.setHorizontalAlignment(JLabel.RIGHT); dataFileDescriptorLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0)); - // From column: - JLabel sourceLabel = new JLabel("Source column:"); - sourceLabel.setToolTipText("First column is 0."); - sourceColTextField = new JTextField(5); - sourceColTextField.setEnabled(true); - sourceColTextField.setText("0"); - // Must add document listener, not add action listener - sourceColTextField.getDocument().addDocumentListener(this); - // To column: - JLabel destLabel = new JLabel("Destination column:"); - destLabel.setToolTipText("First column is 0."); - destLabel.setBorder(BorderFactory.createEmptyBorder(0,0,10,0)); - destColTextField = new JTextField(5); - destColTextField.setEnabled(true); - destColTextField.setText("1"); - destColTextField.getDocument().addDocumentListener(this); + // 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); + } - // Checkbox for all pairs - allPairsCheckBox = new JCheckBox("All pairs?"); - allPairsCheckBox.setToolTipText("Compute for all pairs of columns?"); - allPairsCheckBox.addChangeListener(this); - - // CheckBox for statistical signficance + // 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); @@ -465,42 +480,40 @@ public abstract class AutoAnalyser extends JFrame paramsPanel.add(dataFileDescriptorLabel, c); - // Add the all pairs label and checkbox - c.gridwidth = GridBagConstraints.REMAINDER; //end row - c.gridx = 1; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1.0; - paramsPanel.add(allPairsCheckBox, c); - c.gridx = -1; // reset to no indication - - // Add the source selector - c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last - c.fill = GridBagConstraints.NONE; //reset to default - c.weightx = 0.0; //reset to default - paramsPanel.add(sourceLabel, c); - c.gridwidth = GridBagConstraints.REMAINDER; //end row - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1.0; - paramsPanel.add(sourceColTextField, c); - // Add the destination selector - c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last - c.fill = GridBagConstraints.NONE; //reset to default - c.weightx = 0.0; //reset to default - paramsPanel.add(destLabel, c); - c.gridwidth = GridBagConstraints.REMAINDER; //end row - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1.0; - paramsPanel.add(destColTextField, 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 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 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); @@ -579,7 +592,6 @@ public abstract class AutoAnalyser extends JFrame statSigAnalyticCheckBox.setSelected(false); statSigAnalyticCheckBox.setEnabled(false); } - } // Else nothing extra to do } @@ -673,34 +685,24 @@ public abstract class AutoAnalyser extends JFrame return; } - int singleSourceColumn = -1, singleDestColumn = -1; - Vector sourceDestPairs = new Vector(); - if (allPairsCheckBox.isSelected()) { - // We're doing all pairs - for (int s = 0; s < dataColumns; s++) { - for (int d = 0; d < dataColumns; d++) { - sourceDestPairs.add(new int[] {s, d}); + int[] singleCalcColumns = new int[numVariables]; + Vector variableCombinations = new Vector(); + if (allCombosCheckBox.isSelected()) { + // We're doing all combinations + fillOutAllCombinations(variableCombinations); + } else { + // 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; } } - } else { - // we're doing a single source-destination pair - singleSourceColumn = Integer.parseInt(sourceColTextField.getText()); - singleDestColumn = Integer.parseInt(destColTextField.getText()); - sourceDestPairs.add(new int[] {singleSourceColumn, singleDestColumn}); - if ((singleSourceColumn < 0) || (singleSourceColumn >= dataColumns)) { - JOptionPane.showMessageDialog(this, - String.format("Source column must be between 0 and %d for this data set", - dataColumns-1)); - resultsLabel.setText(" "); - return; - } - if ((singleDestColumn < 0) || (singleDestColumn >= dataColumns)) { - JOptionPane.showMessageDialog(this, - String.format("Destination column must be between 0 and %d for this data set", - dataColumns-1)); - resultsLabel.setText(" "); - return; - } + variableCombinations.add(singleCalcColumns); } // Generate headers: @@ -745,8 +747,8 @@ public abstract class AutoAnalyser extends JFrame try{ // Create both discrete and continuous calculators to make // following code simpler: - ChannelCalculatorCommon calcContinuous = null; - ChannelCalculatorDiscrete calcDiscrete = null; + InfoMeasureCalculatorContinuous calcContinuous = null; + InfoMeasureCalculatorDiscrete calcDiscrete = null; // Construct an instance of the selected calculator: String javaConstructorLine = null; @@ -781,6 +783,14 @@ public abstract class AutoAnalyser extends JFrame 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); @@ -798,15 +808,21 @@ public abstract class AutoAnalyser extends JFrame javaCode.append(" ArrayFileReader afr = new ArrayFileReader(dataFile);\n"); if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { javaCode.append(" int[][] data = afr.getInt2DMatrix();\n"); - if (! allPairsCheckBox.isSelected()) { - javaCode.append(" int[] source = MatrixUtils.selectColumn(data, " + singleSourceColumn + ");\n"); - javaCode.append(" int[] dest = MatrixUtils.selectColumn(data, " + singleDestColumn + ");\n\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 { javaCode.append(" double[][] data = afr.getDouble2DMatrix();\n"); - if (! allPairsCheckBox.isSelected()) { - javaCode.append(" double[] source = MatrixUtils.selectColumn(data, " + singleSourceColumn + ");\n"); - javaCode.append(" double[] dest = MatrixUtils.selectColumn(data, " + singleDestColumn + ");\n\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 @@ -818,21 +834,38 @@ public abstract class AutoAnalyser extends JFrame } pythonCode.append("# As numpy array:\n"); pythonCode.append("data = numpy.array(dataRaw)\n"); - if (! allPairsCheckBox.isSelected()) { - pythonCode.append("source = JArray(JInt, 1)(data[:," + singleSourceColumn + "].tolist())\n"); - pythonCode.append("dest = JArray(JInt, 1)(data[:," + singleDestColumn + "].tolist())\n\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"); + } + pythonCode.append("\n"); + } else { + for (int i=0; i < numVariables; i++) { + pythonCode.append(variableColNumLabels[i].toLowerCase() + " = data[:," + + singleCalcColumns[i] + "]\n"); + } + pythonCode.append("\n"); + } } // 3. Matlab matlabCode.append("% " + loadDataComment); matlabCode.append("data = load('" + dataFile.getAbsolutePath() + "');\n"); - if (! allPairsCheckBox.isSelected()) { + if (! allCombosCheckBox.isSelected()) { matlabCode.append("% Column indices start from 1 in Matlab:\n"); if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { - matlabCode.append("source = octaveToJavaIntArray(data(:," + (singleSourceColumn+1) + "));\n"); - matlabCode.append("dest = octaveToJavaIntArray(data(:," + (singleDestColumn+1) + "));\n\n"); + for (int i=0; i < numVariables; i++) { + matlabCode.append(variableColNumLabels[i].toLowerCase() + " = octaveToJavaIntArray(data(:," + + (singleCalcColumns[i]+1) + "));\n"); + } + matlabCode.append("\n"); } else { - matlabCode.append("source = octaveToJavaDoubleArray(data(:," + (singleSourceColumn+1) + "));\n"); - matlabCode.append("dest = octaveToJavaDoubleArray(data(:," + (singleDestColumn+1) + "));\n\n"); + for (int i=0; i < numVariables; i++) { + matlabCode.append(variableColNumLabels[i].toLowerCase() + " = octaveToJavaDoubleArray(data(:," + + (singleCalcColumns[i]+1) + "));\n"); + } + matlabCode.append("\n"); } } @@ -936,59 +969,46 @@ public abstract class AutoAnalyser extends JFrame String javaPrefix = " "; String pythonPrefix = ""; String matlabPrefix = ""; - if (sourceDestPairs.size() > 1) { - // We're doing all pairs + StringBuffer extraFormatTerms = new StringBuffer(); + if (variableCombinations.size() > 1) { + // We're doing all combinations. + // 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 < numVariables; i++) { + javaPrefix += " "; + pythonPrefix += "\t"; + matlabPrefix += "\t"; + extraFormatTerms.append(columnVariables[i] + ", "); + } - // Set up loops in the code: - // 1. Java code - javaCode.append(" \n"); - javaCode.append(" // Compute for all pairs:\n"); - javaCode.append(" for (int s = 0; s < " + dataColumns + - "; s++) {\n"); - javaCode.append(" for (int d = 0; d < " + dataColumns + - "; d++) {\n"); - javaPrefix = " "; - javaCode.append(javaPrefix + "// For each source-dest pair:\n"); - javaCode.append(javaPrefix + "if (s == d) {\n"); - javaCode.append(javaPrefix + " continue;\n"); - javaCode.append(javaPrefix + "}\n"); - // 2. Python code - pythonCode.append("\n"); - pythonCode.append("# Compute for all pairs:\n"); - pythonCode.append("for s in range(" + dataColumns + "):\n"); - pythonCode.append("\tfor d in range(" + dataColumns + "):\n"); - pythonPrefix = "\t\t"; - pythonCode.append(pythonPrefix+ "# For each source-dest pair:\n"); - pythonCode.append(pythonPrefix + "if (s == d):\n"); - pythonCode.append(pythonPrefix + "\tcontinue\n"); - // 3. Matlab code - matlabCode.append("\n"); - matlabCode.append("% Compute for all pairs:\n"); - matlabCode.append("for s = 1:" + dataColumns + "\n"); - matlabCode.append("\tfor d = 1:" + dataColumns + "\n"); - matlabPrefix = "\t\t"; - matlabCode.append(matlabPrefix + "% For each source-dest pair:\n"); - matlabCode.append(matlabPrefix + "if (s == d)\n"); - matlabCode.append(matlabPrefix + "\tcontinue;\n"); - matlabCode.append(matlabPrefix + "end\n"); - // 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)) { - javaCode.append(javaPrefix + "int[] source = MatrixUtils.selectColumn(data, s);\n"); - javaCode.append(javaPrefix + "int[] dest = MatrixUtils.selectColumn(data, d);\n\n"); - matlabCode.append(matlabPrefix + "source = octaveToJavaIntArray(data(:, s));\n"); - matlabCode.append(matlabPrefix + "dest = octaveToJavaIntArray(data(:, d));\n\n"); - pythonCode.append(pythonPrefix + "source = JArray(JInt, 1)(data[:, s].tolist())\n"); - pythonCode.append(pythonPrefix + "dest = JArray(JInt, 1)(data[:, d].tolist())\n\n"); + 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 { - javaCode.append(javaPrefix + "double[] source = MatrixUtils.selectColumn(data, s);\n"); - javaCode.append(javaPrefix + "double[] dest = MatrixUtils.selectColumn(data, d);\n\n"); - matlabCode.append(matlabPrefix + "source = octaveToJavaDoubleArray(data(:, s));\n"); - matlabCode.append(matlabPrefix + "dest = octaveToJavaDoubleArray(data(:, d));\n\n"); - pythonCode.append(pythonPrefix + "source = data[:, s]\n"); - pythonCode.append(pythonPrefix + "dest = data[:, d]\n\n"); + 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() + + " = data[:, " + columnVariables[i] + "]\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 @@ -1011,15 +1031,23 @@ public abstract class AutoAnalyser extends JFrame } 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 + "(source, dest);\n"); + javaCode.append(javaPrefix + "calc." + setObservationsMethod + "(" + methodArguments + ");\n"); // 2. Python pythonCode.append(pythonPrefix + "# " + supplyDataComment); - pythonCode.append(pythonPrefix + "calc." + setObservationsMethod + "(source, dest)\n"); + pythonCode.append(pythonPrefix + "calc." + setObservationsMethod + "(" + methodArguments + ")\n"); // 3. Matlab matlabCode.append(matlabPrefix + "% " + supplyDataComment); - matlabCode.append(matlabPrefix + "calc." + setObservationsMethod + "(source, dest);\n"); + matlabCode.append(matlabPrefix + "calc." + setObservationsMethod + "(" + methodArguments + ");\n"); // Compute the result String computeComment = "5. Compute the estimate:\n"; @@ -1030,15 +1058,16 @@ public abstract class AutoAnalyser extends JFrame matlabCode.append(matlabPrefix + "% " + computeComment); matlabCode.append(matlabPrefix + "result = calc.computeAverageLocalOfObservations();\n"); - String resultsPrefixString, extraFormatTerms; - if (sourceDestPairs.size() > 1) { - resultsPrefixString = String.format(measureAcronym + "_%s(col_%%d -> col_%%d) = ", - selectedCalcType); - extraFormatTerms = "s, d, "; + String resultsPrefixString; + if (variableCombinations.size() > 1) { + resultsPrefixString = String.format(measureAcronym + "_%s(%s) = ", + selectedCalcType, variableRelationshipFormatString); } else { - resultsPrefixString = String.format(measureAcronym + "_%s(col_%d -> col_%d) = ", - selectedCalcType, singleSourceColumn, singleDestColumn); - extraFormatTerms = ""; + resultsPrefixString = String.format(measureAcronym + "_%s(%s) = ", + selectedCalcType, + formatStringWithColumnNumbers( + variableRelationshipFormatString, + singleCalcColumns)); } String resultsSuffixString = ""; @@ -1076,18 +1105,11 @@ public abstract class AutoAnalyser extends JFrame "%.4f " + units + resultsSuffixString + "\\n', ...\n\t" + matlabPrefix + extraFormatTerms + "result" + statSigFormatTerms + ");\n"); - if (sourceDestPairs.size() > 1) { - // We're doing all pairs + if (variableCombinations.size() > 1) { + // We're doing all combos // Finalise the loops in the code: - // 1. Java code - javaCode.append(" }\n"); - javaCode.append(" }\n"); - // 2. Python code - // Nothing to do - // 3. Matlab code - matlabCode.append("\tend\n"); - matlabCode.append("end\n"); + finaliseLoopsForAllCombos(javaCode, pythonCode, matlabCode); // And tell the user to see results in the console: resultsLabel.setText("See console for all pairs calculation results"); @@ -1126,11 +1148,11 @@ public abstract class AutoAnalyser extends JFrame // 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[] pair : sourceDestPairs) { - int sourceColumn = pair[0]; - int destColumn = pair[1]; + for (int[] columnCombo : variableCombinations) { - if (sourceColumn == destColumn) { + if ((variableCombinations.size() > 1) && skipColumnCombo(columnCombo)) { + System.out.print("Skipping column combo "); + MatrixUtils.printArray(System.out, columnCombo); continue; } @@ -1141,25 +1163,8 @@ public abstract class AutoAnalyser extends JFrame calcContinuous.initialise(); } - // Set observations - if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { - calcDiscrete.addObservations( - MatrixUtils.selectColumn(dataDiscrete, sourceColumn), - MatrixUtils.selectColumn(dataDiscrete, destColumn)); - } else { - // TODO We should be able to directly call setObservations(double[], double[]) - // here but we can't right now because ChannelCalculator does not - // define this method. It would be complicated to fix this - // because it involves Conditional MI calculator it seems. - // Revisit this later -- for now fix by deferring to child classes - // calcContinuous.setObservations( - // MatrixUtils.selectColumn(data, sourceColumn), - // MatrixUtils.selectColumn(data, destColumn)); - setObservations(calcContinuous, - MatrixUtils.selectColumn(data, sourceColumn), - MatrixUtils.selectColumn(data, destColumn)); - } - + setObservations(calcDiscrete, calcContinuous, columnCombo); + // Compute the result: double result; if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { @@ -1189,11 +1194,15 @@ public abstract class AutoAnalyser extends JFrame } else { // permutation test of statistical significance: EmpiricalMeasurementDistribution measDist; + EmpiricalNullDistributionComputer empiricalNullDistCalc; if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { - measDist = calcDiscrete.computeSignificance(numPermutationsToCheck); + empiricalNullDistCalc = + (EmpiricalNullDistributionComputer) calcDiscrete; } else { - measDist = calcContinuous.computeSignificance(numPermutationsToCheck); + empiricalNullDistCalc = + (EmpiricalNullDistributionComputer) calcContinuous; } + measDist = empiricalNullDistCalc.computeSignificance(numPermutationsToCheck); resultsSuffixStringFormatted = String.format( resultsSuffixString, measDist.getMeanOfDistribution(), @@ -1204,11 +1213,16 @@ public abstract class AutoAnalyser extends JFrame } String resultsText; - if (sourceDestPairs.size() > 1) { + if (variableCombinations.size() > 1) { // We're doing all pairs - resultsText = String.format( - resultsPrefixString + "%.4f %s" + resultsSuffixStringFormatted, - sourceColumn, destColumn, result, units); + // 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, @@ -1219,7 +1233,7 @@ public abstract class AutoAnalyser extends JFrame System.out.println(resultsText); } - if ((sourceDestPairs.size() == 1) && + if ((variableCombinations.size() == 1) && !selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { // Read the current property values back out (in case of // automated parameter assignment) @@ -1249,11 +1263,64 @@ public abstract class AutoAnalyser extends JFrame } + /** + * 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 variableCombinations); + + /** + * 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 ChannelCalculatorCommon assignCalcObjectContinuous(String selectedCalcType) throws Exception; + protected abstract InfoMeasureCalculatorContinuous assignCalcObjectContinuous(String selectedCalcType) throws Exception; /** * Method to assign and initialise our discrete calculator class @@ -1269,11 +1336,11 @@ public abstract class AutoAnalyser extends JFrame * */ protected class DiscreteCalcAndArguments { - ChannelCalculatorDiscrete calc; + InfoMeasureCalculatorDiscrete calc; int base; String arguments; - DiscreteCalcAndArguments(ChannelCalculatorDiscrete calc, int base, + DiscreteCalcAndArguments(InfoMeasureCalculatorDiscrete calc, int base, String arguments) { this.calc = calc; this.base = base; @@ -1281,16 +1348,17 @@ public abstract class AutoAnalyser extends JFrame } } - /** + /** * Method to set the observations on the underlying calculator * - * @param calc - * @param source - * @param dest + * @param calcDiscrete + * @param calcContinuous + * @param columnCombo int array for the columns of data being computed with. * @throws Exception */ - protected abstract void setObservations(ChannelCalculatorCommon calc, - double[] source, double[] dest) throws Exception; + protected abstract void setObservations(InfoMeasureCalculatorDiscrete calcDiscrete, + InfoMeasureCalculatorContinuous calcContinuous, + int[] columnCombo) throws Exception; /** @@ -1417,45 +1485,23 @@ public abstract class AutoAnalyser extends JFrame System.out.println("Getting calc properties"); String selectedCalcType = (String) calcTypeComboBox.getSelectedItem(); - String[] classSpecificPropertyNames = null; - String[] classSpecificPropertiesFieldNames = null; - String[] classSpecificPropertyDescriptions = null; - ChannelCalculatorCommon calc = null; + CalcProperties calcProperties = null; try { - if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { - calcClass = discreteClass; - calc = null; // Not used - classSpecificPropertyNames = discreteProperties; - classSpecificPropertiesFieldNames = null; // Not used - classSpecificPropertyDescriptions = discretePropertyDescriptions; - } else { - calc = assignCalcObjectContinuous(selectedCalcType); - calcClass = calc.getClass(); - if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) { - classSpecificPropertyNames = gaussianProperties; - classSpecificPropertiesFieldNames = gaussianPropertiesFieldNames; - classSpecificPropertyDescriptions = gaussianPropertyDescriptions; - } else if (selectedCalcType.startsWith(CALC_TYPE_KRASKOV)) { - // The if statement will work for both MI Kraskov calculators - classSpecificPropertyNames = kraskovProperties; - classSpecificPropertiesFieldNames = kraskovPropertiesFieldNames; - classSpecificPropertyDescriptions = kraskovPropertyDescriptions; - } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) { - classSpecificPropertyNames = kernelProperties; - classSpecificPropertiesFieldNames = kernelPropertiesFieldNames; - classSpecificPropertyDescriptions = kernelPropertyDescriptions; - } else { - calcClass = null; - throw new Exception("No recognised calculator selected: " + - selectedCalcType); - } - } + 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; + calcClass = calcProperties.calcClass; + Object calc = calcProperties.calc; // Now get all of the possible properties for this class: propertyNames = new Vector(); @@ -1513,20 +1559,65 @@ public abstract class AutoAnalyser extends JFrame // Now extract the default values for all of these properties: propertyValues = new HashMap(); for (String propName : propertyNames) { - String defaultPropValue = null; try { - defaultPropValue = calc.getProperty(propName); + 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"); } - propertyValues.put(propName, defaultPropValue); } } } + /** + * 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; + } + + /** + * 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)) { + CalcProperties calcProperties = new CalcProperties(); + calcProperties.calcClass = discreteClass; + calcProperties.calc = null; // Not used + calcProperties.classSpecificPropertyNames = discreteProperties; + calcProperties.classSpecificPropertiesFieldNames = null; // Not used + calcProperties.classSpecificPropertyDescriptions = discretePropertyDescriptions; + 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 @@ -1594,21 +1685,16 @@ public abstract class AutoAnalyser extends JFrame // so we'll just act on potential changes from both. // This won't cause a problem here - if (e.getSource() == allPairsCheckBox) { + if (e.getSource() == allCombosCheckBox) { // "All pairs" checkbox -- update in case changed: - if (allPairsCheckBox.isSelected()) { - sourceColTextField.setEnabled(false); - destColTextField.setEnabled(false); + if (allCombosCheckBox.isSelected()) { + for (int i = 0; i < numVariables; i++) { + variableColTextFields[i].setEnabled(false); + } } else { - sourceColTextField.setEnabled(true); - destColTextField.setEnabled(true); - } - } else if (e.getSource() == computeResultCheckBox) { - // Compute checkbox -- update in case changed: - if (computeResultCheckBox.isSelected()) { - computeButton.setText(buttonTextCodeAndCompute); - } else { - computeButton.setText(buttonTextCodeOnly); + 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()); @@ -1626,9 +1712,16 @@ public abstract class AutoAnalyser extends JFrame 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 diff --git a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserChannelCalculator.java b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserChannelCalculator.java new file mode 100644 index 0000000..17632da --- /dev/null +++ b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserChannelCalculator.java @@ -0,0 +1,207 @@ +/* + * 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.ChannelCalculator; +import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous; +import infodynamics.measures.discrete.ChannelCalculatorDiscrete; +import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete; +import infodynamics.utils.MatrixUtils; + +import java.util.Vector; + +/** + * This abstract class provides a GUI to build a simple calculation + * over a source-target channel, + * and supply the code to execute it. + * Child classes fix this to a TE or MI calculation. + * + * + * @author Joseph Lizier + * + */ +public abstract class AutoAnalyserChannelCalculator extends AutoAnalyser { + + /** + * Need serialVersionUID to be serializable + */ + private static final long serialVersionUID = 1L; + + // Property names for specific continuous calculators: + protected String[] gaussianProperties; + protected String[] gaussianPropertiesFieldNames; + protected String[] gaussianPropertyDescriptions; + protected String[] kernelProperties; + protected String[] kernelPropertiesFieldNames; + protected String[] kernelPropertyDescriptions; + protected String[] kraskovProperties; + protected String[] kraskovPropertiesFieldNames; + protected String[] kraskovPropertyDescriptions; + + /** + * Constructor to initialise the GUI for a channel calculator + */ + protected void makeSpecificInitialisations() { + numVariables = 2; + variableColNumLabels = new String[] {"Source", "Destination"}; + useAllCombosCheckBox = true; + useStatSigCheckBox = true; + wordForCombinations = "pairs"; + variableRelationshipFormatString = "col_%d -> col_%d"; + } + + @Override + protected void fillOutAllCombinations(Vector variableCombinations) { + // All combinations here means all pairs + for (int s = 0; s < dataColumns; s++) { + for (int d = 0; d < dataColumns; d++) { + variableCombinations.add(new int[] {s, d}); + } + } + } + + @Override + protected String[] setUpLoopsForAllCombos(StringBuffer javaCode, + StringBuffer pythonCode, StringBuffer matlabCode) { + // Set up loops in the code: + // 1. Java code + javaCode.append(" \n"); + javaCode.append(" // Compute for all pairs:\n"); + javaCode.append(" for (int s = 0; s < " + dataColumns + + "; s++) {\n"); + javaCode.append(" for (int d = 0; d < " + dataColumns + + "; d++) {\n"); + String javaPrefix = " "; + javaCode.append(javaPrefix + "// For each source-dest pair:\n"); + javaCode.append(javaPrefix + "if (s == d) {\n"); + javaCode.append(javaPrefix + " continue;\n"); + javaCode.append(javaPrefix + "}\n"); + // 2. Python code + pythonCode.append("\n"); + pythonCode.append("# Compute for all pairs:\n"); + pythonCode.append("for s in range(" + dataColumns + "):\n"); + pythonCode.append("\tfor d in range(" + dataColumns + "):\n"); + String pythonPrefix = "\t\t"; + pythonCode.append(pythonPrefix+ "# For each source-dest pair:\n"); + pythonCode.append(pythonPrefix + "if (s == d):\n"); + pythonCode.append(pythonPrefix + "\tcontinue\n"); + // 3. Matlab code + matlabCode.append("\n"); + matlabCode.append("% Compute for all pairs:\n"); + matlabCode.append("for s = 1:" + dataColumns + "\n"); + matlabCode.append("\tfor d = 1:" + dataColumns + "\n"); + String matlabPrefix = "\t\t"; + matlabCode.append(matlabPrefix + "% For each source-dest pair:\n"); + matlabCode.append(matlabPrefix + "if (s == d)\n"); + matlabCode.append(matlabPrefix + "\tcontinue;\n"); + matlabCode.append(matlabPrefix + "end\n"); + + // Return the variables to index each column: + return new String[] {"s", "d"}; + } + + @Override + protected void finaliseLoopsForAllCombos(StringBuffer javaCode, + StringBuffer pythonCode, StringBuffer matlabCode) { + + // 1. Java code + javaCode.append(" }\n"); + javaCode.append(" }\n"); + // 2. Python code + // Nothing to do + // 3. Matlab code + matlabCode.append("\tend\n"); + matlabCode.append("end\n"); + } + + @Override + protected String formatStringWithColumnNumbers(String formatStr, int[] columnNumbers) { + // We format the source and target variable numbers into the + // return string here: + return String.format(formatStr, + columnNumbers[0], columnNumbers[1]); + } + + @Override + protected boolean skipColumnCombo(int[] columnCombo) { + if (columnCombo[0] == columnCombo[1]) { + // source and destination columns are the same here, + // so don't compute the channel measure + return true; + } + return false; + } + + @Override + protected void setObservations(InfoMeasureCalculatorDiscrete calcDiscrete, + InfoMeasureCalculatorContinuous calcContinuous, + int[] columnCombo) throws Exception { + + String selectedCalcType = (String) + calcTypeComboBox.getSelectedItem(); + + int sourceColumn = columnCombo[0]; + int destColumn = columnCombo[1]; + + // Set observations + if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) { + ChannelCalculatorDiscrete cc = (ChannelCalculatorDiscrete) calcDiscrete; + cc.addObservations( + MatrixUtils.selectColumn(dataDiscrete, sourceColumn), + MatrixUtils.selectColumn(dataDiscrete, destColumn)); + } else { + ChannelCalculator cc = (ChannelCalculator) calcContinuous; + cc.setObservations( + MatrixUtils.selectColumn(data, sourceColumn), + MatrixUtils.selectColumn(data, destColumn)); + } + } + + protected CalcProperties assignCalcProperties(String selectedCalcType) + throws Exception { + // Let the super class handle discrete calculators + CalcProperties calcProperties = super.assignCalcProperties(selectedCalcType); + if (calcProperties == null) { + // We need to assign properties for a continuous calculator + calcProperties = new CalcProperties(); + calcProperties.calc = assignCalcObjectContinuous(selectedCalcType); + calcProperties.calcClass = calcProperties.calc.getClass(); + if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) { + calcProperties.classSpecificPropertyNames = gaussianProperties; + calcProperties.classSpecificPropertiesFieldNames = gaussianPropertiesFieldNames; + calcProperties.classSpecificPropertyDescriptions = gaussianPropertyDescriptions; + } else if (selectedCalcType.startsWith(CALC_TYPE_KRASKOV)) { + // The if statement will work for both MI Kraskov calculators + calcProperties.classSpecificPropertyNames = kraskovProperties; + calcProperties.classSpecificPropertiesFieldNames = kraskovPropertiesFieldNames; + calcProperties.classSpecificPropertyDescriptions = kraskovPropertyDescriptions; + } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) { + calcProperties.classSpecificPropertyNames = kernelProperties; + calcProperties.classSpecificPropertiesFieldNames = kernelPropertiesFieldNames; + calcProperties.classSpecificPropertyDescriptions = kernelPropertyDescriptions; + } else { + calcProperties = null; + throw new Exception("No recognised calculator selected: " + + selectedCalcType); + } + } + return calcProperties; + } + +} diff --git a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserMI.java b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserMI.java index 5a2b39e..13afaeb 100644 --- a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserMI.java +++ b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserMI.java @@ -18,7 +18,6 @@ package infodynamics.demos.autoanalysis; -import infodynamics.measures.continuous.ChannelCalculatorCommon; import infodynamics.measures.continuous.MutualInfoCalculatorMultiVariate; import infodynamics.measures.continuous.gaussian.MutualInfoCalculatorMultiVariateGaussian; import infodynamics.measures.continuous.kernel.MutualInfoCalculatorMultiVariateKernel; @@ -41,7 +40,7 @@ import java.awt.event.MouseListener; * @author Joseph Lizier * */ -public class AutoAnalyserMI extends AutoAnalyser +public class AutoAnalyserMI extends AutoAnalyserChannelCalculator implements ActionListener, DocumentListener, MouseListener { /** @@ -59,6 +58,8 @@ public class AutoAnalyserMI extends AutoAnalyser */ protected void makeSpecificInitialisations() { + super.makeSpecificInitialisations(); + // Set up the properties for MI: measureAcronym = "MI"; appletTitle = "JIDT Mutual Information Auto-Analyser"; @@ -159,7 +160,8 @@ public class AutoAnalyserMI extends AutoAnalyser /** * Method to assign and initialise our continuous calculator class */ - protected ChannelCalculatorCommon assignCalcObjectContinuous(String selectedCalcType) throws Exception { + @Override + protected MutualInfoCalculatorMultiVariate assignCalcObjectContinuous(String selectedCalcType) throws Exception { if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) { return new MutualInfoCalculatorMultiVariateGaussian(); } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV_ALG1)) { @@ -205,13 +207,6 @@ public class AutoAnalyserMI extends AutoAnalyser base + ", " + timeDiff); } - protected void setObservations(ChannelCalculatorCommon calc, - double[] source, double[] dest) throws Exception { - // We know this is a MutualInfoCalculatorMultiVariate - MutualInfoCalculatorMultiVariate miCalc = (MutualInfoCalculatorMultiVariate) calc; - miCalc.setObservations(source, dest); - } - /** * @param args */ diff --git a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserTE.java b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserTE.java index 75a6ace..29ee653 100644 --- a/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserTE.java +++ b/demos/java/infodynamics/demos/autoanalysis/AutoAnalyserTE.java @@ -18,7 +18,6 @@ package infodynamics.demos.autoanalysis; -import infodynamics.measures.continuous.ChannelCalculatorCommon; import infodynamics.measures.continuous.ConditionalMutualInfoMultiVariateCommon; import infodynamics.measures.continuous.TransferEntropyCalculator; import infodynamics.measures.continuous.gaussian.TransferEntropyCalculatorGaussian; @@ -41,7 +40,7 @@ import java.awt.event.MouseListener; * @author Joseph Lizier * */ -public class AutoAnalyserTE extends AutoAnalyser +public class AutoAnalyserTE extends AutoAnalyserChannelCalculator implements ActionListener, DocumentListener, MouseListener { /** @@ -60,10 +59,17 @@ public class AutoAnalyserTE extends AutoAnalyser */ protected void makeSpecificInitialisations() { + super.makeSpecificInitialisations(); + // Set up the properties for TE: measureAcronym = "TE"; appletTitle = "JIDT Transfer Entropy Auto-Analyser"; + calcTypes = new String[] { + CALC_TYPE_DISCRETE, CALC_TYPE_GAUSSIAN, + CALC_TYPE_KRASKOV, CALC_TYPE_KERNEL}; + unitsForEachCalc = new String[] {"bits", "nats", "nats", "bits"}; + // Discrete: discreteClass = TransferEntropyCalculatorDiscrete.class; discreteProperties = new String[] { @@ -214,7 +220,7 @@ public class AutoAnalyserTE extends AutoAnalyser /** * Method to assign and initialise our continuous calculator class */ - protected ChannelCalculatorCommon assignCalcObjectContinuous(String selectedCalcType) throws Exception { + protected TransferEntropyCalculator assignCalcObjectContinuous(String selectedCalcType) throws Exception { if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) { return new TransferEntropyCalculatorGaussian(); } else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV)) { @@ -294,13 +300,6 @@ public class AutoAnalyserTE extends AutoAnalyser base + ", " + k + ", " + k_tau + ", " + l + ", " + l_tau + ", " + delay); } - protected void setObservations(ChannelCalculatorCommon calc, - double[] source, double[] dest) throws Exception { - // We know this is a TransferEntropyCalculator - TransferEntropyCalculator teCalc = (TransferEntropyCalculator) calc; - teCalc.setObservations(source, dest); - } - /** * @param args */