mirror of https://github.com/jlizier/jidt
Merge branch 'master' into gpu
This commit is contained in:
commit
446ac4ae59
|
|
@ -1,6 +1,6 @@
|
|||
# Java Information Dynamics Toolkit (JIDT)
|
||||
|
||||
Copyright (C) 2012-2014 [Joseph T. Lizier](http://lizier.me/joseph/); 2014-2016 [Joseph T. Lizier](http://lizier.me/joseph/) and Ipek Özdemir
|
||||
Copyright (C) 2012-2014 [Joseph T. Lizier](http://lizier.me/joseph/); 2014-2016 [Joseph T. Lizier](http://lizier.me/joseph/) and Ipek Özdemir; 2017- [Joseph T. Lizier](http://lizier.me/joseph/), Ipek Özdemir and [Pedro Mediano](https://www.doc.ic.ac.uk/~pam213/)
|
||||
|
||||
*JIDT* provides a stand-alone, open-source code Java implementation (also usable in [Matlab, Octave](../../wiki/UseInOctaveMatlab), [Python](../../wiki/UseInPython), [R](../../wiki/UseInR), [Julia](../../wiki/UseInJulia) and [Clojure](../../wiki/UseInClojure)) of information-theoretic measures of distributed computation in complex systems: i.e. information storage, transfer and modification.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<property name="javadocsdir" location="javadocs"/>
|
||||
<property name="versionfile" value="version-${version}.txt"/>
|
||||
<!-- To enable GPU code, set the following variable to true -->
|
||||
<property name="enablegpu" value="true"/>
|
||||
<property name="enablegpu" value="false"/>
|
||||
|
||||
<path id="project.classpath">
|
||||
<pathelement location="bin"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Make sure the latest example source file is compiled.
|
||||
javac -classpath "..\java;..\..\infodynamics.jar" "..\java\infodynamics\demos\autoanalysis\AutoAnalyserAIS.java"
|
||||
|
||||
REM Run the example:
|
||||
java -classpath "..\java;..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserAIS
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Make sure the latest example source file is compiled.
|
||||
javac -classpath "../java:../../infodynamics.jar" "../java/infodynamics/demos/autoanalysis/AutoAnalyserAIS.java"
|
||||
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserAIS
|
||||
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
# Make sure the latest example source file is compiled.
|
||||
javac -classpath "../java:../../infodynamics.jar" "../java/infodynamics/demos/autoanalysis/GeneratedCalculator.java"
|
||||
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.GeneratedCalculator
|
||||
if [ $? == 0 ]; then
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.GeneratedCalculator
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Make sure the latest example source file is compiled.
|
||||
javac -classpath "..\java;..\..\infodynamics.jar" "..\java\infodynamics\demos\autoanalysis\AutoAnalyserCMI.java"
|
||||
|
||||
REM Run the example:
|
||||
java -classpath "..\java;..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCMI
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Make sure the latest example source file is compiled.
|
||||
javac -classpath "../java:../../infodynamics.jar" "../java/infodynamics/demos/autoanalysis/AutoAnalyserCMI.java"
|
||||
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCMI
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Make sure the latest example source file is compiled.
|
||||
javac -classpath "..\java;..\..\infodynamics.jar" "..\java\infodynamics\demos\autoanalysis\AutoAnalyserCTE.java"
|
||||
|
||||
REM Run the example:
|
||||
java -classpath "..\java;..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCTE
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Make sure the latest example source file is compiled.
|
||||
javac -classpath "../java:../../infodynamics.jar" "../java/infodynamics/demos/autoanalysis/AutoAnalyserCTE.java"
|
||||
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCTE
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@ECHO OFF
|
||||
|
||||
REM Make sure the latest example source file is compiled.
|
||||
javac -classpath "..\java;..\..\infodynamics.jar" "..\java\infodynamics\demos\autoanalysis\AutoAnalyserEntropy.java"
|
||||
|
||||
REM Run the example:
|
||||
java -classpath "..\java;..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserEntropy
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Make sure the latest example source file is compiled.
|
||||
javac -classpath "../java:../../infodynamics.jar" "../java/infodynamics/demos/autoanalysis/AutoAnalyserEntropy.java"
|
||||
|
||||
# Run the example:
|
||||
java -classpath "../java:../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserEntropy
|
||||
|
||||
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
(.setObservations teCalc sourceArray destArray)
|
||||
; For copied source, should give something close to expected value for correlated Gaussians:
|
||||
; TODO The analytic result quoted here isn't quite right, see e.g. octave demos (can't be bothered fixing here...)
|
||||
(println "TE result " (.computeAverageLocalOfObservations teCalc)
|
||||
" expected to be close to " (/ (Math/log (/ 1 (- 1 (* covariance covariance)))) (Math/log 2))
|
||||
" for these correlated Gaussians but biased upward")
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
; data is a set of random variables) - the result will be of the order
|
||||
; of what we expect, but not exactly equal to it; in fact, there will
|
||||
; be a large variance around it.
|
||||
; TODO The analytic result quoted here isn't quite right, see e.g. octave demos (can't be bothered fixing here...)
|
||||
(println "TE result " (.computeAverageLocalOfObservations teCalc)
|
||||
" nats expected to be close to " (Math/log (/ 1 (- 1 (* covariance covariance))))
|
||||
" nats for these correlated Gaussians")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# First column is random; Second column is parity of its two previous values and previous value of first column
|
||||
% First column is random; Second column is parity of its two previous values and previous value of first column
|
||||
1 0
|
||||
0 1
|
||||
1 1
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,4 @@
|
|||
# Two random columns of data in [0..1) each
|
||||
% Two random columns of data in [0..1) each
|
||||
2.97438473e-01 1.58412125e-01
|
||||
3.72029104e-02 7.49930621e-01
|
||||
4.30819357e-01 4.23417397e-01
|
||||
|
|
|
|||
|
|
@ -58,11 +58,16 @@ public class Example3TeContinuousDataKernel {
|
|||
teCalc.setProperty("NORMALISE", "true"); // Normalise the individual variables (default)
|
||||
teCalc.initialise(1, 0.5); // Use history length 1 (Schreiber k=1), kernel width of 0.5 normalised units
|
||||
teCalc.setObservations(sourceArray, destArray);
|
||||
// For copied source, should give something close to expected value for correlated Gaussians:
|
||||
double result = teCalc.computeAverageLocalOfObservations();
|
||||
// For copied source, should give something close to expected value for correlated Gaussians:.
|
||||
// Expected correlation is expected covariance / product of expected standard deviations:
|
||||
// (where square of destArray standard dev is sum of squares of std devs of
|
||||
// underlying distributions)
|
||||
double corr_expected = covariance /
|
||||
(1.0 * Math.sqrt(Math.pow(covariance,2) + Math.pow(1-covariance,2)));
|
||||
System.out.printf("TE result %.4f bits; expected to be close to " +
|
||||
"%.4f bits for these correlated Gaussians but biased upwards\n",
|
||||
result, Math.log(1.0/(1-Math.pow(covariance,2)))/Math.log(2));
|
||||
result, -0.5 * Math.log(1-Math.pow(corr_expected,2))/Math.log(2));
|
||||
|
||||
teCalc.initialise(); // Initialise leaving the parameters the same
|
||||
teCalc.setObservations(sourceArray2, destArray);
|
||||
|
|
|
|||
|
|
@ -65,9 +65,14 @@ public class Example4TeContinuousDataKraskov {
|
|||
// data is a set of random variables) - the result will be of the order
|
||||
// of what we expect, but not exactly equal to it; in fact, there will
|
||||
// be a large variance around it.
|
||||
// Expected correlation is expected covariance / product of expected standard deviations:
|
||||
// (where square of destArray standard dev is sum of squares of std devs of
|
||||
// underlying distributions)
|
||||
double corr_expected = covariance /
|
||||
(1.0 * Math.sqrt(Math.pow(covariance,2) + Math.pow(1-covariance,2)));
|
||||
System.out.printf("TE result %.4f nats; expected to be close to " +
|
||||
"%.4f nats for these correlated Gaussians\n",
|
||||
result, Math.log(1.0/(1-Math.pow(covariance,2))));
|
||||
result, -0.5 * Math.log(1-Math.pow(corr_expected,2)));
|
||||
|
||||
// Perform calculation with uncorrelated source:
|
||||
teCalc.initialise(); // Initialise leaving the parameters the same
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* 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.ActiveInfoStorageCalculator;
|
||||
import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous;
|
||||
import infodynamics.measures.continuous.gaussian.ActiveInfoStorageCalculatorGaussian;
|
||||
import infodynamics.measures.continuous.kernel.ActiveInfoStorageCalculatorKernel;
|
||||
import infodynamics.measures.continuous.kernel.MutualInfoCalculatorMultiVariateKernel;
|
||||
import infodynamics.measures.continuous.kraskov.ActiveInfoStorageCalculatorKraskov;
|
||||
import infodynamics.measures.continuous.kraskov.MutualInfoCalculatorMultiVariateKraskov;
|
||||
import infodynamics.measures.discrete.ActiveInformationCalculatorDiscrete;
|
||||
import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* This class provides a GUI to build a simple calculation
|
||||
* of active information storage,
|
||||
* and supply the code to execute it.
|
||||
*
|
||||
*
|
||||
* @author Joseph Lizier
|
||||
*
|
||||
*/
|
||||
public class AutoAnalyserAIS extends AutoAnalyser {
|
||||
|
||||
/**
|
||||
* Need serialVersionUID to be serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected static final String DISCRETE_PROPNAME_K = "k_HISTORY";
|
||||
|
||||
// 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 = 1;
|
||||
variableColNumLabels = new String[] {"Variable"};
|
||||
useAllCombosCheckBox = true;
|
||||
useStatSigCheckBox = true;
|
||||
wordForCombinations = "variables";
|
||||
variableRelationshipFormatString = "col_%d";
|
||||
disableVariableColTextFieldsForAllCombos = new boolean[] {true};
|
||||
indentsForAllCombos = 1;
|
||||
|
||||
// Set up the properties for Entropy:
|
||||
measureAcronym = "AIS";
|
||||
appletTitle = "JIDT Active Information Storage Auto-Analyser";
|
||||
|
||||
calcTypes = new String[] {
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KRASKOV, CALC_TYPE_KERNEL};
|
||||
unitsForEachCalc = new String[] {"bits", "bits", "nats", "nats", "bits"};
|
||||
|
||||
// Discrete:
|
||||
discreteClass = ActiveInformationCalculatorDiscrete.class;
|
||||
discreteProperties = new String[] {
|
||||
DISCRETE_PROPNAME_BASE,
|
||||
DISCRETE_PROPNAME_K
|
||||
};
|
||||
discretePropertyDefaultValues = new String[] {
|
||||
"2",
|
||||
"1"
|
||||
};
|
||||
discretePropertyDescriptions = new String[] {
|
||||
"Number of discrete states available for each variable (i.e. 2 for binary)",
|
||||
"History embedding length (k_HISTORY)"
|
||||
};
|
||||
|
||||
// Continuous:
|
||||
abstractContinuousClass = ActiveInfoStorageCalculator.class;
|
||||
// Common properties for all continuous calcs:
|
||||
commonContPropertyNames = new String[] {
|
||||
ActiveInfoStorageCalculator.K_PROP_NAME,
|
||||
ActiveInfoStorageCalculator.TAU_PROP_NAME
|
||||
};
|
||||
commonContPropertiesFieldNames = new String[] {
|
||||
"K_PROP_NAME",
|
||||
"TAU_PROP_NAME"
|
||||
};
|
||||
commonContPropertyDescriptions = new String[] {
|
||||
"History embedding length (k_HISTORY)",
|
||||
"History embedding delay (k_TAU)"
|
||||
};
|
||||
// Gaussian properties:
|
||||
gaussianProperties = new String[] {
|
||||
};
|
||||
gaussianPropertiesFieldNames = new String[] {
|
||||
};
|
||||
gaussianPropertyDescriptions = new String[] {
|
||||
};
|
||||
// Kernel:
|
||||
kernelProperties = new String[] {
|
||||
MutualInfoCalculatorMultiVariateKernel.KERNEL_WIDTH_PROP_NAME,
|
||||
MutualInfoCalculatorMultiVariateKernel.DYN_CORR_EXCL_TIME_NAME,
|
||||
MutualInfoCalculatorMultiVariateKernel.NORMALISE_PROP_NAME,
|
||||
};
|
||||
kernelPropertiesFieldNames = new String[] {
|
||||
"KERNEL_WIDTH_PROP_NAME",
|
||||
"DYN_CORR_EXCL_TIME_NAME",
|
||||
"NORMALISE_PROP_NAME"
|
||||
};
|
||||
kernelPropertyDescriptions = new String[] {
|
||||
"Kernel width to be used in the calculation. <br/>If the property " +
|
||||
MutualInfoCalculatorMultiVariateKernel.NORMALISE_PROP_NAME +
|
||||
" is set, then this is a number of standard deviations; " +
|
||||
"otherwise it is an absolute value.",
|
||||
"Dynamic correlation exclusion time or <br/>Theiler window (see Kantz and Schreiber); " +
|
||||
"0 (default) means no dynamic exclusion window",
|
||||
"(boolean) whether to normalise <br/>each incoming time-series to mean 0, standard deviation 1, or not (recommended)",
|
||||
};
|
||||
// KSG (Kraskov):
|
||||
kraskovProperties = new String[] {
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_NORMALISE,
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_K,
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE,
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME,
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE,
|
||||
MutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS,
|
||||
ActiveInfoStorageCalculatorKraskov.PROP_AUTO_EMBED_METHOD,
|
||||
ActiveInfoStorageCalculatorKraskov.PROP_K_SEARCH_MAX,
|
||||
ActiveInfoStorageCalculatorKraskov.PROP_TAU_SEARCH_MAX,
|
||||
ActiveInfoStorageCalculatorKraskov.PROP_RAGWITZ_NUM_NNS,
|
||||
};
|
||||
kraskovPropertiesFieldNames = new String[] {
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_NORMALISE",
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_K",
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE",
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME",
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE",
|
||||
"MutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS",
|
||||
"PROP_AUTO_EMBED_METHOD",
|
||||
"PROP_K_SEARCH_MAX",
|
||||
"PROP_TAU_SEARCH_MAX",
|
||||
"PROP_RAGWITZ_NUM_NNS"
|
||||
};
|
||||
kraskovPropertyDescriptions = new String[] {
|
||||
"(boolean) whether to normalise <br/>each incoming time-series to mean 0, standard deviation 1, or not (recommended)",
|
||||
"Number of k nearest neighbours to use <br/>in the full joint kernel space in the KSG algorithm",
|
||||
"Standard deviation for an amount <br/>of random Gaussian noise to add to each variable, " +
|
||||
"to avoid having neighbourhoods with artificially large counts. <br/>" +
|
||||
"(\"false\" may be used to indicate \"0\".). The amount is added in after any normalisation.",
|
||||
"Dynamic correlation exclusion time or <br/>Theiler window (see Kantz and Schreiber); " +
|
||||
"0 (default) means no dynamic exclusion window",
|
||||
"<br/>Norm type to use in KSG algorithm between the points in each marginal space. <br/>Options are: " +
|
||||
"\"MAX_NORM\" (default), otherwise \"EUCLIDEAN\" or \"EUCLIDEAN_SQUARED\" (both equivalent here)",
|
||||
"Number of parallel threads to use <br/>in computation: an integer > 0 or \"USE_ALL\" " +
|
||||
"(default, to indicate to use all available processors)",
|
||||
"Method to automatically determine embedding length (k_HISTORY)<br/> and delay (k_TAU) for " +
|
||||
"the samples. Default is \"" + ActiveInfoStorageCalculatorKraskov.AUTO_EMBED_METHOD_NONE +
|
||||
"\" meaning values are set manually; other values include: <br/> -- \"" + ActiveInfoStorageCalculatorKraskov.AUTO_EMBED_METHOD_RAGWITZ +
|
||||
"\" for use of the Ragwitz criteria for both source and destination (searching up to \"" + ActiveInfoStorageCalculatorKraskov.PROP_K_SEARCH_MAX +
|
||||
"\" and \"" + ActiveInfoStorageCalculatorKraskov.PROP_TAU_SEARCH_MAX + "\"); <br/> -- \"" + ActiveInfoStorageCalculatorKraskov.AUTO_EMBED_METHOD_MAX_CORR_AIS +
|
||||
"\" for maximising the (bias corrected) Active Info Storage (searching up to \"" + ActiveInfoStorageCalculatorKraskov.PROP_K_SEARCH_MAX +
|
||||
"\" and \"" + ActiveInfoStorageCalculatorKraskov.PROP_TAU_SEARCH_MAX + "\"); <br/>Use of values other than \"" + ActiveInfoStorageCalculatorKraskov.AUTO_EMBED_METHOD_NONE +
|
||||
"\" leads to any previous settings for embedding lengths and delays to be overwritten after observations are supplied",
|
||||
"Max. embedding length to search to <br/>if auto embedding (as determined by " + ActiveInfoStorageCalculatorKraskov.PROP_AUTO_EMBED_METHOD + ")",
|
||||
"Max. embedding delay to search to <br/>if auto embedding (as determined by " + ActiveInfoStorageCalculatorKraskov.PROP_AUTO_EMBED_METHOD + ")",
|
||||
"Number of k nearest neighbours for <br/>Ragwitz auto embedding (if used; defaults to match property \"k\")"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillOutAllCombinations(Vector<int[]> variableCombinations) {
|
||||
// All combinations here means all variables
|
||||
for (int s = 0; s < dataColumns; s++) {
|
||||
variableCombinations.add(new int[] {s});
|
||||
}
|
||||
}
|
||||
|
||||
@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 variables:\n");
|
||||
javaCode.append(" for (int v = 0; v < " + dataColumns +
|
||||
"; v++) {\n");
|
||||
String javaPrefix = " ";
|
||||
javaCode.append(javaPrefix + "// For each variable:\n");
|
||||
// 2. Python code
|
||||
pythonCode.append("\n");
|
||||
pythonCode.append("# Compute for all variables:\n");
|
||||
pythonCode.append("for v in range(" + dataColumns + "):\n");
|
||||
String pythonPrefix = " ";
|
||||
pythonCode.append(pythonPrefix+ "# For each variable:\n");
|
||||
// 3. Matlab code
|
||||
matlabCode.append("\n");
|
||||
matlabCode.append("% Compute for all variables:\n");
|
||||
matlabCode.append("for v = 1:" + dataColumns + "\n");
|
||||
String matlabPrefix = "\t";
|
||||
matlabCode.append(matlabPrefix + "% For each variable:\n");
|
||||
|
||||
// Return the variables to index each column:
|
||||
return new String[] {"v"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finaliseLoopsForAllCombos(StringBuffer javaCode,
|
||||
StringBuffer pythonCode, StringBuffer matlabCode) {
|
||||
|
||||
// 1. Java code
|
||||
javaCode.append(" }\n");
|
||||
// 2. Python code
|
||||
// Nothing to do
|
||||
// 3. Matlab code
|
||||
matlabCode.append("end\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatStringWithColumnNumbers(String formatStr, int[] columnNumbers) {
|
||||
// We format the variable number into the
|
||||
// return string here:
|
||||
return String.format(formatStr,
|
||||
columnNumbers[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean skipColumnCombo(int[] columnCombo) {
|
||||
// No reason to skip any columns here
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObservations(InfoMeasureCalculatorDiscrete calcDiscrete,
|
||||
InfoMeasureCalculatorContinuous calcContinuous,
|
||||
int[] columnCombo) throws Exception {
|
||||
|
||||
String selectedCalcType = (String)
|
||||
calcTypeComboBox.getSelectedItem();
|
||||
|
||||
int variableColumn = columnCombo[0];
|
||||
|
||||
// Set observations
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
|
||||
ActiveInformationCalculatorDiscrete calc = (ActiveInformationCalculatorDiscrete) calcDiscrete;
|
||||
calc.addObservations(
|
||||
MatrixUtils.selectColumn(dataDiscrete, variableColumn));
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
|
||||
ActiveInformationCalculatorDiscrete calc = (ActiveInformationCalculatorDiscrete) calcDiscrete;
|
||||
calc.addObservations(
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, variableColumn),
|
||||
// Should be no parse error on the alphabet size by now
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))));
|
||||
} else {
|
||||
ActiveInfoStorageCalculator calc = (ActiveInfoStorageCalculator) calcContinuous;
|
||||
calc.setObservations(
|
||||
MatrixUtils.selectColumn(data, variableColumn));
|
||||
}
|
||||
}
|
||||
|
||||
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.equalsIgnoreCase(CALC_TYPE_KRASKOV)) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our continuous calculator class
|
||||
*/
|
||||
@Override
|
||||
protected ActiveInfoStorageCalculator assignCalcObjectContinuous(String selectedCalcType) throws Exception {
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
|
||||
return new ActiveInfoStorageCalculatorGaussian();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV)) {
|
||||
return new ActiveInfoStorageCalculatorKraskov();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) {
|
||||
return new ActiveInfoStorageCalculatorKernel();
|
||||
} else {
|
||||
throw new Exception("No recognised continuous calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our discrete calculator class
|
||||
*/
|
||||
protected DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception {
|
||||
int base, k;
|
||||
try {
|
||||
String basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
base = Integer.parseInt(basePropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot read a value for property " + DISCRETE_PROPNAME_BASE);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String kPropValueStr = propertyValues.get(DISCRETE_PROPNAME_K);
|
||||
k = Integer.parseInt(kPropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot read a value for property " + DISCRETE_PROPNAME_K);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DiscreteCalcAndArguments(
|
||||
new ActiveInformationCalculatorDiscrete(base, k),
|
||||
base,
|
||||
base + ", " + k);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new AutoAnalyserAIS();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* 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.ConditionalMutualInfoCalculatorMultiVariate;
|
||||
import infodynamics.measures.continuous.ConditionalMutualInfoMultiVariateCommon;
|
||||
import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous;
|
||||
import infodynamics.measures.continuous.gaussian.ConditionalMutualInfoCalculatorMultiVariateGaussian;
|
||||
import infodynamics.measures.continuous.kraskov.ConditionalMutualInfoCalculatorMultiVariateKraskov;
|
||||
import infodynamics.measures.continuous.kraskov.ConditionalMutualInfoCalculatorMultiVariateKraskov1;
|
||||
import infodynamics.measures.continuous.kraskov.ConditionalMutualInfoCalculatorMultiVariateKraskov2;
|
||||
import infodynamics.measures.discrete.ConditionalMutualInformationCalculatorDiscrete;
|
||||
import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This class provides a GUI to build a simple conditional mutual information calculation,
|
||||
* and supply the code to execute it.
|
||||
*
|
||||
*
|
||||
* @author Joseph Lizier
|
||||
*
|
||||
*/
|
||||
public class AutoAnalyserCMI extends AutoAnalyser
|
||||
implements ActionListener, DocumentListener, MouseListener {
|
||||
|
||||
/**
|
||||
* 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[] kraskovProperties;
|
||||
protected String[] kraskovPropertiesFieldNames;
|
||||
protected String[] kraskovPropertyDescriptions;
|
||||
|
||||
protected static final String CALC_TYPE_KRASKOV_ALG1 = CALC_TYPE_KRASKOV + " alg. 1";
|
||||
protected static final String CALC_TYPE_KRASKOV_ALG2 = CALC_TYPE_KRASKOV + " alg. 2";
|
||||
|
||||
/**
|
||||
* Constructor to initialise the GUI for CMI
|
||||
*/
|
||||
protected void makeSpecificInitialisations() {
|
||||
|
||||
numVariables = 3;
|
||||
variableColNumLabels = new String[] {"Source", "Destination", "Conditional"};
|
||||
useAllCombosCheckBox = true;
|
||||
useStatSigCheckBox = true;
|
||||
wordForCombinations = "pairs";
|
||||
variableRelationshipFormatString = "col_%d -> col_%d | col_%d";
|
||||
disableVariableColTextFieldsForAllCombos = new boolean[] {true, true, false};
|
||||
indentsForAllCombos = 2;
|
||||
|
||||
// Set up the properties for CMI:
|
||||
measureAcronym = "CMI";
|
||||
appletTitle = "JIDT Conditional MI Auto-Analyser";
|
||||
|
||||
calcTypes = new String[] {
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KRASKOV_ALG1, CALC_TYPE_KRASKOV_ALG2};
|
||||
// No kernel calculator defined for CMI (yet, and unlikely to happen)
|
||||
unitsForEachCalc = new String[] {"bits", "bits", "nats", "nats", "nats"};
|
||||
|
||||
// Discrete:
|
||||
discreteClass = ConditionalMutualInformationCalculatorDiscrete.class;
|
||||
discreteProperties = new String[] {
|
||||
DISCRETE_PROPNAME_BASE
|
||||
};
|
||||
discretePropertyDefaultValues = new String[] {
|
||||
"2"
|
||||
};
|
||||
discretePropertyDescriptions = new String[] {
|
||||
"Number of discrete states available for each variable (i.e. 2 for binary).<br/>" +
|
||||
"Can be set individually for each variable -- see code."
|
||||
};
|
||||
|
||||
// Continuous:
|
||||
abstractContinuousClass = ConditionalMutualInfoCalculatorMultiVariate.class;
|
||||
// Common properties for all continuous calcs:
|
||||
commonContPropertyNames = new String[] {
|
||||
// None
|
||||
};
|
||||
commonContPropertiesFieldNames = new String[] {
|
||||
// None
|
||||
};
|
||||
commonContPropertyDescriptions = new String[] {
|
||||
// None
|
||||
};
|
||||
// Gaussian properties:
|
||||
gaussianProperties = new String[] {
|
||||
};
|
||||
gaussianPropertiesFieldNames = new String[] {
|
||||
};
|
||||
gaussianPropertyDescriptions = new String[] {
|
||||
};
|
||||
// KSG (Kraskov):
|
||||
kraskovProperties = new String[] {
|
||||
ConditionalMutualInfoMultiVariateCommon.PROP_NORMALISE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS,
|
||||
};
|
||||
kraskovPropertiesFieldNames = new String[] {
|
||||
"ConditionalMutualInfoMultiVariateCommon.PROP_NORMALISE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS",
|
||||
};
|
||||
kraskovPropertyDescriptions = new String[] {
|
||||
"(boolean) whether to normalise <br/>each incoming time-series to mean 0, standard deviation 1, or not (recommended)",
|
||||
"Number of k nearest neighbours to use <br/>in the full joint kernel space in the KSG algorithm",
|
||||
"Standard deviation for an amount <br/>of random Gaussian noise to add to each variable, " +
|
||||
"to avoid having neighbourhoods with artificially large counts. <br/>" +
|
||||
"(\"false\" may be used to indicate \"0\".). The amount is added in after any normalisation.",
|
||||
"Dynamic correlation exclusion time or <br/>Theiler window (see Kantz and Schreiber); " +
|
||||
"0 (default) means no dynamic exclusion window",
|
||||
"<br/>Norm type to use in KSG algorithm between the points in each marginal space. <br/>Options are: " +
|
||||
"\"MAX_NORM\" (default), otherwise \"EUCLIDEAN\" or \"EUCLIDEAN_SQUARED\" (both equivalent here)",
|
||||
"Number of parallel threads to use <br/>in computation: an integer > 0 or \"USE_ALL\" " +
|
||||
"(default, to indicate to use all available processors)",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillOutAllCombinations(Vector<int[]> variableCombinations) throws Exception {
|
||||
// All combinations here means all pairs of sources and destinations,
|
||||
// with the conditional fixed.
|
||||
int conditional = Integer.parseInt(variableColTextFields[2].getText());
|
||||
if (conditional >= dataColumns) {
|
||||
throw new Exception(String.format("%s column must be between 0 and %d for this data set",
|
||||
variableColNumLabels[2], dataColumns-1));
|
||||
}
|
||||
for (int s = 0; s < dataColumns; s++) {
|
||||
for (int d = 0; d < dataColumns; d++) {
|
||||
variableCombinations.add(new int[] {s, d, conditional});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] setUpLoopsForAllCombos(StringBuffer javaCode,
|
||||
StringBuffer pythonCode, StringBuffer matlabCode) {
|
||||
// Set up loops in the code:
|
||||
int conditional = Integer.parseInt(variableColTextFields[2].getText());
|
||||
// 1. Java code
|
||||
javaCode.append(" \n");
|
||||
javaCode.append(" int c = " + conditional + ";\n");
|
||||
javaCode.append(" // Compute for all source-destination 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 (given conditional):\n");
|
||||
javaCode.append(javaPrefix + "if ((s == d) || (s == c) || (d == c)) {\n");
|
||||
javaCode.append(javaPrefix + " continue;\n");
|
||||
javaCode.append(javaPrefix + "}\n");
|
||||
// 2. Python code
|
||||
pythonCode.append("\n");
|
||||
pythonCode.append("c = " + conditional + "\n");
|
||||
pythonCode.append("# Compute for all pairs:\n");
|
||||
pythonCode.append("for s in range(" + dataColumns + "):\n");
|
||||
pythonCode.append(" for d in range(" + dataColumns + "):\n");
|
||||
String pythonPrefix = " ";
|
||||
pythonCode.append(pythonPrefix+ "# For each source-dest pair (given conditional):\n");
|
||||
pythonCode.append(pythonPrefix + "if ((s == d) or (s == c) or (d == c)):\n");
|
||||
pythonCode.append(pythonPrefix + " continue\n");
|
||||
// 3. Matlab code
|
||||
matlabCode.append("\n");
|
||||
matlabCode.append("c = " + (conditional+1) + ";\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 (given conditional):\n");
|
||||
matlabCode.append(matlabPrefix + "if ((s == d) || (s == c) || (d == c))\n");
|
||||
matlabCode.append(matlabPrefix + "\tcontinue;\n");
|
||||
matlabCode.append(matlabPrefix + "end\n");
|
||||
|
||||
// Return the variables to index each column:
|
||||
return new String[] {"s", "d", "c"};
|
||||
}
|
||||
|
||||
@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], columnNumbers[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean skipColumnCombo(int[] columnCombo) {
|
||||
if ((columnCombo[0] == columnCombo[1]) ||
|
||||
(columnCombo[0] == columnCombo[2]) ||
|
||||
(columnCombo[1] == columnCombo[2])) {
|
||||
// Two columns are the same here,
|
||||
// so don't compute the conditional MI
|
||||
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];
|
||||
int condColumn = columnCombo[2];
|
||||
|
||||
// Set observations
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
|
||||
ConditionalMutualInformationCalculatorDiscrete cmiCalc =
|
||||
(ConditionalMutualInformationCalculatorDiscrete) calcDiscrete;
|
||||
cmiCalc.addObservations(
|
||||
MatrixUtils.selectColumn(dataDiscrete, sourceColumn),
|
||||
MatrixUtils.selectColumn(dataDiscrete, destColumn),
|
||||
MatrixUtils.selectColumn(dataDiscrete, condColumn));
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
|
||||
ConditionalMutualInformationCalculatorDiscrete cmiCalc =
|
||||
(ConditionalMutualInformationCalculatorDiscrete) calcDiscrete;
|
||||
cmiCalc.addObservations(
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, sourceColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))),
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, destColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))),
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, condColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))));
|
||||
} else {
|
||||
ConditionalMutualInfoCalculatorMultiVariate cmiCalc =
|
||||
(ConditionalMutualInfoCalculatorMultiVariate) calcContinuous;
|
||||
cmiCalc.setObservations(
|
||||
MatrixUtils.selectColumn(data, sourceColumn),
|
||||
MatrixUtils.selectColumn(data, destColumn),
|
||||
MatrixUtils.selectColumn(data, condColumn));
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
calcProperties = null;
|
||||
throw new Exception("No recognised calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
}
|
||||
return calcProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our continuous calculator class
|
||||
*/
|
||||
@Override
|
||||
protected ConditionalMutualInfoCalculatorMultiVariate assignCalcObjectContinuous(String selectedCalcType) throws Exception {
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
|
||||
return new ConditionalMutualInfoCalculatorMultiVariateGaussian();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV_ALG1)) {
|
||||
return new ConditionalMutualInfoCalculatorMultiVariateKraskov1();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV_ALG2)) {
|
||||
return new ConditionalMutualInfoCalculatorMultiVariateKraskov2();
|
||||
} else {
|
||||
throw new Exception("No recognised continuous calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our discrete calculator class
|
||||
*/
|
||||
protected DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception {
|
||||
int base;
|
||||
try {
|
||||
String basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
base = Integer.parseInt(basePropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot find a value for property " + DISCRETE_PROPNAME_BASE);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DiscreteCalcAndArguments(
|
||||
new ConditionalMutualInformationCalculatorDiscrete(base, base, base),
|
||||
base,
|
||||
base + ", " + base + ", " + base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new AutoAnalyserCMI();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* 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.ConditionalMutualInfoMultiVariateCommon;
|
||||
import infodynamics.measures.continuous.ConditionalTransferEntropyCalculator;
|
||||
import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous;
|
||||
import infodynamics.measures.continuous.gaussian.ConditionalTransferEntropyCalculatorGaussian;
|
||||
import infodynamics.measures.continuous.kraskov.ConditionalMutualInfoCalculatorMultiVariateKraskov;
|
||||
import infodynamics.measures.continuous.kraskov.ConditionalTransferEntropyCalculatorKraskov;
|
||||
import infodynamics.measures.discrete.ConditionalTransferEntropyCalculatorDiscrete;
|
||||
import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This class provides a GUI to build a simple conditional transfer entropy calculation,
|
||||
* and supply the code to execute it.
|
||||
*
|
||||
*
|
||||
* @author Joseph Lizier
|
||||
*
|
||||
*/
|
||||
public class AutoAnalyserCTE extends AutoAnalyser
|
||||
implements ActionListener, DocumentListener, MouseListener {
|
||||
|
||||
/**
|
||||
* Need serialVersionUID to be serializable
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected static final String DISCRETE_PROPNAME_K = "k_HISTORY";
|
||||
|
||||
// Property names for specific continuous calculators:
|
||||
protected String[] gaussianProperties;
|
||||
protected String[] gaussianPropertiesFieldNames;
|
||||
protected String[] gaussianPropertyDescriptions;
|
||||
protected String[] kraskovProperties;
|
||||
protected String[] kraskovPropertiesFieldNames;
|
||||
protected String[] kraskovPropertyDescriptions;
|
||||
|
||||
/**
|
||||
* Constructor to initialise the GUI for Conditional TE
|
||||
*/
|
||||
protected void makeSpecificInitialisations() {
|
||||
|
||||
numVariables = 3;
|
||||
variableColNumLabels = new String[] {"Source", "Destination", "Conditional"};
|
||||
useAllCombosCheckBox = true;
|
||||
useStatSigCheckBox = true;
|
||||
wordForCombinations = "pairs";
|
||||
variableRelationshipFormatString = "col_%d -> col_%d | col_%d";
|
||||
disableVariableColTextFieldsForAllCombos = new boolean[] {true, true, false};
|
||||
indentsForAllCombos = 2;
|
||||
|
||||
// Set up the properties for CTE:
|
||||
measureAcronym = "CTE";
|
||||
appletTitle = "JIDT Conditional TE Auto-Analyser";
|
||||
|
||||
calcTypes = new String[] {
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KRASKOV};
|
||||
// No kernel calculator defined for CMI (yet, and unlikely to happen)
|
||||
unitsForEachCalc = new String[] {"bits", "bits", "nats", "nats"};
|
||||
|
||||
// Discrete:
|
||||
discreteClass = ConditionalTransferEntropyCalculatorDiscrete.class;
|
||||
discreteProperties = new String[] {
|
||||
DISCRETE_PROPNAME_BASE,
|
||||
DISCRETE_PROPNAME_K
|
||||
};
|
||||
discretePropertyDefaultValues = new String[] {
|
||||
"2",
|
||||
"1"
|
||||
};
|
||||
discretePropertyDescriptions = new String[] {
|
||||
"Number of discrete states available for each variable (i.e. 2 for binary).<br/>" +
|
||||
"Can be set individually for each variable -- see code.",
|
||||
"Destination history embedding length (k_HISTORY)",
|
||||
};
|
||||
|
||||
// Continuous:
|
||||
abstractContinuousClass = ConditionalTransferEntropyCalculator.class;
|
||||
// Common properties for all continuous calcs:
|
||||
commonContPropertyNames = new String[] {
|
||||
ConditionalTransferEntropyCalculator.K_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.K_TAU_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.L_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.L_TAU_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.DELAY_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.COND_EMBED_LENGTHS_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.COND_EMBED_DELAYS_PROP_NAME,
|
||||
ConditionalTransferEntropyCalculator.COND_DELAYS_PROP_NAME
|
||||
};
|
||||
commonContPropertiesFieldNames = new String[] {
|
||||
"K_PROP_NAME",
|
||||
"K_TAU_PROP_NAME",
|
||||
"L_PROP_NAME",
|
||||
"L_TAU_PROP_NAME",
|
||||
"DELAY_PROP_NAME",
|
||||
"COND_EMBED_LENGTHS_PROP_NAME",
|
||||
"COND_EMBED_DELAYS_PROP_NAME",
|
||||
"COND_DELAYS_PROP_NAME"
|
||||
};
|
||||
commonContPropertyDescriptions = new String[] {
|
||||
"Destination history embedding length (k_HISTORY)",
|
||||
"Destination history embedding delay (k_TAU)",
|
||||
"Source history embedding length (l_HISTORY)",
|
||||
"Source history embeding delay (l_TAU)",
|
||||
"Delay from source to destination (in time steps)",
|
||||
"Conditional history embedding length",
|
||||
"Conditional history embeding delay",
|
||||
"Delay from conditional to destination (in time steps)"
|
||||
};
|
||||
// Gaussian properties:
|
||||
gaussianProperties = new String[] {
|
||||
};
|
||||
gaussianPropertiesFieldNames = new String[] {
|
||||
};
|
||||
gaussianPropertyDescriptions = new String[] {
|
||||
};
|
||||
// KSG (Kraskov):
|
||||
kraskovProperties = new String[] {
|
||||
ConditionalMutualInfoMultiVariateCommon.PROP_NORMALISE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE,
|
||||
ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS,
|
||||
ConditionalTransferEntropyCalculatorKraskov.PROP_KRASKOV_ALG_NUM
|
||||
};
|
||||
kraskovPropertiesFieldNames = new String[] {
|
||||
"ConditionalMutualInfoMultiVariateCommon.PROP_NORMALISE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_ADD_NOISE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_DYN_CORR_EXCL_TIME",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NORM_TYPE",
|
||||
"ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_NUM_THREADS",
|
||||
"PROP_KRASKOV_ALG_NUM"
|
||||
};
|
||||
kraskovPropertyDescriptions = new String[] {
|
||||
"(boolean) whether to normalise <br/>each incoming time-series to mean 0, standard deviation 1, or not (recommended)",
|
||||
"Number of k nearest neighbours to use <br/>in the full joint kernel space in the KSG algorithm",
|
||||
"Standard deviation for an amount <br/>of random Gaussian noise to add to each variable, " +
|
||||
"to avoid having neighbourhoods with artificially large counts. <br/>" +
|
||||
"(\"false\" may be used to indicate \"0\".). The amount is added in after any normalisation.",
|
||||
"Dynamic correlation exclusion time or <br/>Theiler window (see Kantz and Schreiber); " +
|
||||
"0 (default) means no dynamic exclusion window",
|
||||
"<br/>Norm type to use in KSG algorithm between the points in each marginal space. <br/>Options are: " +
|
||||
"\"MAX_NORM\" (default), otherwise \"EUCLIDEAN\" or \"EUCLIDEAN_SQUARED\" (both equivalent here)",
|
||||
"Number of parallel threads to use <br/>in computation: an integer > 0 or \"USE_ALL\" " +
|
||||
"(default, to indicate to use all available processors)",
|
||||
"Which KSG algorithm to use (1 or 2)",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillOutAllCombinations(Vector<int[]> variableCombinations) throws Exception {
|
||||
// All combinations here means all pairs of sources and destinations,
|
||||
// with the conditional fixed.
|
||||
int conditional = Integer.parseInt(variableColTextFields[2].getText());
|
||||
if (conditional >= dataColumns) {
|
||||
throw new Exception(String.format("%s column must be between 0 and %d for this data set",
|
||||
variableColNumLabels[2], dataColumns-1));
|
||||
}
|
||||
for (int s = 0; s < dataColumns; s++) {
|
||||
for (int d = 0; d < dataColumns; d++) {
|
||||
variableCombinations.add(new int[] {s, d, conditional});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] setUpLoopsForAllCombos(StringBuffer javaCode,
|
||||
StringBuffer pythonCode, StringBuffer matlabCode) {
|
||||
// Set up loops in the code:
|
||||
int conditional = Integer.parseInt(variableColTextFields[2].getText());
|
||||
// 1. Java code
|
||||
javaCode.append(" \n");
|
||||
javaCode.append(" int c = " + conditional + ";\n");
|
||||
javaCode.append(" // Compute for all source-destination 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 (given conditional):\n");
|
||||
javaCode.append(javaPrefix + "if ((s == d) || (s == c) || (d == c)) {\n");
|
||||
javaCode.append(javaPrefix + " continue;\n");
|
||||
javaCode.append(javaPrefix + "}\n");
|
||||
// 2. Python code
|
||||
pythonCode.append("\n");
|
||||
pythonCode.append("c = " + conditional + "\n");
|
||||
pythonCode.append("# Compute for all pairs:\n");
|
||||
pythonCode.append("for s in range(" + dataColumns + "):\n");
|
||||
pythonCode.append(" for d in range(" + dataColumns + "):\n");
|
||||
String pythonPrefix = " ";
|
||||
pythonCode.append(pythonPrefix+ "# For each source-dest pair (given conditional):\n");
|
||||
pythonCode.append(pythonPrefix + "if ((s == d) or (s == c) or (d == c)):\n");
|
||||
pythonCode.append(pythonPrefix + " continue\n");
|
||||
// 3. Matlab code
|
||||
matlabCode.append("\n");
|
||||
matlabCode.append("c = " + (conditional+1) + ";\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 (given conditional):\n");
|
||||
matlabCode.append(matlabPrefix + "if ((s == d) || (s == c) || (d == c))\n");
|
||||
matlabCode.append(matlabPrefix + "\tcontinue;\n");
|
||||
matlabCode.append(matlabPrefix + "end\n");
|
||||
|
||||
// Return the variables to index each column:
|
||||
return new String[] {"s", "d", "c"};
|
||||
}
|
||||
|
||||
@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], columnNumbers[2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean skipColumnCombo(int[] columnCombo) {
|
||||
if ((columnCombo[0] == columnCombo[1]) ||
|
||||
(columnCombo[0] == columnCombo[2]) ||
|
||||
(columnCombo[1] == columnCombo[2])) {
|
||||
// Two columns are the same here,
|
||||
// so don't compute the conditional TE
|
||||
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];
|
||||
int condColumn = columnCombo[2];
|
||||
|
||||
// Set observations
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
|
||||
ConditionalTransferEntropyCalculatorDiscrete cteCalc =
|
||||
(ConditionalTransferEntropyCalculatorDiscrete) calcDiscrete;
|
||||
cteCalc.addObservations(
|
||||
MatrixUtils.selectColumn(dataDiscrete, sourceColumn),
|
||||
MatrixUtils.selectColumn(dataDiscrete, destColumn),
|
||||
MatrixUtils.selectColumn(dataDiscrete, condColumn));
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
|
||||
ConditionalTransferEntropyCalculatorDiscrete cteCalc =
|
||||
(ConditionalTransferEntropyCalculatorDiscrete) calcDiscrete;
|
||||
cteCalc.addObservations(
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, sourceColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))),
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, destColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))),
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, condColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))));
|
||||
} else {
|
||||
ConditionalTransferEntropyCalculator cteCalc =
|
||||
(ConditionalTransferEntropyCalculator) calcContinuous;
|
||||
cteCalc.setObservations(
|
||||
MatrixUtils.selectColumn(data, sourceColumn),
|
||||
MatrixUtils.selectColumn(data, destColumn),
|
||||
MatrixUtils.selectColumn(data, condColumn));
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
calcProperties = null;
|
||||
throw new Exception("No recognised calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
}
|
||||
return calcProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our continuous calculator class
|
||||
*/
|
||||
@Override
|
||||
protected ConditionalTransferEntropyCalculator assignCalcObjectContinuous(String selectedCalcType) throws Exception {
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
|
||||
return new ConditionalTransferEntropyCalculatorGaussian();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KRASKOV)) {
|
||||
return new ConditionalTransferEntropyCalculatorKraskov();
|
||||
} else {
|
||||
throw new Exception("No recognised continuous calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our discrete calculator class
|
||||
*/
|
||||
protected DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception {
|
||||
int base, k;
|
||||
try {
|
||||
String basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
base = Integer.parseInt(basePropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot find a value for property " + DISCRETE_PROPNAME_BASE);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String kPropValueStr = propertyValues.get(DISCRETE_PROPNAME_K);
|
||||
k = Integer.parseInt(kPropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot read a value for property " + DISCRETE_PROPNAME_K);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DiscreteCalcAndArguments(
|
||||
// Initialise always with one other info contributor for now
|
||||
new ConditionalTransferEntropyCalculatorDiscrete(base, k, 1),
|
||||
base,
|
||||
base + ", " + k + ", " + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new AutoAnalyserCTE();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* 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.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";
|
||||
|
||||
disableVariableColTextFieldsForAllCombos = new boolean[] {true, true};
|
||||
indentsForAllCombos = 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillOutAllCombinations(Vector<int[]> 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(" for d in range(" + dataColumns + "):\n");
|
||||
String pythonPrefix = " ";
|
||||
pythonCode.append(pythonPrefix+ "# For each source-dest pair:\n");
|
||||
pythonCode.append(pythonPrefix + "if (s == d):\n");
|
||||
pythonCode.append(pythonPrefix + " continue\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 if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
|
||||
ChannelCalculatorDiscrete cc = (ChannelCalculatorDiscrete) calcDiscrete;
|
||||
cc.addObservations(
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, sourceColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))),
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, destColumn),
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))));
|
||||
} 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* 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.EntropyCalculator;
|
||||
import infodynamics.measures.continuous.InfoMeasureCalculatorContinuous;
|
||||
import infodynamics.measures.continuous.gaussian.EntropyCalculatorGaussian;
|
||||
import infodynamics.measures.continuous.kernel.EntropyCalculatorKernel;
|
||||
import infodynamics.measures.continuous.kozachenko.EntropyCalculatorMultiVariateKozachenko;
|
||||
import infodynamics.measures.discrete.EntropyCalculatorDiscrete;
|
||||
import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete;
|
||||
import infodynamics.measures.discrete.MutualInformationCalculatorDiscrete;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* This class provides a GUI to build a simple calculation
|
||||
* of entropy,
|
||||
* and supply the code to execute it.
|
||||
*
|
||||
*
|
||||
* @author Joseph Lizier
|
||||
*
|
||||
*/
|
||||
public class AutoAnalyserEntropy 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[] klProperties;
|
||||
protected String[] klPropertiesFieldNames;
|
||||
protected String[] klPropertyDescriptions;
|
||||
|
||||
/**
|
||||
* Constructor to initialise the GUI for a channel calculator
|
||||
*/
|
||||
protected void makeSpecificInitialisations() {
|
||||
numVariables = 1;
|
||||
variableColNumLabels = new String[] {"Variable"};
|
||||
useAllCombosCheckBox = true;
|
||||
useStatSigCheckBox = false;
|
||||
wordForCombinations = "variables";
|
||||
variableRelationshipFormatString = "col_%d";
|
||||
disableVariableColTextFieldsForAllCombos = new boolean[] {true};
|
||||
indentsForAllCombos = 1;
|
||||
|
||||
// Set up the properties for Entropy:
|
||||
measureAcronym = "H";
|
||||
appletTitle = "JIDT Entropy Auto-Analyser";
|
||||
|
||||
calcTypes = new String[] {
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KOZ_LEO, CALC_TYPE_KERNEL};
|
||||
unitsForEachCalc = new String[] {"bits", "bits", "nats", "nats", "bits"};
|
||||
|
||||
// Discrete:
|
||||
discreteClass = MutualInformationCalculatorDiscrete.class;
|
||||
discreteProperties = new String[] {
|
||||
DISCRETE_PROPNAME_BASE
|
||||
};
|
||||
discretePropertyDefaultValues = new String[] {
|
||||
"2"
|
||||
};
|
||||
discretePropertyDescriptions = new String[] {
|
||||
"Number of discrete states available for each variable (i.e. 2 for binary)"
|
||||
};
|
||||
|
||||
// Continuous:
|
||||
abstractContinuousClass = EntropyCalculator.class;
|
||||
// Common properties for all continuous calcs:
|
||||
commonContPropertyNames = new String[] {
|
||||
// None
|
||||
};
|
||||
commonContPropertiesFieldNames = new String[] {
|
||||
// None
|
||||
};
|
||||
commonContPropertyDescriptions = new String[] {
|
||||
// None
|
||||
};
|
||||
// Gaussian properties:
|
||||
gaussianProperties = new String[] {
|
||||
};
|
||||
gaussianPropertiesFieldNames = new String[] {
|
||||
};
|
||||
gaussianPropertyDescriptions = new String[] {
|
||||
};
|
||||
// Kernel:
|
||||
kernelProperties = new String[] {
|
||||
EntropyCalculatorKernel.KERNEL_WIDTH_PROP_NAME,
|
||||
EntropyCalculatorKernel.NORMALISE_PROP_NAME,
|
||||
};
|
||||
kernelPropertiesFieldNames = new String[] {
|
||||
"KERNEL_WIDTH_PROP_NAME",
|
||||
"NORMALISE_PROP_NAME"
|
||||
};
|
||||
kernelPropertyDescriptions = new String[] {
|
||||
"Kernel width to be used in the calculation. <br/>If the property " +
|
||||
EntropyCalculatorKernel.NORMALISE_PROP_NAME +
|
||||
" is set, then this is a number of standard deviations; " +
|
||||
"otherwise it is an absolute value.",
|
||||
"(boolean) whether to normalise <br/>the incoming time-series to mean 0, standard deviation 1, or not (recommended)",
|
||||
};
|
||||
// KSG (Kraskov):
|
||||
klProperties = new String[] {
|
||||
// There is a NUM_DIMENSIONS_PROP_NAME property, but this will
|
||||
// only be relevant if we go to multivariate later.
|
||||
};
|
||||
klPropertiesFieldNames = new String[] {
|
||||
};
|
||||
klPropertyDescriptions = new String[] {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillOutAllCombinations(Vector<int[]> variableCombinations) {
|
||||
// All combinations here means all pairs
|
||||
for (int s = 0; s < dataColumns; s++) {
|
||||
variableCombinations.add(new int[] {s});
|
||||
}
|
||||
}
|
||||
|
||||
@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 variables:\n");
|
||||
javaCode.append(" for (int v = 0; v < " + dataColumns +
|
||||
"; v++) {\n");
|
||||
String javaPrefix = " ";
|
||||
javaCode.append(javaPrefix + "// For each variable:\n");
|
||||
// 2. Python code
|
||||
pythonCode.append("\n");
|
||||
pythonCode.append("# Compute for all variables:\n");
|
||||
pythonCode.append("for v in range(" + dataColumns + "):\n");
|
||||
String pythonPrefix = " ";
|
||||
pythonCode.append(pythonPrefix+ "# For each variable:\n");
|
||||
// 3. Matlab code
|
||||
matlabCode.append("\n");
|
||||
matlabCode.append("% Compute for all variables:\n");
|
||||
matlabCode.append("for v = 1:" + dataColumns + "\n");
|
||||
String matlabPrefix = "\t";
|
||||
matlabCode.append(matlabPrefix + "% For each variable:\n");
|
||||
|
||||
// Return the variables to index each column:
|
||||
return new String[] {"v"};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finaliseLoopsForAllCombos(StringBuffer javaCode,
|
||||
StringBuffer pythonCode, StringBuffer matlabCode) {
|
||||
|
||||
// 1. Java code
|
||||
javaCode.append(" }\n");
|
||||
// 2. Python code
|
||||
// Nothing to do
|
||||
// 3. Matlab code
|
||||
matlabCode.append("end\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatStringWithColumnNumbers(String formatStr, int[] columnNumbers) {
|
||||
// We format the variable number into the
|
||||
// return string here:
|
||||
return String.format(formatStr,
|
||||
columnNumbers[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean skipColumnCombo(int[] columnCombo) {
|
||||
// No reason to skip any columns here
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObservations(InfoMeasureCalculatorDiscrete calcDiscrete,
|
||||
InfoMeasureCalculatorContinuous calcContinuous,
|
||||
int[] columnCombo) throws Exception {
|
||||
|
||||
String selectedCalcType = (String)
|
||||
calcTypeComboBox.getSelectedItem();
|
||||
|
||||
int variableColumn = columnCombo[0];
|
||||
|
||||
// Set observations
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_DISCRETE)) {
|
||||
EntropyCalculatorDiscrete calc = (EntropyCalculatorDiscrete) calcDiscrete;
|
||||
calc.addObservations(
|
||||
MatrixUtils.selectColumn(dataDiscrete, variableColumn));
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_BINNED)) {
|
||||
EntropyCalculatorDiscrete calc = (EntropyCalculatorDiscrete) calcDiscrete;
|
||||
calc.addObservations(
|
||||
MatrixUtils.discretise(
|
||||
MatrixUtils.selectColumn(data, variableColumn),
|
||||
// Should be no parse error on the alphabet size by now
|
||||
Integer.parseInt(propertyValues.get(DISCRETE_PROPNAME_BASE))));
|
||||
} else {
|
||||
EntropyCalculator calc = (EntropyCalculator) calcContinuous;
|
||||
calc.setObservations(
|
||||
MatrixUtils.selectColumn(data, variableColumn));
|
||||
}
|
||||
}
|
||||
|
||||
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.equalsIgnoreCase(CALC_TYPE_KOZ_LEO)) {
|
||||
calcProperties.classSpecificPropertyNames = klProperties;
|
||||
calcProperties.classSpecificPropertiesFieldNames = klPropertiesFieldNames;
|
||||
calcProperties.classSpecificPropertyDescriptions = klPropertyDescriptions;
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our continuous calculator class
|
||||
*/
|
||||
@Override
|
||||
protected EntropyCalculator assignCalcObjectContinuous(String selectedCalcType) throws Exception {
|
||||
if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_GAUSSIAN)) {
|
||||
return new EntropyCalculatorGaussian();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KOZ_LEO)) {
|
||||
return new EntropyCalculatorMultiVariateKozachenko();
|
||||
} else if (selectedCalcType.equalsIgnoreCase(CALC_TYPE_KERNEL)) {
|
||||
return new EntropyCalculatorKernel();
|
||||
} else {
|
||||
throw new Exception("No recognised continuous calculator selected: " +
|
||||
selectedCalcType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to assign and initialise our discrete calculator class
|
||||
*/
|
||||
protected DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception {
|
||||
String basePropValueStr;
|
||||
try {
|
||||
basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot find a value for property " + DISCRETE_PROPNAME_BASE);
|
||||
return null;
|
||||
}
|
||||
int base;
|
||||
try {
|
||||
base = Integer.parseInt(basePropValueStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Cannot parse number for property " + DISCRETE_PROPNAME_BASE
|
||||
+ ": " + nfe.getMessage());
|
||||
resultsLabel.setText("Cannot parse number for property " +
|
||||
DISCRETE_PROPNAME_BASE + ": " + nfe.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DiscreteCalcAndArguments(
|
||||
new EntropyCalculatorDiscrete(base),
|
||||
base,
|
||||
Integer.toString(base));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
new AutoAnalyserEntropy();
|
||||
}
|
||||
}
|
||||
|
|
@ -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,15 +58,17 @@ public class AutoAnalyserMI extends AutoAnalyser
|
|||
*/
|
||||
protected void makeSpecificInitialisations() {
|
||||
|
||||
super.makeSpecificInitialisations();
|
||||
|
||||
// Set up the properties for MI:
|
||||
measureAcronym = "MI";
|
||||
appletTitle = "JIDT Mutual Information Auto-Analyser";
|
||||
|
||||
calcTypes = new String[] {
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_DISCRETE, CALC_TYPE_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KRASKOV_ALG1, CALC_TYPE_KRASKOV_ALG2,
|
||||
CALC_TYPE_KERNEL};
|
||||
unitsForEachCalc = new String[] {"bits", "nats", "nats", "nats", "bits"};
|
||||
unitsForEachCalc = new String[] {"bits", "bits", "nats", "nats", "nats", "bits"};
|
||||
|
||||
// Discrete:
|
||||
discreteClass = MutualInformationCalculatorDiscrete.class;
|
||||
|
|
@ -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)) {
|
||||
|
|
@ -179,9 +181,10 @@ public class AutoAnalyserMI extends AutoAnalyser
|
|||
* Method to assign and initialise our discrete calculator class
|
||||
*/
|
||||
protected DiscreteCalcAndArguments assignCalcObjectDiscrete() throws Exception {
|
||||
String timeDiffPropValueStr, basePropValueStr;
|
||||
int timeDiff, base;
|
||||
try {
|
||||
timeDiffPropValueStr = propertyValues.get(DISCRETE_PROPNAME_TIME_DIFF);
|
||||
String timeDiffPropValueStr = propertyValues.get(DISCRETE_PROPNAME_TIME_DIFF);
|
||||
timeDiff = Integer.parseInt(timeDiffPropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
|
|
@ -189,15 +192,14 @@ public class AutoAnalyserMI extends AutoAnalyser
|
|||
return null;
|
||||
}
|
||||
try {
|
||||
basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
String basePropValueStr = propertyValues.get(DISCRETE_PROPNAME_BASE);
|
||||
base = Integer.parseInt(basePropValueStr);
|
||||
} catch (Exception ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
ex.getMessage());
|
||||
resultsLabel.setText("Cannot find a value for property " + DISCRETE_PROPNAME_BASE);
|
||||
return null;
|
||||
}
|
||||
int timeDiff = Integer.parseInt(timeDiffPropValueStr);
|
||||
int base = Integer.parseInt(basePropValueStr);
|
||||
|
||||
return new DiscreteCalcAndArguments(
|
||||
new MutualInformationCalculatorDiscrete(base, timeDiff),
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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_BINNED, CALC_TYPE_GAUSSIAN,
|
||||
CALC_TYPE_KRASKOV, CALC_TYPE_KERNEL};
|
||||
unitsForEachCalc = new String[] {"bits", "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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -42,9 +42,13 @@ jcall(teCalc, "setProperty", Void, (JString, JString), "NORMALISE", "true"); # N
|
|||
jcall(teCalc, "initialise", Void, (jint, jdouble), 1, 0.5); # Use history length 1 (Schreiber k=1), kernel width of 0.5 normalised units
|
||||
jcall(teCalc, "setObservations", Void, (Array{jdouble,1}, Array{jdouble,1}),
|
||||
sourceArray, destArray);
|
||||
# For copied source, should give something close to expected value for correlated Gaussians:
|
||||
result = jcall(teCalc, "computeAverageLocalOfObservations", jdouble, ());
|
||||
@printf("TE result %.4f bits; expected to be close to %.4f bits for these correlated Gaussians but biased upwards\n", result, log(1/(1-covariance^2))/log(2));
|
||||
# For copied source, should give something close to expected value for correlated Gaussians:
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected = covariance / (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
@printf("TE result %.4f bits; expected to be close to %.4f bits for these correlated Gaussians but biased upwards\n", result, -0.5*log(1-corr_expected^2)/log(2));
|
||||
|
||||
jcall(teCalc, "initialise", Void, ()); # Initialise leaving the parameters the same
|
||||
jcall(teCalc, "setObservations", Void, (Array{jdouble,1}, Array{jdouble,1}),
|
||||
|
|
|
|||
|
|
@ -49,8 +49,12 @@ result = jcall(teCalc, "computeAverageLocalOfObservations", jdouble, ());
|
|||
# data is a set of random variables) - the result will be of the order
|
||||
# of what we expect, but not exactly equal to it; in fact, there will
|
||||
# be a large variance around it.
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected = covariance / (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
@printf("TE result %.4f nats; expected to be close to %.4f nats for these correlated Gaussians\n",
|
||||
result, log(1/(1-covariance^2)));
|
||||
result, -0.5*log(1-corr_expected^2));
|
||||
|
||||
# Perform calculation with uncorrelated source:
|
||||
jcall(teCalc, "initialise", Void, ()); # Initialise leaving the parameters the same
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function plotRawCa(states, rule, plotOptions, saveIt, saveFormat)
|
|||
plotOptions.plotRows = size(states, 1);
|
||||
end
|
||||
if not(isfield(plotOptions, 'plotCols'))
|
||||
plotOptions.plotCols = columns(states);
|
||||
plotOptions.plotCols = size(states, 2);
|
||||
end
|
||||
if not(isfield(plotOptions, 'plotStartRow'))
|
||||
plotOptions.plotStartRow = 1;
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@
|
|||
% - cells - number of cells in the CA
|
||||
% - steps - number of rows to execute the CA for (including the random initial row)
|
||||
% - debug - turn on various debug messages
|
||||
% - seedOrState - if a scalar, it is the state input for the random number generator (so one can repeat CA investigations for the same initial state).
|
||||
% - if a vector, it is the initial state for the CA (must be of length cells)
|
||||
% - seedOrState (optional) - if a scalar, it is the state input for the random number generator (so one can repeat CA investigations for the same initial state).
|
||||
% - if a vector, it is the initial state for the CA (must be of length cells)
|
||||
%
|
||||
% Outputs:
|
||||
% - caStates - a run, from random initial conditions, of a CA of the given parameters.
|
||||
|
|
@ -128,7 +128,7 @@ function [caStates, ruleTable, executedRules] = runCA(neighbourhood, base, rule,
|
|||
fprintf('%d', ruleTable(i));
|
||||
end
|
||||
fprintf('\n');
|
||||
else
|
||||
elseif (isscalar(rule))
|
||||
% The rule is specified as an integer
|
||||
if (rule > base .^ (base .^ neighbourhood) - 1)
|
||||
error(sprintf('Rule %d is not within the limits of this base %d and neighbourhood %d (max is %d)', ...
|
||||
|
|
@ -156,6 +156,12 @@ function [caStates, ruleTable, executedRules] = runCA(neighbourhood, base, rule,
|
|||
fprintf('%d', ruleTable(i));
|
||||
end
|
||||
fprintf('\n');
|
||||
else
|
||||
% We have a vector supplied for the rule, as a rule table already
|
||||
if (length(rule) ~= base .^ neighbourhood)
|
||||
error('Rule table vector (length %d) is not of expected length %d\n', length(rule), base .^ neighbourhood);
|
||||
end
|
||||
ruleTable = rule;
|
||||
end
|
||||
|
||||
caStates = zeros(steps, cells);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
%
|
||||
% Utility function to save raw CA data to a text file format.
|
||||
% Unfortunately MAtlab's -ascii format only writes full doubles, making much larger files than we need.
|
||||
% So I wrote this.
|
||||
|
||||
function saveCA(filename, caStates)
|
||||
|
||||
warning('off','MATLAB:Java:DuplicateClass');
|
||||
javaaddpath('../../../infodynamics.jar');
|
||||
addpath('..');
|
||||
|
||||
arrayWriter = javaObject('infodynamics.utils.ArrayFileWriter');
|
||||
arrayWriter.makeIntMatrixFile(octaveToJavaIntMatrix(caStates), filename);
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -34,10 +34,14 @@ teCalc=javaObject('infodynamics.measures.continuous.kernel.TransferEntropyCalcul
|
|||
teCalc.setProperty('NORMALISE', 'true'); % Normalise the individual variables
|
||||
teCalc.initialise(1, 0.5); % Use history length 1 (Schreiber k=1), kernel width of 0.5 normalised units
|
||||
teCalc.setObservations(sourceArray, destArray);
|
||||
% For copied source, should give something close to expected value for correlated Gaussians:
|
||||
result = teCalc.computeAverageLocalOfObservations();
|
||||
% For copied source, should give something close to expected value for correlated Gaussians.
|
||||
% Expected correlation is expected covariance / product of expected standard deviations:
|
||||
% (where square of destArray standard dev is sum of squares of std devs of
|
||||
% underlying distributions)
|
||||
corr_expected = covariance ./ (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
fprintf('TE result %.4f bits; expected to be close to %.4f bits for these correlated Gaussians but biased upwards\n', ...
|
||||
result, log(1/(1-covariance^2))/log(2));
|
||||
result, -0.5*log(1-corr_expected^2)/log(2));
|
||||
teCalc.initialise(); % Initialise leaving the parameters the same
|
||||
teCalc.setObservations(sourceArray2, destArray);
|
||||
% For random source, it should give something close to 0 bits
|
||||
|
|
|
|||
|
|
@ -40,8 +40,12 @@ result = teCalc.computeAverageLocalOfObservations();
|
|||
% data is a set of random variables) - the result will be of the order
|
||||
% of what we expect, but not exactly equal to it; in fact, there will
|
||||
% be a large variance around it.
|
||||
% Expected correlation is expected covariance / product of expected standard deviations:
|
||||
% (where square of destArray standard dev is sum of squares of std devs of
|
||||
% underlying distributions)
|
||||
corr_expected = covariance ./ (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
fprintf('TE result %.4f nats; expected to be close to %.4f nats for these correlated Gaussians\n', ...
|
||||
result, log(1/(1-covariance^2)));
|
||||
result, - 0.5 * log(1-corr_expected^2));
|
||||
% Perform calculation with uncorrelated source:
|
||||
teCalc.initialise(); % Initialise leaving the parameters the same
|
||||
teCalc.setObservations(sourceArray2, destArray);
|
||||
|
|
|
|||
|
|
@ -45,10 +45,14 @@ teCalc = teCalcClass()
|
|||
teCalc.setProperty("NORMALISE", "true") # Normalise the individual variables
|
||||
teCalc.initialise(1, 0.5) # Use history length 1 (Schreiber k=1), kernel width of 0.5 normalised units
|
||||
teCalc.setObservations(JArray(JDouble, 1)(sourceArray), JArray(JDouble, 1)(destArray))
|
||||
# For copied source, should give something close to 1 bit:
|
||||
result = teCalc.computeAverageLocalOfObservations()
|
||||
# For copied source, should give something close to 1 bit:
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected = covariance / (1 * math.sqrt(covariance**2 + (1-covariance)**2));
|
||||
print("TE result %.4f bits; expected to be close to %.4f bits for these correlated Gaussians but biased upwards" % \
|
||||
(result, math.log(1/(1-math.pow(covariance,2)))/math.log(2)))
|
||||
(result, -0.5*math.log(1-math.pow(corr_expected,2))/math.log(2)))
|
||||
teCalc.initialise() # Initialise leaving the parameters the same
|
||||
teCalc.setObservations(JArray(JDouble, 1)(sourceArray2), JArray(JDouble, 1)(destArray))
|
||||
# For random source, it should give something close to 0 bits
|
||||
|
|
|
|||
|
|
@ -52,8 +52,12 @@ result = teCalc.computeAverageLocalOfObservations()
|
|||
# data is a set of random variables) - the result will be of the order
|
||||
# of what we expect, but not exactly equal to it; in fact, there will
|
||||
# be a large variance around it.
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected = covariance / (1 * math.sqrt(covariance**2 + (1-covariance)**2));
|
||||
print("TE result %.4f nats; expected to be close to %.4f nats for these correlated Gaussians" % \
|
||||
(result, math.log(1/(1-math.pow(covariance,2)))))
|
||||
(result, -0.5 * math.log(1-corr_expected**2)))
|
||||
# Perform calculation with uncorrelated source:
|
||||
teCalc.initialise() # Initialise leaving the parameters the same
|
||||
teCalc.setObservations(JArray(JDouble, 1)(sourceArray2), JArray(JDouble, 1)(destArray))
|
||||
|
|
|
|||
|
|
@ -41,9 +41,13 @@ teCalc<-.jnew("infodynamics/measures/continuous/kernel/TransferEntropyCalculator
|
|||
.jcall(teCalc,"V","setProperty", "NORMALISE", "true") # Normalise the individual variables
|
||||
.jcall(teCalc,"V","initialise", 1L, 0.5) # Use history length 1 (Schreiber k=1), kernel width of 0.5 normalised units
|
||||
.jcall(teCalc,"V","setObservations", sourceArray, destArray)
|
||||
# For copied source, should give something close to expected value for correlated Gaussians:
|
||||
result <- .jcall(teCalc,"D","computeAverageLocalOfObservations")
|
||||
cat("TE result ", result, "bits; expected to be close to ", log(1/(1-covariance^2))/log(2), " bits for these correlated Gaussians but biased upwards\n")
|
||||
# For copied source, should give something close to expected value for correlated Gaussians.
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected <- covariance / (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
cat("TE result ", result, "bits; expected to be close to ", -0.5*log(1-corr_expected^2)/log(2), " bits for these correlated Gaussians but biased upwards\n")
|
||||
|
||||
.jcall(teCalc,"V","initialise") # Initialise leaving the parameters the same
|
||||
.jcall(teCalc,"V","setObservations", sourceArray2, destArray)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,11 @@ result <- .jcall(teCalc,"D","computeAverageLocalOfObservations")
|
|||
# data is a set of random variables) - the result will be of the order
|
||||
# of what we expect, but not exactly equal to it; in fact, there will
|
||||
# be a large variance around it.
|
||||
cat("TE result ", result, "nats; expected to be close to ", log(1/(1-covariance^2)), " nats for these correlated Gaussians\n")
|
||||
# Expected correlation is expected covariance / product of expected standard deviations:
|
||||
# (where square of destArray standard dev is sum of squares of std devs of
|
||||
# underlying distributions)
|
||||
corr_expected <- covariance / (1 * sqrt(covariance^2 + (1-covariance)^2));
|
||||
cat("TE result ", result, "nats; expected to be close to ", -0.5*log(1-corr_expected^2), " nats for these correlated Gaussians\n")
|
||||
|
||||
# Perform calculation with uncorrelated source:
|
||||
.jcall(teCalc,"V","initialise") # Initialise leaving the parameters the same
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package infodynamics.measures.continuous;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
|
||||
/**
|
||||
* Interface for implementations of
|
||||
|
|
@ -84,7 +84,8 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface ActiveInfoStorageCalculator {
|
||||
public interface ActiveInfoStorageCalculator extends
|
||||
InfoMeasureCalculatorContinuous, EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Property name for embedding length <code>k</code> of
|
||||
|
|
@ -100,13 +101,6 @@ public interface ActiveInfoStorageCalculator {
|
|||
*/
|
||||
public static final String TAU_PROP_NAME = "TAU";
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing (or default) values of parameters
|
||||
* Clears any PDFs of previously supplied observations.
|
||||
*
|
||||
*/
|
||||
public void initialise() throws Exception;
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with some parameters
|
||||
* supplied here, and existing (or default) values of other parameters
|
||||
|
|
@ -148,23 +142,9 @@ public interface ActiveInfoStorageCalculator {
|
|||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Get current property values for the calculator.
|
||||
*
|
||||
* <p>Valid property names, and what their
|
||||
* values should represent, are the same as those for
|
||||
* {@link #setProperty(String, String)}</p>
|
||||
*
|
||||
* <p>Unknown property values are responded to with a null return value.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @return current value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public String getProperty(String propertyName) throws Exception;
|
||||
|
||||
/**
|
||||
* Sets a single time-series from which to compute the PDF for the AIS.
|
||||
* Cannot be called in conjunction with other methods for setting/adding
|
||||
|
|
@ -247,13 +227,6 @@ public interface ActiveInfoStorageCalculator {
|
|||
*/
|
||||
public void addObservations(double[] observations, boolean[] valid) throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the AIS from the previously-supplied samples.
|
||||
*
|
||||
* @return the AIS estimate
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the local AIS values for each of the
|
||||
* previously-supplied samples.
|
||||
|
|
@ -294,104 +267,4 @@ public interface ActiveInfoStorageCalculator {
|
|||
*/
|
||||
public double[] computeLocalUsingPreviousObservations(double[] newObservations) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the AIS would look like,
|
||||
* under a null hypothesis that the previous <code>k</code> values of our
|
||||
* samples had no relation to the next value in the time-series.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for AIS
|
||||
* as a mutual information. Basically, the marginal PDFs
|
||||
* of the past <code>k</code> values, and that of the next value,
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of AIS under these conditions is generated.</p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int[][])})
|
||||
* creates <i>random</i> shufflings of the next values for the surrogate AIS
|
||||
* calculations.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of AIS scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the AIS would look like,
|
||||
* under a null hypothesis that the previous <code>k</code> values of our
|
||||
* samples had no relation to the next value in the time-series.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for AIS
|
||||
* as a mutual information. Basically, the marginal PDFs
|
||||
* of the past <code>k</code> values, and that of the next value,
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of AIS under these conditions is generated.</p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the next values
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length L being
|
||||
* the value returned by {@link #getNumObservations()},
|
||||
* containing a permutation of the values in 0..(L-1).
|
||||
* @return the distribution of AIS scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number L observations that were previously supplied.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int[][] newOrderings) throws Exception;
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Return the AIS last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise()} call.
|
||||
*
|
||||
* @return the last computed AIS value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[])}, {@link #addObservations(double[])}
|
||||
* etc.
|
||||
*
|
||||
* <p>Note that the number of samples is not equal to the length of time-series
|
||||
* supplied (since we need to accumulate the first
|
||||
* <code>(k-1)*tau + 1</code>
|
||||
* values of each time-series before taking a sample for AIS).
|
||||
* </p>
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
* @throws Exception
|
||||
*/
|
||||
public int getNumObservations() throws Exception;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
package infodynamics.measures.continuous;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
|
||||
/**
|
||||
* Interface for implementations of Active Information Storage estimators on
|
||||
* multivariate continuous time-series data. That is, it is applied to
|
||||
|
|
@ -103,4 +101,6 @@ public interface ActiveInfoStorageCalculatorMultiVariate {
|
|||
*/
|
||||
public void initialise(int dimensions, int k, int tau) throws Exception;
|
||||
|
||||
// TODO We seem to be missing a lot of functionality which should be defined here,
|
||||
// as implemented in the univariate measure. will have to go back and do this.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ package infodynamics.measures.continuous;
|
|||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package infodynamics.measures.continuous;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
|
||||
/**
|
||||
* A basic interface for calculators computing measures on a <i>channel</i> from a
|
||||
|
|
@ -76,45 +76,9 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @see ChannelCalculatorMultiVariate
|
||||
*
|
||||
*/
|
||||
public abstract interface ChannelCalculatorCommon {
|
||||
public abstract interface ChannelCalculatorCommon
|
||||
extends InfoMeasureCalculatorContinuous, EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing
|
||||
* (or default) values of parameters.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void initialise() throws Exception;
|
||||
|
||||
/**
|
||||
* Set properties for the calculator.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>No general properties are defined at the interface level here, i.e.
|
||||
* there are only properties defined by child interfaces and classes.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Get current property values for the calculator.
|
||||
*
|
||||
* <p>Valid property names, and what their
|
||||
* values should represent, are the same as those for
|
||||
* {@link #setProperty(String, String)}</p>
|
||||
*
|
||||
* <p>Unknown property values are responded to with a null return value.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @return current value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public String getProperty(String propertyName) throws Exception;
|
||||
|
||||
/**
|
||||
* Signal that we will add in the samples for computing the PDF
|
||||
* from several disjoint time-series or trials via calls to
|
||||
|
|
@ -140,13 +104,6 @@ public abstract interface ChannelCalculatorCommon {
|
|||
*/
|
||||
public boolean getAddedMoreThanOneObservationSet();
|
||||
|
||||
/**
|
||||
* Compute the channel measure from the previously-supplied samples.
|
||||
*
|
||||
* @return the estimate of the channel measure
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* <p>Computes the local values of the implemented channel measure,
|
||||
* for each valid observation in the previously supplied observations
|
||||
|
|
@ -166,96 +123,4 @@ public abstract interface ChannelCalculatorCommon {
|
|||
*/
|
||||
public double[] computeLocalOfPreviousObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the channel measure would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value.
|
||||
* (Precise null hypothesis varies between MI and TE).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int[][])})
|
||||
* creates <i>random</i> shufflings of the next values for the surrogate AIS
|
||||
* calculations.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of channel measure scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the channel measure would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value.
|
||||
* (Precise null hypothesis varies between MI and TE).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the source values
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length N (where
|
||||
* would be the value returned by {@link #getNumObservations()}),
|
||||
* containing a permutation of the values in 0..(N-1).
|
||||
* @return the distribution of channel measure scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number N samples that were previously supplied.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int[][] newOrderings) throws Exception;
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Return the channel measure last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise()} call.
|
||||
*
|
||||
* @return the last computed channel measure value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[])}, {@link #addObservations(double[])}
|
||||
* etc.
|
||||
*
|
||||
* <p>Note that the number of samples may not be equal to the length of time-series
|
||||
* supplied (e.g. for transfer entropy, where we need to accumulate
|
||||
* a number of samples for the past history of the destination).
|
||||
* </p>
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
*/
|
||||
public int getNumObservations() throws Exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
package infodynamics.measures.continuous;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
|
||||
/**
|
||||
* <p>Interface for implementations of the <b>conditional mutual information</b>,
|
||||
|
|
@ -75,7 +76,8 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @see "T. M. Cover and J. A. Thomas, 'Elements of Information
|
||||
Theory' (John Wiley & Sons, New York, 1991)."
|
||||
*/
|
||||
public interface ConditionalMutualInfoCalculatorMultiVariate {
|
||||
public interface ConditionalMutualInfoCalculatorMultiVariate
|
||||
extends InfoMeasureCalculatorContinuous, EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, clearing PDFs,
|
||||
|
|
@ -89,35 +91,6 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
*/
|
||||
public void initialise(int var1Dimensions, int var2Dimensions, int condDimentions) throws Exception;
|
||||
|
||||
/**
|
||||
* Set properties for the calculator.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>No general properties are defined at the interface level here, i.e.
|
||||
* there are only properties defined by child interfaces and classes.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Get current property values for the calculator.
|
||||
*
|
||||
* <p>Valid property names, and what their
|
||||
* values should represent, are the same as those for
|
||||
* {@link #setProperty(String, String)}</p>
|
||||
*
|
||||
* <p>Unknown property values are responded to with a null return value.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @return current value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public String getProperty(String propertyName) throws Exception;
|
||||
|
||||
/**
|
||||
* Sets a single series from which to compute the PDF.
|
||||
* Cannot be called in conjunction with
|
||||
|
|
@ -141,6 +114,29 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
public void setObservations(double[][] var1, double[][] var2,
|
||||
double[][] cond) throws Exception;
|
||||
|
||||
/**
|
||||
* Sets a single series from which to compute the PDF.
|
||||
* Cannot be called in conjunction with
|
||||
* {@link #startAddObservations()} / {@link #addObservations(double[][], double[][], double[][])} or
|
||||
* {@link #addObservations(double[][], double[][], double[][], int, int)} /
|
||||
* {@link #finaliseAddObservations()}.</p>
|
||||
*
|
||||
* <p>The supplied series may be (multivariate) time-series or
|
||||
* simply a set of separate observations without a time interpretation.
|
||||
*
|
||||
* <p>This method can only be used where dimensions of all
|
||||
* variables have been set to 1.</p>
|
||||
*
|
||||
* @param var1 univariate observations for variable 1
|
||||
* @param var2 univariate observations for variable 2
|
||||
* Length must match <code>var1</code>, and their indices must correspond.
|
||||
* @param cond univariate observations for the conditional
|
||||
* Length must match <code>var1</code>, and their indices must correspond.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void setObservations(double[] var1, double[] var2,
|
||||
double[] cond) throws Exception;
|
||||
|
||||
/**
|
||||
* Sets a single series from which to compute the PDF,
|
||||
* where all the various observations are valid.
|
||||
|
|
@ -237,6 +233,31 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
public void addObservations(double[][] var1, double[][] var2,
|
||||
double[][] cond) throws Exception;
|
||||
|
||||
/**
|
||||
* <p>Adds a new set of observations to update the PDFs with - is
|
||||
* intended to be called multiple times.
|
||||
* Must be called after {@link #startAddObservations()}; call
|
||||
* {@link #finaliseAddObservations()} once all observations have
|
||||
* been supplied.</p>
|
||||
*
|
||||
* <p>Note that the arrays must not be over-written by the user
|
||||
* until after finaliseAddObservations() has been called
|
||||
* (they are not copied by this method necessarily, but the method
|
||||
* may simply hold a pointer to them).</p>
|
||||
*
|
||||
* <p>This method can only be used where dimensions of all
|
||||
* variables have been set to 1.</p>
|
||||
*
|
||||
* @param var1 univariate observations for variable 1
|
||||
* @param var2 univariate observations for variable 2
|
||||
* Length must match <code>var1</code>, and their indices must correspond.
|
||||
* @param cond univariate observations for the conditional
|
||||
* Length must match <code>var1</code>, and their indices must correspond.
|
||||
* @throws Exception
|
||||
*/
|
||||
public void addObservations(double[] var1, double[] var2,
|
||||
double[] cond) throws Exception;
|
||||
|
||||
/**
|
||||
* <p>Adds a new sub-series of observations to update the PDFs with - is
|
||||
* intended to be called multiple times.
|
||||
|
|
@ -272,15 +293,6 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
*/
|
||||
public void finaliseAddObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the average conditional MI from the previously-supplied samples.
|
||||
*
|
||||
* @return the estimate of the conditional MI in either bits or nats
|
||||
* depending on the estimator
|
||||
* @throws Exception
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* <p>Computes the local values of the conditional mutual information,
|
||||
* for each valid observation in the previously supplied observations
|
||||
|
|
@ -303,66 +315,53 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
public double[] computeLocalOfPreviousObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the conditional MI would look like,
|
||||
* under a null hypothesis that the variable identified by
|
||||
* <code>variableToReorder</code> had no relation to the
|
||||
* other variable, given the conditional.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for
|
||||
* conditional MI. Basically, we shuffle the observations of
|
||||
* <code>variableToReorder</code> against the other variable
|
||||
* and the conditional.
|
||||
* This keeps the marginal and joint PDFs of the unshuffled variable
|
||||
* and conditional the same
|
||||
* but destroys any correlation between the named variable and the others.
|
||||
* <p><b>Note</b> -- in contrast to the generic computeSignificance() method
|
||||
* described in the documentation below, this method for a conditional MI calculator currently fixes the relationship
|
||||
* between variable 2 and the conditional, and shuffles
|
||||
* variable 1 with respect to these.
|
||||
* To shuffle variable 2 instead, call {@link #computeSignificance(int, int)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[][], double[][], double[][])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int, int[][])})
|
||||
* creates <i>random</i> shufflings of the next values for the surrogate AIS
|
||||
* calculations.</p>
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Defined as per {@link #computeSignificance(int)} except that this method allows
|
||||
* the user to specify which variable is shuffled (whilst the other has its
|
||||
* relationship with the conditional preserved).
|
||||
*
|
||||
* @param variableToReorder which variable to shuffle:
|
||||
* 1 for variable 1, 2 for variable 2.
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of channel measure scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @see {@link #computeSignificance(int)}
|
||||
* @throws Exception
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int variableToReorder,
|
||||
int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the conditional MI would look like,
|
||||
* under a null hypothesis that the variable identified by
|
||||
* <code>variableToReorder</code> had no relation to the
|
||||
* other variable, given the conditional.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for
|
||||
* conditional MI. Basically, we shuffle the observations of
|
||||
* <code>variableToReorder</code> against the other variable
|
||||
* and the conditional.
|
||||
* This keeps the marginal and joint PDFs of the unshuffled variable
|
||||
* and conditional the same
|
||||
* but destroys any correlation between the named variable and the others.
|
||||
* <p><b>Note</b> -- in contrast to the generic computeSignificance() method
|
||||
* described in the documentation below, this method for a conditional MI calculator currently fixes the relationship
|
||||
* between variable 2 and the conditional, and shuffles
|
||||
* variable 1 with respect to these.
|
||||
* To shuffle variable 2 instead, call {@link #computeSignificance(int, int[][])}.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[][], double[][], double[][])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int, int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int[][] newOrderings) throws Exception;
|
||||
|
||||
/**
|
||||
* Defined as per {@link #computeSignificance(int[][])} except that this method allows
|
||||
* the user to specify which variable is shuffled (whilst the other has its
|
||||
* relationship with the conditional preserved).
|
||||
*
|
||||
* @param variableToReorder which variable to shuffle:
|
||||
* 1 for variable 1, 2 for variable 2.
|
||||
|
|
@ -375,8 +374,7 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
* would be the value returned by {@link #getNumObservations()}),
|
||||
* containing a permutation of the values in 0..(N-1).
|
||||
* @return the distribution of channel measure scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @see {@link #computeSignificance(int[][])} except that this method all
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number N samples that were previously supplied.
|
||||
*/
|
||||
|
|
@ -425,32 +423,11 @@ public interface ConditionalMutualInfoCalculatorMultiVariate {
|
|||
throws Exception;
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Return the conditional MI last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise(int, int, int)} call.
|
||||
*
|
||||
* @return the last computed conditional MI value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[][], double[][], double[][])}, {@link #addObservations(double[][], double[][], double[][])}
|
||||
* etc.
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
* @throws Exception if the implementing class computes MI without
|
||||
* explicit observations (e.g. see
|
||||
* {@link infodynamics.measures.continuous.gaussian.ConditionalMutualInfoCalculatorMultiVariateGaussian})
|
||||
*/
|
||||
@Override
|
||||
public int getNumObservations() throws Exception;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -165,7 +165,10 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements
|
|||
*/
|
||||
protected double[] condStds = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
initialise(dimensionsVar1, dimensionsVar2, dimensionsCond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise(int var1Dimensions, int var2Dimensions, int condDimensions) {
|
||||
|
|
@ -234,6 +237,15 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements
|
|||
addedMoreThanOneObservationSet = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObservations(double[] var1, double[] var2,
|
||||
double[] cond) throws Exception {
|
||||
startAddObservations();
|
||||
addObservations(var1, var2, cond);
|
||||
finaliseAddObservations();
|
||||
addedMoreThanOneObservationSet = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAddObservations() {
|
||||
vectorOfVar1Observations = new Vector<double[][]>();
|
||||
|
|
@ -273,6 +285,23 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addObservations(double[] var1, double[] var2,
|
||||
double[] cond) throws Exception {
|
||||
if ((dimensionsVar1 != 1) || (dimensionsVar2 != 1) ||
|
||||
(dimensionsCond != 1)) {
|
||||
throw new Exception("The number of dimensions for each variable (having been initialised to " +
|
||||
dimensionsVar1 + ", " + dimensionsVar2 + " & " +
|
||||
dimensionsCond + ") can only be 1 when " +
|
||||
"the univariate addObservations(double[],double[],double[]) and " +
|
||||
"setObservations(double[],double[],double[]) methods are called");
|
||||
}
|
||||
addObservations(MatrixUtils.reshape(var1, var1.length, 1),
|
||||
MatrixUtils.reshape(var2, var2.length, 1),
|
||||
MatrixUtils.reshape(cond, cond.length, 1));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addObservations(double[][] var1, double[][] var2,
|
||||
double[][] cond,
|
||||
|
|
@ -390,6 +419,12 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements
|
|||
vectorOfCondObservations = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int numPermutationsToCheck) throws Exception {
|
||||
return computeSignificance(1, numPermutationsToCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int variableToReorder, int numPermutationsToCheck) throws Exception {
|
||||
|
|
@ -403,6 +438,12 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements
|
|||
return computeSignificance(variableToReorder, newOrderings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int[][] newOrderings) throws Exception {
|
||||
return computeSignificance(1, newOrderings);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>As described in
|
||||
* {@link ConditionalMutualInfoCalculatorMultiVariate#computeSignificance(int, int[][])}
|
||||
|
|
|
|||
|
|
@ -98,17 +98,17 @@ public class ConditionalTransferEntropyCalculatorViaCondMutualInfo implements
|
|||
* Array of embedding lengths for each conditional variable.
|
||||
* Can be an empty array or null if there are no conditional variables.
|
||||
*/
|
||||
protected int[] condEmbedDims = null;
|
||||
protected int[] condEmbedDims = new int[] {1};
|
||||
/**
|
||||
* Array of embedding delays for the conditional variables.
|
||||
* Must be same length as condEmbedDims array.
|
||||
*/
|
||||
protected int[] cond_taus = null;
|
||||
protected int[] cond_taus = new int[] {1};
|
||||
/**
|
||||
* Array of time lags between last element of each conditional variable
|
||||
* and destination next value.
|
||||
*/
|
||||
protected int[] condDelays = null;
|
||||
protected int[] condDelays = new int[] {1};
|
||||
|
||||
/**
|
||||
* Time index of the last point in the destination embedding of the first
|
||||
|
|
@ -380,11 +380,23 @@ public class ConditionalTransferEntropyCalculatorViaCondMutualInfo implements
|
|||
} else if (propertyName.equalsIgnoreCase(DELAY_PROP_NAME)) {
|
||||
return Integer.toString(delay);
|
||||
} else if (propertyName.equalsIgnoreCase(COND_EMBED_LENGTHS_PROP_NAME)) {
|
||||
return MatrixUtils.arrayToString(condEmbedDims);
|
||||
if (condEmbedDims == null) {
|
||||
return "";
|
||||
} else {
|
||||
return MatrixUtils.arrayToString(condEmbedDims);
|
||||
}
|
||||
} else if (propertyName.equalsIgnoreCase(COND_EMBED_DELAYS_PROP_NAME)) {
|
||||
return MatrixUtils.arrayToString(cond_taus);
|
||||
if (cond_taus == null) {
|
||||
return "";
|
||||
} else {
|
||||
return MatrixUtils.arrayToString(cond_taus);
|
||||
}
|
||||
} else if (propertyName.equalsIgnoreCase(COND_DELAYS_PROP_NAME)) {
|
||||
return MatrixUtils.arrayToString(condDelays);
|
||||
if (condDelays == null) {
|
||||
return "";
|
||||
} else {
|
||||
return MatrixUtils.arrayToString(condDelays);
|
||||
}
|
||||
} else {
|
||||
// No property matches for this class, assume it is for the underlying
|
||||
// conditional MI calculator
|
||||
|
|
|
|||
|
|
@ -46,29 +46,7 @@ Theory' (John Wiley & Sons, New York, 1991).</li>
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface EntropyCalculator {
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing (or default) values of calculator-specific parameters
|
||||
* Clears any PDFs of previously supplied observations.
|
||||
*/
|
||||
public void initialise() throws Exception;
|
||||
|
||||
/**
|
||||
* Set properties for the underlying calculator implementation.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>No general properties are defined at the interface level, i.e.
|
||||
* there are only calculator-specific properties.</p>
|
||||
*
|
||||
* <p>Unknown property values are ignored.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
public interface EntropyCalculator extends InfoMeasureCalculatorContinuous {
|
||||
|
||||
// TODO Add addObservations() methods for entropy calculator
|
||||
|
||||
|
|
@ -81,17 +59,4 @@ public interface EntropyCalculator {
|
|||
*/
|
||||
public void setObservations(double observations[]);
|
||||
|
||||
/**
|
||||
* Compute the entropy from the previously-supplied samples.
|
||||
*
|
||||
* @return the entropy estimate
|
||||
*/
|
||||
public double computeAverageLocalOfObservations();
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,8 +54,36 @@ Theory' (John Wiley & Sons, New York, 1991).</li>
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface EntropyCalculatorMultiVariate {
|
||||
public interface EntropyCalculatorMultiVariate
|
||||
extends InfoMeasureCalculatorContinuous {
|
||||
|
||||
/**
|
||||
* Property name for the number of dimensions
|
||||
*/
|
||||
public static final String NUM_DIMENSIONS_PROP_NAME = "NUM_DIMENSIONS";
|
||||
|
||||
/**
|
||||
* Set properties for the underlying calculator implementation.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>Property names defined at the interface level, and what their
|
||||
* values should represent, include:</p>
|
||||
* <ul>
|
||||
* <li>{@link #NUM_DIMENSIONS_PROP_NAME} -- number of dimensions in the joint
|
||||
* variable that we are computing the entropy of.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Unknown property values are ignored.</p>
|
||||
*
|
||||
* <p>Note that implementing classes may defined additional properties.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing (or default) values
|
||||
* of calculator-specific parameters.
|
||||
|
|
@ -65,20 +93,6 @@ public interface EntropyCalculatorMultiVariate {
|
|||
*/
|
||||
public void initialise(int dimensions);
|
||||
|
||||
/**
|
||||
* Set properties for the underlying calculator implementation.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>No general properties are defined at the interface level, i.e.
|
||||
* there are only calculator-specific properties.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Set the observations for which to compute the PDFs for the entropy
|
||||
* Should only be called once, the last call contains the
|
||||
|
|
@ -93,13 +107,6 @@ public interface EntropyCalculatorMultiVariate {
|
|||
*/
|
||||
public void setObservations(double observations[][]) throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the entropy from the previously-supplied samples.
|
||||
*
|
||||
* @return the entropy estimate, in bits or nats depending on the estimator.
|
||||
*/
|
||||
public double computeAverageLocalOfObservations();
|
||||
|
||||
/**
|
||||
* Compute the local entropy values for each of the
|
||||
* supplied samples in <code>newObservations</code>.
|
||||
|
|
@ -127,29 +134,4 @@ public interface EntropyCalculatorMultiVariate {
|
|||
*/
|
||||
public double[] computeLocalOfPreviousObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Return the entropy last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise(int)} call.
|
||||
*
|
||||
* @return the last computed entropy value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[][])}.
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
* @throws Exception
|
||||
*/
|
||||
public int getNumObservations() throws Exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano
|
||||
*
|
||||
* 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.measures.continuous;
|
||||
|
||||
/**
|
||||
* <p>Base interface for our information-theoretic calculators
|
||||
* on continuous (double[]) data,
|
||||
* providing common functionality
|
||||
* for user-level measure classes.</p>
|
||||
*
|
||||
* <p>
|
||||
* Usage of the child classes implementing this interface is intended to follow this paradigm:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>Construct the calculator;</li>
|
||||
* <li>set properties via {@link #setProperty(String, String)};</li>
|
||||
* <li>Initialise the calculator using {@link #initialise()} or
|
||||
* other initialise methods defined by child classes;</li>
|
||||
* <li>Provide the observations/samples for the calculator
|
||||
* to set up the PDFs, using one or more calls to
|
||||
* sets of "addObservations" methods defined by child classes, then</li>
|
||||
* <li>Compute the required quantities, being one or more of:
|
||||
* <ul>
|
||||
* <li>the average measure: {@link #computeAverageLocalOfObservations()};</li>
|
||||
* <li>or other quantities as defined by child classes.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>
|
||||
* Return to step 3 to re-use the calculator on a new data set.
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface InfoMeasureCalculatorContinuous {
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing (or default) values of parameters.
|
||||
* Clears any PDFs of previously supplied observations.
|
||||
*/
|
||||
public void initialise() throws Exception;
|
||||
|
||||
/**
|
||||
* Set properties for the underlying calculator implementation.
|
||||
* New property values are not guaranteed to take effect until the next call
|
||||
* to an initialise method.
|
||||
*
|
||||
* <p>Property names are defined by the implementing class
|
||||
* (generally in their documentation).
|
||||
* Unknown property values are ignored.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
* Get current property values for the calculator.
|
||||
*
|
||||
* <p>Valid property names, and what their
|
||||
* values should represent, are the same as those for
|
||||
* {@link #setProperty(String, String)}</p>
|
||||
*
|
||||
* <p>Unknown property values are responded to with a null return value.</p>
|
||||
*
|
||||
* @param propertyName name of the property
|
||||
* @return current value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
public String getProperty(String propertyName) throws Exception;
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* "setObservations", "addObservations" etc.
|
||||
*
|
||||
* <p>Note that the number of samples may not be equal to the length of time-series
|
||||
* supplied (e.g. for transfer entropy, where we need to accumulate
|
||||
* a number of samples for the past history of the destination).
|
||||
* </p>
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
*/
|
||||
public int getNumObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the average value of the measure
|
||||
* from the previously-supplied samples.
|
||||
*
|
||||
* @return the estimate of the measure
|
||||
* (in bits or nats, depending on the estimator)
|
||||
*/
|
||||
public abstract double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Return the measure last calculated in a call to
|
||||
* {@link #computeAverageLocalOfObservations()}
|
||||
* or related methods after the previous
|
||||
* {@link #initialise()} call.
|
||||
*
|
||||
* @return the last computed measure value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
}
|
||||
|
|
@ -76,7 +76,8 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface MultiInfoCalculator {
|
||||
public interface MultiInfoCalculator
|
||||
extends InfoMeasureCalculatorContinuous {
|
||||
|
||||
/**
|
||||
* Property name for whether to normalise incoming values to mean 0,
|
||||
|
|
@ -120,6 +121,7 @@ public interface MultiInfoCalculator {
|
|||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
|
|
@ -189,14 +191,6 @@ public interface MultiInfoCalculator {
|
|||
*/
|
||||
public void finaliseAddObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the multi-information from the previously-supplied samples.
|
||||
*
|
||||
* @return the estimate of the multi-information
|
||||
* @throws Exception
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* <p>Computes the local values of the multi-information,
|
||||
* for each valid observation in the previously supplied observations
|
||||
|
|
@ -337,20 +331,4 @@ public interface MultiInfoCalculator {
|
|||
* is not equal to the number N samples that were previously supplied.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][][] newOrderings) throws Exception;
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Return the multi-information last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise(int)} call.
|
||||
*
|
||||
* @return the last computed multi-information value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public abstract class MultiInfoCalculatorCommon implements MultiInfoCalculator {
|
|||
/**
|
||||
* Number of joint variables to consider
|
||||
*/
|
||||
protected int dimensions = 0;
|
||||
protected int dimensions = 1;
|
||||
/**
|
||||
* Number of samples supplied
|
||||
*/
|
||||
|
|
@ -81,6 +81,11 @@ public abstract class MultiInfoCalculatorCommon implements MultiInfoCalculator {
|
|||
private double samplingFactor = 0.1;
|
||||
private Random rand;
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
initialise(dimensions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise(int dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
|
|
@ -127,6 +132,17 @@ public abstract class MultiInfoCalculatorCommon implements MultiInfoCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(PROP_NORMALISE)) {
|
||||
return Boolean.toString(normalise);
|
||||
} else if (propertyName.equalsIgnoreCase(SAMPLING_FACTOR_PROP_NAME)) {
|
||||
return Double.toString(samplingFactor);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObservations(double[][] observations) throws Exception {
|
||||
startAddObservations();
|
||||
|
|
@ -258,6 +274,11 @@ public abstract class MultiInfoCalculatorCommon implements MultiInfoCalculator {
|
|||
return miSurrogateCalculator.computeAverageLocalOfObservations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumObservations() throws Exception {
|
||||
return totalObservations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ package infodynamics.measures.continuous;
|
|||
* @see "T. M. Cover and J. A. Thomas, 'Elements of Information
|
||||
Theory' (John Wiley & Sons, New York, 1991)."
|
||||
*/
|
||||
public interface MutualInfoCalculatorMultiVariate extends ChannelCalculatorMultiVariate {
|
||||
public interface MutualInfoCalculatorMultiVariate
|
||||
extends ChannelCalculator, ChannelCalculatorMultiVariate {
|
||||
|
||||
/**
|
||||
* Property name for time difference between source and destination (0 by default,
|
||||
|
|
|
|||
|
|
@ -259,6 +259,21 @@ public abstract class MutualInfoMultiVariateCommon implements
|
|||
vectorOfDestinationObservations.add(destToAdd);
|
||||
}
|
||||
|
||||
public void addObservations(double[] source, double[] destination,
|
||||
int startTime, int numTimeSteps) throws Exception {
|
||||
|
||||
if ((dimensionsDest != 1) || (dimensionsSource != 1)) {
|
||||
throw new Exception("The number of source and dest dimensions (having been initialised to " +
|
||||
dimensionsSource + " and " + dimensionsDest + ") can only be 1 when " +
|
||||
"the univariate addObservations(double[],double[]) and " +
|
||||
"setObservations(double[],double[]) methods are called");
|
||||
}
|
||||
addObservations(MatrixUtils.reshape(source, source.length, 1),
|
||||
MatrixUtils.reshape(destination, destination.length, 1),
|
||||
startTime, numTimeSteps);
|
||||
}
|
||||
|
||||
|
||||
public void setObservations(double[][] source, double[][] destination,
|
||||
boolean[] sourceValid, boolean[] destValid) throws Exception {
|
||||
|
||||
|
|
@ -274,6 +289,20 @@ public abstract class MutualInfoMultiVariateCommon implements
|
|||
finaliseAddObservations();
|
||||
}
|
||||
|
||||
public void setObservations(double[] source, double[] destination,
|
||||
boolean[] sourceValid, boolean[] destValid) throws Exception {
|
||||
|
||||
if ((dimensionsDest != 1) || (dimensionsSource != 1)) {
|
||||
throw new Exception("The number of source and dest dimensions (having been initialised to " +
|
||||
dimensionsSource + " and " + dimensionsDest + ") can only be 1 when " +
|
||||
"the univariate addObservations(double[],double[]) and " +
|
||||
"setObservations(double[],double[]) methods are called");
|
||||
}
|
||||
setObservations(MatrixUtils.reshape(source, source.length, 1),
|
||||
MatrixUtils.reshape(destination, destination.length, 1),
|
||||
sourceValid, destValid);
|
||||
}
|
||||
|
||||
public void setObservations(double[][] source, double[][] destination,
|
||||
boolean[][] sourceValid, boolean[][] destValid) throws Exception {
|
||||
|
||||
|
|
@ -485,6 +514,20 @@ public abstract class MutualInfoMultiVariateCommon implements
|
|||
return miSurrogateCalculator.computeAverageLocalOfObservations();
|
||||
}
|
||||
|
||||
public double[] computeLocalUsingPreviousObservations(
|
||||
double[] newSourceObservations, double[] newDestObservations)
|
||||
throws Exception {
|
||||
if ((dimensionsDest != 1) || (dimensionsSource != 1)) {
|
||||
throw new Exception("The number of source and dest dimensions (having been initialised to " +
|
||||
dimensionsSource + " and " + dimensionsDest + ") can only be 1 when " +
|
||||
"the univariate addObservations(double[],double[]) and " +
|
||||
"setObservations(double[],double[]) methods are called");
|
||||
}
|
||||
return computeLocalUsingPreviousObservations(
|
||||
MatrixUtils.reshape(newSourceObservations, newSourceObservations.length, 1),
|
||||
MatrixUtils.reshape(newDestObservations, newDestObservations.length, 1));
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package infodynamics.measures.continuous;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
|
||||
/**
|
||||
* Interface for implementations of
|
||||
|
|
@ -94,7 +94,8 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface PredictiveInfoCalculator {
|
||||
public interface PredictiveInfoCalculator
|
||||
extends InfoMeasureCalculatorContinuous, EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Property name for embedding length <code>k</code> of
|
||||
|
|
@ -117,13 +118,6 @@ public interface PredictiveInfoCalculator {
|
|||
*/
|
||||
public static final String TAU_PROP_NAME = "TAU";
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing (or default) values of parameters
|
||||
* Clears any PDFs of previously supplied observations.
|
||||
*
|
||||
*/
|
||||
public void initialise() throws Exception;
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with some parameters
|
||||
* supplied here, and existing (or default) values of other parameters
|
||||
|
|
@ -166,6 +160,7 @@ public interface PredictiveInfoCalculator {
|
|||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception;
|
||||
|
||||
/**
|
||||
|
|
@ -234,13 +229,6 @@ public interface PredictiveInfoCalculator {
|
|||
public void setObservations(double[] observations,
|
||||
boolean[] valid) throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the PI from the previously-supplied samples.
|
||||
*
|
||||
* @return the PI estimate
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception;
|
||||
|
||||
/**
|
||||
* Compute the local PI values for each of the
|
||||
* previously-supplied samples.
|
||||
|
|
@ -282,103 +270,4 @@ public interface PredictiveInfoCalculator {
|
|||
*/
|
||||
public double[] computeLocalUsingPreviousObservations(double[] newObservations) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the PI would look like,
|
||||
* under a null hypothesis that the previous <code>k</code> values of our
|
||||
* samples had no relation to the next <code>k</code> values in the time-series.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for PI
|
||||
* as a mutual information. Basically, the marginal PDFs
|
||||
* of the past <code>k</code> values, and that of the next <code>k</code> values,
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of PI under these conditions is generated.</p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int[][])})
|
||||
* creates <i>random</i> shufflings of the next vectors for the surrogate PI
|
||||
* calculations.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of PI scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the PI would look like,
|
||||
* under a null hypothesis that the previous <code>k</code> values of our
|
||||
* samples had no relation to the next <code>k</code> values in the time-series.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for PI
|
||||
* as a mutual information. Basically, the marginal PDFs
|
||||
* of the past <code>k</code> values, and that of the next <code>k</code> values,
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of PI under these conditions is generated.</p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the next <code>k</code> values vectors
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length L being
|
||||
* the value returned by {@link #getNumObservations()},
|
||||
* containing a permutation of the values in 0..(L-1).
|
||||
* @return the distribution of PI scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number L observations that were previously supplied.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
int[][] newOrderings) throws Exception;
|
||||
|
||||
/**
|
||||
* Set or clear debug mode for extra debug printing to stdout
|
||||
*
|
||||
* @param debug new setting for debug mode (on/off)
|
||||
*/
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Return the PI last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise()} call.
|
||||
*
|
||||
* @return the last computed PI value
|
||||
*/
|
||||
public double getLastAverage();
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[])}, {@link #addObservations(double[])}
|
||||
* etc.
|
||||
*
|
||||
* <p>Note that the number of samples is not equal to the length of time-series
|
||||
* supplied (since we need to accumulate the first and last
|
||||
* <code>(k-1)*tau + 1</code>
|
||||
* values of each time-series).
|
||||
* </p>
|
||||
*
|
||||
* @return the number of samples to be used for the PDFs
|
||||
* @throws Exception
|
||||
*/
|
||||
public int getNumObservations() throws Exception;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,22 @@ public class PredictiveInfoCalculatorViaMutualInfo implements
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(K_PROP_NAME) ||
|
||||
propertyName.equalsIgnoreCase(K_EMBEDDING_PROP_NAME)) {
|
||||
return Integer.toString(k);
|
||||
} else if (propertyName.equalsIgnoreCase(TAU_PROP_NAME)) {
|
||||
return Integer.toString(tau);
|
||||
} else {
|
||||
// No property was set on this class, assume it is for the underlying
|
||||
// MI calculator, even if it is for
|
||||
// MutualInfoCalculatorMultiVariate.PROP_TIME_DIFF which
|
||||
// is not a valid property for the PI calculator:
|
||||
return miCalc.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#setObservations(double[])
|
||||
*/
|
||||
|
|
@ -372,9 +388,6 @@ public class PredictiveInfoCalculatorViaMutualInfo implements
|
|||
return startAndEndTimePairs;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#computeAverageLocalOfObservations()
|
||||
*/
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() throws Exception {
|
||||
return miCalc.computeAverageLocalOfObservations();
|
||||
|
|
@ -417,7 +430,7 @@ public class PredictiveInfoCalculatorViaMutualInfo implements
|
|||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#computeSignificance(int)
|
||||
* @see infodynamics.utils.EmpiricalNullDistributionComputer#computeSignificance(int)
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
|
|
@ -426,7 +439,7 @@ public class PredictiveInfoCalculatorViaMutualInfo implements
|
|||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#computeSignificance(int[][])
|
||||
* @see infodynamics.utils.EmpiricalNullDistributionComputer#computeSignificance(int[][])
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(
|
||||
|
|
@ -434,26 +447,17 @@ public class PredictiveInfoCalculatorViaMutualInfo implements
|
|||
return miCalc.computeSignificance(newOrderings);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#setDebug(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
miCalc.setDebug(debug);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#getLastAverage()
|
||||
*/
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return miCalc.getLastAverage();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.PredictiveInfoCalculator#getNumObservations()
|
||||
*/
|
||||
@Override
|
||||
public int getNumObservations() throws Exception {
|
||||
return miCalc.getNumObservations();
|
||||
|
|
|
|||
|
|
@ -528,7 +528,8 @@ public class ConditionalMutualInfoCalculatorMultiVariateGaussian
|
|||
// return new ChiSquareMeasurementDistribution(2.0*((double)totalObservations)*lastAverage,
|
||||
// dimensionsVar1 * dimensionsVar2);
|
||||
// Taking the subsets into account:
|
||||
return new ChiSquareMeasurementDistribution(2.0*((double)totalObservations)*lastAverage,
|
||||
return new ChiSquareMeasurementDistribution(lastAverage,
|
||||
totalObservations,
|
||||
var1IndicesInCovariance.length * var2IndicesInCovariance.length);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,16 @@ public class EntropyCalculatorGaussian implements EntropyCalculator {
|
|||
*/
|
||||
protected boolean debug;
|
||||
|
||||
/**
|
||||
* Total number of observations supplied.
|
||||
*/
|
||||
protected int totalObservations;
|
||||
|
||||
/**
|
||||
* Store the last computed average Entropy
|
||||
*/
|
||||
protected double lastAverage;
|
||||
|
||||
/**
|
||||
* Construct an instance
|
||||
*/
|
||||
|
|
@ -66,6 +76,7 @@ public class EntropyCalculatorGaussian implements EntropyCalculator {
|
|||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
|
@ -73,6 +84,7 @@ public class EntropyCalculatorGaussian implements EntropyCalculator {
|
|||
public void setObservations(double[] observations) {
|
||||
variance = MatrixUtils.stdDev(observations);
|
||||
variance *= variance;
|
||||
totalObservations = observations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,10 +112,13 @@ public class EntropyCalculatorGaussian implements EntropyCalculator {
|
|||
* @return the entropy of the previously provided observations or from the supplied
|
||||
* covariance matrix. Entropy returned in <b>nats</b>, not bits!
|
||||
*/
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() {
|
||||
return 0.5 * Math.log(2.0*Math.PI*Math.E*variance);
|
||||
lastAverage = 0.5 * Math.log(2.0*Math.PI*Math.E*variance);
|
||||
return lastAverage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
|
@ -111,9 +126,29 @@ public class EntropyCalculatorGaussian implements EntropyCalculator {
|
|||
/**
|
||||
* No properties are defined here, so this method will have no effect.
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue)
|
||||
throws Exception {
|
||||
// No properties to set here
|
||||
}
|
||||
|
||||
/**
|
||||
* No properties are defined here, so this method will always return null.
|
||||
*/
|
||||
@Override
|
||||
public String getProperty(String propertyName)
|
||||
throws Exception {
|
||||
// No properties to return here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumObservations() throws Exception {
|
||||
return totalObservations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return lastAverage;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,22 +72,22 @@ public class EntropyCalculatorMultiVariateGaussian
|
|||
/**
|
||||
* Number of dimensions for our multivariate data
|
||||
*/
|
||||
protected int dimensions;
|
||||
protected int dimensions = 1;
|
||||
|
||||
/**
|
||||
* Determinant of the covariance matrix; stored to save computation time
|
||||
*/
|
||||
protected double detCovariance;
|
||||
protected double detCovariance = 0.0;
|
||||
|
||||
/**
|
||||
* Last average entropy we computed
|
||||
*/
|
||||
protected double lastAverage;
|
||||
protected double lastAverage = 0;
|
||||
|
||||
/**
|
||||
* Whether we are in debug mode
|
||||
*/
|
||||
protected boolean debug;
|
||||
protected boolean debug = false;
|
||||
|
||||
/**
|
||||
* Construct an instance
|
||||
|
|
@ -96,12 +96,18 @@ public class EntropyCalculatorMultiVariateGaussian
|
|||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() throws Exception {
|
||||
initialise(dimensions);
|
||||
}
|
||||
|
||||
public void initialise(int dimensions) {
|
||||
means = null;
|
||||
L = null;
|
||||
observations = null;
|
||||
this.dimensions = dimensions;
|
||||
detCovariance = 0;
|
||||
lastAverage = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,6 +115,7 @@ public class EntropyCalculatorMultiVariateGaussian
|
|||
* dimensions, or covariance matrix is not positive definite (reflecting
|
||||
* redundant variables in the observations)
|
||||
*/
|
||||
@Override
|
||||
public void setObservations(double[][] observations) throws Exception {
|
||||
// Check that the observations was of the correct number of dimensions:
|
||||
if (observations[0].length != dimensions) {
|
||||
|
|
@ -187,6 +194,7 @@ public class EntropyCalculatorMultiVariateGaussian
|
|||
* @return the joint entropy of the previously provided observations or from the
|
||||
* supplied covariance matrix. Returned in nats (NOT bits).
|
||||
*/
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() {
|
||||
// Simple way:
|
||||
// detCovariance = MatrixUtils.determinantSymmPosDefMatrix(covariance);
|
||||
|
|
@ -197,18 +205,38 @@ public class EntropyCalculatorMultiVariateGaussian
|
|||
return lastAverage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* No properties are defined here, so this method will have no effect.
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue)
|
||||
throws Exception {
|
||||
// No properties to set here
|
||||
boolean propertySet = true;
|
||||
if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
dimensions = Integer.parseInt(propertyValue);
|
||||
} else {
|
||||
// No property was set
|
||||
propertySet = false;
|
||||
}
|
||||
if (debug && propertySet) {
|
||||
System.out.println(this.getClass().getSimpleName() + ": Set property " + propertyName +
|
||||
" to " + propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
return Integer.toString(dimensions);
|
||||
} else {
|
||||
// No property was set, and no superclass to call:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return lastAverage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package infodynamics.measures.continuous.gaussian;
|
|||
|
||||
import infodynamics.measures.continuous.MultiInfoCalculatorCommon;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
import infodynamics.utils.MathsUtils;
|
||||
|
||||
/**
|
||||
* <p>Computes the differential multi-information of a given multivariate
|
||||
|
|
|
|||
|
|
@ -342,7 +342,8 @@ public class MutualInfoCalculatorMultiVariateGaussian
|
|||
* @throws Exception
|
||||
*/
|
||||
public ChiSquareMeasurementDistribution computeSignificance() {
|
||||
return new ChiSquareMeasurementDistribution(2*totalObservations*lastAverage,
|
||||
return new ChiSquareMeasurementDistribution(lastAverage,
|
||||
totalObservations,
|
||||
dimensionsSource * dimensionsDest);
|
||||
}
|
||||
|
||||
|
|
@ -517,5 +518,4 @@ public class MutualInfoCalculatorMultiVariateGaussian
|
|||
|
||||
return localValues;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
* Number of observations supplied
|
||||
*/
|
||||
protected int totalObservations = 0;
|
||||
/**
|
||||
* Last computed average
|
||||
*/
|
||||
private double lastAverage;
|
||||
/**
|
||||
* Whether we're in debug mode
|
||||
*/
|
||||
|
|
@ -93,8 +97,10 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
svke = new KernelEstimatorUniVariate();
|
||||
svke.setDebug(debug);
|
||||
svke.setNormalise(normalise);
|
||||
lastAverage = 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
initialise(kernelWidth);
|
||||
}
|
||||
|
|
@ -110,15 +116,18 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
*/
|
||||
public void initialise(double kernelWidth) {
|
||||
this.kernelWidth = kernelWidth;
|
||||
lastAverage = 0.0;
|
||||
svke.initialise(kernelWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObservations(double observations[]) {
|
||||
this.observations = observations;
|
||||
svke.setObservations(observations);
|
||||
totalObservations = observations.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() {
|
||||
double entropy = 0.0;
|
||||
for (int t = 0; t < observations.length; t++) {
|
||||
|
|
@ -131,9 +140,11 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
(entropy/Math.log(2.0)));
|
||||
}
|
||||
}
|
||||
return entropy / (double) totalObservations / Math.log(2.0);
|
||||
lastAverage = entropy / (double) totalObservations / Math.log(2.0);
|
||||
return lastAverage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
if (svke != null) {
|
||||
|
|
@ -163,6 +174,7 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception {
|
||||
boolean propertySet = true;
|
||||
|
||||
|
|
@ -186,4 +198,27 @@ public class EntropyCalculatorKernel implements EntropyCalculator {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(KERNEL_WIDTH_PROP_NAME) ||
|
||||
propertyName.equalsIgnoreCase(EPSILON_PROP_NAME)) {
|
||||
return Double.toString(kernelWidth);
|
||||
} else if (propertyName.equalsIgnoreCase(NORMALISE_PROP_NAME)) {
|
||||
return Boolean.toString(normalise);
|
||||
} else {
|
||||
// no superclass to try:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumObservations() throws Exception {
|
||||
return totalObservations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return lastAverage;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
*/
|
||||
public static final String NORMALISE_PROP_NAME = "NORMALISE";
|
||||
|
||||
/**
|
||||
* Number of joint variables/dimensions
|
||||
*/
|
||||
protected int dimensions = 1;
|
||||
|
||||
/**
|
||||
* Default value for kernel width
|
||||
*/
|
||||
|
|
@ -101,6 +106,11 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
lastEntropy = 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() throws Exception {
|
||||
initialise(dimensions);
|
||||
}
|
||||
|
||||
public void initialise(int dimensions) {
|
||||
initialise(dimensions, kernelWidth);
|
||||
}
|
||||
|
|
@ -118,17 +128,20 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
*/
|
||||
public void initialise(int dimensions, double kernelWidth) {
|
||||
this.kernelWidth = kernelWidth;
|
||||
this.dimensions = dimensions;
|
||||
mvke.initialise(dimensions, kernelWidth);
|
||||
// this.dimensions = dimensions;
|
||||
lastEntropy = 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObservations(double observations[][]) {
|
||||
mvke.setObservations(observations);
|
||||
totalObservations = observations.length;
|
||||
this.observations = observations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() {
|
||||
double entropy = 0.0;
|
||||
for (int b = 0; b < totalObservations; b++) {
|
||||
|
|
@ -143,12 +156,28 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
return lastEntropy;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double[] computeLocalOfPreviousObservations() {
|
||||
return computeLocalUsingPreviousObservations(observations);
|
||||
return computeLocalUsingPreviousObservations(true, observations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] computeLocalUsingPreviousObservations(double states[][]) {
|
||||
return computeLocalUsingPreviousObservations(false, states);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for computing locals of a set of observations
|
||||
* based on existing PDFs.
|
||||
*
|
||||
* @param isPreviousObservations whether we're using the points
|
||||
* that the PDFs were computed from or not.
|
||||
* @param states samples to compute the local joint entropies on.
|
||||
* @return
|
||||
*/
|
||||
protected double[] computeLocalUsingPreviousObservations(
|
||||
boolean isPreviousObservations, double states[][]) {
|
||||
|
||||
double entropy = 0.0;
|
||||
double[] localEntropy = new double[states.length];
|
||||
for (int b = 0; b < states.length; b++) {
|
||||
|
|
@ -161,14 +190,19 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
}
|
||||
}
|
||||
entropy /= (double) totalObservations / Math.log(2.0);
|
||||
if (isPreviousObservations) {
|
||||
lastEntropy = entropy;
|
||||
}
|
||||
return localEntropy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
mvke.setDebug(debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return lastEntropy;
|
||||
}
|
||||
|
|
@ -187,6 +221,7 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
* is an absolute value. Default is {@link #DEFAULT_KERNEL_WIDTH}.</li>
|
||||
* <li>{@link #NORMALISE_PROP_NAME} -- whether to normalise the incoming variable values
|
||||
* to mean 0, standard deviation 1, or not (default false). Sets {@link #normalise}.</li>
|
||||
* <li>any valid properties for {@link EntropyCalculatorMultiVariate#setProperty(String, String)}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Unknown property values are ignored.</p>
|
||||
|
|
@ -195,6 +230,7 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
* @param propertyValue value of the property
|
||||
* @throws Exception for invalid property values
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) throws Exception {
|
||||
boolean propertySet = true;
|
||||
|
||||
|
|
@ -208,6 +244,8 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
} else if (propertyName.equalsIgnoreCase(NORMALISE_PROP_NAME)) {
|
||||
normalise = Boolean.parseBoolean(propertyValue);
|
||||
mvke.setNormalise(normalise);
|
||||
} else if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
dimensions = Integer.parseInt(propertyValue);
|
||||
} else {
|
||||
// No property was set
|
||||
propertySet = false;
|
||||
|
|
@ -218,6 +256,22 @@ public class EntropyCalculatorMultiVariateKernel implements EntropyCalculatorMul
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(KERNEL_WIDTH_PROP_NAME) ||
|
||||
propertyName.equalsIgnoreCase(EPSILON_PROP_NAME)) {
|
||||
return Double.toString(kernelWidth);
|
||||
} else if (propertyName.equalsIgnoreCase(NORMALISE_PROP_NAME)) {
|
||||
return Boolean.toString(normalise);
|
||||
} else if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
return Integer.toString(dimensions);
|
||||
} else {
|
||||
// No property was set, and no superclass to call:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumObservations() throws Exception {
|
||||
return totalObservations;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,6 +209,19 @@ public class MultiInfoCalculatorKernel
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(KERNEL_WIDTH_PROP_NAME) ||
|
||||
propertyName.equalsIgnoreCase(EPSILON_PROP_NAME)) {
|
||||
return Double.toString(kernelWidth);
|
||||
} else if (propertyName.equalsIgnoreCase(DYN_CORR_EXCL_TIME_NAME)) {
|
||||
return Integer.toString(dynCorrExclTime);
|
||||
} else {
|
||||
// Try the superclass, including for PROP_NORMALISE
|
||||
return super.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAddObservations() {
|
||||
if (dynCorrExcl) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class MutualInfoCalculatorMultiVariateKernel
|
|||
public static final String NORMALISE_PROP_NAME = "NORMALISE";
|
||||
|
||||
private boolean dynCorrExcl = false;
|
||||
private int dynCorrExclTime = 100;
|
||||
private int dynCorrExclTime = 0;
|
||||
/**
|
||||
* Property name for a dynamics exclusion time window (see Kantz and Schreiber),
|
||||
* default is 0 which means no dynamic exclusion window.
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@
|
|||
|
||||
package infodynamics.measures.continuous.kozachenko;
|
||||
|
||||
import infodynamics.measures.continuous.EntropyCalculator;
|
||||
import infodynamics.measures.continuous.EntropyCalculatorMultiVariate;
|
||||
import infodynamics.utils.EuclideanUtils;
|
||||
import infodynamics.utils.MathsUtils;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
|
||||
/**
|
||||
* <p>Computes the differential entropy of a given set of observations
|
||||
|
|
@ -56,13 +58,13 @@ import infodynamics.utils.MathsUtils;
|
|||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public class EntropyCalculatorMultiVariateKozachenko
|
||||
implements EntropyCalculatorMultiVariate {
|
||||
implements EntropyCalculator, EntropyCalculatorMultiVariate {
|
||||
|
||||
protected boolean debug = false;
|
||||
private int totalObservations;
|
||||
private int dimensions;
|
||||
private int dimensions = 1;
|
||||
protected double[][] rawData;
|
||||
private double lastEntropy;
|
||||
private double lastAverage = 0.0;
|
||||
private double[] lastLocalEntropy;
|
||||
private boolean isComputed;
|
||||
|
||||
|
|
@ -73,11 +75,15 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
*/
|
||||
public EntropyCalculatorMultiVariateKozachenko() {
|
||||
totalObservations = 0;
|
||||
dimensions = 0;
|
||||
isComputed = false;
|
||||
lastLocalEntropy = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() throws Exception {
|
||||
initialise(dimensions);
|
||||
}
|
||||
|
||||
public void initialise(int dimensions) {
|
||||
this.dimensions = dimensions;
|
||||
rawData = null;
|
||||
|
|
@ -86,14 +92,33 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
lastLocalEntropy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* No properties are defined here, so this method will have no effect.
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue)
|
||||
throws Exception {
|
||||
// No properties here to set
|
||||
boolean propertySet = true;
|
||||
if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
dimensions = Integer.parseInt(propertyValue);
|
||||
} else {
|
||||
// No property was set
|
||||
propertySet = false;
|
||||
}
|
||||
if (debug && propertySet) {
|
||||
System.out.println(this.getClass().getSimpleName() + ": Set property " + propertyName +
|
||||
" to " + propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(NUM_DIMENSIONS_PROP_NAME)) {
|
||||
return Integer.toString(dimensions);
|
||||
} else {
|
||||
// No property was set, and no superclass to call:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObservations(double[][] observations) {
|
||||
rawData = observations;
|
||||
totalObservations = observations.length;
|
||||
|
|
@ -101,6 +126,19 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
lastLocalEntropy = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see infodynamics.measures.continuous.EntropyCalculator#setObservations(double[])
|
||||
*
|
||||
* This method here to ensure we make compatibility with the
|
||||
* EntropyCalculator interface.
|
||||
*/
|
||||
@Override
|
||||
public void setObservations(double[] observations) {
|
||||
rawData = MatrixUtils.reshape(observations, observations.length, 1);
|
||||
setObservations(rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Each row of the data is an observation; each column of
|
||||
* the row is a new variable in the multivariate observation.
|
||||
|
|
@ -137,9 +175,10 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
/**
|
||||
* @return entropy in natural units
|
||||
*/
|
||||
@Override
|
||||
public double computeAverageLocalOfObservations() {
|
||||
if (isComputed) {
|
||||
return lastEntropy;
|
||||
return lastAverage;
|
||||
}
|
||||
double sdTermHere = sdTerm(totalObservations, dimensions);
|
||||
double emConstHere = eulerMacheroniTerm(totalObservations);
|
||||
|
|
@ -167,7 +206,7 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
}
|
||||
entropy += emConstHere;
|
||||
entropy += sdTermHere;
|
||||
lastEntropy = entropy;
|
||||
lastAverage = entropy;
|
||||
isComputed = true;
|
||||
return entropy;
|
||||
}
|
||||
|
|
@ -175,6 +214,7 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
/**
|
||||
* @return local entropies in natural units
|
||||
*/
|
||||
@Override
|
||||
public double[] computeLocalOfPreviousObservations() {
|
||||
if (lastLocalEntropy != null) {
|
||||
return lastLocalEntropy;
|
||||
|
|
@ -205,7 +245,7 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
}
|
||||
}
|
||||
entropy /= (double) totalObservations;
|
||||
lastEntropy = entropy;
|
||||
lastAverage = entropy;
|
||||
lastLocalEntropy = localEntropy;
|
||||
return localEntropy;
|
||||
}
|
||||
|
|
@ -213,6 +253,7 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
/**
|
||||
* Not implemented yet
|
||||
*/
|
||||
@Override
|
||||
public double[] computeLocalUsingPreviousObservations(double[][] states) throws Exception {
|
||||
throw new Exception("Local method for other data not implemented");
|
||||
}
|
||||
|
|
@ -289,14 +330,17 @@ public class EntropyCalculatorMultiVariateKozachenko
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLastAverage() {
|
||||
return lastEntropy;
|
||||
return lastAverage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumObservations() {
|
||||
return totalObservations;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,4 +232,14 @@ public class ConditionalTransferEntropyCalculatorKraskov
|
|||
props.put(propertyName, propertyValue); // This will keep properties for the super class as well as the cond MI calculator, but this is ok
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(PROP_KRASKOV_ALG_NUM)) {
|
||||
return Integer.toString(kraskovAlgorithmNumber);
|
||||
} else {
|
||||
// Assume it was a property for the parent class or underlying conditional MI calculator
|
||||
return super.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ public abstract class MultiInfoCalculatorKraskov
|
|||
* <ul>
|
||||
* <li>{@link #PROP_K} -- number of k nearest neighbours to use in joint kernel space
|
||||
* in the KSG algorithm (default is 4).</li>
|
||||
* <li>{@link #PROP_NORM_TYPE} -- normalization type to apply to
|
||||
* <li>{@link #PROP_NORM_TYPE} -- norm type to apply to
|
||||
* working out the norms between the points in each marginal space.
|
||||
* Options are defined by {@link KdTree#setNormType(String)} -
|
||||
* default is {@link EuclideanUtils#NORM_MAX_NORM}.</li>
|
||||
|
|
@ -198,7 +198,9 @@ public abstract class MultiInfoCalculatorKraskov
|
|||
* so can be considered as a number of standard deviations of the data.
|
||||
* (Recommended by Kraskov. MILCA uses 1e-8; but adds in
|
||||
* a random amount of noise in [0,noiseLevel) ).
|
||||
* Default 1e-8 to match the noise order in MILCA toolkit..</li>
|
||||
* Default 1e-8 to match the noise order in MILCA toolkit.</li>
|
||||
* <li>Any property accepted by the superclass
|
||||
* {@link MultiInfoCalculatorCommon#setProperty(String, String)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Unknown property values are ignored.</p>
|
||||
|
|
@ -244,6 +246,24 @@ public abstract class MultiInfoCalculatorKraskov
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String propertyName) throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(PROP_K)) {
|
||||
return Integer.toString(k);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_NORM_TYPE)) {
|
||||
return KdTree.convertNormTypeToString(normType);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_DYN_CORR_EXCL_TIME)) {
|
||||
return Integer.toString(dynCorrExclTime);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_ADD_NOISE)) {
|
||||
return Double.toString(noiseLevel);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_NUM_THREADS)) {
|
||||
return Integer.toString(numThreads);
|
||||
} else {
|
||||
// try the superclass, including for PROP_NORMALISE:
|
||||
return super.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set observations from two separate time series: join the rows at each time step
|
||||
* together to make a joint vector, then effectively call {@link #setObservations(double[][])}
|
||||
|
|
|
|||
43
java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov1.java
Executable file → Normal file
43
java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov1.java
Executable file → Normal file
|
|
@ -120,6 +120,49 @@ public class MutualInfoCalculatorMultiVariateKraskov1
|
|||
} else {
|
||||
return new double[] {sumDiGammas, sumNx, sumNy};
|
||||
}
|
||||
}
|
||||
|
||||
public int[][] partialNeighbourCountFromObservations(
|
||||
int startTimePoint, int numTimePoints) throws Exception {
|
||||
|
||||
double startTime = Calendar.getInstance().getTimeInMillis();
|
||||
|
||||
ensureKdTreesConstructed();
|
||||
|
||||
int[][] neighbourCounts = new int[numTimePoints][2];
|
||||
|
||||
for (int t = startTimePoint; t < startTimePoint + numTimePoints; t++) {
|
||||
// Compute eps for this time step by
|
||||
// finding the kth closest neighbour for point t:
|
||||
PriorityQueue<NeighbourNodeData> nnPQ =
|
||||
kdTreeJoint.findKNearestNeighbours(k, t, dynCorrExclTime);
|
||||
// First element in the PQ is the kth NN,
|
||||
// and epsilon = kthNnData.distance
|
||||
NeighbourNodeData kthNnData = nnPQ.poll();
|
||||
|
||||
// Count the number of points whose x distance is less
|
||||
// than eps, and whose y distance is less than
|
||||
// epsilon = kthNnData.distance
|
||||
int n_x = nnSearcherSource.countPointsStrictlyWithinR(
|
||||
t, kthNnData.distance, dynCorrExclTime);
|
||||
int n_y = nnSearcherDest.countPointsStrictlyWithinR(
|
||||
t, kthNnData.distance, dynCorrExclTime);
|
||||
|
||||
neighbourCounts[t - startTimePoint][0] = n_x;
|
||||
neighbourCounts[t - startTimePoint][1] = n_y;
|
||||
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
Calendar rightNow2 = Calendar.getInstance();
|
||||
long endTime = rightNow2.getTimeInMillis();
|
||||
System.out.println("Subset " + startTimePoint + ":" +
|
||||
(startTimePoint + numTimePoints) + " Calculation time: " +
|
||||
((endTime - startTime)/1000.0) + " sec" );
|
||||
}
|
||||
|
||||
return neighbourCounts;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@
|
|||
|
||||
package infodynamics.measures.discrete;
|
||||
|
||||
import infodynamics.utils.AnalyticMeasurementDistribution;
|
||||
import infodynamics.utils.AnalyticNullDistributionComputer;
|
||||
import infodynamics.utils.ChiSquareMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
import infodynamics.utils.RandomGenerator;
|
||||
|
||||
|
|
@ -68,8 +72,11 @@ import infodynamics.utils.RandomGenerator;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator {
|
||||
public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator
|
||||
implements EmpiricalNullDistributionComputer, AnalyticNullDistributionComputer {
|
||||
|
||||
protected boolean aisComputed = false;
|
||||
|
||||
/**
|
||||
* User was formerly forced to create new instances through this factory method.
|
||||
* Retained for backwards compatibility.
|
||||
|
|
@ -95,6 +102,12 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
super(base, history);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
super.initialise();
|
||||
aisComputed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addObservations(int states[]) {
|
||||
int timeSteps = states.length;
|
||||
|
|
@ -282,7 +295,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// Compute MI contribution:
|
||||
if (p_joint > 0.0) {
|
||||
double logTerm = p_joint / (p_next * p_prev);
|
||||
double localValue = Math.log(logTerm) / log_base;
|
||||
double localValue = Math.log(logTerm) / log_2;
|
||||
miCont = p_joint * localValue;
|
||||
if (localValue > max) {
|
||||
max = localValue;
|
||||
|
|
@ -297,6 +310,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
}
|
||||
|
||||
average = mi;
|
||||
aisComputed = true;
|
||||
return mi;
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +334,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
if (p_joint > 0.0) {
|
||||
double logTerm = p_joint / p_prev;
|
||||
// Entropy rate takes the negative log:
|
||||
double localValue = - Math.log(logTerm) / log_base;
|
||||
double localValue = - Math.log(logTerm) / log_2;
|
||||
entRateCont = p_joint * localValue;
|
||||
} else {
|
||||
entRateCont = 0.0;
|
||||
|
|
@ -348,7 +362,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
( (double) nextCount[next] *
|
||||
(double) pastCount[past] );
|
||||
logTerm *= (double) observations;
|
||||
return Math.log(logTerm) / log_base;
|
||||
return Math.log(logTerm) / log_2;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -380,7 +394,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// and we've got two counts on the bottom
|
||||
// but one count on the top:
|
||||
logTerm *= (double) observations;
|
||||
localActive[t] = Math.log(logTerm) / log_base;
|
||||
localActive[t] = Math.log(logTerm) / log_2;
|
||||
average += localActive[t];
|
||||
if (localActive[t] > max) {
|
||||
max = localActive[t];
|
||||
|
|
@ -431,7 +445,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// and we've got two counts on the bottom
|
||||
// but one count on the top:
|
||||
logTerm *= (double) observations;
|
||||
localActive[r][c] = Math.log(logTerm) / log_base;
|
||||
localActive[r][c] = Math.log(logTerm) / log_2;
|
||||
average += localActive[r][c];
|
||||
if (localActive[r][c] > max) {
|
||||
max = localActive[r][c];
|
||||
|
|
@ -487,7 +501,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// and we've got two counts on the bottom
|
||||
// but one count on the top:
|
||||
logTerm *= (double) observations;
|
||||
localActive[t][r][c] = Math.log(logTerm) / log_base;
|
||||
localActive[t][r][c] = Math.log(logTerm) / log_2;
|
||||
average += localActive[t][r][c];
|
||||
if (localActive[t][r][c] > max) {
|
||||
max = localActive[t][r][c];
|
||||
|
|
@ -536,7 +550,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// and we've got two counts on the bottom
|
||||
// but one count on the top:
|
||||
logTerm *= (double) observations;
|
||||
localActive[r] = Math.log(logTerm) / log_base;
|
||||
localActive[r] = Math.log(logTerm) / log_2;
|
||||
average += localActive[r];
|
||||
if (localActive[r] > max) {
|
||||
max = localActive[r];
|
||||
|
|
@ -585,7 +599,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// and we've got two counts on the bottom
|
||||
// but one count on the top:
|
||||
logTerm *= (double) observations;
|
||||
localActive[t] = Math.log(logTerm) / log_base;
|
||||
localActive[t] = Math.log(logTerm) / log_2;
|
||||
average += localActive[t];
|
||||
if (localActive[t] > max) {
|
||||
max = localActive[t];
|
||||
|
|
@ -721,6 +735,17 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
return measDistribution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnalyticMeasurementDistribution computeSignificance()
|
||||
throws Exception {
|
||||
if (!aisComputed) {
|
||||
computeAverageLocalOfObservations();
|
||||
}
|
||||
return new ChiSquareMeasurementDistribution(average,
|
||||
observations,
|
||||
(base - 1) * (base_power_k - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug method to write the current probability distribution functions
|
||||
*
|
||||
|
|
@ -742,7 +767,7 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
|
|||
// Compute MI contribution:
|
||||
if (p_joint * p_next * p_prev > 0.0) {
|
||||
double logTerm = p_joint / (p_next * p_prev);
|
||||
double localValue = Math.log(logTerm) / log_base;
|
||||
double localValue = Math.log(logTerm) / log_2;
|
||||
miCont = p_joint * localValue;
|
||||
System.out.println(String.format("%7d %.2f %7d %.2f %.2f %.2f %.2f",
|
||||
nextVal, p_next, prevVal, p_prev, p_joint, logTerm, localValue));
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package infodynamics.measures.discrete;
|
||||
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
|
||||
/**
|
||||
* A basic interface for calculators computing measures on a univariate <i>channel</i>
|
||||
|
|
@ -54,7 +54,7 @@ import infodynamics.utils.EmpiricalMeasurementDistribution;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface ChannelCalculatorDiscrete {
|
||||
public interface ChannelCalculatorDiscrete extends EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Initialise the calculator for (re-)use, with the existing
|
||||
|
|
@ -104,27 +104,4 @@ public interface ChannelCalculatorDiscrete {
|
|||
*/
|
||||
public double computeAverageLocalOfObservations();
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the channel measure would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value.
|
||||
* (Precise null hypothesis varies between MI and TE).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(int[], int[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of channel measure scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package infodynamics.measures.discrete;
|
|||
import infodynamics.utils.AnalyticMeasurementDistribution;
|
||||
import infodynamics.utils.AnalyticNullDistributionComputer;
|
||||
import infodynamics.utils.ChiSquareMeasurementDistribution;
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.RandomGenerator;
|
||||
|
|
@ -64,7 +65,8 @@ Theory' (John Wiley & Sons, New York, 1991).</li>
|
|||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public class ConditionalMutualInformationCalculatorDiscrete
|
||||
extends InfoMeasureCalculatorDiscrete implements AnalyticNullDistributionComputer {
|
||||
extends InfoMeasureCalculatorDiscrete
|
||||
implements EmpiricalNullDistributionComputer, AnalyticNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Store the number of symbols for each variable
|
||||
|
|
@ -403,29 +405,13 @@ public class ConditionalMutualInformationCalculatorDiscrete
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the conditional MI would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value (in the
|
||||
* context of the conditional).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE.
|
||||
* <b>Note that this method currently fixes the relationship
|
||||
* <p><b>Note</b> -- in contrast to the generic computeSignificance() method
|
||||
* described in the documentation below, this method for a conditional MI calculator currently fixes the relationship
|
||||
* between variable 2 and the conditional, and shuffles
|
||||
* variable 1 with respect to these.</b>
|
||||
* variable 1 with respect to these.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(int[], int[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of conditional MI scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @inheritDoc
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
|
||||
RandomGenerator rg = new RandomGenerator();
|
||||
|
|
@ -435,46 +421,17 @@ public class ConditionalMutualInformationCalculatorDiscrete
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the conditional MI would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination values.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for
|
||||
* a conditional mutual information. Basically, the marginal PDFs
|
||||
* of each marginal
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of conditional MI under these conditions is generated.
|
||||
* <b>Note that this method currently fixes the relationship
|
||||
* <p><b>Note</b> -- in contrast to the generic computeSignificance() method
|
||||
* described in the documentation below, this method for a conditional MI calculator currently fixes the relationship
|
||||
* between variable 2 and the conditional, and shuffles
|
||||
* variable 1 with respect to these.</b>
|
||||
* variable 1 with respect to these.
|
||||
* </p>
|
||||
*
|
||||
* TODO Need to alter the method signature to allow callers to specify
|
||||
* which variable is shuffled. (Note to self: when doing this, will
|
||||
* need to update machine learning code to the new method signature)
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the values
|
||||
* of variable 1
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length N (where
|
||||
* would be the value returned by {@link #getNumObservations()}),
|
||||
* containing a permutation of the values in 0..(N-1).
|
||||
* @return the distribution of conditional MI scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number N samples that were previously supplied.
|
||||
* @inheritDoc
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
|
||||
|
||||
|
|
@ -544,7 +501,8 @@ public class ConditionalMutualInformationCalculatorDiscrete
|
|||
if (!condMiComputed) {
|
||||
computeAverageLocalOfObservations();
|
||||
}
|
||||
return new ChiSquareMeasurementDistribution(2.0*((double)observations)*average,
|
||||
return new ChiSquareMeasurementDistribution(average,
|
||||
observations,
|
||||
(base1 - 1)*(base2 - 1)*condBase);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package infodynamics.measures.discrete;
|
||||
|
||||
import infodynamics.utils.EmpiricalNullDistributionComputer;
|
||||
import infodynamics.utils.MathsUtils;
|
||||
import infodynamics.utils.MatrixUtils;
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
|
|
@ -109,10 +110,12 @@ import infodynamics.utils.RandomGenerator;
|
|||
* <a href="http://www.doc.ic.ac.uk/~pam213">www</a>)
|
||||
*
|
||||
*/
|
||||
public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCalculatorDiscrete {
|
||||
public class ConditionalTransferEntropyCalculatorDiscrete
|
||||
extends InfoMeasureCalculatorDiscrete
|
||||
implements EmpiricalNullDistributionComputer {
|
||||
|
||||
protected int k = 0; // history length k.
|
||||
protected int base_others = 0; // base of the conditional variables
|
||||
protected int base_others = 0; // base of the conditional variables
|
||||
protected int base_power_k = 0;
|
||||
protected int base_power_num_others = 0;
|
||||
protected int numOtherInfoContributors = 0;
|
||||
|
|
@ -223,7 +226,7 @@ public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCal
|
|||
super(base);
|
||||
|
||||
k = history;
|
||||
base_others = base; // If unspecified, the base of the conditional variables is the same as src and tgt
|
||||
base_others = base; // If unspecified, the base of the conditional variables is the same as src and tgt
|
||||
this.numOtherInfoContributors = numOtherInfoContributors;
|
||||
base_power_k = MathsUtils.power(base, k);
|
||||
base_power_num_others = MathsUtils.power(base, numOtherInfoContributors);
|
||||
|
|
@ -624,32 +627,20 @@ public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCal
|
|||
return te;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the
|
||||
* conditional TE would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value
|
||||
* (in the context of the destination past and conditionals).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(int[], int[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @return the distribution of conditional TE scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
|
||||
RandomGenerator rg = new RandomGenerator();
|
||||
// (Not necessary to check for distinct random perturbations)
|
||||
int[][] newOrderings = rg.generateRandomPerturbations(observations, numPermutationsToCheck);
|
||||
return computeSignificance(newOrderings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
|
||||
double actualTE = computeAverageLocalOfObservations();
|
||||
|
||||
int numPermutationsToCheck = newOrderings.length;
|
||||
|
||||
// Reconstruct the source values (not necessarily in order)
|
||||
int[] sourceValues = new int[observations];
|
||||
int t_s = 0;
|
||||
|
|
@ -694,12 +685,7 @@ public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCal
|
|||
}
|
||||
}
|
||||
|
||||
// Construct new source orderings based on the source probabilities only
|
||||
// Generate the re-ordered indices:
|
||||
RandomGenerator rg = new RandomGenerator();
|
||||
// (Not necessary to check for distinct random perturbations)
|
||||
int[][] newOrderings = rg.generateRandomPerturbations(observations, numPermutationsToCheck);
|
||||
|
||||
// TODO stop using deprecated method
|
||||
ConditionalTransferEntropyCalculatorDiscrete cte = newInstance(base, k, numOtherInfoContributors);
|
||||
cte.initialise();
|
||||
cte.observations = observations;
|
||||
|
|
@ -957,7 +943,7 @@ public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCal
|
|||
* (indexed only by time step)
|
||||
* @return time-series of local conditional TE values
|
||||
*/
|
||||
private double[] computeLocalFromPreviousObservations
|
||||
public double[] computeLocalFromPreviousObservations
|
||||
(int source[], int dest[], int[] conditional){
|
||||
|
||||
int rows = source.length;
|
||||
|
|
@ -1015,7 +1001,7 @@ public class ConditionalTransferEntropyCalculatorDiscrete extends InfoMeasureCal
|
|||
* (indexed first by time step then by variable number)
|
||||
* @return time-series of local conditional TE values
|
||||
*/
|
||||
private double[] computeLocalFromPreviousObservations
|
||||
public double[] computeLocalFromPreviousObservations
|
||||
(int source[], int dest[], int[][] conditionals){
|
||||
|
||||
int rows = source.length;
|
||||
|
|
|
|||
|
|
@ -222,40 +222,7 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi
|
|||
return computeSignificance(newOrderings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a bootstrapped distribution of what the MI would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination values.
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for
|
||||
* a mutual information. Basically, the marginal PDFs
|
||||
* of each marginal
|
||||
* are preserved, while their joint PDF is destroyed, and the
|
||||
* distribution of MI under these conditions is generated.</p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using {@link #addObservations(double[])} etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the next values
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length N (where
|
||||
* would be the value returned by {@link #getNumObservations()}),
|
||||
* containing a permutation of the values in 0..(N-1).
|
||||
* @return the distribution of MI scores under this null hypothesis.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @throws Exception where the length of each permutation in newOrderings
|
||||
* is not equal to the number N samples that were previously supplied.
|
||||
*/
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
|
||||
double actualMI = computeAverageLocalOfObservations();
|
||||
|
||||
|
|
@ -317,7 +284,8 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi
|
|||
if (!miComputed) {
|
||||
computeAverageLocalOfObservations();
|
||||
}
|
||||
return new ChiSquareMeasurementDistribution(2.0*((double)observations)*average,
|
||||
return new ChiSquareMeasurementDistribution(average,
|
||||
observations,
|
||||
(base - 1) * (base - 1));
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +354,6 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi
|
|||
}
|
||||
}
|
||||
average = average/(double) observations;
|
||||
miComputed = true;
|
||||
|
||||
return localMI;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1107,14 +1107,17 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
for (int pastVal = 0; pastVal < base_power_k; pastVal++) {
|
||||
// compute p(past)
|
||||
// double p_past = (double) pastCount[pastVal] / (double) observations;
|
||||
if (pastCount[pastVal] == 0) {
|
||||
continue;
|
||||
}
|
||||
for (int destVal = 0; destVal < base; destVal++) {
|
||||
// compute p(dest,past)
|
||||
// double p_dest_past = (double) destPastCount[destVal][pastVal] / (double) observations;
|
||||
if (nextPastCount[destVal][pastVal] == 0) {
|
||||
continue;
|
||||
}
|
||||
double denom = (double) nextPastCount[destVal][pastVal] / (double) pastCount[pastVal];
|
||||
for (int sourceVal = 0; sourceVal < base_power_l; sourceVal++) {
|
||||
// compute p(source,dest,past)
|
||||
double p_source_dest_past = (double) sourceNextPastCount[sourceVal][destVal][pastVal] / (double) observations;
|
||||
// compute p(source,past)
|
||||
// double p_source_past = (double) sourcePastCount[sourceVal][pastVal] / (double) observations;
|
||||
// Compute TE contribution:
|
||||
if (sourceNextPastCount[sourceVal][destVal][pastVal] != 0) {
|
||||
/* Double check: should never happen
|
||||
|
|
@ -1125,9 +1128,13 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
}
|
||||
*/
|
||||
|
||||
// compute p(source,dest,past)
|
||||
double p_source_dest_past = (double) sourceNextPastCount[sourceVal][destVal][pastVal] / (double) observations;
|
||||
// compute p(source,past)
|
||||
// double p_source_past = (double) sourcePastCount[sourceVal][pastVal] / (double) observations;
|
||||
double logTerm = ((double) sourceNextPastCount[sourceVal][destVal][pastVal] / (double) sourcePastCount[sourceVal][pastVal]) /
|
||||
((double) nextPastCount[destVal][pastVal] / (double) pastCount[pastVal]);
|
||||
double localValue = Math.log(logTerm) / log_2;
|
||||
(denom);
|
||||
double localValue = Math.log(logTerm); // We'll / log_2 later, to save a floating pt op;
|
||||
teCont = p_source_dest_past * localValue;
|
||||
if (localValue > max) {
|
||||
max = localValue;
|
||||
|
|
@ -1145,6 +1152,11 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
}
|
||||
}
|
||||
|
||||
te /= log_2;
|
||||
max /= log_2;
|
||||
min /= log_2;
|
||||
meanSqLocals /= (log_2 * log_2);
|
||||
|
||||
average = te;
|
||||
std = Math.sqrt(meanSqLocals - average * average);
|
||||
estimateComputed = true;
|
||||
|
|
@ -1205,8 +1217,18 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) {
|
||||
RandomGenerator rg = new RandomGenerator();
|
||||
// (Not necessary to check for distinct random perturbations)
|
||||
int[][] newOrderings = rg.generateRandomPerturbations(observations, numPermutationsToCheck);
|
||||
return computeSignificance(newOrderings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) {
|
||||
double actualTE = computeAverageLocalOfObservations();
|
||||
|
||||
int numPermutationsToCheck = newOrderings.length;
|
||||
|
||||
// Reconstruct the *joint* source values (not necessarily in order, but using joint values retains their l-tuples)
|
||||
int[] sourceValues = new int[observations];
|
||||
int t_s = 0;
|
||||
|
|
@ -1238,12 +1260,6 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
}
|
||||
}
|
||||
|
||||
// Construct new source orderings based on the source probabilities only
|
||||
// Generate the re-ordered indices:
|
||||
RandomGenerator rg = new RandomGenerator();
|
||||
// (Not necessary to check for distinct random perturbations)
|
||||
int[][] newOrderings = rg.generateRandomPerturbations(observations, numPermutationsToCheck);
|
||||
|
||||
// If we want a calculator just like this one, we should provide all of
|
||||
// the same parameters:
|
||||
TransferEntropyCalculatorDiscrete ate2 =
|
||||
|
|
@ -1287,10 +1303,9 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
if (!estimateComputed) {
|
||||
computeAverageLocalOfObservations();
|
||||
}
|
||||
// We set the "actual value" as 2*N*TE, since this marries up properly
|
||||
// with the requisite chi^2 distribution
|
||||
return new ChiSquareMeasurementDistribution(2.0*((double)observations)*average,
|
||||
(sourceHistoryEmbedLength*base - 1)*(base - 1)*(k*base));
|
||||
return new ChiSquareMeasurementDistribution(average,
|
||||
observations,
|
||||
(base_power_l - 1)*(base - 1)*(base_power_k));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1386,6 +1401,7 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
|
|||
(double) sourcePastCount[thisSourceVal][thisPastVal]) /
|
||||
((double) nextPastCount[destVal][thisPastVal] / (double) pastCount[thisPastVal]);
|
||||
localTE[t] = Math.log(logTerm) / log_2;
|
||||
average += localTE[t];
|
||||
if (localTE[t] > max) {
|
||||
max = localTE[t];
|
||||
} else if (localTE[t] < min) {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ public interface MutualInfoCalculatorMultiVariateWithDiscrete {
|
|||
* @param propertyName name of property
|
||||
* @param propertyValue value of property
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue);
|
||||
public void setProperty(String propertyName, String propertyValue)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Set the properties from which the mutual information should be computed.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ import infodynamics.utils.MathsUtils;
|
|||
import infodynamics.utils.MatrixUtils;
|
||||
import infodynamics.utils.EmpiricalMeasurementDistribution;
|
||||
import infodynamics.utils.RandomGenerator;
|
||||
import infodynamics.utils.KdTree;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* <p>Compute the Mutual Information between a vector of continuous variables and discrete
|
||||
|
|
@ -57,19 +63,32 @@ import infodynamics.utils.RandomGenerator;
|
|||
*
|
||||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
* @author Pedro A.M. Mediano (<a href="pmediano at imperial.ac.uk">email</a>,
|
||||
* <a href="http://www.doc.ic.ac.uk/~pam213">www</a>)
|
||||
*/
|
||||
public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements MutualInfoCalculatorMultiVariateWithDiscrete {
|
||||
public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements MutualInfoCalculatorMultiVariateWithDiscrete, Cloneable {
|
||||
|
||||
// Multiplier used in hueristic for determining whether to use a linear search
|
||||
// for min kth element or a binary search.
|
||||
protected static final double CUTOFF_MULTIPLIER = 1.5;
|
||||
|
||||
/**
|
||||
* we compute distances to the kth neighbour
|
||||
*/
|
||||
protected int k = 4;
|
||||
/**
|
||||
* The set of continuous data observations, retained in case the user wants
|
||||
* to retrieve the local entropy values of these.
|
||||
* They're held in the order in which they were supplied in the
|
||||
* {@link addObservations(double[][], int[])} functions.
|
||||
*/
|
||||
protected double[][] continuousData;
|
||||
/**
|
||||
* The set of discrete data observations, retained in case the user wants
|
||||
* to retrieve the local entropy values of these.
|
||||
* They're held in the order in which they were supplied in the
|
||||
* {@link addObservations(double[][], int[])} functions.
|
||||
*/
|
||||
protected int[] discreteData;
|
||||
/**
|
||||
* Counts of how many observations belong to each discrete bin.
|
||||
*/
|
||||
protected int[] counts;
|
||||
/**
|
||||
* Number of possible states of the discrete variable
|
||||
|
|
@ -79,22 +98,88 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
* Number of dimenions of the joint continuous variable
|
||||
*/
|
||||
protected int dimensions;
|
||||
/**
|
||||
* Number of observations added to the calculator
|
||||
*/
|
||||
protected int totalObservations;
|
||||
/**
|
||||
* Whether to print debug information
|
||||
*/
|
||||
protected boolean debug;
|
||||
/**
|
||||
* Last computed value of MI
|
||||
*/
|
||||
protected double mi;
|
||||
/**
|
||||
* Whether MI has been computed with the current observations
|
||||
*/
|
||||
protected boolean miComputed;
|
||||
|
||||
protected EuclideanUtils normCalculator;
|
||||
// Storage for the norms from each observation to each other one
|
||||
protected double[][] xNorms;
|
||||
// Keep the norms each time (making reordering very quick)
|
||||
// (Should only be set to false for testing)
|
||||
public static boolean tryKeepAllPairsNorms = true;
|
||||
public static int MAX_DATA_SIZE_FOR_KEEP_ALL_PAIRS_NORM = 2000;
|
||||
|
||||
/**
|
||||
* Saved digammaN for average and local computations
|
||||
*/
|
||||
protected double digammaN;
|
||||
/**
|
||||
* Saved digammaK for average and local computations
|
||||
*/
|
||||
protected double digammaK;
|
||||
/**
|
||||
* Storage for continuous observations supplied via {@link #addObservations(double[][], int[])}
|
||||
* type calls
|
||||
*/
|
||||
protected Vector<double[][]> vectorOfContinuousObservations;
|
||||
/**
|
||||
* Storage for discrete observations supplied via {@link #addObservations(double[][], int[])}
|
||||
* type calls
|
||||
*/
|
||||
protected Vector<int[]> vectorOfDiscreteObservations;
|
||||
/**
|
||||
* KdTree for range searches in full continuous dataset
|
||||
*/
|
||||
protected KdTree kdTreeJoint;
|
||||
/**
|
||||
* KdTrees for neighbour searches in binned continuous datasets
|
||||
*/
|
||||
protected KdTree[] kdTreeBins;
|
||||
/**
|
||||
* The norm type in use (see {@link #PROP_NORM_TYPE})
|
||||
*/
|
||||
protected int normType = EuclideanUtils.NORM_MAX_NORM;
|
||||
/**
|
||||
* Property name for the number of K nearest neighbours used in
|
||||
* the KSG algorithm in the full joint space (default 4).
|
||||
*/
|
||||
public final static String PROP_K = "k";
|
||||
/**
|
||||
* Property name for what type of norm to use between data points
|
||||
* for each marginal variable -- Options are defined by
|
||||
* {@link KdTree#setNormType(String)} and the
|
||||
* default is {@link EuclideanUtils#NORM_MAX_NORM}.
|
||||
*/
|
||||
public final static String PROP_NORM_TYPE = "NORM_TYPE";
|
||||
/**
|
||||
* Property name for whether to normalise the incoming data to
|
||||
* mean 0, standard deviation 1 (default true)
|
||||
*/
|
||||
public static final String PROP_NORMALISE = "NORMALISE";
|
||||
|
||||
/**
|
||||
* Property name for time difference between source and destination (0 by default,
|
||||
* must be >= 0)
|
||||
*/
|
||||
public static final String PROP_TIME_DIFF = "TIME_DIFF";
|
||||
/**
|
||||
* Property name for an amount of random Gaussian noise to be
|
||||
* added to the data (default is 1e-8, matching the MILCA toolkit).
|
||||
*/
|
||||
public static final String PROP_ADD_NOISE = "NOISE_LEVEL_TO_ADD";
|
||||
|
||||
// TODO: properly implement dynCorrExclTime
|
||||
// /**
|
||||
// * Property name for a dynamics exclusion time window
|
||||
// * otherwise known as Theiler window (see Kantz and Schreiber).
|
||||
// * Default is 0 which means no dynamic exclusion window.
|
||||
// */
|
||||
// public static final String PROP_DYN_CORR_EXCL_TIME = "DYN_CORR_EXCL";
|
||||
|
||||
/**
|
||||
* Track whether we're going to normalise the joint variables individually
|
||||
*/
|
||||
|
|
@ -107,10 +192,34 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
* Track the std devs of the joint variables if we are normalising them
|
||||
*/
|
||||
protected double[] stds;
|
||||
/**
|
||||
* Time difference from the source to the destination observations
|
||||
* (ie destination lags the source by this time:
|
||||
* we compute I(source_{n}; dest_{n+timeDiff}).
|
||||
* (Note that our internal sourceObservations and destObservations
|
||||
* are adjusted so that there is no timeDiff between them).
|
||||
*/
|
||||
protected int timeDiff = 0;
|
||||
/**
|
||||
* Whether to add an amount of random noise to the incoming data
|
||||
*/
|
||||
protected boolean addNoise = true;
|
||||
/**
|
||||
* Amount of random Gaussian noise to add to the incoming data
|
||||
*/
|
||||
protected double noiseLevel = (double) 1e-8;
|
||||
/**
|
||||
* Size of dynamic correlation exclusion window. By default is set to 0.
|
||||
*
|
||||
* NOTE non-zero exclusion windows are not yet supported in mixed calculators.
|
||||
*/
|
||||
protected int dynCorrExclTime = 0;
|
||||
|
||||
/**
|
||||
* Construct an instance of the KSG mixed MI calculator
|
||||
*/
|
||||
public MutualInfoCalculatorMultiVariateWithDiscreteKraskov() {
|
||||
super();
|
||||
normCalculator = new EuclideanUtils(EuclideanUtils.NORM_MAX_NORM);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,13 +231,15 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
public void initialise(int dimensions, int base) {
|
||||
mi = 0.0;
|
||||
miComputed = false;
|
||||
xNorms = null;
|
||||
totalObservations = 0;
|
||||
continuousData = null;
|
||||
means = null;
|
||||
stds = null;
|
||||
discreteData = null;
|
||||
this.dimensions = dimensions;
|
||||
this.base = base;
|
||||
kdTreeJoint = null;
|
||||
kdTreeBins = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,23 +253,68 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
* default is {@link EuclideanUtils#NORM_MAX_NORM}.
|
||||
* <li>{@link #PROP_NORMALISE} - whether to normalise the individual
|
||||
* variables (true by default)</li>
|
||||
* <li>{@link #PROP_TIME_DIFF} - Time difference between source and
|
||||
* destination (0 by default). Must be >= 0.</li>
|
||||
* <li>{@link #PROP_ADD_NOISE} -- a standard deviation for an amount of
|
||||
* random Gaussian noise to add to
|
||||
* each variable, to avoid having neighbourhoods with artificially
|
||||
* large counts. (We also accept "false" to indicate "0".)
|
||||
* The amount is added in after any normalisation,
|
||||
* so can be considered as a number of standard deviations of the data.
|
||||
* (Recommended by Kraskov. MILCA uses 1e-8; but adds in
|
||||
* a random amount of noise in [0,noiseLevel) ).
|
||||
* Default 1e-8 to match the noise order in MILCA toolkit.</li>
|
||||
* <li>{@link #PROP_DYN_CORR_EXCL_TIME} -- a dynamics exclusion time window,
|
||||
* also known as Theiler window (see Kantz and Schreiber);
|
||||
* default is 0 which means no dynamic exclusion window.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param propertyName name of the property to set
|
||||
* @param propertyValue value to set on that property
|
||||
*/
|
||||
public void setProperty(String propertyName, String propertyValue) {
|
||||
public void setProperty(String propertyName, String propertyValue)
|
||||
throws Exception {
|
||||
if (propertyName.equalsIgnoreCase(PROP_K)) {
|
||||
k = Integer.parseInt(propertyValue);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_NORM_TYPE)) {
|
||||
normCalculator.setNormToUse(propertyValue);
|
||||
normType = KdTree.validateNormType(propertyValue);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_NORMALISE)) {
|
||||
normalise = Boolean.parseBoolean(propertyValue);
|
||||
// TODO: properly implement dynCorrExclTime
|
||||
// } else if (propertyName.equalsIgnoreCase(PROP_DYN_CORR_EXCL_TIME)) {
|
||||
// dynCorrExclTime = Integer.parseInt(propertyValue);
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_ADD_NOISE)) {
|
||||
if (propertyValue.equals("0") ||
|
||||
propertyValue.equalsIgnoreCase("false")) {
|
||||
addNoise = false;
|
||||
noiseLevel = 0;
|
||||
} else {
|
||||
addNoise = true;
|
||||
noiseLevel = Double.parseDouble(propertyValue);
|
||||
}
|
||||
} else if (propertyName.equalsIgnoreCase(PROP_TIME_DIFF)) {
|
||||
timeDiff = Integer.parseInt(propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void addObservations(double[][] source, double[][] destination) throws Exception {
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
public void addObservations(double[][] continuousObservations,
|
||||
int[] discreteObservations) throws Exception {
|
||||
if (vectorOfContinuousObservations == null) {
|
||||
// startAddObservations was not called first
|
||||
throw new RuntimeException("User did not call startAddObservations before addObservations");
|
||||
}
|
||||
if (continuousObservations.length != discreteObservations.length) {
|
||||
throw new Exception("Time steps for observations2 " +
|
||||
discreteObservations.length + " does not match the length " +
|
||||
"of observations1 " + continuousObservations.length);
|
||||
}
|
||||
if (continuousObservations[0].length != dimensions) {
|
||||
throw new Exception("The continuous observations do not have the expected number of variables (" + dimensions + ")");
|
||||
}
|
||||
if (continuousObservations.length > Math.abs(timeDiff)) {
|
||||
vectorOfContinuousObservations.add(continuousObservations);
|
||||
vectorOfDiscreteObservations.add(discreteObservations);
|
||||
}
|
||||
}
|
||||
|
||||
public void addObservations(double[][] source, double[][] destination, int startTime, int numTimeSteps) throws Exception {
|
||||
|
|
@ -173,69 +329,141 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the calculator to receive observations.
|
||||
*
|
||||
* Must be called between {@link #initialise()} and any addObservations
|
||||
* method. Does not need to be called if
|
||||
* {@link setObservations(double[][], int[])} is used.
|
||||
*/
|
||||
public void startAddObservations() {
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
vectorOfContinuousObservations = new Vector<double[][]>();
|
||||
vectorOfDiscreteObservations = new Vector<int[]>();
|
||||
}
|
||||
|
||||
public void finaliseAddObservations() {
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
/**
|
||||
* Prepare observations previously added through any addObservations method
|
||||
* for calculations.
|
||||
*
|
||||
* Must be called between any addObservations method and any compute method.
|
||||
* Does not need to be called if {@link setObservations(double[][], int[])}
|
||||
* is used.
|
||||
*/
|
||||
public void finaliseAddObservations() throws Exception {
|
||||
if (vectorOfContinuousObservations.size() < 1) {
|
||||
throw new Exception("Cannot compute MI with a null set of data");
|
||||
}
|
||||
|
||||
public void setObservations(double[][] continuousObservations,
|
||||
int[] discreteObservations) throws Exception {
|
||||
if (continuousObservations.length != discreteObservations.length) {
|
||||
throw new Exception("Time steps for observations2 " +
|
||||
discreteObservations.length + " does not match the length " +
|
||||
"of observations1 " + continuousObservations.length);
|
||||
// First work out the size to allocate the joint vectors, and do the allocation:
|
||||
totalObservations = 0;
|
||||
for (double[][] destination : vectorOfContinuousObservations) {
|
||||
totalObservations += destination.length - Math.abs(timeDiff);
|
||||
}
|
||||
if (continuousObservations[0].length == 0) {
|
||||
throw new Exception("Computing MI with a null set of data");
|
||||
continuousData = new double[totalObservations][dimensions];
|
||||
discreteData = new int[totalObservations];
|
||||
|
||||
// Construct the joint vectors from the given observations
|
||||
// (removing redundant data which is outside any timeDiff)
|
||||
int startObservation = 0;
|
||||
Iterator<double[][]> iterator = vectorOfContinuousObservations.iterator();
|
||||
for (int[] dct : vectorOfDiscreteObservations) {
|
||||
double[][] cnt = iterator.next();
|
||||
// Copy the data from these given observations into our master
|
||||
// array, aligning them incorporating the timeDiff. Depending on whether
|
||||
// timeDiff >= 0, we have to put first the elements of one array or the
|
||||
// other.
|
||||
if (timeDiff >= 0) {
|
||||
MatrixUtils.arrayCopy(cnt, 0, 0,
|
||||
continuousData, startObservation, 0,
|
||||
cnt.length - timeDiff, dimensions);
|
||||
System.arraycopy(dct, timeDiff, discreteData, startObservation, dct.length - timeDiff);
|
||||
startObservation += cnt.length - timeDiff;
|
||||
|
||||
} else {
|
||||
MatrixUtils.arrayCopy(cnt, Math.abs(timeDiff), 0,
|
||||
continuousData, startObservation, 0,
|
||||
cnt.length - Math.abs(timeDiff), dimensions);
|
||||
System.arraycopy(dct, 0, discreteData, startObservation, dct.length - Math.abs(timeDiff));
|
||||
startObservation += cnt.length - Math.abs(timeDiff);
|
||||
}
|
||||
|
||||
}
|
||||
if (continuousObservations[0].length != dimensions) {
|
||||
throw new Exception("The continuous observations do not have the expected number of variables (" + dimensions + ")");
|
||||
}
|
||||
continuousData = continuousObservations;
|
||||
discreteData = discreteObservations;
|
||||
// We don't need to keep the vectors of observation sets anymore:
|
||||
vectorOfContinuousObservations = null;
|
||||
vectorOfDiscreteObservations = null;
|
||||
|
||||
if (normalise) {
|
||||
// Take a copy since we're going to normalise it
|
||||
// And we need to keep the means/stds ready to normalise local values
|
||||
// We need to keep the means/stds ready to normalise local values
|
||||
// that are supplied later:
|
||||
means = MatrixUtils.means(continuousObservations);
|
||||
stds = MatrixUtils.stdDevs(continuousObservations, means);
|
||||
continuousData = MatrixUtils.normaliseIntoNewArray(continuousObservations, means, stds);
|
||||
means = MatrixUtils.means(continuousData);
|
||||
stds = MatrixUtils.stdDevs(continuousData, means);
|
||||
MatrixUtils.normalise(continuousData, means, stds);
|
||||
}
|
||||
|
||||
// count the discrete states:
|
||||
counts = new int[base];
|
||||
for (int t = 0; t < discreteData.length; t++) {
|
||||
counts[discreteData[t]]++;
|
||||
}
|
||||
try {
|
||||
for (int t = 0; t < discreteData.length; t++) {
|
||||
counts[discreteData[t]]++;
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
totalObservations = 0;
|
||||
continuousData = null;
|
||||
discreteData = null;
|
||||
throw new RuntimeException("Values of the discrete variable must range from 0 to base-1");
|
||||
}
|
||||
|
||||
for (int b = 0; b < counts.length; b++) {
|
||||
if (counts[b] < k) {
|
||||
throw new RuntimeException("This implementation assumes there are at least k items in each discrete bin");
|
||||
}
|
||||
}
|
||||
|
||||
if (addNoise) {
|
||||
Random random = new Random();
|
||||
// Add Gaussian noise of std dev noiseLevel to the data
|
||||
for (int r = 0; r < totalObservations; r++) {
|
||||
for (int c = 0; c < dimensions; c++) {
|
||||
continuousData[r][c] +=
|
||||
random.nextGaussian()*noiseLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
digammaN = MathsUtils.digamma(totalObservations);
|
||||
digammaK = MathsUtils.digamma(k);
|
||||
|
||||
ensureKdTreesConstructed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the norms for each marginal time series
|
||||
*
|
||||
*/
|
||||
protected void computeNorms() {
|
||||
int N = continuousData.length; // number of observations
|
||||
|
||||
xNorms = new double[N][N];
|
||||
for (int t = 0; t < N; t++) {
|
||||
// Compute the norms from t to all other time points
|
||||
for (int t2 = 0; t2 < N; t2++) {
|
||||
if (t2 == t) {
|
||||
xNorms[t][t2] = Double.POSITIVE_INFINITY;
|
||||
continue;
|
||||
}
|
||||
// Compute norm in the continuous space
|
||||
xNorms[t][t2] = normCalculator.norm(continuousData[t], continuousData[t2]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setObservations(double[][] continuousObservations,
|
||||
int[] discreteObservations) throws Exception {
|
||||
startAddObservations();
|
||||
addObservations(continuousObservations, discreteObservations);
|
||||
finaliseAddObservations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to ensure that the Kd-tree data structures to represent the
|
||||
* observational data have been constructed (should be called prior to attempting
|
||||
* to use these data structures)
|
||||
*/
|
||||
public void ensureKdTreesConstructed() {
|
||||
if (kdTreeJoint == null) {
|
||||
kdTreeJoint = new KdTree(continuousData);
|
||||
kdTreeJoint.setNormType(normType);
|
||||
}
|
||||
if (kdTreeBins == null) {
|
||||
kdTreeBins = new KdTree[base];
|
||||
for (int b = 0; b < base; b++) {
|
||||
kdTreeBins[b] = new KdTree(
|
||||
MatrixUtils.extractSelectedPointsMatchingCondition(
|
||||
continuousData, discreteData, b));
|
||||
kdTreeBins[b].setNormType(normType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute what the average MI would look like were the second time series reordered
|
||||
|
|
@ -247,84 +475,81 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public double computeAverageLocalOfObservations(int[] reordering) throws Exception {
|
||||
int N = continuousData.length; // number of observations
|
||||
if (!tryKeepAllPairsNorms || (N > MAX_DATA_SIZE_FOR_KEEP_ALL_PAIRS_NORM)) {
|
||||
// Generate a new re-ordered set of discrete data
|
||||
int[] originalDiscreteData = discreteData;
|
||||
discreteData = MatrixUtils.extractSelectedTimePoints(discreteData, reordering);
|
||||
// Compute the MI
|
||||
double newMI = computeAverageLocalOfObservationsWhileComputingDistances();
|
||||
// restore data2
|
||||
discreteData = originalDiscreteData;
|
||||
return newMI;
|
||||
}
|
||||
public double computeAverageLocalOfObservations(int[] newOrdering)
|
||||
throws Exception {
|
||||
|
||||
// Otherwise we will use the norms we've already computed, and use a "virtual"
|
||||
// reordered data2.
|
||||
int[] reorderedDiscreteData = MatrixUtils.extractSelectedTimePoints(discreteData, reordering);
|
||||
|
||||
if (xNorms == null) {
|
||||
computeNorms();
|
||||
if (newOrdering == null) {
|
||||
return computeAverageLocalOfObservations();
|
||||
}
|
||||
|
||||
// Count the average number of points within eps_x and eps_y
|
||||
double averageDiGammas = 0;
|
||||
double avNx = 0;
|
||||
double avNy = 0;
|
||||
// Take a clone of the object to compute the MI of the surrogates:
|
||||
// (this is a shallow copy, it doesn't make new copies of all
|
||||
// the arrays)
|
||||
MutualInfoCalculatorMultiVariateWithDiscreteKraskov miSurrogateCalculator =
|
||||
(MutualInfoCalculatorMultiVariateWithDiscreteKraskov) this.clone();
|
||||
|
||||
for (int t = 0; t < N; t++) {
|
||||
// Compute eps_x and eps_y for this time step:
|
||||
// using x norms to all neighbours
|
||||
// (note that norm of point t to itself will be set to infinity).
|
||||
// Then find the k closest neighbours in the same discrete bin
|
||||
double eps_x = MatrixUtils.kthMinSubjectTo(xNorms[t], k, reorderedDiscreteData, reorderedDiscreteData[t]);
|
||||
|
||||
// Count the number of points whose x distance is less
|
||||
// than or equal to eps_x
|
||||
int n_x = 0;
|
||||
for (int t2 = 0; t2 < N; t2++) {
|
||||
if (xNorms[t][t2] <= eps_x) {
|
||||
n_x++;
|
||||
}
|
||||
}
|
||||
// n_y is number of points in that bin minus that point
|
||||
int n_y = counts[reorderedDiscreteData[t]] - 1;
|
||||
avNx += n_x;
|
||||
avNy += n_y;
|
||||
// And take the digamma before adding into the
|
||||
// average:
|
||||
averageDiGammas += MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
|
||||
}
|
||||
averageDiGammas /= (double) N;
|
||||
if (debug) {
|
||||
avNx /= (double)N;
|
||||
avNy /= (double)N;
|
||||
System.out.println(String.format("Average n_x=%.3f, Average n_y=%.3f", avNx, avNy));
|
||||
}
|
||||
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// mi = MathsUtils.digamma(k) - 1.0/(double)k - averageDiGammas + MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
mi = MathsUtils.digamma(k) - averageDiGammas + MathsUtils.digamma(N);
|
||||
miComputed = true;
|
||||
return mi;
|
||||
// Generate a new re-ordered data set
|
||||
int[] shuffledDiscreteData = MatrixUtils.extractSelectedTimePoints(discreteData, newOrdering);
|
||||
// Since the observations in the original calculator have already been
|
||||
// shifted according to timeDiff, set timeDiff = 0 in the surrogate calculator.
|
||||
miSurrogateCalculator.setProperty(PROP_TIME_DIFF, "0");
|
||||
// Perform new initialisations
|
||||
miSurrogateCalculator.initialise(dimensions, base);
|
||||
// Set new observations
|
||||
miSurrogateCalculator.setObservations(continuousData, shuffledDiscreteData);
|
||||
// Compute the MI
|
||||
return miSurrogateCalculator.computeAverageLocalOfObservations();
|
||||
}
|
||||
|
||||
public double computeAverageLocalOfObservations() throws Exception {
|
||||
if (!tryKeepAllPairsNorms || (continuousData.length > MAX_DATA_SIZE_FOR_KEEP_ALL_PAIRS_NORM)) {
|
||||
return computeAverageLocalOfObservationsWhileComputingDistances();
|
||||
}
|
||||
|
||||
// Postcondition: we'll compute the norms before the main loop,
|
||||
// unless they have already been computed:
|
||||
|
||||
if (xNorms == null) {
|
||||
computeNorms();
|
||||
}
|
||||
int N = continuousData.length; // number of observations
|
||||
/**
|
||||
* Calculate the average MI in nats.
|
||||
*/
|
||||
public double computeAverageLocalOfObservations() throws Exception {
|
||||
return computeFromObservations(false)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Computes the local values of the MI,
|
||||
* for each valid observation in the previously supplied observations
|
||||
* (with PDFs computed using all of the previously supplied observation sets).</p>
|
||||
*
|
||||
* <p>If the samples were supplied via a single call such as
|
||||
* {@link #setObservations(double[][], int[])},
|
||||
* then the return value is a single time-series of local
|
||||
* channel measure values corresponding to these samples.</p>
|
||||
*
|
||||
* <p>Otherwise where disjoint time-series observations were supplied using several
|
||||
* calls such as {@link #addObservations(double[][], int[])}
|
||||
* then the local values for each disjoint observation set will be appended here
|
||||
* to create a single "time-series" return array.</p>
|
||||
*
|
||||
* @return the "time-series" of local MIs in nats
|
||||
* @throws Exception
|
||||
*/
|
||||
public double[] computeLocalOfPreviousObservations() throws Exception {
|
||||
return computeFromObservations(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This protected method handles the computation of either the average or
|
||||
* local MI (over parts of the total observations).
|
||||
*
|
||||
* <p>The method returns:<ol>
|
||||
* <li>for (returnLocals == false), an array of size 1,
|
||||
* containing the average MI </li>
|
||||
* <li>for local MIs (returnLocals == true), the array of local MI values</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param returnLocals whether to return an array or local values, or else
|
||||
* sums of these values
|
||||
* @return either the average MI, or array of local MI value, in nats not bits
|
||||
* @throws Exception
|
||||
*/
|
||||
protected double[] computeFromObservations(boolean returnLocals) throws Exception {
|
||||
|
||||
int[] cumcount = new int[base];
|
||||
Arrays.fill(cumcount, 0);
|
||||
|
||||
|
||||
// Count the average number of points within eps_x and eps_y
|
||||
double averageDiGammas = 0;
|
||||
|
|
@ -332,25 +557,43 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
double avNy = 0;
|
||||
|
||||
double testSum = 0.0; // Used for debugging prints
|
||||
|
||||
for (int t = 0; t < N; t++) {
|
||||
// Compute eps_x and eps_y for this time step:
|
||||
// using x norms to all neighbours
|
||||
// (note that norm of point t to itself will be set to infinity).
|
||||
// Then find the k closest neighbours in the same discrete bin
|
||||
double eps_x = MatrixUtils.kthMinSubjectTo(xNorms[t], k, discreteData, discreteData[t]);
|
||||
int N = totalObservations;
|
||||
double[] locals = null;
|
||||
if (returnLocals) {
|
||||
locals = new double[N];
|
||||
}
|
||||
|
||||
for (int t = 0; t < N; t++) {
|
||||
|
||||
// Compute eps_x for this time step:
|
||||
// find neighbours in the same discrete bin, then
|
||||
// count points within eps_x in the whole dataset.
|
||||
int b = discreteData[t];
|
||||
double eps_x = kdTreeBins[b].findKNearestNeighbours(k, cumcount[b]++).poll().distance;
|
||||
int n_x = kdTreeJoint.countPointsWithinOrOnR(t, eps_x, dynCorrExclTime);
|
||||
|
||||
// Number of points in discrete bin b, not counting itself
|
||||
int n_y = counts[b] - 1;
|
||||
|
||||
// TODO: properly implement dynCorrExclTime. Code left here for future
|
||||
// development. To implement exclusion window in the full joint space we need
|
||||
// to track the sample indices in each point in the kdTreeBins. See comments in
|
||||
// https://github.com/jlizier/jidt/pull/58#pullrequestreview-64865317
|
||||
//
|
||||
// if (dynCorrExclTime > 0) {
|
||||
// // Add one here because later we are going to subtract the point itself
|
||||
// // again in the for-loop when tt == t
|
||||
// n_y++;
|
||||
// for (int tt = t - dynCorrExclTime; tt <= t + dynCorrExclTime; tt++) {
|
||||
// if ((tt < 0) || (tt >= totalObservations)) {
|
||||
// continue;
|
||||
// }
|
||||
// if (discreteData[tt] == b) {
|
||||
// n_y--;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Count the number of points whose x distance is less
|
||||
// than or equal to eps_x (not including this point)
|
||||
int n_x = 0;
|
||||
for (int t2 = 0; t2 < N; t2++) {
|
||||
if ((t2 != t) && (xNorms[t][t2] <= eps_x)) {
|
||||
// xNorms has infinity for t == t2 anyway ...
|
||||
n_x++;
|
||||
}
|
||||
}
|
||||
// n_y is number of points in that bin, minus this point
|
||||
int n_y = counts[discreteData[t]] - 1;
|
||||
avNx += n_x;
|
||||
avNy += n_y;
|
||||
// And take the digamma before adding into the
|
||||
|
|
@ -358,13 +601,17 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
double localSum = MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
|
||||
averageDiGammas += localSum;
|
||||
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// double localValue = MathsUtils.digamma(k) - 1.0/(double)k - localSum + MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
double localValue = digammaK + digammaN - localSum;
|
||||
if (returnLocals) {
|
||||
locals[t] = localValue;
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// double localValue = MathsUtils.digamma(k) - 1.0/(double)k - localSum + MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
double localValue = MathsUtils.digamma(k) - localSum + MathsUtils.digamma(N);
|
||||
testSum += localValue;
|
||||
if (dimensions == 1) {
|
||||
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
|
||||
|
|
@ -383,7 +630,7 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
avNx, MathsUtils.digamma((int) avNx), MathsUtils.digamma((int) avNx - 1), avNy, MathsUtils.digamma((int) avNy)));
|
||||
System.out.printf("Independent average num in joint box is %.3f\n", (avNx * avNy / (double) N));
|
||||
System.out.println(String.format("digamma(k)=%.3f - averageDiGammas=%.3f + digamma(N)=%.3f\n",
|
||||
MathsUtils.digamma(k), averageDiGammas, MathsUtils.digamma(N)));
|
||||
digammaK, averageDiGammas, digammaN));
|
||||
}
|
||||
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
|
|
@ -391,110 +638,34 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
// for an entropy over m subspaces.
|
||||
// mi = MathsUtils.digamma(k) - 1.0/(double)k - averageDiGammas + MathsUtils.digamma(N);
|
||||
// Instead, do:
|
||||
mi = MathsUtils.digamma(k) - averageDiGammas + MathsUtils.digamma(N);
|
||||
mi = digammaK + digammaN - averageDiGammas;
|
||||
miComputed = true;
|
||||
return mi;
|
||||
|
||||
double[] returnValues;
|
||||
if (returnLocals) {
|
||||
returnValues = locals;
|
||||
} else {
|
||||
returnValues = new double[] {mi};
|
||||
}
|
||||
return returnValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method correctly computes the average local MI, but recomputes the x and y
|
||||
* distances between all tuples in time.
|
||||
* Kept here for cases where we have too many observations
|
||||
* to keep the norm between all pairs, and for testing purposes.
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public double computeAverageLocalOfObservationsWhileComputingDistances() throws Exception {
|
||||
int N = continuousData.length; // number of observations
|
||||
|
||||
// Count the average number of points within eps_x and eps_y
|
||||
double averageDiGammas = 0;
|
||||
double avNx = 0;
|
||||
double avNy = 0;
|
||||
|
||||
double testSum = 0.0; // Used for debugging prints
|
||||
|
||||
for (int t = 0; t < N; t++) {
|
||||
// Compute eps_x and eps_y for this time step:
|
||||
// First get x norms to all neighbours
|
||||
// (note that norm of point t to itself will be set to infinity).
|
||||
double[] norms = new double[N];
|
||||
for (int t2 = 0; t2 < N; t2++) {
|
||||
if (t2 == t) {
|
||||
norms[t2] = Double.POSITIVE_INFINITY;
|
||||
continue;
|
||||
}
|
||||
// Compute norm in the continuous space
|
||||
norms[t2] = normCalculator.norm(continuousData[t], continuousData[t2]);
|
||||
}
|
||||
|
||||
// Then find the k closest neighbours in the same discrete bin
|
||||
double eps_x = MatrixUtils.kthMinSubjectTo(norms, k, discreteData, discreteData[t]);
|
||||
|
||||
// Count the number of points whose x distance is less
|
||||
// than or equal to eps_x
|
||||
int n_x = 0;
|
||||
for (int t2 = 0; t2 < N; t2++) {
|
||||
if (norms[t2] <= eps_x) {
|
||||
n_x++;
|
||||
}
|
||||
}
|
||||
// n_y is number of points in that bin, minus that point
|
||||
int n_y = counts[discreteData[t]] - 1;
|
||||
avNx += n_x;
|
||||
avNy += n_y;
|
||||
// And take the digamma before adding into the
|
||||
// average:
|
||||
double localSum = MathsUtils.digamma(n_x) + MathsUtils.digamma(n_y);
|
||||
averageDiGammas += localSum;
|
||||
|
||||
if (debug) {
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// double localValue = MathsUtils.digamma(k) - 1.0/(double)k - localSum + MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
double localValue = MathsUtils.digamma(k) - localSum + MathsUtils.digamma(N);
|
||||
testSum += localValue;
|
||||
if (dimensions == 1) {
|
||||
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
|
||||
t, continuousData[t][0], eps_x, n_x, n_y, localValue, testSum);
|
||||
} else {
|
||||
System.out.printf("t=%d: eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
|
||||
t, eps_x, n_x, n_y, localValue, testSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
averageDiGammas /= (double) N;
|
||||
if (debug) {
|
||||
avNx /= (double)N;
|
||||
avNy /= (double)N;
|
||||
System.out.println(String.format("Average n_x=%.3f, Average n_y=%.3f", avNx, avNy));
|
||||
}
|
||||
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// mi = MathsUtils.digamma(k) - 1.0/(double)k - averageDiGammas + MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
mi = MathsUtils.digamma(k) - averageDiGammas + MathsUtils.digamma(N);
|
||||
miComputed = true;
|
||||
return mi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the significance of the mutual information of the previously supplied observations.
|
||||
* We destroy the p(x,y) correlations, while retaining the p(x), p(y) marginals, to check how
|
||||
* significant this mutual information actually was.
|
||||
* Compute the significance of the mutual information of the previously
|
||||
* supplied observations.
|
||||
*
|
||||
* We destroy the p(x,y) correlations, while retaining the p(x), p(y)
|
||||
* marginals, to check how significant this mutual information actually was.
|
||||
*
|
||||
* This is in the spirit of Chavez et. al., "Statistical assessment of nonlinear causality:
|
||||
* application to epileptic EEG signals", Journal of Neuroscience Methods 124 (2003) 113-128
|
||||
* which was performed for Transfer entropy.
|
||||
* This is in the spirit of Chavez et. al., "Statistical assessment of
|
||||
* nonlinear causality: application to epileptic EEG signals", Journal of
|
||||
* Neuroscience Methods 124 (2003) 113-128 which was performed for transfer
|
||||
* entropy.
|
||||
*
|
||||
* @param numPermutationsToCheck
|
||||
* @return the proportion of MI scores from the distribution which have higher or equal MIs to ours.
|
||||
* (i.e. 1 - CDF of our score)
|
||||
* @return the proportion of MI scores from the distribution which have
|
||||
* higher or equal MIs to ours. (i.e. 1 - CDF of our score)
|
||||
*/
|
||||
public synchronized EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) throws Exception {
|
||||
// Generate the re-ordered indices:
|
||||
|
|
@ -505,16 +676,20 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
}
|
||||
|
||||
/**
|
||||
* Compute the significance of the mutual information of the previously supplied observations.
|
||||
* We destroy the p(x,y) correlations, while retaining the p(x), p(y) marginals, to check how
|
||||
* significant this mutual information actually was.
|
||||
* Compute the significance of the mutual information of the previously
|
||||
* supplied observations.
|
||||
*
|
||||
* We destroy the p(x,y) correlations, while retaining the p(x), p(y)
|
||||
* marginals, to check how significant this mutual information actually was.
|
||||
*
|
||||
* This is in the spirit of Chavez et. al., "Statistical assessment of nonlinear causality:
|
||||
* application to epileptic EEG signals", Journal of Neuroscience Methods 124 (2003) 113-128
|
||||
* which was performed for Transfer entropy.
|
||||
* This is in the spirit of Chavez et. al., "Statistical assessment of
|
||||
* nonlinear causality: application to epileptic EEG signals", Journal of
|
||||
* Neuroscience Methods 124 (2003) 113-128 which was performed for transfer
|
||||
* entropy.
|
||||
*
|
||||
* @param newOrderings the specific new orderings to use
|
||||
* @return the proportion of MI scores from the distribution which have higher or equal MIs to ours.
|
||||
* @return the proportion of MI scores from the distribution which have
|
||||
* higher or equal MIs to ours.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) throws Exception {
|
||||
|
||||
|
|
@ -561,8 +736,8 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
* it should be ignored, since the data point was not counted in its
|
||||
* own counts in the standard version anyway.</p>
|
||||
*
|
||||
* <p><b>Importantly</b>, the supplied observations are intended to be new observations,
|
||||
* not those fed in to compute the PDFs from. There would be
|
||||
* <p><b>Importantly</b>, the supplied observations are intended to be new
|
||||
* observations, not those fed in to compute the PDFs from. There would be
|
||||
* some subtle changes necessary to accomodate computing locals on
|
||||
* the same data set (e.g. not counting the current point as one
|
||||
* of those within eps_x etc.).
|
||||
|
|
@ -584,58 +759,27 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
continuousNewStates = MatrixUtils.normaliseIntoNewArray(
|
||||
continuousNewStates, means, stds);
|
||||
}
|
||||
|
||||
// Thanks to Rosalind Wang for picking up the bug in not using two different N's here
|
||||
int N_newObservations = continuousNewStates.length; // number of new observations we're evaluating
|
||||
int N_samplesForPdfs = continuousData.length; // number of observations we're using to compute the PDFs
|
||||
|
||||
double[] locals = new double[N_newObservations];
|
||||
|
||||
// Don't need the 1/k correction here because the conditional entropy term
|
||||
// is taken over the continuous space only. The correction is (m-1)/k
|
||||
// for an entropy over m subspaces.
|
||||
// double fixedPartOfLocals = MathsUtils.digamma(k) - 1.0/(double)k +
|
||||
// MathsUtils.digamma(N);
|
||||
// Instead do:
|
||||
double fixedPartOfLocals = MathsUtils.digamma(k) + MathsUtils.digamma(N_samplesForPdfs); // Use N_samplesForPdfs here because that's what would be in denominator of probability functions
|
||||
double testSum = 0.0;
|
||||
if (debug) {
|
||||
System.out.printf("digamma(k)=%.3f + digamma(N)=%.3f\n",
|
||||
MathsUtils.digamma(k), MathsUtils.digamma(N_samplesForPdfs));
|
||||
}
|
||||
double avNx = 0;
|
||||
double avNy = 0;
|
||||
|
||||
for (int t = 0; t < N_newObservations; t++) {
|
||||
// Compute eps_x and eps_y for this time step:
|
||||
// First get x norms to all points in the previously
|
||||
// given observations.
|
||||
double[] norms = new double[continuousData.length];
|
||||
for (int t2 = 0; t2 < continuousData.length; t2++) {
|
||||
// Compute norm in the continuous space
|
||||
norms[t2] = normCalculator.norm(continuousNewStates[t], continuousData[t2]);
|
||||
}
|
||||
|
||||
// Then find the k closest neighbours in the same discrete bin
|
||||
double eps_x = MatrixUtils.kthMinSubjectTo(norms, k, discreteData, discreteNewStates[t]);
|
||||
double fixedPartOfLocals = digammaK + MathsUtils.digamma(totalObservations); // Use N_samplesForPdfs here because that's what would be in denominator of probability functions
|
||||
double testSum = 0.0, avNx = 0.0, avNy = 0.0;
|
||||
double[] locals = new double[discreteNewStates.length];
|
||||
for (int t = 0; t < discreteNewStates.length; t++) {
|
||||
|
||||
int b = discreteNewStates[t];
|
||||
|
||||
double[][] x = new double[][] {continuousNewStates[t]};
|
||||
double eps_x = kdTreeBins[b].findKNearestNeighbours(k, x).poll().distance;
|
||||
int n_x = kdTreeJoint.countPointsWithinR(x, eps_x, true);
|
||||
int n_y = counts[b];
|
||||
|
||||
// Count the number of points whose x distance is less
|
||||
// than or equal to eps_x
|
||||
int n_x = 0;
|
||||
for (int t2 = 0; t2 < continuousData.length; t2++) {
|
||||
if (norms[t2] <= eps_x) {
|
||||
n_x++;
|
||||
}
|
||||
}
|
||||
// n_y is number of points in that discrete bin
|
||||
int n_y = counts[discreteData[t]];
|
||||
avNx += n_x;
|
||||
avNy += n_y;
|
||||
// Now compute the local value:
|
||||
locals[t] = fixedPartOfLocals -
|
||||
MathsUtils.digamma(n_x) - MathsUtils.digamma(n_y);
|
||||
if (debug) {
|
||||
|
||||
if (debug) {
|
||||
testSum += locals[t];
|
||||
avNx += n_x;
|
||||
avNy += n_y;
|
||||
if (dimensions == 1) {
|
||||
System.out.printf("t=%d: x=%.3f, eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
|
||||
t, continuousNewStates[t][0], eps_x, n_x, n_y, locals[t], testSum);
|
||||
|
|
@ -643,11 +787,11 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
System.out.printf("t=%d: eps_x=%.3f, n_x=%d, n_y=%d, local=%.3f, running total = %.5f\n",
|
||||
t, eps_x, n_x, n_y, locals[t], testSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
avNx /= (double)N_newObservations;
|
||||
avNy /= (double)N_newObservations;
|
||||
avNx /= (double) discreteNewStates.length;
|
||||
avNy /= (double) discreteNewStates.length;
|
||||
System.out.printf("Average n_x=%.3f, Average n_y=%.3f\n", avNx, avNy);
|
||||
}
|
||||
|
||||
|
|
@ -658,11 +802,30 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKraskov implements Mutu
|
|||
this.debug = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MI last calculated in a call to {@link #computeAverageLocalOfObservations()}
|
||||
* or {@link #computeLocalOfPreviousObservations()} after the previous
|
||||
* {@link #initialise()} call.
|
||||
*
|
||||
* @return the last computed average mutual information
|
||||
*/
|
||||
public double getLastAverage() {
|
||||
return mi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of samples to be used for the PDFs here
|
||||
* which have been supplied by calls to
|
||||
* {@link #setObservations(double[][], int[])},
|
||||
* {@link #addObservations(double[][], int[])}
|
||||
* etc.
|
||||
*
|
||||
* <p>Note that the number of samples may not be equal to the length of time-series
|
||||
* supplied (i.e. where a {@link PROP_TIME_DIFF}
|
||||
* is set).
|
||||
* </p>
|
||||
*/
|
||||
public int getNumObservations() {
|
||||
return continuousData.length;
|
||||
return totalObservations;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,16 +27,120 @@ package infodynamics.utils;
|
|||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public class AnalyticMeasurementDistribution extends MeasurementDistribution {
|
||||
public abstract class AnalyticMeasurementDistribution extends MeasurementDistribution {
|
||||
|
||||
/**
|
||||
* Construct the instance
|
||||
*
|
||||
* @param actualValue observed value of the information-theoretic measure
|
||||
* @param pValue p-value that surrogate measurements are greater than
|
||||
* the observed value.
|
||||
* the observed value. (1 - CDF of null at the observed value)
|
||||
*/
|
||||
protected AnalyticMeasurementDistribution(double actualValue, double pValue) {
|
||||
super(actualValue, pValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the <b>analytic</b> p-value
|
||||
* for the given estimate (i.e. the input argument, not the
|
||||
* estimate produced by any particular observations supplied)
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value (possibly
|
||||
* in the context of a conditional value).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE as per the references below.
|
||||
* </p>
|
||||
*
|
||||
* <p><b>References:</b><br/>
|
||||
* <ul>
|
||||
* <li>J.T. Lizier, "JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems", 2014.</li>
|
||||
* <li>Brillinger, <a href="http://www.stat.berkeley.edu/~brill/Papers/MIBJPS.pdf">
|
||||
* "Some data analyses using mutual information"</a>,
|
||||
* Brazilian Journal of Probability and Statistics, <b>18</b>, p. 163, (2004)</li>
|
||||
* <li>Cheng et al., <a href="http://www.jds-online.com/file_download/112/JDS-369.pdf">
|
||||
* "Data Information in Contingency Tables: A Fallacy of Hierarchical Loglinear Models"</a>,
|
||||
* Journal of Data Science, <b>4</b>, p. 387 (2006).</li>
|
||||
* <li>Geweke, <a href="http://dx.doi.org/10.1080/01621459.1982.10477803">
|
||||
* "Measurement of Linear Dependence and Feedback between Multiple Time Series"</a>,
|
||||
* Journal of the American Statistical Association, <b>77</b>, p. 304-313 (1982).</li>
|
||||
* <li>Barnett and Bossomaier, <a href="http://arxiv.org/abs/1205.6339">
|
||||
* "Transfer Entropy as a Log-likelihood Ratio"</a>,
|
||||
* Physical Review Letters, <b>109</b>, p. 138105+ (2012).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return p-value for the given channel measure score under this null hypothesis.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract double computePValueForGivenEstimate(double estimate);
|
||||
|
||||
/**
|
||||
* Computes p-values corresponding to a set of estimates, each done via
|
||||
* {@link #computePValueForGivenEstimate(double)}
|
||||
*
|
||||
* @param estimates array of estimates to return corresponding p-values for.
|
||||
* @return
|
||||
*/
|
||||
public double[] computePValuesForGivenEstimates(double[] estimates) {
|
||||
double[] pValues = new double[estimates.length];
|
||||
for (int i = 0; i < estimates.length; i++) {
|
||||
pValues[i] = computePValueForGivenEstimate(estimates[i]);
|
||||
}
|
||||
return estimates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the estimated observed measured value corresponding to
|
||||
* a given p-value
|
||||
* (derived from how the given estimates are <b>analytically</b> distributed
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value, possibly
|
||||
* in the context of a conditional value).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE as per the references below.
|
||||
* </p>
|
||||
*
|
||||
* <p><b>References:</b><br/>
|
||||
* <ul>
|
||||
* <li>J.T. Lizier, "JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems", 2014.</li>
|
||||
* <li>Brillinger, <a href="http://www.stat.berkeley.edu/~brill/Papers/MIBJPS.pdf">
|
||||
* "Some data analyses using mutual information"</a>,
|
||||
* Brazilian Journal of Probability and Statistics, <b>18</b>, p. 163, (2004)</li>
|
||||
* <li>Cheng et al., <a href="http://www.jds-online.com/file_download/112/JDS-369.pdf">
|
||||
* "Data Information in Contingency Tables: A Fallacy of Hierarchical Loglinear Models"</a>,
|
||||
* Journal of Data Science, <b>4</b>, p. 387 (2006).</li>
|
||||
* <li>Geweke, <a href="http://dx.doi.org/10.1080/01621459.1982.10477803">
|
||||
* "Measurement of Linear Dependence and Feedback between Multiple Time Series"</a>,
|
||||
* Journal of the American Statistical Association, <b>77</b>, p. 304-313 (1982).</li>
|
||||
* <li>Barnett and Bossomaier, <a href="http://arxiv.org/abs/1205.6339">
|
||||
* "Transfer Entropy as a Log-likelihood Ratio"</a>,
|
||||
* Physical Review Letters, <b>109</b>, p. 138105+ (2012).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param pValue the sample p-value for the given channel measure score under this null hypothesis.
|
||||
* @return the estimate of the channel measure score corresponding to
|
||||
* the given p-value under this null hypothesis.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract double computeEstimateForGivenPValue(double pValue);
|
||||
|
||||
/**
|
||||
* Computes estimates corresponding to a set of p-values, each done via
|
||||
* {@link #computeEstimateForGivenPValue(double)}
|
||||
*
|
||||
* @param pValues array of p-values to return corresponding estimates for.
|
||||
* @return
|
||||
*/
|
||||
public double[] computeEstimatesForGivenPValues(double[] pValues) {
|
||||
double[] estimates = new double[pValues.length];
|
||||
for (int i = 0; i < pValues.length; i++) {
|
||||
estimates[i] = computeEstimateForGivenPValue(pValues[i]);
|
||||
}
|
||||
return estimates;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ package infodynamics.utils;
|
|||
* Calculators implementing this interface must provide a
|
||||
* {@link #computeSignificance()} method to compute
|
||||
* the statistical significance of their measurement, returning an analytically
|
||||
* determined distribution {@link AnalyticNullDistributionComputer}.
|
||||
* determined distribution {@link AnalyticMeasurementDistribution}.
|
||||
*
|
||||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@
|
|||
|
||||
package infodynamics.utils;
|
||||
|
||||
import infodynamics.utils.commonsmath3.distribution.ChiSquaredDistribution;
|
||||
|
||||
/**
|
||||
* Class to represent analytic distributions of info theoretic measurements under
|
||||
* some null hypothesis of a relationship between the variables, where that
|
||||
* distribution is a Chi Square distribution.
|
||||
* some null hypothesis of a relationship between the variables, where the
|
||||
* distribution of <b>a function of</b> those information-theoretic measurements
|
||||
* is a Chi Square distribution.
|
||||
*
|
||||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
|
|
@ -35,13 +38,43 @@ public class ChiSquareMeasurementDistribution extends
|
|||
protected int degreesOfFreedom;
|
||||
|
||||
/**
|
||||
* Construct the distribution
|
||||
* The number of observations that the information theoretic estimate
|
||||
* is computed from
|
||||
*/
|
||||
protected int numObservations;
|
||||
|
||||
/**
|
||||
* An object of the chi2dist class from commons.math3,
|
||||
* which we'll use to access the distribution values
|
||||
*/
|
||||
protected ChiSquaredDistribution chi2dist;
|
||||
|
||||
/**
|
||||
* Construct the distribution.
|
||||
* Note: the Chi squared distribution is technically
|
||||
* of 2*numObservations*(the info theoretic estimate), not
|
||||
* of the info theoretic measurement itself.
|
||||
*
|
||||
* @param actualValue actual observed value
|
||||
* @param actualValue actual observed information-theoretic value
|
||||
* @param numObservations the number of observations that the information theoretic estimate
|
||||
* is computed from
|
||||
* @param degreesOfFreedom degrees of freedom for the distribution
|
||||
*/
|
||||
public ChiSquareMeasurementDistribution(double actualValue, int degreesOfFreedom) {
|
||||
super(actualValue, 1 - MathsUtils.chiSquareCdf(actualValue, degreesOfFreedom));
|
||||
public ChiSquareMeasurementDistribution(double actualValue,
|
||||
int numObservations, int degreesOfFreedom) {
|
||||
super(actualValue, 1 - MathsUtils.chiSquareCdf(2.0*((double)numObservations)*actualValue, degreesOfFreedom));
|
||||
this.numObservations = numObservations;
|
||||
this.degreesOfFreedom = degreesOfFreedom;
|
||||
chi2dist = new ChiSquaredDistribution(degreesOfFreedom);
|
||||
}
|
||||
|
||||
public double computePValueForGivenEstimate(double estimate) {
|
||||
return 1 - MathsUtils.chiSquareCdf(2.0*((double)numObservations)*estimate, degreesOfFreedom);
|
||||
}
|
||||
|
||||
public double computeEstimateForGivenPValue(double pValue) {
|
||||
return chi2dist.inverseCumulativeProbability(1 - pValue) / (2.0*((double)numObservations));
|
||||
// Could also call the following, but this doesn't re-use our objects:
|
||||
// return MathsUtils.chiSquareInv(1 - pValue, degreesOfFreedom);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, Joseph T. Lizier, Ipek Oezdemir and Pedro Mediano
|
||||
*
|
||||
* 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.utils;
|
||||
|
||||
/**
|
||||
* Calculators implementing this interface must provide
|
||||
* {@link #computeSignificance(int)} and {@link #computeSignificance(int[][])}
|
||||
* methods to compute
|
||||
* the statistical significance of their measurement, returning an empirically
|
||||
* determined distribution {@link EmpiricalMeasurementDistribution}.
|
||||
*
|
||||
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
|
||||
* <a href="http://lizier.me/joseph/">www</a>)
|
||||
*/
|
||||
public interface EmpiricalNullDistributionComputer {
|
||||
|
||||
/**
|
||||
* Generate an <b>empirical</b> (bootstrapped) distribution of what the given measure would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value (possibly
|
||||
* in the context of a conditional value).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done for MI,
|
||||
* conditional MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using addObservations methods etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p><b>References:</b><br/>
|
||||
* <ul>
|
||||
* <li>J.T. Lizier, "JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems", 2014.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param numPermutationsToCheck number of surrogate samples to bootstrap
|
||||
* to generate the distribution.
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @return the empirical distribution of measure scores under this null hypothesis.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck) throws Exception;
|
||||
|
||||
/**
|
||||
* Generate an <b>empirical</b> (bootstrapped) distribution of what the given measure would look like,
|
||||
* under a null hypothesis that the source values of our
|
||||
* samples had no relation to the destination value (possibly
|
||||
* in the context of a conditional value).
|
||||
*
|
||||
* <p>See Section II.E "Statistical significance testing" of
|
||||
* the JIDT paper below for a description of how this is done MI,
|
||||
* conditional MI and TE.
|
||||
* </p>
|
||||
*
|
||||
* <p>Note that if several disjoint time-series have been added
|
||||
* as observations using addObservations methods etc.,
|
||||
* then these separate "trials" will be mixed up in the generation
|
||||
* of surrogates here.</p>
|
||||
*
|
||||
* <p>This method (in contrast to {@link #computeSignificance(int)})
|
||||
* allows the user to specify how to construct the surrogates,
|
||||
* such that repeatable results may be obtained.</p>
|
||||
*
|
||||
* <p><b>References:</b><br/>
|
||||
* <ul>
|
||||
* <li>J.T. Lizier, "JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems", 2014.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param newOrderings a specification of how to shuffle the next values
|
||||
* to create the surrogates to generate the distribution with. The first
|
||||
* index is the permutation number (i.e. newOrderings.length is the number
|
||||
* of surrogate samples we use to bootstrap to generate the distribution here.)
|
||||
* Each array newOrderings[i] should be an array of length N (where
|
||||
* would be the value returned by a call to getNumObservations() for the given measure)
|
||||
* containing a permutation of the values in 0..(N-1).
|
||||
* @see "J.T. Lizier, 'JIDT: An information-theoretic
|
||||
* toolkit for studying the dynamics of complex systems', 2014."
|
||||
* @return the empirical distribution of measure scores under this null hypothesis.
|
||||
* @throws Exception where e.g. the newOrderings don't supply arrays of the correct
|
||||
* length matching the number of observations that we have.
|
||||
*/
|
||||
public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package infodynamics.utils;
|
||||
|
||||
import infodynamics.utils.commonsmath3.distribution.ChiSquaredDistribution;
|
||||
import infodynamics.utils.commonsmath3.special.Gamma;
|
||||
|
||||
/**
|
||||
|
|
@ -336,7 +337,7 @@ public class MathsUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* <p>Return the value of the cummulative distribution function of the
|
||||
* <p>Return the value of the cumulative distribution function of the
|
||||
* chi-square distribution, evaluated at x, for k degrees of freedom.</p>
|
||||
*
|
||||
* <p>Note that this relies on our approximation of the error function,
|
||||
|
|
@ -360,6 +361,20 @@ public class MathsUtils {
|
|||
return Gamma.regularizedGammaP(((double)k)/2.0, x/2.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the quantile (inverse of the CDF) for the
|
||||
* given cdf value (between 0 and 1) of the chi-square distribution
|
||||
* with l degrees of freedom.
|
||||
*
|
||||
* @param cdf value of the CDF to evaluate the inverse for
|
||||
* @param k degrees of freedom (must have k>0)
|
||||
* @return
|
||||
*/
|
||||
public static double chiSquareInv(double cdf, int k) {
|
||||
ChiSquaredDistribution chi2dist = new ChiSquaredDistribution(k);
|
||||
return chi2dist.inverseCumulativeProbability(cdf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the lower Incomplete Gamma function,
|
||||
* given arguments s/2 and x/2.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3;
|
||||
|
||||
/**
|
||||
* Interface representing a <a href="http://mathworld.wolfram.com/Field.html">field</a>.
|
||||
* <p>
|
||||
* Classes implementing this interface will often be singletons.
|
||||
* </p>
|
||||
* @param <T> the type of the field elements
|
||||
* @see FieldElement
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface Field<T> {
|
||||
|
||||
/** Get the additive identity of the field.
|
||||
* <p>
|
||||
* The additive identity is the element e<sub>0</sub> of the field such that
|
||||
* for all elements a of the field, the equalities a + e<sub>0</sub> =
|
||||
* e<sub>0</sub> + a = a hold.
|
||||
* </p>
|
||||
* @return additive identity of the field
|
||||
*/
|
||||
T getZero();
|
||||
|
||||
/** Get the multiplicative identity of the field.
|
||||
* <p>
|
||||
* The multiplicative identity is the element e<sub>1</sub> of the field such that
|
||||
* for all elements a of the field, the equalities a × e<sub>1</sub> =
|
||||
* e<sub>1</sub> × a = a hold.
|
||||
* </p>
|
||||
* @return multiplicative identity of the field
|
||||
*/
|
||||
T getOne();
|
||||
|
||||
/**
|
||||
* Returns the runtime class of the FieldElement.
|
||||
*
|
||||
* @return The {@code Class} object that represents the runtime
|
||||
* class of this object.
|
||||
*/
|
||||
Class<? extends FieldElement<T>> getRuntimeClass();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.MathArithmeticException;
|
||||
import infodynamics.utils.commonsmath3.exception.NullArgumentException;
|
||||
|
||||
|
||||
/**
|
||||
* Interface representing <a href="http://mathworld.wolfram.com/Field.html">field</a> elements.
|
||||
* @param <T> the type of the field elements
|
||||
* @see Field
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface FieldElement<T> {
|
||||
|
||||
/** Compute this + a.
|
||||
* @param a element to add
|
||||
* @return a new element representing this + a
|
||||
* @throws NullArgumentException if {@code a} is {@code null}.
|
||||
*/
|
||||
T add(T a) throws NullArgumentException;
|
||||
|
||||
/** Compute this - a.
|
||||
* @param a element to subtract
|
||||
* @return a new element representing this - a
|
||||
* @throws NullArgumentException if {@code a} is {@code null}.
|
||||
*/
|
||||
T subtract(T a) throws NullArgumentException;
|
||||
|
||||
/**
|
||||
* Returns the additive inverse of {@code this} element.
|
||||
* @return the opposite of {@code this}.
|
||||
*/
|
||||
T negate();
|
||||
|
||||
/** Compute n × this. Multiplication by an integer number is defined
|
||||
* as the following sum
|
||||
* <center>
|
||||
* n × this = ∑<sub>i=1</sub><sup>n</sup> this.
|
||||
* </center>
|
||||
* @param n Number of times {@code this} must be added to itself.
|
||||
* @return A new element representing n × this.
|
||||
*/
|
||||
T multiply(int n);
|
||||
|
||||
/** Compute this × a.
|
||||
* @param a element to multiply
|
||||
* @return a new element representing this × a
|
||||
* @throws NullArgumentException if {@code a} is {@code null}.
|
||||
*/
|
||||
T multiply(T a) throws NullArgumentException;
|
||||
|
||||
/** Compute this ÷ a.
|
||||
* @param a element to divide by
|
||||
* @return a new element representing this ÷ a
|
||||
* @throws NullArgumentException if {@code a} is {@code null}.
|
||||
* @throws MathArithmeticException if {@code a} is zero
|
||||
*/
|
||||
T divide(T a) throws NullArgumentException, MathArithmeticException;
|
||||
|
||||
/**
|
||||
* Returns the multiplicative inverse of {@code this} element.
|
||||
* @return the inverse of {@code this}.
|
||||
* @throws MathArithmeticException if {@code this} is zero
|
||||
*/
|
||||
T reciprocal() throws MathArithmeticException;
|
||||
|
||||
/** Get the {@link Field} to which the instance belongs.
|
||||
* @return {@link Field} to which the instance belongs
|
||||
*/
|
||||
Field<T> getField();
|
||||
}
|
||||
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.DimensionMismatchException;
|
||||
|
||||
/**
|
||||
* Interface representing a <a href="http://mathworld.wolfram.com/RealNumber.html">real</a>
|
||||
* <a href="http://mathworld.wolfram.com/Field.html">field</a>.
|
||||
* @param <T> the type of the field elements
|
||||
* @see FieldElement
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface RealFieldElement<T> extends FieldElement<T> {
|
||||
|
||||
/** Get the real value of the number.
|
||||
* @return real value
|
||||
*/
|
||||
double getReal();
|
||||
|
||||
/** '+' operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this+a
|
||||
*/
|
||||
T add(double a);
|
||||
|
||||
/** '-' operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this-a
|
||||
*/
|
||||
T subtract(double a);
|
||||
|
||||
/** '×' operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this×a
|
||||
*/
|
||||
T multiply(double a);
|
||||
|
||||
/** '÷' operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this÷a
|
||||
*/
|
||||
T divide(double a);
|
||||
|
||||
/** IEEE remainder operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this - n × a where n is the closest integer to this/a
|
||||
* (the even integer is chosen for n if this/a is halfway between two integers)
|
||||
*/
|
||||
T remainder(double a);
|
||||
|
||||
/** IEEE remainder operator.
|
||||
* @param a right hand side parameter of the operator
|
||||
* @return this - n × a where n is the closest integer to this/a
|
||||
* (the even integer is chosen for n if this/a is halfway between two integers)
|
||||
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
|
||||
*/
|
||||
T remainder(T a)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/** absolute value.
|
||||
* @return abs(this)
|
||||
*/
|
||||
T abs();
|
||||
|
||||
/** Get the smallest whole number larger than instance.
|
||||
* @return ceil(this)
|
||||
*/
|
||||
T ceil();
|
||||
|
||||
/** Get the largest whole number smaller than instance.
|
||||
* @return floor(this)
|
||||
*/
|
||||
T floor();
|
||||
|
||||
/** Get the whole number that is the nearest to the instance, or the even one if x is exactly half way between two integers.
|
||||
* @return a double number r such that r is an integer r - 0.5 ≤ this ≤ r + 0.5
|
||||
*/
|
||||
T rint();
|
||||
|
||||
/** Get the closest long to instance value.
|
||||
* @return closest long to {@link #getReal()}
|
||||
*/
|
||||
long round();
|
||||
|
||||
/** Compute the signum of the instance.
|
||||
* The signum is -1 for negative numbers, +1 for positive numbers and 0 otherwise
|
||||
* @return -1.0, -0.0, +0.0, +1.0 or NaN depending on sign of a
|
||||
*/
|
||||
T signum();
|
||||
|
||||
/**
|
||||
* Returns the instance with the sign of the argument.
|
||||
* A NaN {@code sign} argument is treated as positive.
|
||||
*
|
||||
* @param sign the sign for the returned value
|
||||
* @return the instance with the same sign as the {@code sign} argument
|
||||
*/
|
||||
T copySign(T sign);
|
||||
|
||||
/**
|
||||
* Returns the instance with the sign of the argument.
|
||||
* A NaN {@code sign} argument is treated as positive.
|
||||
*
|
||||
* @param sign the sign for the returned value
|
||||
* @return the instance with the same sign as the {@code sign} argument
|
||||
*/
|
||||
T copySign(double sign);
|
||||
|
||||
/**
|
||||
* Multiply the instance by a power of 2.
|
||||
* @param n power of 2
|
||||
* @return this × 2<sup>n</sup>
|
||||
*/
|
||||
T scalb(int n);
|
||||
|
||||
/**
|
||||
* Returns the hypotenuse of a triangle with sides {@code this} and {@code y}
|
||||
* - sqrt(<i>this</i><sup>2</sup> +<i>y</i><sup>2</sup>)
|
||||
* avoiding intermediate overflow or underflow.
|
||||
*
|
||||
* <ul>
|
||||
* <li> If either argument is infinite, then the result is positive infinity.</li>
|
||||
* <li> else, if either argument is NaN then the result is NaN.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param y a value
|
||||
* @return sqrt(<i>this</i><sup>2</sup> +<i>y</i><sup>2</sup>)
|
||||
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
|
||||
*/
|
||||
T hypot(T y)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
T reciprocal();
|
||||
|
||||
/** Square root.
|
||||
* @return square root of the instance
|
||||
*/
|
||||
T sqrt();
|
||||
|
||||
/** Cubic root.
|
||||
* @return cubic root of the instance
|
||||
*/
|
||||
T cbrt();
|
||||
|
||||
/** N<sup>th</sup> root.
|
||||
* @param n order of the root
|
||||
* @return n<sup>th</sup> root of the instance
|
||||
*/
|
||||
T rootN(int n);
|
||||
|
||||
/** Power operation.
|
||||
* @param p power to apply
|
||||
* @return this<sup>p</sup>
|
||||
*/
|
||||
T pow(double p);
|
||||
|
||||
/** Integer power operation.
|
||||
* @param n power to apply
|
||||
* @return this<sup>n</sup>
|
||||
*/
|
||||
T pow(int n);
|
||||
|
||||
/** Power operation.
|
||||
* @param e exponent
|
||||
* @return this<sup>e</sup>
|
||||
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
|
||||
*/
|
||||
T pow(T e)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/** Exponential.
|
||||
* @return exponential of the instance
|
||||
*/
|
||||
T exp();
|
||||
|
||||
/** Exponential minus 1.
|
||||
* @return exponential minus one of the instance
|
||||
*/
|
||||
T expm1();
|
||||
|
||||
/** Natural logarithm.
|
||||
* @return logarithm of the instance
|
||||
*/
|
||||
T log();
|
||||
|
||||
/** Shifted natural logarithm.
|
||||
* @return logarithm of one plus the instance
|
||||
*/
|
||||
T log1p();
|
||||
|
||||
// TODO: add this method in 4.0, as it is not possible to do it in 3.2
|
||||
// due to incompatibility of the return type in the Dfp class
|
||||
// /** Base 10 logarithm.
|
||||
// * @return base 10 logarithm of the instance
|
||||
// */
|
||||
// T log10();
|
||||
|
||||
/** Cosine operation.
|
||||
* @return cos(this)
|
||||
*/
|
||||
T cos();
|
||||
|
||||
/** Sine operation.
|
||||
* @return sin(this)
|
||||
*/
|
||||
T sin();
|
||||
|
||||
/** Tangent operation.
|
||||
* @return tan(this)
|
||||
*/
|
||||
T tan();
|
||||
|
||||
/** Arc cosine operation.
|
||||
* @return acos(this)
|
||||
*/
|
||||
T acos();
|
||||
|
||||
/** Arc sine operation.
|
||||
* @return asin(this)
|
||||
*/
|
||||
T asin();
|
||||
|
||||
/** Arc tangent operation.
|
||||
* @return atan(this)
|
||||
*/
|
||||
T atan();
|
||||
|
||||
/** Two arguments arc tangent operation.
|
||||
* @param x second argument of the arc tangent
|
||||
* @return atan2(this, x)
|
||||
* @exception DimensionMismatchException if number of free parameters or orders are inconsistent
|
||||
*/
|
||||
T atan2(T x)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/** Hyperbolic cosine operation.
|
||||
* @return cosh(this)
|
||||
*/
|
||||
T cosh();
|
||||
|
||||
/** Hyperbolic sine operation.
|
||||
* @return sinh(this)
|
||||
*/
|
||||
T sinh();
|
||||
|
||||
/** Hyperbolic tangent operation.
|
||||
* @return tanh(this)
|
||||
*/
|
||||
T tanh();
|
||||
|
||||
/** Inverse hyperbolic cosine operation.
|
||||
* @return acosh(this)
|
||||
*/
|
||||
T acosh();
|
||||
|
||||
/** Inverse hyperbolic sine operation.
|
||||
* @return asin(this)
|
||||
*/
|
||||
T asinh();
|
||||
|
||||
/** Inverse hyperbolic tangent operation.
|
||||
* @return atanh(this)
|
||||
*/
|
||||
T atanh();
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a Factors.
|
||||
* @param b Factors.
|
||||
* @return <code>Σ<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
|
||||
* @throws DimensionMismatchException if arrays dimensions don't match
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(T[] a, T[] b)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a Factors.
|
||||
* @param b Factors.
|
||||
* @return <code>Σ<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
|
||||
* @throws DimensionMismatchException if arrays dimensions don't match
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(double[] a, T[] b)
|
||||
throws DimensionMismatchException;
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub>
|
||||
* @see #linearCombination(Object, Object, Object, Object, Object, Object)
|
||||
* @see #linearCombination(Object, Object, Object, Object, Object, Object, Object, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(T a1, T b1, T a2, T b2);
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub>
|
||||
* @see #linearCombination(double, Object, double, Object, double, Object)
|
||||
* @see #linearCombination(double, Object, double, Object, double, Object, double, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(double a1, T b1, double a2, T b2);
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @param a3 first factor of the third term
|
||||
* @param b3 second factor of the third term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub> + a<sub>3</sub>×b<sub>3</sub>
|
||||
* @see #linearCombination(Object, Object, Object, Object)
|
||||
* @see #linearCombination(Object, Object, Object, Object, Object, Object, Object, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(T a1, T b1, T a2, T b2, T a3, T b3);
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @param a3 first factor of the third term
|
||||
* @param b3 second factor of the third term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub> + a<sub>3</sub>×b<sub>3</sub>
|
||||
* @see #linearCombination(double, Object, double, Object)
|
||||
* @see #linearCombination(double, Object, double, Object, double, Object, double, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(double a1, T b1, double a2, T b2, double a3, T b3);
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @param a3 first factor of the third term
|
||||
* @param b3 second factor of the third term
|
||||
* @param a4 first factor of the third term
|
||||
* @param b4 second factor of the third term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub> + a<sub>3</sub>×b<sub>3</sub> +
|
||||
* a<sub>4</sub>×b<sub>4</sub>
|
||||
* @see #linearCombination(Object, Object, Object, Object)
|
||||
* @see #linearCombination(Object, Object, Object, Object, Object, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(T a1, T b1, T a2, T b2, T a3, T b3, T a4, T b4);
|
||||
|
||||
/**
|
||||
* Compute a linear combination.
|
||||
* @param a1 first factor of the first term
|
||||
* @param b1 second factor of the first term
|
||||
* @param a2 first factor of the second term
|
||||
* @param b2 second factor of the second term
|
||||
* @param a3 first factor of the third term
|
||||
* @param b3 second factor of the third term
|
||||
* @param a4 first factor of the third term
|
||||
* @param b4 second factor of the third term
|
||||
* @return a<sub>1</sub>×b<sub>1</sub> +
|
||||
* a<sub>2</sub>×b<sub>2</sub> + a<sub>3</sub>×b<sub>3</sub> +
|
||||
* a<sub>4</sub>×b<sub>4</sub>
|
||||
* @see #linearCombination(double, Object, double, Object)
|
||||
* @see #linearCombination(double, Object, double, Object, double, Object)
|
||||
* @since 3.2
|
||||
*/
|
||||
T linearCombination(double a1, T b1, double a2, T b2, double a3, T b3, double a4, T b4);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.analysis;
|
||||
|
||||
/**
|
||||
* An interface representing a univariate real function.
|
||||
* <p>
|
||||
* When a <em>user-defined</em> function encounters an error during
|
||||
* evaluation, the {@link #value(double) value} method should throw a
|
||||
* <em>user-defined</em> unchecked exception.</p>
|
||||
* <p>
|
||||
* The following code excerpt shows the recommended way to do that using
|
||||
* a root solver as an example, but the same construct is applicable to
|
||||
* ODE integrators or optimizers.</p>
|
||||
*
|
||||
* <pre>
|
||||
* private static class LocalException extends RuntimeException {
|
||||
* // The x value that caused the problem.
|
||||
* private final double x;
|
||||
*
|
||||
* public LocalException(double x) {
|
||||
* this.x = x;
|
||||
* }
|
||||
*
|
||||
* public double getX() {
|
||||
* return x;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* private static class MyFunction implements UnivariateFunction {
|
||||
* public double value(double x) {
|
||||
* double y = hugeFormula(x);
|
||||
* if (somethingBadHappens) {
|
||||
* throw new LocalException(x);
|
||||
* }
|
||||
* return y;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public void compute() {
|
||||
* try {
|
||||
* solver.solve(maxEval, new MyFunction(a, b, c), min, max);
|
||||
* } catch (LocalException le) {
|
||||
* // Retrieve the x value.
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* As shown, the exception is local to the user's code and it is guaranteed
|
||||
* that Apache Commons Math will not catch it.
|
||||
*
|
||||
*/
|
||||
public interface UnivariateFunction {
|
||||
/**
|
||||
* Compute the value of the function.
|
||||
*
|
||||
* @param x Point at which the function value should be computed.
|
||||
* @return the value of the function.
|
||||
* @throws IllegalArgumentException when the activated method itself can
|
||||
* ascertain that a precondition, specified in the API expressed at the
|
||||
* level of the activated method, has been violated.
|
||||
* When Commons Math throws an {@code IllegalArgumentException}, it is
|
||||
* usually the consequence of checking the actual parameters passed to
|
||||
* the method.
|
||||
*/
|
||||
double value(double x);
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
|
||||
/**
|
||||
* Base class for solvers.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractUnivariateSolver
|
||||
extends BaseAbstractUnivariateSolver<UnivariateFunction>
|
||||
implements UnivariateSolver {
|
||||
/**
|
||||
* Construct a solver with given absolute accuracy.
|
||||
*
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
*/
|
||||
protected AbstractUnivariateSolver(final double absoluteAccuracy) {
|
||||
super(absoluteAccuracy);
|
||||
}
|
||||
/**
|
||||
* Construct a solver with given accuracies.
|
||||
*
|
||||
* @param relativeAccuracy Maximum relative error.
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
*/
|
||||
protected AbstractUnivariateSolver(final double relativeAccuracy,
|
||||
final double absoluteAccuracy) {
|
||||
super(relativeAccuracy, absoluteAccuracy);
|
||||
}
|
||||
/**
|
||||
* Construct a solver with given accuracies.
|
||||
*
|
||||
* @param relativeAccuracy Maximum relative error.
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
* @param functionValueAccuracy Maximum function value error.
|
||||
*/
|
||||
protected AbstractUnivariateSolver(final double relativeAccuracy,
|
||||
final double absoluteAccuracy,
|
||||
final double functionValueAccuracy) {
|
||||
super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
|
||||
/** The kinds of solutions that a {@link BracketedUnivariateSolver
|
||||
* (bracketed univariate real) root-finding algorithm} may accept as solutions.
|
||||
* This basically controls whether or not under-approximations and
|
||||
* over-approximations are allowed.
|
||||
*
|
||||
* <p>If all solutions are accepted ({@link #ANY_SIDE}), then the solution
|
||||
* that the root-finding algorithm returns for a given root may be equal to the
|
||||
* actual root, but it may also be an approximation that is slightly smaller
|
||||
* or slightly larger than the actual root. Root-finding algorithms generally
|
||||
* only guarantee that the returned solution is within the requested
|
||||
* tolerances. In certain cases however, in particular for
|
||||
* {@link infodynamics.utils.commonsmath3.ode.events.EventHandler state events} of
|
||||
* {@link infodynamics.utils.commonsmath3.ode.ODEIntegrator ODE solvers}, it
|
||||
* may be necessary to guarantee that a solution is returned that lies on a
|
||||
* specific side the solution.</p>
|
||||
*
|
||||
* @see BracketedUnivariateSolver
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum AllowedSolution {
|
||||
/** There are no additional side restriction on the solutions for
|
||||
* root-finding. That is, both under-approximations and over-approximations
|
||||
* are allowed. So, if a function f(x) has a root at x = x0, then the
|
||||
* root-finding result s may be smaller than x0, equal to x0, or greater
|
||||
* than x0.
|
||||
*/
|
||||
ANY_SIDE,
|
||||
|
||||
/** Only solutions that are less than or equal to the actual root are
|
||||
* acceptable as solutions for root-finding. In other words,
|
||||
* over-approximations are not allowed. So, if a function f(x) has a root
|
||||
* at x = x0, then the root-finding result s must satisfy s <= x0.
|
||||
*/
|
||||
LEFT_SIDE,
|
||||
|
||||
/** Only solutions that are greater than or equal to the actual root are
|
||||
* acceptable as solutions for root-finding. In other words,
|
||||
* under-approximations are not allowed. So, if a function f(x) has a root
|
||||
* at x = x0, then the root-finding result s must satisfy s >= x0.
|
||||
*/
|
||||
RIGHT_SIDE,
|
||||
|
||||
/** Only solutions for which values are less than or equal to zero are
|
||||
* acceptable as solutions for root-finding. So, if a function f(x) has
|
||||
* a root at x = x0, then the root-finding result s must satisfy f(s) <= 0.
|
||||
*/
|
||||
BELOW_SIDE,
|
||||
|
||||
/** Only solutions for which values are greater than or equal to zero are
|
||||
* acceptable as solutions for root-finding. So, if a function f(x) has
|
||||
* a root at x = x0, then the root-finding result s must satisfy f(s) >= 0.
|
||||
*/
|
||||
ABOVE_SIDE;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
import infodynamics.utils.commonsmath3.exception.MaxCountExceededException;
|
||||
import infodynamics.utils.commonsmath3.exception.NoBracketingException;
|
||||
import infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException;
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException;
|
||||
import infodynamics.utils.commonsmath3.exception.NullArgumentException;
|
||||
import infodynamics.utils.commonsmath3.util.IntegerSequence;
|
||||
import infodynamics.utils.commonsmath3.util.MathUtils;
|
||||
|
||||
/**
|
||||
* Provide a default implementation for several functions useful to generic
|
||||
* solvers.
|
||||
* The default values for relative and function tolerances are 1e-14
|
||||
* and 1e-15, respectively. It is however highly recommended to not
|
||||
* rely on the default, but rather carefully consider values that match
|
||||
* user's expectations, as well as the specifics of each implementation.
|
||||
*
|
||||
* @param <FUNC> Type of function to solve.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class BaseAbstractUnivariateSolver<FUNC extends UnivariateFunction>
|
||||
implements BaseUnivariateSolver<FUNC> {
|
||||
/** Default relative accuracy. */
|
||||
private static final double DEFAULT_RELATIVE_ACCURACY = 1e-14;
|
||||
/** Default function value accuracy. */
|
||||
private static final double DEFAULT_FUNCTION_VALUE_ACCURACY = 1e-15;
|
||||
/** Function value accuracy. */
|
||||
private final double functionValueAccuracy;
|
||||
/** Absolute accuracy. */
|
||||
private final double absoluteAccuracy;
|
||||
/** Relative accuracy. */
|
||||
private final double relativeAccuracy;
|
||||
/** Evaluations counter. */
|
||||
private IntegerSequence.Incrementor evaluations;
|
||||
/** Lower end of search interval. */
|
||||
private double searchMin;
|
||||
/** Higher end of search interval. */
|
||||
private double searchMax;
|
||||
/** Initial guess. */
|
||||
private double searchStart;
|
||||
/** Function to solve. */
|
||||
private FUNC function;
|
||||
|
||||
/**
|
||||
* Construct a solver with given absolute accuracy.
|
||||
*
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
*/
|
||||
protected BaseAbstractUnivariateSolver(final double absoluteAccuracy) {
|
||||
this(DEFAULT_RELATIVE_ACCURACY,
|
||||
absoluteAccuracy,
|
||||
DEFAULT_FUNCTION_VALUE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a solver with given accuracies.
|
||||
*
|
||||
* @param relativeAccuracy Maximum relative error.
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
*/
|
||||
protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
|
||||
final double absoluteAccuracy) {
|
||||
this(relativeAccuracy,
|
||||
absoluteAccuracy,
|
||||
DEFAULT_FUNCTION_VALUE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a solver with given accuracies.
|
||||
*
|
||||
* @param relativeAccuracy Maximum relative error.
|
||||
* @param absoluteAccuracy Maximum absolute error.
|
||||
* @param functionValueAccuracy Maximum function value error.
|
||||
*/
|
||||
protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
|
||||
final double absoluteAccuracy,
|
||||
final double functionValueAccuracy) {
|
||||
this.absoluteAccuracy = absoluteAccuracy;
|
||||
this.relativeAccuracy = relativeAccuracy;
|
||||
this.functionValueAccuracy = functionValueAccuracy;
|
||||
this.evaluations = IntegerSequence.Incrementor.create();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public int getMaxEvaluations() {
|
||||
return evaluations.getMaximalCount();
|
||||
}
|
||||
/** {@inheritDoc} */
|
||||
public int getEvaluations() {
|
||||
return evaluations.getCount();
|
||||
}
|
||||
/**
|
||||
* @return the lower end of the search interval.
|
||||
*/
|
||||
public double getMin() {
|
||||
return searchMin;
|
||||
}
|
||||
/**
|
||||
* @return the higher end of the search interval.
|
||||
*/
|
||||
public double getMax() {
|
||||
return searchMax;
|
||||
}
|
||||
/**
|
||||
* @return the initial guess.
|
||||
*/
|
||||
public double getStartValue() {
|
||||
return searchStart;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public double getAbsoluteAccuracy() {
|
||||
return absoluteAccuracy;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public double getRelativeAccuracy() {
|
||||
return relativeAccuracy;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public double getFunctionValueAccuracy() {
|
||||
return functionValueAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the objective function value.
|
||||
*
|
||||
* @param point Point at which the objective function must be evaluated.
|
||||
* @return the objective function value at specified point.
|
||||
* @throws TooManyEvaluationsException if the maximal number of evaluations
|
||||
* is exceeded.
|
||||
*/
|
||||
protected double computeObjectiveValue(double point)
|
||||
throws TooManyEvaluationsException {
|
||||
incrementEvaluationCount();
|
||||
return function.value(point);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for computation.
|
||||
* Subclasses must call this method if they override any of the
|
||||
* {@code solve} methods.
|
||||
*
|
||||
* @param f Function to solve.
|
||||
* @param min Lower bound for the interval.
|
||||
* @param max Upper bound for the interval.
|
||||
* @param startValue Start value to use.
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @exception NullArgumentException if f is null
|
||||
*/
|
||||
protected void setup(int maxEval,
|
||||
FUNC f,
|
||||
double min, double max,
|
||||
double startValue)
|
||||
throws NullArgumentException {
|
||||
// Checks.
|
||||
MathUtils.checkNotNull(f);
|
||||
|
||||
// Reset.
|
||||
searchMin = min;
|
||||
searchMax = max;
|
||||
searchStart = startValue;
|
||||
function = f;
|
||||
evaluations = evaluations.withMaximalCount(maxEval).withStart(0);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double solve(int maxEval, FUNC f, double min, double max, double startValue)
|
||||
throws TooManyEvaluationsException,
|
||||
NoBracketingException {
|
||||
// Initialization.
|
||||
setup(maxEval, f, min, max, startValue);
|
||||
|
||||
// Perform computation.
|
||||
return doSolve();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double solve(int maxEval, FUNC f, double min, double max) {
|
||||
return solve(maxEval, f, min, max, min + 0.5 * (max - min));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double solve(int maxEval, FUNC f, double startValue)
|
||||
throws TooManyEvaluationsException,
|
||||
NoBracketingException {
|
||||
return solve(maxEval, f, Double.NaN, Double.NaN, startValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for implementing actual optimization algorithms in derived
|
||||
* classes.
|
||||
*
|
||||
* @return the root.
|
||||
* @throws TooManyEvaluationsException if the maximal number of evaluations
|
||||
* is exceeded.
|
||||
* @throws NoBracketingException if the initial search interval does not bracket
|
||||
* a root and the solver requires it.
|
||||
*/
|
||||
protected abstract double doSolve()
|
||||
throws TooManyEvaluationsException, NoBracketingException;
|
||||
|
||||
/**
|
||||
* Check whether the function takes opposite signs at the endpoints.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @return {@code true} if the function values have opposite signs at the
|
||||
* given points.
|
||||
*/
|
||||
protected boolean isBracketing(final double lower,
|
||||
final double upper) {
|
||||
return UnivariateSolverUtils.isBracketing(function, lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the arguments form a (strictly) increasing sequence.
|
||||
*
|
||||
* @param start First number.
|
||||
* @param mid Second number.
|
||||
* @param end Third number.
|
||||
* @return {@code true} if the arguments form an increasing sequence.
|
||||
*/
|
||||
protected boolean isSequence(final double start,
|
||||
final double mid,
|
||||
final double end) {
|
||||
return UnivariateSolverUtils.isSequence(start, mid, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the endpoints specify an interval.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NumberIsTooLargeException if {@code lower >= upper}.
|
||||
*/
|
||||
protected void verifyInterval(final double lower,
|
||||
final double upper)
|
||||
throws NumberIsTooLargeException {
|
||||
UnivariateSolverUtils.verifyInterval(lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that {@code lower < initial < upper}.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param initial Initial value.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NumberIsTooLargeException if {@code lower >= initial} or
|
||||
* {@code initial >= upper}.
|
||||
*/
|
||||
protected void verifySequence(final double lower,
|
||||
final double initial,
|
||||
final double upper)
|
||||
throws NumberIsTooLargeException {
|
||||
UnivariateSolverUtils.verifySequence(lower, initial, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the endpoints specify an interval and the function takes
|
||||
* opposite signs at the endpoints.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NullArgumentException if the function has not been set.
|
||||
* @throws NoBracketingException if the function has the same sign at
|
||||
* the endpoints.
|
||||
*/
|
||||
protected void verifyBracketing(final double lower,
|
||||
final double upper)
|
||||
throws NullArgumentException,
|
||||
NoBracketingException {
|
||||
UnivariateSolverUtils.verifyBracketing(function, lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the evaluation count by one.
|
||||
* Method {@link #computeObjectiveValue(double)} calls this method internally.
|
||||
* It is provided for subclasses that do not exclusively use
|
||||
* {@code computeObjectiveValue} to solve the function.
|
||||
* See e.g. {@link AbstractUnivariateDifferentiableSolver}.
|
||||
*
|
||||
* @throws TooManyEvaluationsException when the allowed number of function
|
||||
* evaluations has been exhausted.
|
||||
*/
|
||||
protected void incrementEvaluationCount()
|
||||
throws TooManyEvaluationsException {
|
||||
try {
|
||||
evaluations.increment();
|
||||
} catch (MaxCountExceededException e) {
|
||||
throw new TooManyEvaluationsException(e.getMax());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
import infodynamics.utils.commonsmath3.exception.MathIllegalArgumentException;
|
||||
import infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for (univariate real) rootfinding algorithms.
|
||||
* Implementations will search for only one zero in the given interval.
|
||||
*
|
||||
* This class is not intended for use outside of the Apache Commons Math
|
||||
* library, regular user should rely on more specific interfaces like
|
||||
* {@link UnivariateSolver}, {@link PolynomialSolver} or {@link
|
||||
* DifferentiableUnivariateSolver}.
|
||||
* @param <FUNC> Type of function to solve.
|
||||
*
|
||||
* @since 3.0
|
||||
* @see UnivariateSolver
|
||||
* @see PolynomialSolver
|
||||
* @see DifferentiableUnivariateSolver
|
||||
*/
|
||||
public interface BaseUnivariateSolver<FUNC extends UnivariateFunction> {
|
||||
/**
|
||||
* Get the maximum number of function evaluations.
|
||||
*
|
||||
* @return the maximum number of function evaluations.
|
||||
*/
|
||||
int getMaxEvaluations();
|
||||
|
||||
/**
|
||||
* Get the number of evaluations of the objective function.
|
||||
* The number of evaluations corresponds to the last call to the
|
||||
* {@code optimize} method. It is 0 if the method has not been
|
||||
* called yet.
|
||||
*
|
||||
* @return the number of evaluations of the objective function.
|
||||
*/
|
||||
int getEvaluations();
|
||||
|
||||
/**
|
||||
* Get the absolute accuracy of the solver. Solutions returned by the
|
||||
* solver should be accurate to this tolerance, i.e., if ε is the
|
||||
* absolute accuracy of the solver and {@code v} is a value returned by
|
||||
* one of the {@code solve} methods, then a root of the function should
|
||||
* exist somewhere in the interval ({@code v} - ε, {@code v} + ε).
|
||||
*
|
||||
* @return the absolute accuracy.
|
||||
*/
|
||||
double getAbsoluteAccuracy();
|
||||
|
||||
/**
|
||||
* Get the relative accuracy of the solver. The contract for relative
|
||||
* accuracy is the same as {@link #getAbsoluteAccuracy()}, but using
|
||||
* relative, rather than absolute error. If ρ is the relative accuracy
|
||||
* configured for a solver and {@code v} is a value returned, then a root
|
||||
* of the function should exist somewhere in the interval
|
||||
* ({@code v} - ρ {@code v}, {@code v} + ρ {@code v}).
|
||||
*
|
||||
* @return the relative accuracy.
|
||||
*/
|
||||
double getRelativeAccuracy();
|
||||
|
||||
/**
|
||||
* Get the function value accuracy of the solver. If {@code v} is
|
||||
* a value returned by the solver for a function {@code f},
|
||||
* then by contract, {@code |f(v)|} should be less than or equal to
|
||||
* the function value accuracy configured for the solver.
|
||||
*
|
||||
* @return the function value accuracy.
|
||||
*/
|
||||
double getFunctionValueAccuracy();
|
||||
|
||||
/**
|
||||
* Solve for a zero root in the given interval.
|
||||
* A solver may require that the interval brackets a single zero root.
|
||||
* Solvers that do require bracketing should be able to handle the case
|
||||
* where one of the endpoints is itself a root.
|
||||
*
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @param f Function to solve.
|
||||
* @param min Lower bound for the interval.
|
||||
* @param max Upper bound for the interval.
|
||||
* @return a value where the function is zero.
|
||||
* @throws MathIllegalArgumentException
|
||||
* if the arguments do not satisfy the requirements specified by the solver.
|
||||
* @throws TooManyEvaluationsException if
|
||||
* the allowed number of evaluations is exceeded.
|
||||
*/
|
||||
double solve(int maxEval, FUNC f, double min, double max)
|
||||
throws MathIllegalArgumentException, TooManyEvaluationsException;
|
||||
|
||||
/**
|
||||
* Solve for a zero in the given interval, start at {@code startValue}.
|
||||
* A solver may require that the interval brackets a single zero root.
|
||||
* Solvers that do require bracketing should be able to handle the case
|
||||
* where one of the endpoints is itself a root.
|
||||
*
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @param f Function to solve.
|
||||
* @param min Lower bound for the interval.
|
||||
* @param max Upper bound for the interval.
|
||||
* @param startValue Start value to use.
|
||||
* @return a value where the function is zero.
|
||||
* @throws MathIllegalArgumentException
|
||||
* if the arguments do not satisfy the requirements specified by the solver.
|
||||
* @throws TooManyEvaluationsException if
|
||||
* the allowed number of evaluations is exceeded.
|
||||
*/
|
||||
double solve(int maxEval, FUNC f, double min, double max, double startValue)
|
||||
throws MathIllegalArgumentException, TooManyEvaluationsException;
|
||||
|
||||
/**
|
||||
* Solve for a zero in the vicinity of {@code startValue}.
|
||||
*
|
||||
* @param f Function to solve.
|
||||
* @param startValue Start value to use.
|
||||
* @return a value where the function is zero.
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.MathIllegalArgumentException
|
||||
* if the arguments do not satisfy the requirements specified by the solver.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException if
|
||||
* the allowed number of evaluations is exceeded.
|
||||
*/
|
||||
double solve(int maxEval, FUNC f, double startValue);
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
|
||||
/** Interface for {@link UnivariateSolver (univariate real) root-finding
|
||||
* algorithms} that maintain a bracketed solution. There are several advantages
|
||||
* to having such root-finding algorithms:
|
||||
* <ul>
|
||||
* <li>The bracketed solution guarantees that the root is kept within the
|
||||
* interval. As such, these algorithms generally also guarantee
|
||||
* convergence.</li>
|
||||
* <li>The bracketed solution means that we have the opportunity to only
|
||||
* return roots that are greater than or equal to the actual root, or
|
||||
* are less than or equal to the actual root. That is, we can control
|
||||
* whether under-approximations and over-approximations are
|
||||
* {@link AllowedSolution allowed solutions}. Other root-finding
|
||||
* algorithms can usually only guarantee that the solution (the root that
|
||||
* was found) is around the actual root.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>For backwards compatibility, all root-finding algorithms must have
|
||||
* {@link AllowedSolution#ANY_SIDE ANY_SIDE} as default for the allowed
|
||||
* solutions.</p>
|
||||
* @param <FUNC> Type of function to solve.
|
||||
*
|
||||
* @see AllowedSolution
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface BracketedUnivariateSolver<FUNC extends UnivariateFunction>
|
||||
extends BaseUnivariateSolver<FUNC> {
|
||||
|
||||
/**
|
||||
* Solve for a zero in the given interval.
|
||||
* A solver may require that the interval brackets a single zero root.
|
||||
* Solvers that do require bracketing should be able to handle the case
|
||||
* where one of the endpoints is itself a root.
|
||||
*
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @param f Function to solve.
|
||||
* @param min Lower bound for the interval.
|
||||
* @param max Upper bound for the interval.
|
||||
* @param allowedSolution The kind of solutions that the root-finding algorithm may
|
||||
* accept as solutions.
|
||||
* @return A value where the function is zero.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.MathIllegalArgumentException
|
||||
* if the arguments do not satisfy the requirements specified by the solver.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException if
|
||||
* the allowed number of evaluations is exceeded.
|
||||
*/
|
||||
double solve(int maxEval, FUNC f, double min, double max,
|
||||
AllowedSolution allowedSolution);
|
||||
|
||||
/**
|
||||
* Solve for a zero in the given interval, start at {@code startValue}.
|
||||
* A solver may require that the interval brackets a single zero root.
|
||||
* Solvers that do require bracketing should be able to handle the case
|
||||
* where one of the endpoints is itself a root.
|
||||
*
|
||||
* @param maxEval Maximum number of evaluations.
|
||||
* @param f Function to solve.
|
||||
* @param min Lower bound for the interval.
|
||||
* @param max Upper bound for the interval.
|
||||
* @param startValue Start value to use.
|
||||
* @param allowedSolution The kind of solutions that the root-finding algorithm may
|
||||
* accept as solutions.
|
||||
* @return A value where the function is zero.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.MathIllegalArgumentException
|
||||
* if the arguments do not satisfy the requirements specified by the solver.
|
||||
* @throws infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException if
|
||||
* the allowed number of evaluations is exceeded.
|
||||
*/
|
||||
double solve(int maxEval, FUNC f, double min, double max, double startValue,
|
||||
AllowedSolution allowedSolution);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NoBracketingException;
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException;
|
||||
import infodynamics.utils.commonsmath3.exception.TooManyEvaluationsException;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
import infodynamics.utils.commonsmath3.util.Precision;
|
||||
|
||||
/**
|
||||
* This class implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html">
|
||||
* Brent algorithm</a> for finding zeros of real univariate functions.
|
||||
* The function should be continuous but not necessarily smooth.
|
||||
* The {@code solve} method returns a zero {@code x} of the function {@code f}
|
||||
* in the given interval {@code [a, b]} to within a tolerance
|
||||
* {@code 2 eps abs(x) + t} where {@code eps} is the relative accuracy and
|
||||
* {@code t} is the absolute accuracy.
|
||||
* <p>The given interval must bracket the root.</p>
|
||||
* <p>
|
||||
* The reference implementation is given in chapter 4 of
|
||||
* <blockquote>
|
||||
* <b>Algorithms for Minimization Without Derivatives</b>,
|
||||
* <em>Richard P. Brent</em>,
|
||||
* Dover, 2002
|
||||
* </blockquote>
|
||||
*
|
||||
* @see BaseAbstractUnivariateSolver
|
||||
*/
|
||||
public class BrentSolver extends AbstractUnivariateSolver {
|
||||
|
||||
/** Default absolute accuracy. */
|
||||
private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
|
||||
|
||||
/**
|
||||
* Construct a solver with default absolute accuracy (1e-6).
|
||||
*/
|
||||
public BrentSolver() {
|
||||
this(DEFAULT_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
/**
|
||||
* Construct a solver.
|
||||
*
|
||||
* @param absoluteAccuracy Absolute accuracy.
|
||||
*/
|
||||
public BrentSolver(double absoluteAccuracy) {
|
||||
super(absoluteAccuracy);
|
||||
}
|
||||
/**
|
||||
* Construct a solver.
|
||||
*
|
||||
* @param relativeAccuracy Relative accuracy.
|
||||
* @param absoluteAccuracy Absolute accuracy.
|
||||
*/
|
||||
public BrentSolver(double relativeAccuracy,
|
||||
double absoluteAccuracy) {
|
||||
super(relativeAccuracy, absoluteAccuracy);
|
||||
}
|
||||
/**
|
||||
* Construct a solver.
|
||||
*
|
||||
* @param relativeAccuracy Relative accuracy.
|
||||
* @param absoluteAccuracy Absolute accuracy.
|
||||
* @param functionValueAccuracy Function value accuracy.
|
||||
*
|
||||
* @see BaseAbstractUnivariateSolver#BaseAbstractUnivariateSolver(double,double,double)
|
||||
*/
|
||||
public BrentSolver(double relativeAccuracy,
|
||||
double absoluteAccuracy,
|
||||
double functionValueAccuracy) {
|
||||
super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected double doSolve()
|
||||
throws NoBracketingException,
|
||||
TooManyEvaluationsException,
|
||||
NumberIsTooLargeException {
|
||||
double min = getMin();
|
||||
double max = getMax();
|
||||
final double initial = getStartValue();
|
||||
final double functionValueAccuracy = getFunctionValueAccuracy();
|
||||
|
||||
verifySequence(min, initial, max);
|
||||
|
||||
// Return the initial guess if it is good enough.
|
||||
double yInitial = computeObjectiveValue(initial);
|
||||
if (FastMath.abs(yInitial) <= functionValueAccuracy) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
// Return the first endpoint if it is good enough.
|
||||
double yMin = computeObjectiveValue(min);
|
||||
if (FastMath.abs(yMin) <= functionValueAccuracy) {
|
||||
return min;
|
||||
}
|
||||
|
||||
// Reduce interval if min and initial bracket the root.
|
||||
if (yInitial * yMin < 0) {
|
||||
return brent(min, initial, yMin, yInitial);
|
||||
}
|
||||
|
||||
// Return the second endpoint if it is good enough.
|
||||
double yMax = computeObjectiveValue(max);
|
||||
if (FastMath.abs(yMax) <= functionValueAccuracy) {
|
||||
return max;
|
||||
}
|
||||
|
||||
// Reduce interval if initial and max bracket the root.
|
||||
if (yInitial * yMax < 0) {
|
||||
return brent(initial, max, yInitial, yMax);
|
||||
}
|
||||
|
||||
throw new NoBracketingException(min, max, yMin, yMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a zero inside the provided interval.
|
||||
* This implementation is based on the algorithm described at page 58 of
|
||||
* the book
|
||||
* <blockquote>
|
||||
* <b>Algorithms for Minimization Without Derivatives</b>,
|
||||
* <it>Richard P. Brent</it>,
|
||||
* Dover 0-486-41998-3
|
||||
* </blockquote>
|
||||
*
|
||||
* @param lo Lower bound of the search interval.
|
||||
* @param hi Higher bound of the search interval.
|
||||
* @param fLo Function value at the lower bound of the search interval.
|
||||
* @param fHi Function value at the higher bound of the search interval.
|
||||
* @return the value where the function is zero.
|
||||
*/
|
||||
private double brent(double lo, double hi,
|
||||
double fLo, double fHi) {
|
||||
double a = lo;
|
||||
double fa = fLo;
|
||||
double b = hi;
|
||||
double fb = fHi;
|
||||
double c = a;
|
||||
double fc = fa;
|
||||
double d = b - a;
|
||||
double e = d;
|
||||
|
||||
final double t = getAbsoluteAccuracy();
|
||||
final double eps = getRelativeAccuracy();
|
||||
|
||||
while (true) {
|
||||
if (FastMath.abs(fc) < FastMath.abs(fb)) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a;
|
||||
fa = fb;
|
||||
fb = fc;
|
||||
fc = fa;
|
||||
}
|
||||
|
||||
final double tol = 2 * eps * FastMath.abs(b) + t;
|
||||
final double m = 0.5 * (c - b);
|
||||
|
||||
if (FastMath.abs(m) <= tol ||
|
||||
Precision.equals(fb, 0)) {
|
||||
return b;
|
||||
}
|
||||
if (FastMath.abs(e) < tol ||
|
||||
FastMath.abs(fa) <= FastMath.abs(fb)) {
|
||||
// Force bisection.
|
||||
d = m;
|
||||
e = d;
|
||||
} else {
|
||||
double s = fb / fa;
|
||||
double p;
|
||||
double q;
|
||||
// The equality test (a == c) is intentional,
|
||||
// it is part of the original Brent's method and
|
||||
// it should NOT be replaced by proximity test.
|
||||
if (a == c) {
|
||||
// Linear interpolation.
|
||||
p = 2 * m * s;
|
||||
q = 1 - s;
|
||||
} else {
|
||||
// Inverse quadratic interpolation.
|
||||
q = fa / fc;
|
||||
final double r = fb / fc;
|
||||
p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
|
||||
q = (q - 1) * (r - 1) * (s - 1);
|
||||
}
|
||||
if (p > 0) {
|
||||
q = -q;
|
||||
} else {
|
||||
p = -p;
|
||||
}
|
||||
s = e;
|
||||
e = d;
|
||||
if (p >= 1.5 * m * q - FastMath.abs(tol * q) ||
|
||||
p >= FastMath.abs(0.5 * s * q)) {
|
||||
// Inverse quadratic interpolation gives a value
|
||||
// in the wrong direction, or progress is slow.
|
||||
// Fall back to bisection.
|
||||
d = m;
|
||||
e = d;
|
||||
} else {
|
||||
d = p / q;
|
||||
}
|
||||
}
|
||||
a = b;
|
||||
fa = fb;
|
||||
|
||||
if (FastMath.abs(d) > tol) {
|
||||
b += d;
|
||||
} else if (m > 0) {
|
||||
b += tol;
|
||||
} else {
|
||||
b -= tol;
|
||||
}
|
||||
fb = computeObjectiveValue(b);
|
||||
if ((fb > 0 && fc > 0) ||
|
||||
(fb <= 0 && fc <= 0)) {
|
||||
c = a;
|
||||
fc = fa;
|
||||
d = b - a;
|
||||
e = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for (univariate real) root-finding algorithms.
|
||||
* Implementations will search for only one zero in the given interval.
|
||||
*
|
||||
*/
|
||||
public interface UnivariateSolver
|
||||
extends BaseUnivariateSolver<UnivariateFunction> {}
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.analysis.solvers;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
import infodynamics.utils.commonsmath3.exception.NoBracketingException;
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.NullArgumentException;
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Utility routines for {@link UnivariateSolver} objects.
|
||||
*
|
||||
*/
|
||||
public class UnivariateSolverUtils {
|
||||
/**
|
||||
* Class contains only static methods.
|
||||
*/
|
||||
private UnivariateSolverUtils() {}
|
||||
|
||||
/**
|
||||
* Convenience method to find a zero of a univariate real function. A default
|
||||
* solver is used.
|
||||
*
|
||||
* @param function Function.
|
||||
* @param x0 Lower bound for the interval.
|
||||
* @param x1 Upper bound for the interval.
|
||||
* @return a value where the function is zero.
|
||||
* @throws NoBracketingException if the function has the same sign at the
|
||||
* endpoints.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static double solve(UnivariateFunction function, double x0, double x1)
|
||||
throws NullArgumentException,
|
||||
NoBracketingException {
|
||||
if (function == null) {
|
||||
throw new NullArgumentException(LocalizedFormats.FUNCTION);
|
||||
}
|
||||
final UnivariateSolver solver = new BrentSolver();
|
||||
return solver.solve(Integer.MAX_VALUE, function, x0, x1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to find a zero of a univariate real function. A default
|
||||
* solver is used.
|
||||
*
|
||||
* @param function Function.
|
||||
* @param x0 Lower bound for the interval.
|
||||
* @param x1 Upper bound for the interval.
|
||||
* @param absoluteAccuracy Accuracy to be used by the solver.
|
||||
* @return a value where the function is zero.
|
||||
* @throws NoBracketingException if the function has the same sign at the
|
||||
* endpoints.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static double solve(UnivariateFunction function,
|
||||
double x0, double x1,
|
||||
double absoluteAccuracy)
|
||||
throws NullArgumentException,
|
||||
NoBracketingException {
|
||||
if (function == null) {
|
||||
throw new NullArgumentException(LocalizedFormats.FUNCTION);
|
||||
}
|
||||
final UnivariateSolver solver = new BrentSolver(absoluteAccuracy);
|
||||
return solver.solve(Integer.MAX_VALUE, function, x0, x1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a root found by a non-bracketing solver to lie on a specified side,
|
||||
* as if the solver were a bracketing one.
|
||||
*
|
||||
* @param maxEval maximal number of new evaluations of the function
|
||||
* (evaluations already done for finding the root should have already been subtracted
|
||||
* from this number)
|
||||
* @param f function to solve
|
||||
* @param bracketing bracketing solver to use for shifting the root
|
||||
* @param baseRoot original root found by a previous non-bracketing solver
|
||||
* @param min minimal bound of the search interval
|
||||
* @param max maximal bound of the search interval
|
||||
* @param allowedSolution the kind of solutions that the root-finding algorithm may
|
||||
* accept as solutions.
|
||||
* @return a root approximation, on the specified side of the exact root
|
||||
* @throws NoBracketingException if the function has the same sign at the
|
||||
* endpoints.
|
||||
*/
|
||||
public static double forceSide(final int maxEval, final UnivariateFunction f,
|
||||
final BracketedUnivariateSolver<UnivariateFunction> bracketing,
|
||||
final double baseRoot, final double min, final double max,
|
||||
final AllowedSolution allowedSolution)
|
||||
throws NoBracketingException {
|
||||
|
||||
if (allowedSolution == AllowedSolution.ANY_SIDE) {
|
||||
// no further bracketing required
|
||||
return baseRoot;
|
||||
}
|
||||
|
||||
// find a very small interval bracketing the root
|
||||
final double step = FastMath.max(bracketing.getAbsoluteAccuracy(),
|
||||
FastMath.abs(baseRoot * bracketing.getRelativeAccuracy()));
|
||||
double xLo = FastMath.max(min, baseRoot - step);
|
||||
double fLo = f.value(xLo);
|
||||
double xHi = FastMath.min(max, baseRoot + step);
|
||||
double fHi = f.value(xHi);
|
||||
int remainingEval = maxEval - 2;
|
||||
while (remainingEval > 0) {
|
||||
|
||||
if ((fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0)) {
|
||||
// compute the root on the selected side
|
||||
return bracketing.solve(remainingEval, f, xLo, xHi, baseRoot, allowedSolution);
|
||||
}
|
||||
|
||||
// try increasing the interval
|
||||
boolean changeLo = false;
|
||||
boolean changeHi = false;
|
||||
if (fLo < fHi) {
|
||||
// increasing function
|
||||
if (fLo >= 0) {
|
||||
changeLo = true;
|
||||
} else {
|
||||
changeHi = true;
|
||||
}
|
||||
} else if (fLo > fHi) {
|
||||
// decreasing function
|
||||
if (fLo <= 0) {
|
||||
changeLo = true;
|
||||
} else {
|
||||
changeHi = true;
|
||||
}
|
||||
} else {
|
||||
// unknown variation
|
||||
changeLo = true;
|
||||
changeHi = true;
|
||||
}
|
||||
|
||||
// update the lower bound
|
||||
if (changeLo) {
|
||||
xLo = FastMath.max(min, xLo - step);
|
||||
fLo = f.value(xLo);
|
||||
remainingEval--;
|
||||
}
|
||||
|
||||
// update the higher bound
|
||||
if (changeHi) {
|
||||
xHi = FastMath.min(max, xHi + step);
|
||||
fHi = f.value(xHi);
|
||||
remainingEval--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw new NoBracketingException(LocalizedFormats.FAILED_BRACKETING,
|
||||
xLo, xHi, fLo, fHi,
|
||||
maxEval - remainingEval, maxEval, baseRoot,
|
||||
min, max);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
|
||||
* double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
|
||||
* with {@code q} and {@code r} set to 1.0 and {@code maximumIterations} set to {@code Integer.MAX_VALUE}.
|
||||
* <p>
|
||||
* <strong>Note: </strong> this method can take {@code Integer.MAX_VALUE}
|
||||
* iterations to throw a {@code ConvergenceException.} Unless you are
|
||||
* confident that there is a root between {@code lowerBound} and
|
||||
* {@code upperBound} near {@code initial}, it is better to use
|
||||
* {@link #bracket(UnivariateFunction, double, double, double, double,double, int)
|
||||
* bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)},
|
||||
* explicitly specifying the maximum number of iterations.</p>
|
||||
*
|
||||
* @param function Function.
|
||||
* @param initial Initial midpoint of interval being expanded to
|
||||
* bracket a root.
|
||||
* @param lowerBound Lower bound (a is never lower than this value)
|
||||
* @param upperBound Upper bound (b never is greater than this
|
||||
* value).
|
||||
* @return a two-element array holding a and b.
|
||||
* @throws NoBracketingException if a root cannot be bracketted.
|
||||
* @throws NotStrictlyPositiveException if {@code maximumIterations <= 0}.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static double[] bracket(UnivariateFunction function,
|
||||
double initial,
|
||||
double lowerBound, double upperBound)
|
||||
throws NullArgumentException,
|
||||
NotStrictlyPositiveException,
|
||||
NoBracketingException {
|
||||
return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
|
||||
* double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
|
||||
* with {@code q} and {@code r} set to 1.0.
|
||||
* @param function Function.
|
||||
* @param initial Initial midpoint of interval being expanded to
|
||||
* bracket a root.
|
||||
* @param lowerBound Lower bound (a is never lower than this value).
|
||||
* @param upperBound Upper bound (b never is greater than this
|
||||
* value).
|
||||
* @param maximumIterations Maximum number of iterations to perform
|
||||
* @return a two element array holding a and b.
|
||||
* @throws NoBracketingException if the algorithm fails to find a and b
|
||||
* satisfying the desired conditions.
|
||||
* @throws NotStrictlyPositiveException if {@code maximumIterations <= 0}.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static double[] bracket(UnivariateFunction function,
|
||||
double initial,
|
||||
double lowerBound, double upperBound,
|
||||
int maximumIterations)
|
||||
throws NullArgumentException,
|
||||
NotStrictlyPositiveException,
|
||||
NoBracketingException {
|
||||
return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, maximumIterations);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method attempts to find two values a and b satisfying <ul>
|
||||
* <li> {@code lowerBound <= a < initial < b <= upperBound} </li>
|
||||
* <li> {@code f(a) * f(b) <= 0} </li>
|
||||
* </ul>
|
||||
* If {@code f} is continuous on {@code [a,b]}, this means that {@code a}
|
||||
* and {@code b} bracket a root of {@code f}.
|
||||
* <p>
|
||||
* The algorithm checks the sign of \( f(l_k) \) and \( f(u_k) \) for increasing
|
||||
* values of k, where \( l_k = max(lower, initial - \delta_k) \),
|
||||
* \( u_k = min(upper, initial + \delta_k) \), using recurrence
|
||||
* \( \delta_{k+1} = r \delta_k + q, \delta_0 = 0\) and starting search with \( k=1 \).
|
||||
* The algorithm stops when one of the following happens: <ul>
|
||||
* <li> at least one positive and one negative value have been found -- success!</li>
|
||||
* <li> both endpoints have reached their respective limits -- NoBracketingException </li>
|
||||
* <li> {@code maximumIterations} iterations elapse -- NoBracketingException </li></ul>
|
||||
* <p>
|
||||
* If different signs are found at first iteration ({@code k=1}), then the returned
|
||||
* interval will be \( [a, b] = [l_1, u_1] \). If different signs are found at a later
|
||||
* iteration {@code k>1}, then the returned interval will be either
|
||||
* \( [a, b] = [l_{k+1}, l_{k}] \) or \( [a, b] = [u_{k}, u_{k+1}] \). A root solver called
|
||||
* with these parameters will therefore start with the smallest bracketing interval known
|
||||
* at this step.
|
||||
* </p>
|
||||
* <p>
|
||||
* Interval expansion rate is tuned by changing the recurrence parameters {@code r} and
|
||||
* {@code q}. When the multiplicative factor {@code r} is set to 1, the sequence is a
|
||||
* simple arithmetic sequence with linear increase. When the multiplicative factor {@code r}
|
||||
* is larger than 1, the sequence has an asymptotically exponential rate. Note than the
|
||||
* additive parameter {@code q} should never be set to zero, otherwise the interval would
|
||||
* degenerate to the single initial point for all values of {@code k}.
|
||||
* </p>
|
||||
* <p>
|
||||
* As a rule of thumb, when the location of the root is expected to be approximately known
|
||||
* within some error margin, {@code r} should be set to 1 and {@code q} should be set to the
|
||||
* order of magnitude of the error margin. When the location of the root is really a wild guess,
|
||||
* then {@code r} should be set to a value larger than 1 (typically 2 to double the interval
|
||||
* length at each iteration) and {@code q} should be set according to half the initial
|
||||
* search interval length.
|
||||
* </p>
|
||||
* <p>
|
||||
* As an example, if we consider the trivial function {@code f(x) = 1 - x} and use
|
||||
* {@code initial = 4}, {@code r = 1}, {@code q = 2}, the algorithm will compute
|
||||
* {@code f(4-2) = f(2) = -1} and {@code f(4+2) = f(6) = -5} for {@code k = 1}, then
|
||||
* {@code f(4-4) = f(0) = +1} and {@code f(4+4) = f(8) = -7} for {@code k = 2}. Then it will
|
||||
* return the interval {@code [0, 2]} as the smallest one known to be bracketing the root.
|
||||
* As shown by this example, the initial value (here {@code 4}) may lie outside of the returned
|
||||
* bracketing interval.
|
||||
* </p>
|
||||
* @param function function to check
|
||||
* @param initial Initial midpoint of interval being expanded to
|
||||
* bracket a root.
|
||||
* @param lowerBound Lower bound (a is never lower than this value).
|
||||
* @param upperBound Upper bound (b never is greater than this
|
||||
* value).
|
||||
* @param q additive offset used to compute bounds sequence (must be strictly positive)
|
||||
* @param r multiplicative factor used to compute bounds sequence
|
||||
* @param maximumIterations Maximum number of iterations to perform
|
||||
* @return a two element array holding the bracketing values.
|
||||
* @exception NoBracketingException if function cannot be bracketed in the search interval
|
||||
*/
|
||||
public static double[] bracket(final UnivariateFunction function, final double initial,
|
||||
final double lowerBound, final double upperBound,
|
||||
final double q, final double r, final int maximumIterations)
|
||||
throws NoBracketingException {
|
||||
|
||||
if (function == null) {
|
||||
throw new NullArgumentException(LocalizedFormats.FUNCTION);
|
||||
}
|
||||
if (q <= 0) {
|
||||
throw new NotStrictlyPositiveException(q);
|
||||
}
|
||||
if (maximumIterations <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.INVALID_MAX_ITERATIONS, maximumIterations);
|
||||
}
|
||||
verifySequence(lowerBound, initial, upperBound);
|
||||
|
||||
// initialize the recurrence
|
||||
double a = initial;
|
||||
double b = initial;
|
||||
double fa = Double.NaN;
|
||||
double fb = Double.NaN;
|
||||
double delta = 0;
|
||||
|
||||
for (int numIterations = 0;
|
||||
(numIterations < maximumIterations) && (a > lowerBound || b < upperBound);
|
||||
++numIterations) {
|
||||
|
||||
final double previousA = a;
|
||||
final double previousFa = fa;
|
||||
final double previousB = b;
|
||||
final double previousFb = fb;
|
||||
|
||||
delta = r * delta + q;
|
||||
a = FastMath.max(initial - delta, lowerBound);
|
||||
b = FastMath.min(initial + delta, upperBound);
|
||||
fa = function.value(a);
|
||||
fb = function.value(b);
|
||||
|
||||
if (numIterations == 0) {
|
||||
// at first iteration, we don't have a previous interval
|
||||
// we simply compare both sides of the initial interval
|
||||
if (fa * fb <= 0) {
|
||||
// the first interval already brackets a root
|
||||
return new double[] { a, b };
|
||||
}
|
||||
} else {
|
||||
// we have a previous interval with constant sign and expand it,
|
||||
// we expect sign changes to occur at boundaries
|
||||
if (fa * previousFa <= 0) {
|
||||
// sign change detected at near lower bound
|
||||
return new double[] { a, previousA };
|
||||
} else if (fb * previousFb <= 0) {
|
||||
// sign change detected at near upper bound
|
||||
return new double[] { previousB, b };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// no bracketing found
|
||||
throw new NoBracketingException(a, b, fa, fb);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the midpoint of two values.
|
||||
*
|
||||
* @param a first value.
|
||||
* @param b second value.
|
||||
* @return the midpoint.
|
||||
*/
|
||||
public static double midpoint(double a, double b) {
|
||||
return (a + b) * 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the interval bounds bracket a root. That is, if the
|
||||
* values at the endpoints are not equal to zero, then the function takes
|
||||
* opposite signs at the endpoints.
|
||||
*
|
||||
* @param function Function.
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @return {@code true} if the function values have opposite signs at the
|
||||
* given points.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static boolean isBracketing(UnivariateFunction function,
|
||||
final double lower,
|
||||
final double upper)
|
||||
throws NullArgumentException {
|
||||
if (function == null) {
|
||||
throw new NullArgumentException(LocalizedFormats.FUNCTION);
|
||||
}
|
||||
final double fLo = function.value(lower);
|
||||
final double fHi = function.value(upper);
|
||||
return (fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the arguments form a (strictly) increasing sequence.
|
||||
*
|
||||
* @param start First number.
|
||||
* @param mid Second number.
|
||||
* @param end Third number.
|
||||
* @return {@code true} if the arguments form an increasing sequence.
|
||||
*/
|
||||
public static boolean isSequence(final double start,
|
||||
final double mid,
|
||||
final double end) {
|
||||
return (start < mid) && (mid < end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the endpoints specify an interval.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NumberIsTooLargeException if {@code lower >= upper}.
|
||||
*/
|
||||
public static void verifyInterval(final double lower,
|
||||
final double upper)
|
||||
throws NumberIsTooLargeException {
|
||||
if (lower >= upper) {
|
||||
throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
|
||||
lower, upper, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that {@code lower < initial < upper}.
|
||||
*
|
||||
* @param lower Lower endpoint.
|
||||
* @param initial Initial value.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NumberIsTooLargeException if {@code lower >= initial} or
|
||||
* {@code initial >= upper}.
|
||||
*/
|
||||
public static void verifySequence(final double lower,
|
||||
final double initial,
|
||||
final double upper)
|
||||
throws NumberIsTooLargeException {
|
||||
verifyInterval(lower, initial);
|
||||
verifyInterval(initial, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the endpoints specify an interval and the end points
|
||||
* bracket a root.
|
||||
*
|
||||
* @param function Function.
|
||||
* @param lower Lower endpoint.
|
||||
* @param upper Upper endpoint.
|
||||
* @throws NoBracketingException if the function has the same sign at the
|
||||
* endpoints.
|
||||
* @throws NullArgumentException if {@code function} is {@code null}.
|
||||
*/
|
||||
public static void verifyBracketing(UnivariateFunction function,
|
||||
final double lower,
|
||||
final double upper)
|
||||
throws NullArgumentException,
|
||||
NoBracketingException {
|
||||
if (function == null) {
|
||||
throw new NullArgumentException(LocalizedFormats.FUNCTION);
|
||||
}
|
||||
verifyInterval(lower, upper);
|
||||
if (!isBracketing(function, lower, upper)) {
|
||||
throw new NoBracketingException(lower, upper,
|
||||
function.value(lower),
|
||||
function.value(upper));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.MathInternalError;
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException;
|
||||
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Base class for integer-valued discrete distributions. Default
|
||||
* implementations are provided for some of the methods that do not vary
|
||||
* from distribution to distribution.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractIntegerDistribution implements IntegerDistribution, Serializable {
|
||||
|
||||
/** Serializable version identifier */
|
||||
private static final long serialVersionUID = -1146319659338487221L;
|
||||
|
||||
/**
|
||||
* RandomData instance used to generate samples from the distribution.
|
||||
* @deprecated As of 3.1, to be removed in 4.0. Please use the
|
||||
* {@link #random} instance variable instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected final infodynamics.utils.commonsmath3.random.RandomDataImpl randomData =
|
||||
new infodynamics.utils.commonsmath3.random.RandomDataImpl();
|
||||
|
||||
/**
|
||||
* RNG instance used to generate samples from the distribution.
|
||||
* @since 3.1
|
||||
*/
|
||||
protected final RandomGenerator random;
|
||||
|
||||
/**
|
||||
* @deprecated As of 3.1, to be removed in 4.0. Please use
|
||||
* {@link #AbstractIntegerDistribution(RandomGenerator)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractIntegerDistribution() {
|
||||
// Legacy users are only allowed to access the deprecated "randomData".
|
||||
// New users are forbidden to use this constructor.
|
||||
random = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rng Random number generator.
|
||||
* @since 3.1
|
||||
*/
|
||||
protected AbstractIntegerDistribution(RandomGenerator rng) {
|
||||
random = rng;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation uses the identity
|
||||
* <p>{@code P(x0 < X <= x1) = P(X <= x1) - P(X <= x0)}</p>
|
||||
*/
|
||||
public double cumulativeProbability(int x0, int x1) throws NumberIsTooLargeException {
|
||||
if (x1 < x0) {
|
||||
throw new NumberIsTooLargeException(LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT,
|
||||
x0, x1, true);
|
||||
}
|
||||
return cumulativeProbability(x1) - cumulativeProbability(x0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation returns
|
||||
* <ul>
|
||||
* <li>{@link #getSupportLowerBound()} for {@code p = 0},</li>
|
||||
* <li>{@link #getSupportUpperBound()} for {@code p = 1}, and</li>
|
||||
* <li>{@link #solveInverseCumulativeProbability(double, int, int)} for
|
||||
* {@code 0 < p < 1}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public int inverseCumulativeProbability(final double p) throws OutOfRangeException {
|
||||
if (p < 0.0 || p > 1.0) {
|
||||
throw new OutOfRangeException(p, 0, 1);
|
||||
}
|
||||
|
||||
int lower = getSupportLowerBound();
|
||||
if (p == 0.0) {
|
||||
return lower;
|
||||
}
|
||||
if (lower == Integer.MIN_VALUE) {
|
||||
if (checkedCumulativeProbability(lower) >= p) {
|
||||
return lower;
|
||||
}
|
||||
} else {
|
||||
lower -= 1; // this ensures cumulativeProbability(lower) < p, which
|
||||
// is important for the solving step
|
||||
}
|
||||
|
||||
int upper = getSupportUpperBound();
|
||||
if (p == 1.0) {
|
||||
return upper;
|
||||
}
|
||||
|
||||
// use the one-sided Chebyshev inequality to narrow the bracket
|
||||
// cf. AbstractRealDistribution.inverseCumulativeProbability(double)
|
||||
final double mu = getNumericalMean();
|
||||
final double sigma = FastMath.sqrt(getNumericalVariance());
|
||||
final boolean chebyshevApplies = !(Double.isInfinite(mu) || Double.isNaN(mu) ||
|
||||
Double.isInfinite(sigma) || Double.isNaN(sigma) || sigma == 0.0);
|
||||
if (chebyshevApplies) {
|
||||
double k = FastMath.sqrt((1.0 - p) / p);
|
||||
double tmp = mu - k * sigma;
|
||||
if (tmp > lower) {
|
||||
lower = ((int) FastMath.ceil(tmp)) - 1;
|
||||
}
|
||||
k = 1.0 / k;
|
||||
tmp = mu + k * sigma;
|
||||
if (tmp < upper) {
|
||||
upper = ((int) FastMath.ceil(tmp)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return solveInverseCumulativeProbability(p, lower, upper);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a utility function used by {@link
|
||||
* #inverseCumulativeProbability(double)}. It assumes {@code 0 < p < 1} and
|
||||
* that the inverse cumulative probability lies in the bracket {@code
|
||||
* (lower, upper]}. The implementation does simple bisection to find the
|
||||
* smallest {@code p}-quantile <code>inf{x in Z | P(X<=x) >= p}</code>.
|
||||
*
|
||||
* @param p the cumulative probability
|
||||
* @param lower a value satisfying {@code cumulativeProbability(lower) < p}
|
||||
* @param upper a value satisfying {@code p <= cumulativeProbability(upper)}
|
||||
* @return the smallest {@code p}-quantile of this distribution
|
||||
*/
|
||||
protected int solveInverseCumulativeProbability(final double p, int lower, int upper) {
|
||||
while (lower + 1 < upper) {
|
||||
int xm = (lower + upper) / 2;
|
||||
if (xm < lower || xm > upper) {
|
||||
/*
|
||||
* Overflow.
|
||||
* There will never be an overflow in both calculation methods
|
||||
* for xm at the same time
|
||||
*/
|
||||
xm = lower + (upper - lower) / 2;
|
||||
}
|
||||
|
||||
double pm = checkedCumulativeProbability(xm);
|
||||
if (pm >= p) {
|
||||
upper = xm;
|
||||
} else {
|
||||
lower = xm;
|
||||
}
|
||||
}
|
||||
return upper;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void reseedRandomGenerator(long seed) {
|
||||
random.setSeed(seed);
|
||||
randomData.reSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation uses the
|
||||
* <a href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">
|
||||
* inversion method</a>.
|
||||
*/
|
||||
public int sample() {
|
||||
return inverseCumulativeProbability(random.nextDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation generates the sample by calling
|
||||
* {@link #sample()} in a loop.
|
||||
*/
|
||||
public int[] sample(int sampleSize) {
|
||||
if (sampleSize <= 0) {
|
||||
throw new NotStrictlyPositiveException(
|
||||
LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
|
||||
}
|
||||
int[] out = new int[sampleSize];
|
||||
for (int i = 0; i < sampleSize; i++) {
|
||||
out[i] = sample();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the cumulative probability function and checks for {@code NaN}
|
||||
* values returned. Throws {@code MathInternalError} if the value is
|
||||
* {@code NaN}. Rethrows any exception encountered evaluating the cumulative
|
||||
* probability function. Throws {@code MathInternalError} if the cumulative
|
||||
* probability function returns {@code NaN}.
|
||||
*
|
||||
* @param argument input value
|
||||
* @return the cumulative probability
|
||||
* @throws MathInternalError if the cumulative probability is {@code NaN}
|
||||
*/
|
||||
private double checkedCumulativeProbability(int argument)
|
||||
throws MathInternalError {
|
||||
double result = Double.NaN;
|
||||
result = cumulativeProbability(argument);
|
||||
if (Double.isNaN(result)) {
|
||||
throw new MathInternalError(LocalizedFormats
|
||||
.DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN, argument);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a random variable {@code X} whose values are distributed according to
|
||||
* this distribution, this method returns {@code log(P(X = x))}, where
|
||||
* {@code log} is the natural logarithm. In other words, this method
|
||||
* represents the logarithm of the probability mass function (PMF) for the
|
||||
* distribution. Note that due to the floating point precision and
|
||||
* under/overflow issues, this method will for some distributions be more
|
||||
* precise and faster than computing the logarithm of
|
||||
* {@link #probability(int)}.
|
||||
* <p>
|
||||
* The default implementation simply computes the logarithm of {@code probability(x)}.</p>
|
||||
*
|
||||
* @param x the point at which the PMF is evaluated
|
||||
* @return the logarithm of the value of the probability mass function at {@code x}
|
||||
*/
|
||||
public double logProbability(int x) {
|
||||
return FastMath.log(probability(x));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import infodynamics.utils.commonsmath3.analysis.UnivariateFunction;
|
||||
import infodynamics.utils.commonsmath3.analysis.solvers.UnivariateSolverUtils;
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException;
|
||||
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Base class for probability distributions on the reals.
|
||||
* Default implementations are provided for some of the methods
|
||||
* that do not vary from distribution to distribution.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractRealDistribution
|
||||
implements RealDistribution, Serializable {
|
||||
/** Default accuracy. */
|
||||
public static final double SOLVER_DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
|
||||
/** Serializable version identifier */
|
||||
private static final long serialVersionUID = -38038050983108802L;
|
||||
/**
|
||||
* RandomData instance used to generate samples from the distribution.
|
||||
* @deprecated As of 3.1, to be removed in 4.0. Please use the
|
||||
* {@link #random} instance variable instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected infodynamics.utils.commonsmath3.random.RandomDataImpl randomData =
|
||||
new infodynamics.utils.commonsmath3.random.RandomDataImpl();
|
||||
|
||||
/**
|
||||
* RNG instance used to generate samples from the distribution.
|
||||
* @since 3.1
|
||||
*/
|
||||
protected final RandomGenerator random;
|
||||
|
||||
/** Solver absolute accuracy for inverse cumulative computation */
|
||||
private double solverAbsoluteAccuracy = SOLVER_DEFAULT_ABSOLUTE_ACCURACY;
|
||||
|
||||
/**
|
||||
* @deprecated As of 3.1, to be removed in 4.0. Please use
|
||||
* {@link #AbstractRealDistribution(RandomGenerator)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractRealDistribution() {
|
||||
// Legacy users are only allowed to access the deprecated "randomData".
|
||||
// New users are forbidden to use this constructor.
|
||||
random = null;
|
||||
}
|
||||
/**
|
||||
* @param rng Random number generator.
|
||||
* @since 3.1
|
||||
*/
|
||||
protected AbstractRealDistribution(RandomGenerator rng) {
|
||||
random = rng;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation uses the identity
|
||||
* <p>{@code P(x0 < X <= x1) = P(X <= x1) - P(X <= x0)}</p>
|
||||
*
|
||||
* @deprecated As of 3.1 (to be removed in 4.0). Please use
|
||||
* {@link #probability(double,double)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException {
|
||||
return probability(x0, x1);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a random variable {@code X} whose values are distributed according
|
||||
* to this distribution, this method returns {@code P(x0 < X <= x1)}.
|
||||
*
|
||||
* @param x0 Lower bound (excluded).
|
||||
* @param x1 Upper bound (included).
|
||||
* @return the probability that a random variable with this distribution
|
||||
* takes a value between {@code x0} and {@code x1}, excluding the lower
|
||||
* and including the upper endpoint.
|
||||
* @throws NumberIsTooLargeException if {@code x0 > x1}.
|
||||
*
|
||||
* The default implementation uses the identity
|
||||
* {@code P(x0 < X <= x1) = P(X <= x1) - P(X <= x0)}
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
public double probability(double x0,
|
||||
double x1) {
|
||||
if (x0 > x1) {
|
||||
throw new NumberIsTooLargeException(LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT,
|
||||
x0, x1, true);
|
||||
}
|
||||
return cumulativeProbability(x1) - cumulativeProbability(x0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation returns
|
||||
* <ul>
|
||||
* <li>{@link #getSupportLowerBound()} for {@code p = 0},</li>
|
||||
* <li>{@link #getSupportUpperBound()} for {@code p = 1}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
|
||||
/*
|
||||
* IMPLEMENTATION NOTES
|
||||
* --------------------
|
||||
* Where applicable, use is made of the one-sided Chebyshev inequality
|
||||
* to bracket the root. This inequality states that
|
||||
* P(X - mu >= k * sig) <= 1 / (1 + k^2),
|
||||
* mu: mean, sig: standard deviation. Equivalently
|
||||
* 1 - P(X < mu + k * sig) <= 1 / (1 + k^2),
|
||||
* F(mu + k * sig) >= k^2 / (1 + k^2).
|
||||
*
|
||||
* For k = sqrt(p / (1 - p)), we find
|
||||
* F(mu + k * sig) >= p,
|
||||
* and (mu + k * sig) is an upper-bound for the root.
|
||||
*
|
||||
* Then, introducing Y = -X, mean(Y) = -mu, sd(Y) = sig, and
|
||||
* P(Y >= -mu + k * sig) <= 1 / (1 + k^2),
|
||||
* P(-X >= -mu + k * sig) <= 1 / (1 + k^2),
|
||||
* P(X <= mu - k * sig) <= 1 / (1 + k^2),
|
||||
* F(mu - k * sig) <= 1 / (1 + k^2).
|
||||
*
|
||||
* For k = sqrt((1 - p) / p), we find
|
||||
* F(mu - k * sig) <= p,
|
||||
* and (mu - k * sig) is a lower-bound for the root.
|
||||
*
|
||||
* In cases where the Chebyshev inequality does not apply, geometric
|
||||
* progressions 1, 2, 4, ... and -1, -2, -4, ... are used to bracket
|
||||
* the root.
|
||||
*/
|
||||
if (p < 0.0 || p > 1.0) {
|
||||
throw new OutOfRangeException(p, 0, 1);
|
||||
}
|
||||
|
||||
double lowerBound = getSupportLowerBound();
|
||||
if (p == 0.0) {
|
||||
return lowerBound;
|
||||
}
|
||||
|
||||
double upperBound = getSupportUpperBound();
|
||||
if (p == 1.0) {
|
||||
return upperBound;
|
||||
}
|
||||
|
||||
final double mu = getNumericalMean();
|
||||
final double sig = FastMath.sqrt(getNumericalVariance());
|
||||
final boolean chebyshevApplies;
|
||||
chebyshevApplies = !(Double.isInfinite(mu) || Double.isNaN(mu) ||
|
||||
Double.isInfinite(sig) || Double.isNaN(sig));
|
||||
|
||||
if (lowerBound == Double.NEGATIVE_INFINITY) {
|
||||
if (chebyshevApplies) {
|
||||
lowerBound = mu - sig * FastMath.sqrt((1. - p) / p);
|
||||
} else {
|
||||
lowerBound = -1.0;
|
||||
while (cumulativeProbability(lowerBound) >= p) {
|
||||
lowerBound *= 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (upperBound == Double.POSITIVE_INFINITY) {
|
||||
if (chebyshevApplies) {
|
||||
upperBound = mu + sig * FastMath.sqrt(p / (1. - p));
|
||||
} else {
|
||||
upperBound = 1.0;
|
||||
while (cumulativeProbability(upperBound) < p) {
|
||||
upperBound *= 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final UnivariateFunction toSolve = new UnivariateFunction() {
|
||||
/** {@inheritDoc} */
|
||||
public double value(final double x) {
|
||||
return cumulativeProbability(x) - p;
|
||||
}
|
||||
};
|
||||
|
||||
double x = UnivariateSolverUtils.solve(toSolve,
|
||||
lowerBound,
|
||||
upperBound,
|
||||
getSolverAbsoluteAccuracy());
|
||||
|
||||
if (!isSupportConnected()) {
|
||||
/* Test for plateau. */
|
||||
final double dx = getSolverAbsoluteAccuracy();
|
||||
if (x - dx >= getSupportLowerBound()) {
|
||||
double px = cumulativeProbability(x);
|
||||
if (cumulativeProbability(x - dx) == px) {
|
||||
upperBound = x;
|
||||
while (upperBound - lowerBound > dx) {
|
||||
final double midPoint = 0.5 * (lowerBound + upperBound);
|
||||
if (cumulativeProbability(midPoint) < px) {
|
||||
lowerBound = midPoint;
|
||||
} else {
|
||||
upperBound = midPoint;
|
||||
}
|
||||
}
|
||||
return upperBound;
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the solver absolute accuracy for inverse cumulative computation.
|
||||
* You can override this method in order to use a Brent solver with an
|
||||
* absolute accuracy different from the default.
|
||||
*
|
||||
* @return the maximum absolute error in inverse cumulative probability estimates
|
||||
*/
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void reseedRandomGenerator(long seed) {
|
||||
random.setSeed(seed);
|
||||
randomData.reSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation uses the
|
||||
* <a href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">
|
||||
* inversion method.
|
||||
* </a>
|
||||
*/
|
||||
public double sample() {
|
||||
return inverseCumulativeProbability(random.nextDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The default implementation generates the sample by calling
|
||||
* {@link #sample()} in a loop.
|
||||
*/
|
||||
public double[] sample(int sampleSize) {
|
||||
if (sampleSize <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES,
|
||||
sampleSize);
|
||||
}
|
||||
double[] out = new double[sampleSize];
|
||||
for (int i = 0; i < sampleSize; i++) {
|
||||
out[i] = sample();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return zero.
|
||||
* @since 3.1
|
||||
*/
|
||||
public double probability(double x) {
|
||||
return 0d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the natural logarithm of the probability density function (PDF) of this distribution
|
||||
* evaluated at the specified point {@code x}. In general, the PDF is the derivative of the
|
||||
* {@link #cumulativeProbability(double) CDF}. If the derivative does not exist at {@code x},
|
||||
* then an appropriate replacement should be returned, e.g. {@code Double.POSITIVE_INFINITY},
|
||||
* {@code Double.NaN}, or the limit inferior or limit superior of the difference quotient. Note
|
||||
* that due to the floating point precision and under/overflow issues, this method will for some
|
||||
* distributions be more precise and faster than computing the logarithm of
|
||||
* {@link #density(double)}. The default implementation simply computes the logarithm of
|
||||
* {@code density(x)}.
|
||||
*
|
||||
* @param x the point at which the PDF is evaluated
|
||||
* @return the logarithm of the value of the probability density function at point {@code x}
|
||||
*/
|
||||
public double logDensity(double x) {
|
||||
return FastMath.log(density(x));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NumberIsTooSmallException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.special.Beta;
|
||||
import infodynamics.utils.commonsmath3.special.Gamma;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
import infodynamics.utils.commonsmath3.util.Precision;
|
||||
|
||||
/**
|
||||
* Implements the Beta distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta distribution</a>
|
||||
* @since 2.0 (changed to concrete class in 3.0)
|
||||
*/
|
||||
public class BetaDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier. */
|
||||
private static final long serialVersionUID = -1221965979403477668L;
|
||||
/** First shape parameter. */
|
||||
private final double alpha;
|
||||
/** Second shape parameter. */
|
||||
private final double beta;
|
||||
/** Normalizing factor used in density computations.
|
||||
* updated whenever alpha or beta are changed.
|
||||
*/
|
||||
private double z;
|
||||
/** Inverse cumulative probability accuracy. */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
|
||||
/**
|
||||
* Build a new instance.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param alpha First shape parameter (must be positive).
|
||||
* @param beta Second shape parameter (must be positive).
|
||||
*/
|
||||
public BetaDistribution(double alpha, double beta) {
|
||||
this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new instance.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param alpha First shape parameter (must be positive).
|
||||
* @param beta Second shape parameter (must be positive).
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @since 2.1
|
||||
*/
|
||||
public BetaDistribution(double alpha, double beta, double inverseCumAccuracy) {
|
||||
this(new Well19937c(), alpha, beta, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a β distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param alpha First shape parameter (must be positive).
|
||||
* @param beta Second shape parameter (must be positive).
|
||||
* @since 3.3
|
||||
*/
|
||||
public BetaDistribution(RandomGenerator rng, double alpha, double beta) {
|
||||
this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a β distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param alpha First shape parameter (must be positive).
|
||||
* @param beta Second shape parameter (must be positive).
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @since 3.1
|
||||
*/
|
||||
public BetaDistribution(RandomGenerator rng,
|
||||
double alpha,
|
||||
double beta,
|
||||
double inverseCumAccuracy) {
|
||||
super(rng);
|
||||
|
||||
this.alpha = alpha;
|
||||
this.beta = beta;
|
||||
z = Double.NaN;
|
||||
solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the first shape parameter, {@code alpha}.
|
||||
*
|
||||
* @return the first shape parameter.
|
||||
*/
|
||||
public double getAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the second shape parameter, {@code beta}.
|
||||
*
|
||||
* @return the second shape parameter.
|
||||
*/
|
||||
public double getBeta() {
|
||||
return beta;
|
||||
}
|
||||
|
||||
/** Recompute the normalization factor. */
|
||||
private void recomputeZ() {
|
||||
if (Double.isNaN(z)) {
|
||||
z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double density(double x) {
|
||||
final double logDensity = logDensity(x);
|
||||
return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logDensity(double x) {
|
||||
recomputeZ();
|
||||
if (x < 0 || x > 1) {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
} else if (x == 0) {
|
||||
if (alpha < 1) {
|
||||
throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA, alpha, 1, false);
|
||||
}
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
} else if (x == 1) {
|
||||
if (beta < 1) {
|
||||
throw new NumberIsTooSmallException(LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA, beta, 1, false);
|
||||
}
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
} else {
|
||||
double logX = FastMath.log(x);
|
||||
double log1mX = FastMath.log1p(-x);
|
||||
return (alpha - 1) * logX + (beta - 1) * log1mX - z;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double cumulativeProbability(double x) {
|
||||
if (x <= 0) {
|
||||
return 0;
|
||||
} else if (x >= 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return Beta.regularizedBeta(x, alpha, beta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute accuracy setting of the solver used to estimate
|
||||
* inverse cumulative probabilities.
|
||||
*
|
||||
* @return the solver absolute accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For first shape parameter {@code alpha} and second shape parameter
|
||||
* {@code beta}, the mean is {@code alpha / (alpha + beta)}.
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
final double a = getAlpha();
|
||||
return a / (a + getBeta());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For first shape parameter {@code alpha} and second shape parameter
|
||||
* {@code beta}, the variance is
|
||||
* {@code (alpha * beta) / [(alpha + beta)^2 * (alpha + beta + 1)]}.
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
final double a = getAlpha();
|
||||
final double b = getBeta();
|
||||
final double alphabetasum = a + b;
|
||||
return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 no matter the parameters.
|
||||
*
|
||||
* @return lower bound of the support (always 0)
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always 1 no matter the parameters.
|
||||
*
|
||||
* @return upper bound of the support (always 1)
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc}
|
||||
* <p>
|
||||
* Sampling is performed using Cheng algorithms:
|
||||
* </p>
|
||||
* <p>
|
||||
* R. C. H. Cheng, "Generating beta variates with nonintegral shape parameters.".
|
||||
* Communications of the ACM, 21, 317–322, 1978.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public double sample() {
|
||||
return ChengBetaSampler.sample(random, alpha, beta);
|
||||
}
|
||||
|
||||
/** Utility class implementing Cheng's algorithms for beta distribution sampling.
|
||||
* <p>
|
||||
* R. C. H. Cheng, "Generating beta variates with nonintegral shape parameters.".
|
||||
* Communications of the ACM, 21, 317–322, 1978.
|
||||
* </p>
|
||||
* @since 3.6
|
||||
*/
|
||||
private static final class ChengBetaSampler {
|
||||
|
||||
/**
|
||||
* Returns one sample using Cheng's sampling algorithm.
|
||||
* @param random random generator to use
|
||||
* @param alpha distribution first shape parameter
|
||||
* @param beta distribution second shape parameter
|
||||
* @return sampled value
|
||||
*/
|
||||
static double sample(RandomGenerator random, final double alpha, final double beta) {
|
||||
final double a = FastMath.min(alpha, beta);
|
||||
final double b = FastMath.max(alpha, beta);
|
||||
|
||||
if (a > 1) {
|
||||
return algorithmBB(random, alpha, a, b);
|
||||
} else {
|
||||
return algorithmBC(random, alpha, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one sample using Cheng's BB algorithm, when both α and β are greater than 1.
|
||||
* @param random random generator to use
|
||||
* @param a0 distribution first shape parameter (α)
|
||||
* @param a min(α, β) where α, β are the two distribution shape parameters
|
||||
* @param b max(α, β) where α, β are the two distribution shape parameters
|
||||
* @return sampled value
|
||||
*/
|
||||
private static double algorithmBB(RandomGenerator random,
|
||||
final double a0,
|
||||
final double a,
|
||||
final double b) {
|
||||
final double alpha = a + b;
|
||||
final double beta = FastMath.sqrt((alpha - 2.) / (2. * a * b - alpha));
|
||||
final double gamma = a + 1. / beta;
|
||||
|
||||
double r;
|
||||
double w;
|
||||
double t;
|
||||
do {
|
||||
final double u1 = random.nextDouble();
|
||||
final double u2 = random.nextDouble();
|
||||
final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
|
||||
w = a * FastMath.exp(v);
|
||||
final double z = u1 * u1 * u2;
|
||||
r = gamma * v - 1.3862944;
|
||||
final double s = a + r - w;
|
||||
if (s + 2.609438 >= 5 * z) {
|
||||
break;
|
||||
}
|
||||
|
||||
t = FastMath.log(z);
|
||||
if (s >= t) {
|
||||
break;
|
||||
}
|
||||
} while (r + alpha * (FastMath.log(alpha) - FastMath.log(b + w)) < t);
|
||||
|
||||
w = FastMath.min(w, Double.MAX_VALUE);
|
||||
return Precision.equals(a, a0) ? w / (b + w) : b / (b + w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one sample using Cheng's BC algorithm, when at least one of α and β is smaller than 1.
|
||||
* @param random random generator to use
|
||||
* @param a0 distribution first shape parameter (α)
|
||||
* @param a max(α, β) where α, β are the two distribution shape parameters
|
||||
* @param b min(α, β) where α, β are the two distribution shape parameters
|
||||
* @return sampled value
|
||||
*/
|
||||
private static double algorithmBC(RandomGenerator random,
|
||||
final double a0,
|
||||
final double a,
|
||||
final double b) {
|
||||
final double alpha = a + b;
|
||||
final double beta = 1. / b;
|
||||
final double delta = 1. + a - b;
|
||||
final double k1 = delta * (0.0138889 + 0.0416667 * b) / (a * beta - 0.777778);
|
||||
final double k2 = 0.25 + (0.5 + 0.25 / delta) * b;
|
||||
|
||||
double w;
|
||||
for (;;) {
|
||||
final double u1 = random.nextDouble();
|
||||
final double u2 = random.nextDouble();
|
||||
final double y = u1 * u2;
|
||||
final double z = u1 * y;
|
||||
if (u1 < 0.5) {
|
||||
if (0.25 * u2 + z - y >= k1) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (z <= 0.25) {
|
||||
final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
|
||||
w = a * FastMath.exp(v);
|
||||
break;
|
||||
}
|
||||
|
||||
if (z >= k2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
|
||||
w = a * FastMath.exp(v);
|
||||
if (alpha * (FastMath.log(alpha) - FastMath.log(b + w) + v) - 1.3862944 >= FastMath.log(z)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w = FastMath.min(w, Double.MAX_VALUE);
|
||||
return Precision.equals(a, a0) ? w / (b + w) : b / (b + w);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NotPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.special.Beta;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Implementation of the binomial distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Binomial_distribution">Binomial distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/BinomialDistribution.html">Binomial Distribution (MathWorld)</a>
|
||||
*/
|
||||
public class BinomialDistribution extends AbstractIntegerDistribution {
|
||||
/** Serializable version identifier. */
|
||||
private static final long serialVersionUID = 6751309484392813623L;
|
||||
/** The number of trials. */
|
||||
private final int numberOfTrials;
|
||||
/** The probability of success. */
|
||||
private final double probabilityOfSuccess;
|
||||
|
||||
/**
|
||||
* Create a binomial distribution with the given number of trials and
|
||||
* probability of success.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param trials Number of trials.
|
||||
* @param p Probability of success.
|
||||
* @throws NotPositiveException if {@code trials < 0}.
|
||||
* @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
|
||||
*/
|
||||
public BinomialDistribution(int trials, double p) {
|
||||
this(new Well19937c(), trials, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a binomial distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param trials Number of trials.
|
||||
* @param p Probability of success.
|
||||
* @throws NotPositiveException if {@code trials < 0}.
|
||||
* @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public BinomialDistribution(RandomGenerator rng,
|
||||
int trials,
|
||||
double p) {
|
||||
super(rng);
|
||||
|
||||
if (trials < 0) {
|
||||
throw new NotPositiveException(LocalizedFormats.NUMBER_OF_TRIALS,
|
||||
trials);
|
||||
}
|
||||
if (p < 0 || p > 1) {
|
||||
throw new OutOfRangeException(p, 0, 1);
|
||||
}
|
||||
|
||||
probabilityOfSuccess = p;
|
||||
numberOfTrials = trials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the number of trials for this distribution.
|
||||
*
|
||||
* @return the number of trials.
|
||||
*/
|
||||
public int getNumberOfTrials() {
|
||||
return numberOfTrials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the probability of success for this distribution.
|
||||
*
|
||||
* @return the probability of success.
|
||||
*/
|
||||
public double getProbabilityOfSuccess() {
|
||||
return probabilityOfSuccess;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double probability(int x) {
|
||||
final double logProbability = logProbability(x);
|
||||
return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logProbability(int x) {
|
||||
if (numberOfTrials == 0) {
|
||||
return (x == 0) ? 0. : Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
double ret;
|
||||
if (x < 0 || x > numberOfTrials) {
|
||||
ret = Double.NEGATIVE_INFINITY;
|
||||
} else {
|
||||
ret = SaddlePointExpansion.logBinomialProbability(x,
|
||||
numberOfTrials, probabilityOfSuccess,
|
||||
1.0 - probabilityOfSuccess);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double cumulativeProbability(int x) {
|
||||
double ret;
|
||||
if (x < 0) {
|
||||
ret = 0.0;
|
||||
} else if (x >= numberOfTrials) {
|
||||
ret = 1.0;
|
||||
} else {
|
||||
ret = 1.0 - Beta.regularizedBeta(probabilityOfSuccess,
|
||||
x + 1.0, numberOfTrials - x);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For {@code n} trials and probability parameter {@code p}, the mean is
|
||||
* {@code n * p}.
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
return numberOfTrials * probabilityOfSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For {@code n} trials and probability parameter {@code p}, the variance is
|
||||
* {@code n * p * (1 - p)}.
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
final double p = probabilityOfSuccess;
|
||||
return numberOfTrials * p * (1 - p);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 except for the probability
|
||||
* parameter {@code p = 1}.
|
||||
*
|
||||
* @return lower bound of the support (0 or the number of trials)
|
||||
*/
|
||||
public int getSupportLowerBound() {
|
||||
return probabilityOfSuccess < 1.0 ? 0 : numberOfTrials;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is the number of trials except for the
|
||||
* probability parameter {@code p = 0}.
|
||||
*
|
||||
* @return upper bound of the support (number of trials or 0)
|
||||
*/
|
||||
public int getSupportUpperBound() {
|
||||
return probabilityOfSuccess > 0.0 ? numberOfTrials : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Implementation of the Cauchy distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Cauchy_distribution">Cauchy distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/CauchyDistribution.html">Cauchy Distribution (MathWorld)</a>
|
||||
* @since 1.1 (changed to concrete class in 3.0)
|
||||
*/
|
||||
public class CauchyDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier */
|
||||
private static final long serialVersionUID = 8589540077390120676L;
|
||||
/** The median of this distribution. */
|
||||
private final double median;
|
||||
/** The scale of this distribution. */
|
||||
private final double scale;
|
||||
/** Inverse cumulative probability accuracy */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
|
||||
/**
|
||||
* Creates a Cauchy distribution with the median equal to zero and scale
|
||||
* equal to one.
|
||||
*/
|
||||
public CauchyDistribution() {
|
||||
this(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Cauchy distribution using the given median and scale.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param median Median for this distribution.
|
||||
* @param scale Scale parameter for this distribution.
|
||||
*/
|
||||
public CauchyDistribution(double median, double scale) {
|
||||
this(median, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Cauchy distribution using the given median and scale.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param median Median for this distribution.
|
||||
* @param scale Scale parameter for this distribution.
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates
|
||||
* (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code scale <= 0}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public CauchyDistribution(double median, double scale,
|
||||
double inverseCumAccuracy) {
|
||||
this(new Well19937c(), median, scale, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Cauchy distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param median Median for this distribution.
|
||||
* @param scale Scale parameter for this distribution.
|
||||
* @throws NotStrictlyPositiveException if {@code scale <= 0}.
|
||||
* @since 3.3
|
||||
*/
|
||||
public CauchyDistribution(RandomGenerator rng, double median, double scale) {
|
||||
this(rng, median, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Cauchy distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param median Median for this distribution.
|
||||
* @param scale Scale parameter for this distribution.
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates
|
||||
* (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code scale <= 0}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public CauchyDistribution(RandomGenerator rng,
|
||||
double median,
|
||||
double scale,
|
||||
double inverseCumAccuracy) {
|
||||
super(rng);
|
||||
if (scale <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
|
||||
}
|
||||
this.scale = scale;
|
||||
this.median = median;
|
||||
solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double cumulativeProbability(double x) {
|
||||
return 0.5 + (FastMath.atan((x - median) / scale) / FastMath.PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the median.
|
||||
*
|
||||
* @return the median for this distribution.
|
||||
*/
|
||||
public double getMedian() {
|
||||
return median;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the scale parameter.
|
||||
*
|
||||
* @return the scale parameter for this distribution.
|
||||
*/
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double density(double x) {
|
||||
final double dev = x - median;
|
||||
return (1 / FastMath.PI) * (scale / (dev * dev + scale * scale));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Returns {@code Double.NEGATIVE_INFINITY} when {@code p == 0}
|
||||
* and {@code Double.POSITIVE_INFINITY} when {@code p == 1}.
|
||||
*/
|
||||
@Override
|
||||
public double inverseCumulativeProbability(double p) throws OutOfRangeException {
|
||||
double ret;
|
||||
if (p < 0 || p > 1) {
|
||||
throw new OutOfRangeException(p, 0, 1);
|
||||
} else if (p == 0) {
|
||||
ret = Double.NEGATIVE_INFINITY;
|
||||
} else if (p == 1) {
|
||||
ret = Double.POSITIVE_INFINITY;
|
||||
} else {
|
||||
ret = median + scale * FastMath.tan(FastMath.PI * (p - .5));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The mean is always undefined no matter the parameters.
|
||||
*
|
||||
* @return mean (always Double.NaN)
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The variance is always undefined no matter the parameters.
|
||||
*
|
||||
* @return variance (always Double.NaN)
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always negative infinity no matter
|
||||
* the parameters.
|
||||
*
|
||||
* @return lower bound of the support (always Double.NEGATIVE_INFINITY)
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always positive infinity no matter
|
||||
* the parameters.
|
||||
*
|
||||
* @return upper bound of the support (always Double.POSITIVE_INFINITY)
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
|
||||
/**
|
||||
* Implementation of the chi-squared distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Chi-squared_distribution">Chi-squared distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">Chi-squared Distribution (MathWorld)</a>
|
||||
*/
|
||||
public class ChiSquaredDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier */
|
||||
private static final long serialVersionUID = -8352658048349159782L;
|
||||
/** Internal Gamma distribution. */
|
||||
private final GammaDistribution gamma;
|
||||
/** Inverse cumulative probability accuracy */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
|
||||
/**
|
||||
* Create a Chi-Squared distribution with the given degrees of freedom.
|
||||
*
|
||||
* @param degreesOfFreedom Degrees of freedom.
|
||||
*/
|
||||
public ChiSquaredDistribution(double degreesOfFreedom) {
|
||||
this(degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Chi-Squared distribution with the given degrees of freedom and
|
||||
* inverse cumulative probability accuracy.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param degreesOfFreedom Degrees of freedom.
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @since 2.1
|
||||
*/
|
||||
public ChiSquaredDistribution(double degreesOfFreedom,
|
||||
double inverseCumAccuracy) {
|
||||
this(new Well19937c(), degreesOfFreedom, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Chi-Squared distribution with the given degrees of freedom.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param degreesOfFreedom Degrees of freedom.
|
||||
* @since 3.3
|
||||
*/
|
||||
public ChiSquaredDistribution(RandomGenerator rng, double degreesOfFreedom) {
|
||||
this(rng, degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Chi-Squared distribution with the given degrees of freedom and
|
||||
* inverse cumulative probability accuracy.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param degreesOfFreedom Degrees of freedom.
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @since 3.1
|
||||
*/
|
||||
public ChiSquaredDistribution(RandomGenerator rng,
|
||||
double degreesOfFreedom,
|
||||
double inverseCumAccuracy) {
|
||||
super(rng);
|
||||
|
||||
gamma = new GammaDistribution(degreesOfFreedom / 2, 2);
|
||||
solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the number of degrees of freedom.
|
||||
*
|
||||
* @return the degrees of freedom.
|
||||
*/
|
||||
public double getDegreesOfFreedom() {
|
||||
return gamma.getShape() * 2.0;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double density(double x) {
|
||||
return gamma.density(x);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logDensity(double x) {
|
||||
return gamma.logDensity(x);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double cumulativeProbability(double x) {
|
||||
return gamma.cumulativeProbability(x);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For {@code k} degrees of freedom, the mean is {@code k}.
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
return getDegreesOfFreedom();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return {@code 2 * k}, where {@code k} is the number of degrees of freedom.
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
return 2 * getDegreesOfFreedom();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 no matter the
|
||||
* degrees of freedom.
|
||||
*
|
||||
* @return zero.
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always positive infinity no matter the
|
||||
* degrees of freedom.
|
||||
*
|
||||
* @return {@code Double.POSITIVE_INFINITY}.
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.util.CombinatoricsUtils;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
import infodynamics.utils.commonsmath3.util.ResizableDoubleArray;
|
||||
|
||||
/**
|
||||
* Implementation of the exponential distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Exponential_distribution">Exponential distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">Exponential distribution (MathWorld)</a>
|
||||
*/
|
||||
public class ExponentialDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier */
|
||||
private static final long serialVersionUID = 2401296428283614780L;
|
||||
/**
|
||||
* Used when generating Exponential samples.
|
||||
* Table containing the constants
|
||||
* q_i = sum_{j=1}^i (ln 2)^j/j! = ln 2 + (ln 2)^2/2 + ... + (ln 2)^i/i!
|
||||
* until the largest representable fraction below 1 is exceeded.
|
||||
*
|
||||
* Note that
|
||||
* 1 = 2 - 1 = exp(ln 2) - 1 = sum_{n=1}^infty (ln 2)^n / n!
|
||||
* thus q_i -> 1 as i -> +inf,
|
||||
* so the higher i, the closer to one we get (the series is not alternating).
|
||||
*
|
||||
* By trying, n = 16 in Java is enough to reach 1.0.
|
||||
*/
|
||||
private static final double[] EXPONENTIAL_SA_QI;
|
||||
/** The mean of this distribution. */
|
||||
private final double mean;
|
||||
/** The logarithm of the mean, stored to reduce computing time. **/
|
||||
private final double logMean;
|
||||
/** Inverse cumulative probability accuracy. */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
|
||||
/**
|
||||
* Initialize tables.
|
||||
*/
|
||||
static {
|
||||
/**
|
||||
* Filling EXPONENTIAL_SA_QI table.
|
||||
* Note that we don't want qi = 0 in the table.
|
||||
*/
|
||||
final double LN2 = FastMath.log(2);
|
||||
double qi = 0;
|
||||
int i = 1;
|
||||
|
||||
/**
|
||||
* ArithmeticUtils provides factorials up to 20, so let's use that
|
||||
* limit together with Precision.EPSILON to generate the following
|
||||
* code (a priori, we know that there will be 16 elements, but it is
|
||||
* better to not hardcode it).
|
||||
*/
|
||||
final ResizableDoubleArray ra = new ResizableDoubleArray(20);
|
||||
|
||||
while (qi < 1) {
|
||||
qi += FastMath.pow(LN2, i) / CombinatoricsUtils.factorial(i);
|
||||
ra.addElement(qi);
|
||||
++i;
|
||||
}
|
||||
|
||||
EXPONENTIAL_SA_QI = ra.getElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exponential distribution with the given mean.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param mean mean of this distribution.
|
||||
*/
|
||||
public ExponentialDistribution(double mean) {
|
||||
this(mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an exponential distribution with the given mean.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param mean Mean of this distribution.
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code mean <= 0}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public ExponentialDistribution(double mean, double inverseCumAccuracy) {
|
||||
this(new Well19937c(), mean, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an exponential distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param mean Mean of this distribution.
|
||||
* @throws NotStrictlyPositiveException if {@code mean <= 0}.
|
||||
* @since 3.3
|
||||
*/
|
||||
public ExponentialDistribution(RandomGenerator rng, double mean)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(rng, mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an exponential distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param mean Mean of this distribution.
|
||||
* @param inverseCumAccuracy Maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code mean <= 0}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public ExponentialDistribution(RandomGenerator rng,
|
||||
double mean,
|
||||
double inverseCumAccuracy)
|
||||
throws NotStrictlyPositiveException {
|
||||
super(rng);
|
||||
|
||||
if (mean <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, mean);
|
||||
}
|
||||
this.mean = mean;
|
||||
logMean = FastMath.log(mean);
|
||||
solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the mean.
|
||||
*
|
||||
* @return the mean.
|
||||
*/
|
||||
public double getMean() {
|
||||
return mean;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double density(double x) {
|
||||
final double logDensity = logDensity(x);
|
||||
return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logDensity(double x) {
|
||||
if (x < 0) {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
return -x / mean - logMean;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The implementation of this method is based on:
|
||||
* <ul>
|
||||
* <li>
|
||||
* <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">
|
||||
* Exponential Distribution</a>, equation (1).</li>
|
||||
* </ul>
|
||||
*/
|
||||
public double cumulativeProbability(double x) {
|
||||
double ret;
|
||||
if (x <= 0.0) {
|
||||
ret = 0.0;
|
||||
} else {
|
||||
ret = 1.0 - FastMath.exp(-x / mean);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Returns {@code 0} when {@code p= = 0} and
|
||||
* {@code Double.POSITIVE_INFINITY} when {@code p == 1}.
|
||||
*/
|
||||
@Override
|
||||
public double inverseCumulativeProbability(double p) throws OutOfRangeException {
|
||||
double ret;
|
||||
|
||||
if (p < 0.0 || p > 1.0) {
|
||||
throw new OutOfRangeException(p, 0.0, 1.0);
|
||||
} else if (p == 1.0) {
|
||||
ret = Double.POSITIVE_INFINITY;
|
||||
} else {
|
||||
ret = -mean * FastMath.log(1.0 - p);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p><strong>Algorithm Description</strong>: this implementation uses the
|
||||
* <a href="http://www.jesus.ox.ac.uk/~clifford/a5/chap1/node5.html">
|
||||
* Inversion Method</a> to generate exponentially distributed random values
|
||||
* from uniform deviates.</p>
|
||||
*
|
||||
* @return a random value.
|
||||
* @since 2.2
|
||||
*/
|
||||
@Override
|
||||
public double sample() {
|
||||
// Step 1:
|
||||
double a = 0;
|
||||
double u = random.nextDouble();
|
||||
|
||||
// Step 2 and 3:
|
||||
while (u < 0.5) {
|
||||
a += EXPONENTIAL_SA_QI[0];
|
||||
u *= 2;
|
||||
}
|
||||
|
||||
// Step 4 (now u >= 0.5):
|
||||
u += u - 1;
|
||||
|
||||
// Step 5:
|
||||
if (u <= EXPONENTIAL_SA_QI[0]) {
|
||||
return mean * (a + u);
|
||||
}
|
||||
|
||||
// Step 6:
|
||||
int i = 0; // Should be 1, be we iterate before it in while using 0
|
||||
double u2 = random.nextDouble();
|
||||
double umin = u2;
|
||||
|
||||
// Step 7 and 8:
|
||||
do {
|
||||
++i;
|
||||
u2 = random.nextDouble();
|
||||
|
||||
if (u2 < umin) {
|
||||
umin = u2;
|
||||
}
|
||||
|
||||
// Step 8:
|
||||
} while (u > EXPONENTIAL_SA_QI[i]); // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1
|
||||
|
||||
return mean * (a + umin * EXPONENTIAL_SA_QI[0]);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For mean parameter {@code k}, the mean is {@code k}.
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
return getMean();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For mean parameter {@code k}, the variance is {@code k^2}.
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
final double m = getMean();
|
||||
return m * m;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 no matter the mean parameter.
|
||||
*
|
||||
* @return lower bound of the support (always 0)
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always positive infinity
|
||||
* no matter the mean parameter.
|
||||
*
|
||||
* @return upper bound of the support (always Double.POSITIVE_INFINITY)
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.special.Beta;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Implementation of the F-distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/F-distribution">F-distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/F-Distribution.html">F-distribution (MathWorld)</a>
|
||||
*/
|
||||
public class FDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier. */
|
||||
private static final long serialVersionUID = -8516354193418641566L;
|
||||
/** The numerator degrees of freedom. */
|
||||
private final double numeratorDegreesOfFreedom;
|
||||
/** The numerator degrees of freedom. */
|
||||
private final double denominatorDegreesOfFreedom;
|
||||
/** Inverse cumulative probability accuracy. */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
/** Cached numerical variance */
|
||||
private double numericalVariance = Double.NaN;
|
||||
/** Whether or not the numerical variance has been calculated */
|
||||
private boolean numericalVarianceIsCalculated = false;
|
||||
|
||||
/**
|
||||
* Creates an F distribution using the given degrees of freedom.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param numeratorDegreesOfFreedom Numerator degrees of freedom.
|
||||
* @param denominatorDegreesOfFreedom Denominator degrees of freedom.
|
||||
* @throws NotStrictlyPositiveException if
|
||||
* {@code numeratorDegreesOfFreedom <= 0} or
|
||||
* {@code denominatorDegreesOfFreedom <= 0}.
|
||||
*/
|
||||
public FDistribution(double numeratorDegreesOfFreedom,
|
||||
double denominatorDegreesOfFreedom)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom,
|
||||
DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an F distribution using the given degrees of freedom
|
||||
* and inverse cumulative probability accuracy.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param numeratorDegreesOfFreedom Numerator degrees of freedom.
|
||||
* @param denominatorDegreesOfFreedom Denominator degrees of freedom.
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates.
|
||||
* @throws NotStrictlyPositiveException if
|
||||
* {@code numeratorDegreesOfFreedom <= 0} or
|
||||
* {@code denominatorDegreesOfFreedom <= 0}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public FDistribution(double numeratorDegreesOfFreedom,
|
||||
double denominatorDegreesOfFreedom,
|
||||
double inverseCumAccuracy)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(new Well19937c(), numeratorDegreesOfFreedom,
|
||||
denominatorDegreesOfFreedom, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an F distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param numeratorDegreesOfFreedom Numerator degrees of freedom.
|
||||
* @param denominatorDegreesOfFreedom Denominator degrees of freedom.
|
||||
* @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or
|
||||
* {@code denominatorDegreesOfFreedom <= 0}.
|
||||
* @since 3.3
|
||||
*/
|
||||
public FDistribution(RandomGenerator rng,
|
||||
double numeratorDegreesOfFreedom,
|
||||
double denominatorDegreesOfFreedom)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(rng, numeratorDegreesOfFreedom, denominatorDegreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an F distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param numeratorDegreesOfFreedom Numerator degrees of freedom.
|
||||
* @param denominatorDegreesOfFreedom Denominator degrees of freedom.
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates.
|
||||
* @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or
|
||||
* {@code denominatorDegreesOfFreedom <= 0}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public FDistribution(RandomGenerator rng,
|
||||
double numeratorDegreesOfFreedom,
|
||||
double denominatorDegreesOfFreedom,
|
||||
double inverseCumAccuracy)
|
||||
throws NotStrictlyPositiveException {
|
||||
super(rng);
|
||||
|
||||
if (numeratorDegreesOfFreedom <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.DEGREES_OF_FREEDOM,
|
||||
numeratorDegreesOfFreedom);
|
||||
}
|
||||
if (denominatorDegreesOfFreedom <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.DEGREES_OF_FREEDOM,
|
||||
denominatorDegreesOfFreedom);
|
||||
}
|
||||
this.numeratorDegreesOfFreedom = numeratorDegreesOfFreedom;
|
||||
this.denominatorDegreesOfFreedom = denominatorDegreesOfFreedom;
|
||||
solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
public double density(double x) {
|
||||
return FastMath.exp(logDensity(x));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logDensity(double x) {
|
||||
final double nhalf = numeratorDegreesOfFreedom / 2;
|
||||
final double mhalf = denominatorDegreesOfFreedom / 2;
|
||||
final double logx = FastMath.log(x);
|
||||
final double logn = FastMath.log(numeratorDegreesOfFreedom);
|
||||
final double logm = FastMath.log(denominatorDegreesOfFreedom);
|
||||
final double lognxm = FastMath.log(numeratorDegreesOfFreedom * x +
|
||||
denominatorDegreesOfFreedom);
|
||||
return nhalf * logn + nhalf * logx - logx +
|
||||
mhalf * logm - nhalf * lognxm - mhalf * lognxm -
|
||||
Beta.logBeta(nhalf, mhalf);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The implementation of this method is based on
|
||||
* <ul>
|
||||
* <li>
|
||||
* <a href="http://mathworld.wolfram.com/F-Distribution.html">
|
||||
* F-Distribution</a>, equation (4).
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public double cumulativeProbability(double x) {
|
||||
double ret;
|
||||
if (x <= 0) {
|
||||
ret = 0;
|
||||
} else {
|
||||
double n = numeratorDegreesOfFreedom;
|
||||
double m = denominatorDegreesOfFreedom;
|
||||
|
||||
ret = Beta.regularizedBeta((n * x) / (m + n * x),
|
||||
0.5 * n,
|
||||
0.5 * m);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the numerator degrees of freedom.
|
||||
*
|
||||
* @return the numerator degrees of freedom.
|
||||
*/
|
||||
public double getNumeratorDegreesOfFreedom() {
|
||||
return numeratorDegreesOfFreedom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the denominator degrees of freedom.
|
||||
*
|
||||
* @return the denominator degrees of freedom.
|
||||
*/
|
||||
public double getDenominatorDegreesOfFreedom() {
|
||||
return denominatorDegreesOfFreedom;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For denominator degrees of freedom parameter {@code b}, the mean is
|
||||
* <ul>
|
||||
* <li>if {@code b > 2} then {@code b / (b - 2)},</li>
|
||||
* <li>else undefined ({@code Double.NaN}).
|
||||
* </ul>
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
final double denominatorDF = getDenominatorDegreesOfFreedom();
|
||||
|
||||
if (denominatorDF > 2) {
|
||||
return denominatorDF / (denominatorDF - 2);
|
||||
}
|
||||
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For numerator degrees of freedom parameter {@code a} and denominator
|
||||
* degrees of freedom parameter {@code b}, the variance is
|
||||
* <ul>
|
||||
* <li>
|
||||
* if {@code b > 4} then
|
||||
* {@code [2 * b^2 * (a + b - 2)] / [a * (b - 2)^2 * (b - 4)]},
|
||||
* </li>
|
||||
* <li>else undefined ({@code Double.NaN}).
|
||||
* </ul>
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
if (!numericalVarianceIsCalculated) {
|
||||
numericalVariance = calculateNumericalVariance();
|
||||
numericalVarianceIsCalculated = true;
|
||||
}
|
||||
return numericalVariance;
|
||||
}
|
||||
|
||||
/**
|
||||
* used by {@link #getNumericalVariance()}
|
||||
*
|
||||
* @return the variance of this distribution
|
||||
*/
|
||||
protected double calculateNumericalVariance() {
|
||||
final double denominatorDF = getDenominatorDegreesOfFreedom();
|
||||
|
||||
if (denominatorDF > 4) {
|
||||
final double numeratorDF = getNumeratorDegreesOfFreedom();
|
||||
final double denomDFMinusTwo = denominatorDF - 2;
|
||||
|
||||
return ( 2 * (denominatorDF * denominatorDF) * (numeratorDF + denominatorDF - 2) ) /
|
||||
( (numeratorDF * (denomDFMinusTwo * denomDFMinusTwo) * (denominatorDF - 4)) );
|
||||
}
|
||||
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 no matter the parameters.
|
||||
*
|
||||
* @return lower bound of the support (always 0)
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always positive infinity
|
||||
* no matter the parameters.
|
||||
*
|
||||
* @return upper bound of the support (always Double.POSITIVE_INFINITY)
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,542 @@
|
|||
/*
|
||||
* Java Information Dynamics Toolkit (JIDT)
|
||||
* Copyright (C) 2017, 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class was originally distributed as part of the Apache Commons
|
||||
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
||||
* copied below. This Apache 2 software is now included as a derivative
|
||||
* work in the GPLv3 licensed JIDT project, as per:
|
||||
* http://www.apache.org/licenses/GPL-compatibility.html
|
||||
*
|
||||
* The original Apache source code has been modified as follows:
|
||||
* -- We have modified package names to sit inside the JIDT structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package infodynamics.utils.commonsmath3.distribution;
|
||||
|
||||
import infodynamics.utils.commonsmath3.exception.NotStrictlyPositiveException;
|
||||
import infodynamics.utils.commonsmath3.exception.util.LocalizedFormats;
|
||||
import infodynamics.utils.commonsmath3.random.RandomGenerator;
|
||||
import infodynamics.utils.commonsmath3.random.Well19937c;
|
||||
import infodynamics.utils.commonsmath3.special.Gamma;
|
||||
import infodynamics.utils.commonsmath3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Implementation of the Gamma distribution.
|
||||
*
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Gamma_distribution">Gamma distribution (Wikipedia)</a>
|
||||
* @see <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution (MathWorld)</a>
|
||||
*/
|
||||
public class GammaDistribution extends AbstractRealDistribution {
|
||||
/**
|
||||
* Default inverse cumulative probability accuracy.
|
||||
* @since 2.1
|
||||
*/
|
||||
public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
|
||||
/** Serializable version identifier. */
|
||||
private static final long serialVersionUID = 20120524L;
|
||||
/** The shape parameter. */
|
||||
private final double shape;
|
||||
/** The scale parameter. */
|
||||
private final double scale;
|
||||
/**
|
||||
* The constant value of {@code shape + g + 0.5}, where {@code g} is the
|
||||
* Lanczos constant {@link Gamma#LANCZOS_G}.
|
||||
*/
|
||||
private final double shiftedShape;
|
||||
/**
|
||||
* The constant value of
|
||||
* {@code shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)},
|
||||
* where {@code L(shape)} is the Lanczos approximation returned by
|
||||
* {@link Gamma#lanczos(double)}. This prefactor is used in
|
||||
* {@link #density(double)}, when no overflow occurs with the natural
|
||||
* calculation.
|
||||
*/
|
||||
private final double densityPrefactor1;
|
||||
/**
|
||||
* The constant value of
|
||||
* {@code log(shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))},
|
||||
* where {@code L(shape)} is the Lanczos approximation returned by
|
||||
* {@link Gamma#lanczos(double)}. This prefactor is used in
|
||||
* {@link #logDensity(double)}, when no overflow occurs with the natural
|
||||
* calculation.
|
||||
*/
|
||||
private final double logDensityPrefactor1;
|
||||
/**
|
||||
* The constant value of
|
||||
* {@code shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)},
|
||||
* where {@code L(shape)} is the Lanczos approximation returned by
|
||||
* {@link Gamma#lanczos(double)}. This prefactor is used in
|
||||
* {@link #density(double)}, when overflow occurs with the natural
|
||||
* calculation.
|
||||
*/
|
||||
private final double densityPrefactor2;
|
||||
/**
|
||||
* The constant value of
|
||||
* {@code log(shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))},
|
||||
* where {@code L(shape)} is the Lanczos approximation returned by
|
||||
* {@link Gamma#lanczos(double)}. This prefactor is used in
|
||||
* {@link #logDensity(double)}, when overflow occurs with the natural
|
||||
* calculation.
|
||||
*/
|
||||
private final double logDensityPrefactor2;
|
||||
/**
|
||||
* Lower bound on {@code y = x / scale} for the selection of the computation
|
||||
* method in {@link #density(double)}. For {@code y <= minY}, the natural
|
||||
* calculation overflows.
|
||||
*/
|
||||
private final double minY;
|
||||
/**
|
||||
* Upper bound on {@code log(y)} ({@code y = x / scale}) for the selection
|
||||
* of the computation method in {@link #density(double)}. For
|
||||
* {@code log(y) >= maxLogY}, the natural calculation overflows.
|
||||
*/
|
||||
private final double maxLogY;
|
||||
/** Inverse cumulative probability accuracy. */
|
||||
private final double solverAbsoluteAccuracy;
|
||||
|
||||
/**
|
||||
* Creates a new gamma distribution with specified values of the shape and
|
||||
* scale parameters.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param shape the shape parameter
|
||||
* @param scale the scale parameter
|
||||
* @throws NotStrictlyPositiveException if {@code shape <= 0} or
|
||||
* {@code scale <= 0}.
|
||||
*/
|
||||
public GammaDistribution(double shape, double scale) throws NotStrictlyPositiveException {
|
||||
this(shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new gamma distribution with specified values of the shape and
|
||||
* scale parameters.
|
||||
* <p>
|
||||
* <b>Note:</b> this constructor will implicitly create an instance of
|
||||
* {@link Well19937c} as random generator to be used for sampling only (see
|
||||
* {@link #sample()} and {@link #sample(int)}). In case no sampling is
|
||||
* needed for the created distribution, it is advised to pass {@code null}
|
||||
* as random generator via the appropriate constructors to avoid the
|
||||
* additional initialisation overhead.
|
||||
*
|
||||
* @param shape the shape parameter
|
||||
* @param scale the scale parameter
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code shape <= 0} or
|
||||
* {@code scale <= 0}.
|
||||
* @since 2.1
|
||||
*/
|
||||
public GammaDistribution(double shape, double scale, double inverseCumAccuracy)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(new Well19937c(), shape, scale, inverseCumAccuracy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Gamma distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param shape the shape parameter
|
||||
* @param scale the scale parameter
|
||||
* @throws NotStrictlyPositiveException if {@code shape <= 0} or
|
||||
* {@code scale <= 0}.
|
||||
* @since 3.3
|
||||
*/
|
||||
public GammaDistribution(RandomGenerator rng, double shape, double scale)
|
||||
throws NotStrictlyPositiveException {
|
||||
this(rng, shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Gamma distribution.
|
||||
*
|
||||
* @param rng Random number generator.
|
||||
* @param shape the shape parameter
|
||||
* @param scale the scale parameter
|
||||
* @param inverseCumAccuracy the maximum absolute error in inverse
|
||||
* cumulative probability estimates (defaults to
|
||||
* {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
|
||||
* @throws NotStrictlyPositiveException if {@code shape <= 0} or
|
||||
* {@code scale <= 0}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public GammaDistribution(RandomGenerator rng,
|
||||
double shape,
|
||||
double scale,
|
||||
double inverseCumAccuracy)
|
||||
throws NotStrictlyPositiveException {
|
||||
super(rng);
|
||||
|
||||
if (shape <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, shape);
|
||||
}
|
||||
if (scale <= 0) {
|
||||
throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
|
||||
}
|
||||
|
||||
this.shape = shape;
|
||||
this.scale = scale;
|
||||
this.solverAbsoluteAccuracy = inverseCumAccuracy;
|
||||
this.shiftedShape = shape + Gamma.LANCZOS_G + 0.5;
|
||||
final double aux = FastMath.E / (2.0 * FastMath.PI * shiftedShape);
|
||||
this.densityPrefactor2 = shape * FastMath.sqrt(aux) / Gamma.lanczos(shape);
|
||||
this.logDensityPrefactor2 = FastMath.log(shape) + 0.5 * FastMath.log(aux) -
|
||||
FastMath.log(Gamma.lanczos(shape));
|
||||
this.densityPrefactor1 = this.densityPrefactor2 / scale *
|
||||
FastMath.pow(shiftedShape, -shape) *
|
||||
FastMath.exp(shape + Gamma.LANCZOS_G);
|
||||
this.logDensityPrefactor1 = this.logDensityPrefactor2 - FastMath.log(scale) -
|
||||
FastMath.log(shiftedShape) * shape +
|
||||
shape + Gamma.LANCZOS_G;
|
||||
this.minY = shape + Gamma.LANCZOS_G - FastMath.log(Double.MAX_VALUE);
|
||||
this.maxLogY = FastMath.log(Double.MAX_VALUE) / (shape - 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shape parameter of {@code this} distribution.
|
||||
*
|
||||
* @return the shape parameter
|
||||
* @deprecated as of version 3.1, {@link #getShape()} should be preferred.
|
||||
* This method will be removed in version 4.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getAlpha() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shape parameter of {@code this} distribution.
|
||||
*
|
||||
* @return the shape parameter
|
||||
* @since 3.1
|
||||
*/
|
||||
public double getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scale parameter of {@code this} distribution.
|
||||
*
|
||||
* @return the scale parameter
|
||||
* @deprecated as of version 3.1, {@link #getScale()} should be preferred.
|
||||
* This method will be removed in version 4.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public double getBeta() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scale parameter of {@code this} distribution.
|
||||
*
|
||||
* @return the scale parameter
|
||||
* @since 3.1
|
||||
*/
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public double density(double x) {
|
||||
/* The present method must return the value of
|
||||
*
|
||||
* 1 x a - x
|
||||
* ---------- (-) exp(---)
|
||||
* x Gamma(a) b b
|
||||
*
|
||||
* where a is the shape parameter, and b the scale parameter.
|
||||
* Substituting the Lanczos approximation of Gamma(a) leads to the
|
||||
* following expression of the density
|
||||
*
|
||||
* a e 1 y a
|
||||
* - sqrt(------------------) ---- (-----------) exp(a - y + g),
|
||||
* x 2 pi (a + g + 0.5) L(a) a + g + 0.5
|
||||
*
|
||||
* where y = x / b. The above formula is the "natural" computation, which
|
||||
* is implemented when no overflow is likely to occur. If overflow occurs
|
||||
* with the natural computation, the following identity is used. It is
|
||||
* based on the BOOST library
|
||||
* http://www.boost.org/doc/libs/1_35_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_gamma/igamma.html
|
||||
* Formula (15) needs adaptations, which are detailed below.
|
||||
*
|
||||
* y a
|
||||
* (-----------) exp(a - y + g)
|
||||
* a + g + 0.5
|
||||
* y - a - g - 0.5 y (g + 0.5)
|
||||
* = exp(a log1pm(---------------) - ----------- + g),
|
||||
* a + g + 0.5 a + g + 0.5
|
||||
*
|
||||
* where log1pm(z) = log(1 + z) - z. Therefore, the value to be
|
||||
* returned is
|
||||
*
|
||||
* a e 1
|
||||
* - sqrt(------------------) ----
|
||||
* x 2 pi (a + g + 0.5) L(a)
|
||||
* y - a - g - 0.5 y (g + 0.5)
|
||||
* * exp(a log1pm(---------------) - ----------- + g).
|
||||
* a + g + 0.5 a + g + 0.5
|
||||
*/
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
final double y = x / scale;
|
||||
if ((y <= minY) || (FastMath.log(y) >= maxLogY)) {
|
||||
/*
|
||||
* Overflow.
|
||||
*/
|
||||
final double aux1 = (y - shiftedShape) / shiftedShape;
|
||||
final double aux2 = shape * (FastMath.log1p(aux1) - aux1);
|
||||
final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape +
|
||||
Gamma.LANCZOS_G + aux2;
|
||||
return densityPrefactor2 / x * FastMath.exp(aux3);
|
||||
}
|
||||
/*
|
||||
* Natural calculation.
|
||||
*/
|
||||
return densityPrefactor1 * FastMath.exp(-y) * FastMath.pow(y, shape - 1);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} **/
|
||||
@Override
|
||||
public double logDensity(double x) {
|
||||
/*
|
||||
* see the comment in {@link #density(double)} for computation details
|
||||
*/
|
||||
if (x < 0) {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
final double y = x / scale;
|
||||
if ((y <= minY) || (FastMath.log(y) >= maxLogY)) {
|
||||
/*
|
||||
* Overflow.
|
||||
*/
|
||||
final double aux1 = (y - shiftedShape) / shiftedShape;
|
||||
final double aux2 = shape * (FastMath.log1p(aux1) - aux1);
|
||||
final double aux3 = -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape +
|
||||
Gamma.LANCZOS_G + aux2;
|
||||
return logDensityPrefactor2 - FastMath.log(x) + aux3;
|
||||
}
|
||||
/*
|
||||
* Natural calculation.
|
||||
*/
|
||||
return logDensityPrefactor1 - y + FastMath.log(y) * (shape - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The implementation of this method is based on:
|
||||
* <ul>
|
||||
* <li>
|
||||
* <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">
|
||||
* Chi-Squared Distribution</a>, equation (9).
|
||||
* </li>
|
||||
* <li>Casella, G., & Berger, R. (1990). <i>Statistical Inference</i>.
|
||||
* Belmont, CA: Duxbury Press.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public double cumulativeProbability(double x) {
|
||||
double ret;
|
||||
|
||||
if (x <= 0) {
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = Gamma.regularizedGammaP(shape, x / scale);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected double getSolverAbsoluteAccuracy() {
|
||||
return solverAbsoluteAccuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For shape parameter {@code alpha} and scale parameter {@code beta}, the
|
||||
* mean is {@code alpha * beta}.
|
||||
*/
|
||||
public double getNumericalMean() {
|
||||
return shape * scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* For shape parameter {@code alpha} and scale parameter {@code beta}, the
|
||||
* variance is {@code alpha * beta^2}.
|
||||
*
|
||||
* @return {@inheritDoc}
|
||||
*/
|
||||
public double getNumericalVariance() {
|
||||
return shape * scale * scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The lower bound of the support is always 0 no matter the parameters.
|
||||
*
|
||||
* @return lower bound of the support (always 0)
|
||||
*/
|
||||
public double getSupportLowerBound() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The upper bound of the support is always positive infinity
|
||||
* no matter the parameters.
|
||||
*
|
||||
* @return upper bound of the support (always Double.POSITIVE_INFINITY)
|
||||
*/
|
||||
public double getSupportUpperBound() {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportLowerBoundInclusive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean isSupportUpperBoundInclusive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* The support of this distribution is connected.
|
||||
*
|
||||
* @return {@code true}
|
||||
*/
|
||||
public boolean isSupportConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This implementation uses the following algorithms: </p>
|
||||
*
|
||||
* <p>For 0 < shape < 1: <br/>
|
||||
* Ahrens, J. H. and Dieter, U., <i>Computer methods for
|
||||
* sampling from gamma, beta, Poisson and binomial distributions.</i>
|
||||
* Computing, 12, 223-246, 1974.</p>
|
||||
*
|
||||
* <p>For shape >= 1: <br/>
|
||||
* Marsaglia and Tsang, <i>A Simple Method for Generating
|
||||
* Gamma Variables.</i> ACM Transactions on Mathematical Software,
|
||||
* Volume 26 Issue 3, September, 2000.</p>
|
||||
*
|
||||
* @return random value sampled from the Gamma(shape, scale) distribution
|
||||
*/
|
||||
@Override
|
||||
public double sample() {
|
||||
if (shape < 1) {
|
||||
// [1]: p. 228, Algorithm GS
|
||||
|
||||
while (true) {
|
||||
// Step 1:
|
||||
final double u = random.nextDouble();
|
||||
final double bGS = 1 + shape / FastMath.E;
|
||||
final double p = bGS * u;
|
||||
|
||||
if (p <= 1) {
|
||||
// Step 2:
|
||||
|
||||
final double x = FastMath.pow(p, 1 / shape);
|
||||
final double u2 = random.nextDouble();
|
||||
|
||||
if (u2 > FastMath.exp(-x)) {
|
||||
// Reject
|
||||
continue;
|
||||
} else {
|
||||
return scale * x;
|
||||
}
|
||||
} else {
|
||||
// Step 3:
|
||||
|
||||
final double x = -1 * FastMath.log((bGS - p) / shape);
|
||||
final double u2 = random.nextDouble();
|
||||
|
||||
if (u2 > FastMath.pow(x, shape - 1)) {
|
||||
// Reject
|
||||
continue;
|
||||
} else {
|
||||
return scale * x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now shape >= 1
|
||||
|
||||
final double d = shape - 0.333333333333333333;
|
||||
final double c = 1 / (3 * FastMath.sqrt(d));
|
||||
|
||||
while (true) {
|
||||
final double x = random.nextGaussian();
|
||||
final double v = (1 + c * x) * (1 + c * x) * (1 + c * x);
|
||||
|
||||
if (v <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final double x2 = x * x;
|
||||
final double u = random.nextDouble();
|
||||
|
||||
// Squeeze
|
||||
if (u < 1 - 0.0331 * x2 * x2) {
|
||||
return scale * d * v;
|
||||
}
|
||||
|
||||
if (FastMath.log(u) < 0.5 * x2 + d * (1 - v + FastMath.log(v))) {
|
||||
return scale * d * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue