diff --git a/README.md b/README.md
index c70834d..9e89c32 100644
--- a/README.md
+++ b/README.md
@@ -14,11 +14,11 @@ JIDT is distributed under the [GNU GPL v3 license](http://www.gnu.org/licenses/g
# Getting started
- 1. [Download](../../wiki/Downloads) and [Installation](../../wiki/Installation) is very easy!
- 1. _Quick start_: download the latest [v1.4 full distribution](http://lizier.me/joseph/software/jidt/download.php?file=infodynamics-dist-1.4.zip) (suitable for all platforms) and see the readme.txt file therein.
- 1. [Documentation](../../wiki/Documentation) including: the paper describing JIDT at [arXiv:1408.3270](http://arxiv.org/abs/1408.3270) (distributed with the toolkit), a [Tutorial](../../wiki/Tutorial), and [Javadocs (v1.4 here)](http://lizier.me/joseph/software/jidt/javadocs/v1.4/);
- 1. [Demos](../../wiki/Demos) are included with the full distribution, including a [GUI app](../../wiki/AutoAnalyser) for automatic analysis and code generation (see picture below), [simple java demos](../../wiki/SimpleJavaExamples) and [cellular automata (CA) demos](../../wiki/CellularAutomataDemos).
- 1. These Java tools can easily be used in [Matlab/Octave](../../wiki/OctaveMatlabExamples), [Python](../../wiki/PythonExamples), [R](../../wiki/R_Examples), [Julia](../../wiki/JuliaExamples) and [Clojure](../../wiki/Clojure_Examples)! (click on each language here for examples)
+1. [Download](../../wiki/Downloads) and [Installation](../../wiki/Installation) is very easy!
+ 1. _Quick start_: take a `git clone` or download the latest [v1.5 full distribution](http://lizier.me/joseph/software/jidt/download.php?file=infodynamics-dist-1.5.zip) (suitable for all platforms) and see the readme.txt file therein.
+1. [Documentation](../../wiki/Documentation) including: the paper describing JIDT at [arXiv:1408.3270](http://arxiv.org/abs/1408.3270) (distributed with the toolkit), a [Tutorial](../../wiki/Tutorial), and [Javadocs (v1.5 here)](http://lizier.me/joseph/software/jidt/javadocs/v1.5/);
+1. [Demos](../../wiki/Demos) are included with the full distribution, including a [GUI app](../../wiki/AutoAnalyser) for automatic analysis and code generation (see picture below), [simple java demos](../../wiki/SimpleJavaExamples) and [cellular automata (CA) demos](../../wiki/CellularAutomataDemos).
+ 1. These Java tools can easily be used in [Matlab/Octave](../../wiki/OctaveMatlabExamples), [Python](../../wiki/PythonExamples), [R](../../wiki/R_Examples), [Julia](../../wiki/JuliaExamples) and [Clojure](../../wiki/Clojure_Examples)! (click on each language here for examples)

@@ -39,6 +39,16 @@ See other [PublicationsUsingThisToolkit](../../wiki/PublicationsUsingThisToolkit
# News
+_26/11/2018_ - New jar and full distribution files available for **release v1.5**; Changes for v1.5 include:
+Added GPU (cuda) capability for KSG Conditional Mutual Information calculator (proper documentation to come), brief [wiki page](../../wiki/GPU) and unit tests included;
+Added auto-embedding for TE/AIS with multivariate KSG, and univariate and multivariate Gaussian estimator (plus unit tests), for Ragwitz criteria and Maximum bias-corrected AIS, and also added Maximum bias corrected AIS and TE to handle source embedding as well;
+Kozachenko entropy estimator adds noise to data by default;
+Added bias-correction property to Gaussian and Kernel estimators for MI and conditional MI, including with surrogates (only option for kernel);
+Enabled use of different bases for different variables in MI discrete estimator;
+All new above features enabled in AutoAnalyser;
+Added drop-down menus for parameters in AutoAnalyser;
+Included long-form lecture slides in course folder;
+
_26/11/2017_ - New jar and full distribution files available for **release v1.4**; Changes for v1.4 include:
Major expansion of functionality for AutoAnalysers: adding Launcher applet and capability to double click jar to launch, added Entropy, CMI, CTE and AIS AutoAnalysers, also added binned estimator type, added all variables/pairs analysis, added statistical significance analysis, and ensured functionality of generated Python code with Python3;
Added GPU (cuda) capability for KSG Mutual Information calculator (proper documentation and wiki page to come), including unit tests;
diff --git a/build.xml b/build.xml
index 592ed9d..3f9b4e1 100755
--- a/build.xml
+++ b/build.xml
@@ -5,12 +5,13 @@
-
+
+
@@ -148,6 +149,7 @@
+
@@ -293,24 +295,30 @@
-
+
-
+
-
+
+
+
+
+
+
+
diff --git a/cuda/README-GPU.pdf b/cuda/README-GPU.pdf
new file mode 100644
index 0000000..819ae62
Binary files /dev/null and b/cuda/README-GPU.pdf differ
diff --git a/demos/AutoAnalyser/README-AutoAnalyserDemo.pdf b/demos/AutoAnalyser/README-AutoAnalyserDemo.pdf
index bdde033..6992bf7 100644
Binary files a/demos/AutoAnalyser/README-AutoAnalyserDemo.pdf and b/demos/AutoAnalyser/README-AutoAnalyserDemo.pdf differ
diff --git a/demos/AutoAnalyser/runAISAutoAnalyser.bat b/demos/AutoAnalyser/runAISAutoAnalyser.bat
deleted file mode 100755
index 11bdefc..0000000
--- a/demos/AutoAnalyser/runAISAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserAIS
-
diff --git a/demos/AutoAnalyser/runAISAutoAnalyser.sh b/demos/AutoAnalyser/runAISAutoAnalyser.sh
deleted file mode 100755
index d0898eb..0000000
--- a/demos/AutoAnalyser/runAISAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserAIS
-
diff --git a/demos/AutoAnalyser/runCMIAutoAnalyser.bat b/demos/AutoAnalyser/runCMIAutoAnalyser.bat
deleted file mode 100755
index 0cf7be1..0000000
--- a/demos/AutoAnalyser/runCMIAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCMI
-
diff --git a/demos/AutoAnalyser/runCMIAutoAnalyser.sh b/demos/AutoAnalyser/runCMIAutoAnalyser.sh
deleted file mode 100755
index aad7edb..0000000
--- a/demos/AutoAnalyser/runCMIAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCMI
-
diff --git a/demos/AutoAnalyser/runCTEAutoAnalyser.bat b/demos/AutoAnalyser/runCTEAutoAnalyser.bat
deleted file mode 100755
index 0e4b397..0000000
--- a/demos/AutoAnalyser/runCTEAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCTE
-
diff --git a/demos/AutoAnalyser/runCTEAutoAnalyser.sh b/demos/AutoAnalyser/runCTEAutoAnalyser.sh
deleted file mode 100755
index 5d3e56a..0000000
--- a/demos/AutoAnalyser/runCTEAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserCTE
-
diff --git a/demos/AutoAnalyser/runHAutoAnalyser.bat b/demos/AutoAnalyser/runHAutoAnalyser.bat
deleted file mode 100755
index 825ded1..0000000
--- a/demos/AutoAnalyser/runHAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserEntropy
-
diff --git a/demos/AutoAnalyser/runHAutoAnalyser.sh b/demos/AutoAnalyser/runHAutoAnalyser.sh
deleted file mode 100755
index 2969f73..0000000
--- a/demos/AutoAnalyser/runHAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserEntropy
-
diff --git a/demos/AutoAnalyser/runMIAutoAnalyser.bat b/demos/AutoAnalyser/runMIAutoAnalyser.bat
deleted file mode 100755
index 383aaee..0000000
--- a/demos/AutoAnalyser/runMIAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserMI
-
diff --git a/demos/AutoAnalyser/runMIAutoAnalyser.sh b/demos/AutoAnalyser/runMIAutoAnalyser.sh
deleted file mode 100755
index cb695ea..0000000
--- a/demos/AutoAnalyser/runMIAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserMI
-
diff --git a/demos/AutoAnalyser/runTEAutoAnalyser.bat b/demos/AutoAnalyser/runTEAutoAnalyser.bat
deleted file mode 100755
index 8c5f7aa..0000000
--- a/demos/AutoAnalyser/runTEAutoAnalyser.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@ECHO OFF
-
-REM Run the example:
-java -classpath "..\..\infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserTE
-
diff --git a/demos/AutoAnalyser/runTEAutoAnalyser.sh b/demos/AutoAnalyser/runTEAutoAnalyser.sh
deleted file mode 100755
index be3e3fa..0000000
--- a/demos/AutoAnalyser/runTEAutoAnalyser.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Run the example:
-java -classpath "../../infodynamics.jar" infodynamics.demos.autoanalysis.AutoAnalyserTE
-
diff --git a/demos/README-Demos.pdf b/demos/README-Demos.pdf
index 36949ef..130e43c 100755
Binary files a/demos/README-Demos.pdf and b/demos/README-Demos.pdf differ
diff --git a/demos/java/example10GPUBenchmark.sh b/demos/java/example10GPUBenchmark.sh
old mode 100644
new mode 100755
diff --git a/demos/java/infodynamics/demos/Example10GPUBenchmark.java b/demos/java/infodynamics/demos/Example10GPUBenchmark.java
index d3d5269..d3d0a44 100755
--- a/demos/java/infodynamics/demos/Example10GPUBenchmark.java
+++ b/demos/java/infodynamics/demos/Example10GPUBenchmark.java
@@ -67,9 +67,10 @@ public class Example10GPUBenchmark {
miCalc.setProperty("k", "4");
miCalc.initialise(src[0].length, tgt[0].length);
miCalc.setObservations(src, tgt);
+ int nb_surrogates = 150;
double[] timeAndValue = new double[2];
long startTime = System.nanoTime();
- timeAndValue[1] = miCalc.computeAverageLocalOfObservations();
+ timeAndValue[1] = miCalc.computeSignificance(nb_surrogates).actualValue;
timeAndValue[0] = (System.nanoTime() - startTime)/1000000.0;
return timeAndValue;
}
diff --git a/demos/python/README-PythonDemos.pdf b/demos/python/README-PythonDemos.pdf
index db26c03..049fe85 100755
Binary files a/demos/python/README-PythonDemos.pdf and b/demos/python/README-PythonDemos.pdf differ
diff --git a/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java b/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java
index e09335e..1482838 100755
--- a/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java
+++ b/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java
@@ -717,9 +717,10 @@ public abstract class MutualInfoCalculatorMultiVariateKraskov
System.load(gpuLibraryPath);
}
} catch (Throwable e) {
- String errmsg = "GPU library not found. To compile GPU code set the enablegpu flag to true in build.xml";
+ String errmsg = "GPU library not found. To compile GPU code set the enablegpu flag to true in build.xml, or run `ant gpu jar`.";
+ errmsg += "\nFor more information see the JIDT GPU wiki page: https://github.com/jlizier/jidt/wiki/GPU";
if (gpuLibraryPath.length() > 0) {
- errmsg += "\nGPU library was not found in the path provided. Provide full path including library file name.";
+ errmsg += "\n\nGPU library was not found in the path provided. Provide full path including library file name.";
errmsg += "\nExample: /home/johndoe/myfolder/libKraskov.so";
}
throw new Exception(errmsg);
diff --git a/java/source/infodynamics/measures/spiking/TransferEntropyCalculatorSpiking.java b/java/source/infodynamics/measures/spiking/TransferEntropyCalculatorSpiking.java
index 5aaaf9f..630be29 100755
--- a/java/source/infodynamics/measures/spiking/TransferEntropyCalculatorSpiking.java
+++ b/java/source/infodynamics/measures/spiking/TransferEntropyCalculatorSpiking.java
@@ -282,68 +282,11 @@ public interface TransferEntropyCalculatorSpiking {
* will vary depending on the underlying implementation
*/
public SpikingLocalInformationValues computeLocalOfPreviousObservations() throws Exception;
-
- /**
- * Generate a bootstrapped distribution of what the TE would look like,
- * under a null hypothesis that the source values of our
- * samples had no temporal relation to the destination value.
- *
- * See Section II.E "Statistical significance testing" of
- * the JIDT paper below for a description of how this is done for MI and TE in general.
- *
- *
- * 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.
- *
- * This method (in contrast to {@link #computeSignificance(int[][])})
- * creates random shufflings of the source embedding vectors for the surrogate
- * calculations.
- *
- * @param numPermutationsToCheck number of surrogate samples to bootstrap
- * to generate the distribution.
- * @return the distribution of TE 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 TE would look like,
- * under a null hypothesis that the source values of our
- * samples had no relation to the destination value.
- *
- * See Section II.E "Statistical significance testing" of
- * the JIDT paper below for a description of how this is done for MI and TE.
- *
- *
- * 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.
- *
- * 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.
- *
- * @param newOrderings a specification of how to shuffle the source embedding vectors
- * between all of our samples 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).
- * TODO Need to think this through a little more before implementing.
- * @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;
-
+
+ public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck, double estimatedValue) throws Exception;
+
+ public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck,
+ double estimatedValue, long randomSeed) throws Exception;
/**
* Set or clear debug mode for extra debug printing to stdout
*
diff --git a/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegration.java b/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegration.java
index 690c397..89094d8 100644
--- a/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegration.java
+++ b/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegration.java
@@ -5,6 +5,15 @@ import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Vector;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.PriorityQueue;
+import java.util.Random;
+import java.util.Vector;
+import java.util.ArrayList;
+import java.lang.Math;
+
+//import infodynamics.measures.continuous.kraskov.EuclideanUtils;
import infodynamics.measures.spiking.TransferEntropyCalculatorSpiking;
import infodynamics.utils.EmpiricalMeasurementDistribution;
import infodynamics.utils.KdTree;
@@ -14,1217 +23,904 @@ import infodynamics.utils.NeighbourNodeData;
import infodynamics.utils.FirstIndexComparatorDouble;
import infodynamics.utils.UnivariateNearestNeighbourSearcher;
+import infodynamics.utils.EuclideanUtils;
+import infodynamics.utils.ParsedProperties;
+
/**
- * Computes the transfer entropy between a pair of spike trains,
- * using an integration-based measure in order to match the theoretical
- * form of TE between such spike trains.
+ * Computes the transfer entropy between a pair of spike trains, using an
+ * integration-based measure in order to match the theoretical form of TE
+ * between such spike trains.
*
- * Usage paradigm is as per the interface {@link TransferEntropyCalculatorSpiking}
+ *
+ * Usage paradigm is as per the interface
+ * {@link TransferEntropyCalculatorSpiking}
+ *
*
* @author Joseph Lizier (email,
- * www)
+ * www)
*/
-public class TransferEntropyCalculatorSpikingIntegration implements
- TransferEntropyCalculatorSpiking {
+
+/*
+ * TODO
+ * This implementation of the estimator does not implement dynamic exclusion windows. Such windows make sure
+ * that history embeddings that overlap are not considered in nearest-neighbour searches (as this breaks the
+ * independece assumption). Getting dynamic exclusion windows working will probably require modifications to the
+ * KdTree class.
+ */
+public class TransferEntropyCalculatorSpikingIntegration implements TransferEntropyCalculatorSpiking {
/**
- * Number of past destination spikes to consider (akin to embedding length)
+ * The past destination interspike intervals to consider
+ * (and associated property name and convenience length variable)
+ * The code assumes that the first interval is numbered 1, the next is numbered 2, etc.
+ * It also assumes that the intervals are sorted. The setter method performs sorting to ensure this.
*/
- protected int k = 1;
+ public static final String DEST_PAST_INTERVALS_PROP_NAME = "DEST_PAST_INTERVALS";
+ protected int[] destPastIntervals = new int[] {};
+ protected int numDestPastIntervals = 0;
/**
- * Number of past source spikes to consider (akin to embedding length)
+ * As above but for the sources
*/
- protected int l = 1;
+ public static final String SOURCE_PAST_INTERVALS_PROP_NAME = "SOURCE_PAST_INTERVALS";
+ protected int[] sourcePastIntervals = new int[] {};
+ protected int numSourcePastIntervals = 0;
+
+ /**
+ * As above but for the conditioning processes. There is no property name for this variable,
+ * due to there not currently being a method for converting strings to 2d arrays. Instead,
+ * a separate setter method is implemented.
+ */
+ protected Vector vectorOfCondPastIntervals = new Vector();
+ protected int numCondPastIntervals = 0;
/**
* Number of nearest neighbours to search for in the full joint space
*/
protected int Knns = 4;
-
+
+
/**
- * Storage for source observations supplied via {@link #addObservations(double[], double[])} etc.
+ * Storage for source observations supplied via
+ * {@link #addObservations(double[], double[])} etc.
+
*/
protected Vector vectorOfSourceSpikeTimes = null;
/**
- * Storage for destination observations supplied via {@link #addObservations(double[], double[])} etc.
+ * Storage for destination observations supplied via
+ * {@link #addObservations(double[], double[])} etc.
*/
protected Vector vectorOfDestinationSpikeTimes = null;
-
- // constants for indexing our data storage
- protected final static int NEXT_DEST = 0;
- protected final static int NEXT_SOURCE = 1;
- protected final static int NEXT_POSSIBILITIES = 2;
/**
- * Cache of the timing data for each new observed spiking event in both the source
- * and destination
+ * Storage for conditional observations supplied via
+ * {@link #addObservations(double[], double[])} etc.
*/
- Vector[] eventTimings = null;
- /**
- * Cache of the timing data for each new observed spiking event for the
- * destination only
- */
- Vector destPastAndNextTimings = null;
- /**
- * Cache of the type of event for each new observed spiking event in both the source
- * and destination (i.e. which spiked next)
- */
- Vector eventTypeLocator = null;
- /**
- * Cache for each new observed spiking event of which index it has in the vector
- * of spiking events of the same type
- */
- Vector eventIndexLocator = null;
- /**
- * Cache for each time-series of observed spiking events of how many
- * observations were in that set.
- */
- Vector numEventsPerObservationSet = null;
+ protected Vector vectorOfConditionalSpikeTimes = null;
- /**
- * KdTrees for searching the joint past spaces and time to next spike,
- * for each possibility of which spiked next
- */
- protected KdTree[] kdTreesJoint = null;
+ Vector conditioningEmbeddingsFromSpikes = null;
+ Vector jointEmbeddingsFromSpikes = null;
+ Vector conditioningEmbeddingsFromSamples = null;
+ Vector jointEmbeddingsFromSamples = null;
+ Vector processTimeLengths = null;
- /**
- * KdTrees for searching the joint past spaces,
- * for each possibility of which spiked next
- */
- protected KdTree[] kdTreesSourceDestHistories = null;
+ protected KdTree kdTreeJointAtSpikes = null;
+ protected KdTree kdTreeJointAtSamples = null;
+ protected KdTree kdTreeConditioningAtSpikes = null;
+ protected KdTree kdTreeConditioningAtSamples = null;
- /**
- * KdTrees for searching the past destination space and time to next spike
- */
- protected KdTree kdTreeDestNext = null;
-
- /**
- * KdTrees for searching the past destination space
- */
- protected KdTree kdTreeDestHistory = null;
-
- /**
- * NN searcher for the time to next spike space only, if required
- */
- protected UnivariateNearestNeighbourSearcher nnSearcherDestTimeToNextSpike = null;
-
- /**
- * Property name for the number of nearest neighbours to search
- */
public static final String KNNS_PROP_NAME = "Knns";
/**
- * Property name for adjusting the search radius for the next spike such that
- * it does not cover negative times (with respect to the previous spike, being either
- * source or destination spike)
+ * 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 TRIM_TO_POS_PROP_NAME = "TRIM_RANGE_TO_POS_TIMES";
-
- protected boolean trimToPosNextSpikeTimes = false;
+ public static final String PROP_ADD_NOISE = "NOISE_LEVEL_TO_ADD";
+ /**
+ * 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;
+
/**
* Stores whether we are in debug mode
*/
protected boolean debug = false;
+
+ /**
+ * Property name for the number of random sample points to use as a multiple
+ * of the number of target spikes.
+ */
+ public static final String PROP_SAMPLE_MULTIPLIER = "NUM_SAMPLES_MULTIPLIER";
+ protected double numSamplesMultiplier = 2.0;
+ /**
+ * Property name for the number of random sample points to use in the construction of the surrogates as a multiple
+ * of the number of target spikes.
+ */
+ public static final String PROP_SURROGATE_SAMPLE_MULTIPLIER = "SURROGATE_NUM_SAMPLES_MULTIPLIER";
+ protected double surrogateNumSamplesMultiplier = 2.0;
+
+ /**
+ * Property for the number of nearest neighbours to use in the construction of the surrogates
+ */
+ public static final String PROP_K_PERM = "K_PERM";
+ protected int kPerm = 10;
+
+
+ /**
+ * 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_EUCLIDEAN}.
+ */
+ public final static String PROP_NORM_TYPE = "NORM_TYPE";
+ protected int normType = EuclideanUtils.NORM_EUCLIDEAN;
public TransferEntropyCalculatorSpikingIntegration() {
super();
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(int)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(
+ * int)
*/
@Override
public void initialise() throws Exception {
- initialise(k,l);
+ initialise(0, 0);
}
-
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(int)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(
+ * int)
*/
@Override
public void initialise(int k) throws Exception {
- initialise(k,this.l);
+ initialise(0, 0);
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(int, int)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#initialise(
+ * int, int)
*/
@Override
public void initialise(int k, int l) throws Exception {
- if ((k < 1) || (l < 1)) {
- throw new Exception("Zero history length not supported");
- }
- this.k = k;
- this.l = l;
vectorOfSourceSpikeTimes = null;
vectorOfDestinationSpikeTimes = null;
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#setProperty(java.lang.String, java.lang.String)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#setProperty(
+ * java.lang.String, java.lang.String)
*/
@Override
- public void setProperty(String propertyName, String propertyValue)
- throws Exception {
+ public void setProperty(String propertyName, String propertyValue) throws Exception {
boolean propertySet = true;
- if (propertyName.equalsIgnoreCase(K_PROP_NAME)) {
- k = Integer.parseInt(propertyValue);
- } else if (propertyName.equalsIgnoreCase(L_PROP_NAME)) {
- l = Integer.parseInt(propertyValue);
+ if (propertyName.equalsIgnoreCase(DEST_PAST_INTERVALS_PROP_NAME)) {
+ if (propertyValue.length() == 0) {
+ destPastIntervals = new int[] {};
+ } else {
+ int[] destPastIntervalsTemp = ParsedProperties.parseStringArrayOfInts(propertyValue);
+ for (int interval : destPastIntervalsTemp) {
+ if (interval < 1) {
+ throw new Exception ("Invalid interval number less than 1.");
+ }
+ }
+ destPastIntervals = destPastIntervalsTemp;
+ Arrays.sort(destPastIntervals);
+ }
+ } else if (propertyName.equalsIgnoreCase(SOURCE_PAST_INTERVALS_PROP_NAME)) {
+ if (propertyValue.length() == 0) {
+ sourcePastIntervals = new int[] {};
+ } else {
+ int[] sourcePastIntervalsTemp = ParsedProperties.parseStringArrayOfInts(propertyValue);
+ for (int interval : sourcePastIntervalsTemp) {
+ if (interval < 1) {
+ throw new Exception ("Invalid interval number less than 1.");
+ }
+ }
+ sourcePastIntervals = sourcePastIntervalsTemp;
+ Arrays.sort(sourcePastIntervals);
+ }
} else if (propertyName.equalsIgnoreCase(KNNS_PROP_NAME)) {
Knns = Integer.parseInt(propertyValue);
- } else if (propertyName.equalsIgnoreCase(TRIM_TO_POS_PROP_NAME)) {
- trimToPosNextSpikeTimes = Boolean.parseBoolean(propertyValue);
+ } else if (propertyName.equalsIgnoreCase(PROP_K_PERM)) {
+ kPerm = 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_SAMPLE_MULTIPLIER)) {
+ double tempNumSamplesMultiplier = Double.parseDouble(propertyValue);
+ if (tempNumSamplesMultiplier <= 0) {
+ throw new Exception ("Num samples multiplier must be greater than 0.");
+ } else {
+ numSamplesMultiplier = tempNumSamplesMultiplier;
+ }
+ } else if (propertyName.equalsIgnoreCase(PROP_NORM_TYPE)) {
+ normType = KdTree.validateNormType(propertyValue);
+ } else if (propertyName.equalsIgnoreCase(PROP_SURROGATE_SAMPLE_MULTIPLIER)) {
+ double tempSurrogateNumSamplesMultiplier = Double.parseDouble(propertyValue);
+ if (tempSurrogateNumSamplesMultiplier <= 0) {
+ throw new Exception ("Surrogate Num samples multiplier must be greater than 0.");
+ } else {
+ surrogateNumSamplesMultiplier = tempSurrogateNumSamplesMultiplier;
+ }
} else {
// No property was set on this class
propertySet = false;
}
if (debug && propertySet) {
- System.out.println(this.getClass().getSimpleName() + ": Set property " + propertyName +
- " to " + propertyValue);
+ System.out.println(
+ this.getClass().getSimpleName() + ": Set property " + propertyName + " to " + propertyValue);
}
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#getProperty(java.lang.String)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#getProperty(
+ * java.lang.String)
*/
@Override
public String getProperty(String propertyName) throws Exception {
- if (propertyName.equalsIgnoreCase(K_PROP_NAME)) {
- return Integer.toString(k);
- } else if (propertyName.equalsIgnoreCase(L_PROP_NAME)) {
- return Integer.toString(l);
- } else if (propertyName.equalsIgnoreCase(KNNS_PROP_NAME)) {
+ if (propertyName.equalsIgnoreCase(KNNS_PROP_NAME)) {
return Integer.toString(Knns);
- } else if (propertyName.equalsIgnoreCase(TRIM_TO_POS_PROP_NAME)) {
- return Boolean.toString(trimToPosNextSpikeTimes);
+ } else if (propertyName.equalsIgnoreCase(PROP_ADD_NOISE)) {
+ return Double.toString(noiseLevel);
+ } else if (propertyName.equalsIgnoreCase(PROP_SAMPLE_MULTIPLIER)) {
+ return Double.toString(numSamplesMultiplier);
} else {
// No property matches for this class
return null;
}
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#setObservations(double[], double[])
+ public void appendConditionalIntervals(int[] intervals) throws Exception{
+ for (int interval : intervals) {
+ if (interval < 1) {
+ throw new Exception ("Invalid interval number less than 1.");
+ }
+ }
+ Arrays.sort(intervals);
+ vectorOfCondPastIntervals.add(intervals);
+ }
+
+ public void clearConditionalIntervals() {
+ vectorOfCondPastIntervals = new Vector();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * setObservations(double[], double[])
*/
@Override
- public void setObservations(double[] source, double[] destination)
- throws Exception {
+ public void setObservations(double[] source, double[] destination) throws Exception {
startAddObservations();
addObservations(source, destination);
finaliseAddObservations();
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#startAddObservations()
+ public void setObservations(double[] source, double[] destination, double[][] conditionals) throws Exception {
+ startAddObservations();
+ addObservations(source, destination, conditionals);
+ finaliseAddObservations();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * startAddObservations()
*/
@Override
public void startAddObservations() {
vectorOfSourceSpikeTimes = new Vector();
vectorOfDestinationSpikeTimes = new Vector();
+ vectorOfConditionalSpikeTimes = new Vector();
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#addObservations(double[], double[])
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * addObservations(double[], double[])
*/
@Override
- public void addObservations(double[] source, double[] destination)
- throws Exception {
+ public void addObservations(double[] source, double[] destination) throws Exception {
// Store these observations in our vector for now
vectorOfSourceSpikeTimes.add(source);
vectorOfDestinationSpikeTimes.add(destination);
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#finaliseAddObservations()
+ public void addObservations(double[] source, double[] destination, double[][] conditionals) throws Exception {
+ // Store these observations in our vector for now
+ vectorOfSourceSpikeTimes.add(source);
+ vectorOfDestinationSpikeTimes.add(destination);
+ vectorOfConditionalSpikeTimes.add(conditionals);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * finaliseAddObservations()
*/
@Override
public void finaliseAddObservations() throws Exception {
- // TODO Auto embed if required
- // preFinaliseAddObservations();
- // Run through each spiking time series set and pull out the observation
- // tuples we'll store.
- // Initialise our data stores:
- eventTimings = new Vector[NEXT_POSSIBILITIES];
- for (int next = 0; next < NEXT_POSSIBILITIES; next++) {
- eventTimings[next] = new Vector();
+ // Set these conveniance variables as they are used quite a bit later on
+ numDestPastIntervals = destPastIntervals.length;
+ numSourcePastIntervals = sourcePastIntervals.length;
+ numCondPastIntervals = 0;
+ for (int[] intervals : vectorOfCondPastIntervals) {
+ numCondPastIntervals += intervals.length;
}
- destPastAndNextTimings = new Vector();
- eventTypeLocator = new Vector();
- eventIndexLocator = new Vector();
- numEventsPerObservationSet = new Vector();
-
+
+ conditioningEmbeddingsFromSpikes = new Vector();
+ jointEmbeddingsFromSpikes = new Vector();
+ conditioningEmbeddingsFromSamples = new Vector();
+ jointEmbeddingsFromSamples = new Vector();
+ processTimeLengths = new Vector();
+
// Send all of the observations through:
Iterator sourceIterator = vectorOfSourceSpikeTimes.iterator();
+ Iterator conditionalIterator = null;
+ if (vectorOfConditionalSpikeTimes.size() > 0) {
+ conditionalIterator = vectorOfConditionalSpikeTimes.iterator();
+ }
int timeSeriesIndex = 0;
for (double[] destSpikeTimes : vectorOfDestinationSpikeTimes) {
double[] sourceSpikeTimes = sourceIterator.next();
- timeSeriesIndex++;
-
- processEventsFromSpikingTimeSeries(sourceSpikeTimes, destSpikeTimes,
- timeSeriesIndex, eventTimings, destPastAndNextTimings,
- eventTypeLocator, eventIndexLocator, numEventsPerObservationSet);
- }
-
- // Now we have collected all the events.
- // Load up the search structures:
- // 1. Full joint space:
- // 2. Histories of source and dest only:
- kdTreesJoint = new KdTree[NEXT_POSSIBILITIES];
- kdTreesSourceDestHistories = new KdTree[NEXT_POSSIBILITIES];
- for (int next = 0; next < NEXT_POSSIBILITIES; next++) {
- // This line does not work:
- // double[][][] jointEventTimings = (double[][][]) eventTimings[prev][next].toArray();
- // So we'll do it manually:
- double[][] sourcePastTimings = new double[eventTimings[next].size()][];
- double[][] destPastTimings = new double[eventTimings[next].size()][];
- double[][] nextTimings = new double[eventTimings[next].size()][];
- int i = 0;
- for (double[][] timing : eventTimings[next]) {
- sourcePastTimings[i] = timing[0];
- destPastTimings[i] = timing[1];
- nextTimings[i] = timing[2];
- i++;
+ double[][] conditionalSpikeTimes = null;
+ if (vectorOfConditionalSpikeTimes.size() > 0) {
+ conditionalSpikeTimes = conditionalIterator.next();
+ } else {
+ conditionalSpikeTimes = new double[][] {};
}
- // TODO Should we normalise before we supply to the KdTree?
- // Think about this later. I'm not convinced it's the best
- // approach in this particular case.
- kdTreesJoint[next] = new KdTree(
- new int[] {l, k - 1, 1},
- new double[][][] {sourcePastTimings, destPastTimings, nextTimings});
- kdTreesSourceDestHistories[next] = new KdTree(
- new int[] {l, k - 1},
- new double[][][] {sourcePastTimings, destPastTimings});
+ processEventsFromSpikingTimeSeries(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes, conditioningEmbeddingsFromSpikes,
+ jointEmbeddingsFromSpikes, conditioningEmbeddingsFromSamples, jointEmbeddingsFromSamples,
+ numSamplesMultiplier);
}
- // 3. For the dest past and time to next spike
- // 4. For the dest past only
- double[][] destPastOnlyTimings = new double[destPastAndNextTimings.size()][];
- double[][] nextTimingsForDestPastOnly = new double[destPastAndNextTimings.size()][];
- int i = 0;
- for (double[][] timing : destPastAndNextTimings) {
- destPastOnlyTimings[i] = timing[0];
- nextTimingsForDestPastOnly[i] = timing[1];
- i++;
+
+
+
+ // Convert the vectors to arrays so that they can be put in the trees
+ double[][] arrayedTargetEmbeddingsFromSpikes = new double[conditioningEmbeddingsFromSpikes.size()][numDestPastIntervals + numCondPastIntervals];
+ double[][] arrayedJointEmbeddingsFromSpikes = new double[conditioningEmbeddingsFromSpikes.size()][numDestPastIntervals +
+ numCondPastIntervals + numSourcePastIntervals];
+ for (int i = 0; i < conditioningEmbeddingsFromSpikes.size(); i++) {
+ arrayedTargetEmbeddingsFromSpikes[i] = conditioningEmbeddingsFromSpikes.elementAt(i);
+ arrayedJointEmbeddingsFromSpikes[i] = jointEmbeddingsFromSpikes.elementAt(i);
}
- kdTreeDestNext = new KdTree(
- new int[] {k - 1, 1},
- new double[][][] {destPastOnlyTimings, nextTimingsForDestPastOnly});
-
- if (k == 1) {
- // We need an NN searcher for the time to next spike (dest only)
- nnSearcherDestTimeToNextSpike = new UnivariateNearestNeighbourSearcher(nextTimingsForDestPastOnly);
- } else {
- kdTreeDestHistory = new KdTree(destPastOnlyTimings);
+ double[][] arrayedTargetEmbeddingsFromSamples = new double[conditioningEmbeddingsFromSamples.size()][numDestPastIntervals + numCondPastIntervals];
+ double[][] arrayedJointEmbeddingsFromSamples = new double[conditioningEmbeddingsFromSamples.size()][numDestPastIntervals +
+ numCondPastIntervals + numSourcePastIntervals];
+ for (int i = 0; i < conditioningEmbeddingsFromSamples.size(); i++) {
+ arrayedTargetEmbeddingsFromSamples[i] = conditioningEmbeddingsFromSamples.elementAt(i);
+ arrayedJointEmbeddingsFromSamples[i] = jointEmbeddingsFromSamples.elementAt(i);
+ }
+
+ kdTreeJointAtSpikes = new KdTree(arrayedJointEmbeddingsFromSpikes);
+ kdTreeJointAtSamples = new KdTree(arrayedJointEmbeddingsFromSamples);
+ kdTreeConditioningAtSpikes = new KdTree(arrayedTargetEmbeddingsFromSpikes);
+ kdTreeConditioningAtSamples = new KdTree(arrayedTargetEmbeddingsFromSamples);
+
+ kdTreeJointAtSpikes.setNormType(normType);
+ kdTreeJointAtSamples.setNormType(normType);
+ kdTreeConditioningAtSpikes.setNormType(normType);
+ kdTreeConditioningAtSamples.setNormType(normType);
+ }
+
+ protected void makeEmbeddingsAtPoints(double[] pointsAtWhichToMakeEmbeddings, int indexOfFirstPointToUse,
+ double[] sourceSpikeTimes, double[] destSpikeTimes,
+ double[][] conditionalSpikeTimes,
+ Vector conditioningEmbeddings,
+ Vector jointEmbeddings) {
+
+ Random random = new Random();
+
+ // Initialise the starting points of all the tracking variables
+ int embeddingPointIndex = indexOfFirstPointToUse;
+ int mostRecentDestIndex = destPastIntervals[destPastIntervals.length - 1];
+ int mostRecentSourceIndex = sourcePastIntervals[sourcePastIntervals.length - 1];
+ int[] mostRecentConditioningIndices = new int[vectorOfCondPastIntervals.size()];
+ for (int i = 0; i < vectorOfCondPastIntervals.size(); i++) {
+ mostRecentConditioningIndices[i] = vectorOfCondPastIntervals.elementAt(i).length;
+ }
+
+
+ // Loop through the points at which embeddings need to be made
+ for (; embeddingPointIndex < pointsAtWhichToMakeEmbeddings.length; embeddingPointIndex++) {
+
+ // Advance the tracker of the most recent dest index
+ while (mostRecentDestIndex < (destSpikeTimes.length - 1)) {
+ if (destSpikeTimes[mostRecentDestIndex + 1] < pointsAtWhichToMakeEmbeddings[embeddingPointIndex]) {
+ mostRecentDestIndex++;
+ } else {
+ break;
+ }
+ }
+ // Do the same for the most recent source index
+ while (mostRecentSourceIndex < (sourceSpikeTimes.length - 1)) {
+ if (sourceSpikeTimes[mostRecentSourceIndex + 1] < pointsAtWhichToMakeEmbeddings[embeddingPointIndex]) {
+ mostRecentSourceIndex++;
+ } else {
+ break;
+ }
+ }
+ // Now advance the trackers for the most recent conditioning indices
+ for (int j = 0; j < mostRecentConditioningIndices.length; j++) {
+ while (mostRecentConditioningIndices[j] < (conditionalSpikeTimes[j].length - 1)) {
+ if (conditionalSpikeTimes[j][mostRecentConditioningIndices[j] + 1] < pointsAtWhichToMakeEmbeddings[embeddingPointIndex]) {
+ mostRecentConditioningIndices[j]++;
+ } else {
+ break;
+ }
+ }
+ }
+
+
+ double[] conditioningPast = new double[numDestPastIntervals + numCondPastIntervals];
+ double[] jointPast = new double[numDestPastIntervals + numCondPastIntervals + numSourcePastIntervals];
+
+ // Add the embedding intervals from the target process
+ for (int i = 0; i < destPastIntervals.length; i++) {
+ // Case where we are inserting an interval from an observation point back to the most recent event in the target process
+ if (destPastIntervals[i] == 1) {
+ conditioningPast[i] = pointsAtWhichToMakeEmbeddings[embeddingPointIndex] - destSpikeTimes[mostRecentDestIndex];
+ jointPast[i] = pointsAtWhichToMakeEmbeddings[embeddingPointIndex]
+ - destSpikeTimes[mostRecentDestIndex];
+ // Case where we are inserting an inter-event intervvl from the target process
+ } else {
+ conditioningPast[i] = destSpikeTimes[mostRecentDestIndex - destPastIntervals[i] + 2]
+ - destSpikeTimes[mostRecentDestIndex - destPastIntervals[i] + 1];
+ jointPast[i] = destSpikeTimes[mostRecentDestIndex - destPastIntervals[i] + 2]
+ - destSpikeTimes[mostRecentDestIndex - destPastIntervals[i] + 1];
+ }
+ }
+
+ // Add the embeding intervals from the conditional processes
+ int indexOfNextEmbeddingInterval = numDestPastIntervals;
+ for (int i = 0; i < vectorOfCondPastIntervals.size(); i++) {
+ for (int j = 0; j < vectorOfCondPastIntervals.elementAt(i).length; j++) {
+ // Case where we are inserting an interval from an observation point back to the most recent event in the conditioning process
+ if (vectorOfCondPastIntervals.elementAt(i)[j] == 1) {
+ conditioningPast[indexOfNextEmbeddingInterval] = pointsAtWhichToMakeEmbeddings[embeddingPointIndex]
+ - conditionalSpikeTimes[i][mostRecentConditioningIndices[i]];
+ jointPast[indexOfNextEmbeddingInterval] = pointsAtWhichToMakeEmbeddings[embeddingPointIndex]
+ - conditionalSpikeTimes[i][mostRecentConditioningIndices[i]];
+ // Case where we are inserting an inter-event interval from the conditioning process
+ } else {
+ // Convenience variable
+ int intervalNumber = vectorOfCondPastIntervals.elementAt(i)[j];
+ conditioningPast[indexOfNextEmbeddingInterval] = conditionalSpikeTimes[i][mostRecentConditioningIndices[i] - intervalNumber + 2]
+ - conditionalSpikeTimes[i][mostRecentConditioningIndices[i] - intervalNumber + 1];
+ jointPast[indexOfNextEmbeddingInterval] = conditionalSpikeTimes[i][mostRecentConditioningIndices[i] - intervalNumber + 2]
+ - conditionalSpikeTimes[i][mostRecentConditioningIndices[i] - intervalNumber + 1];
+ }
+ indexOfNextEmbeddingInterval++;
+ }
+ }
+
+ // Add the embedding intervals from the source process (this only gets added to the joint embeddings)
+ for (int i = 0; i < sourcePastIntervals.length; i++) {
+ // Case where we are inserting an interval from an observation point back to the most recent event in the source process
+ if (sourcePastIntervals[i] == 1) {
+ jointPast[indexOfNextEmbeddingInterval] = pointsAtWhichToMakeEmbeddings[embeddingPointIndex]
+ - sourceSpikeTimes[mostRecentSourceIndex];
+ // Case where we are inserting an inter-event interval from the source process
+ } else {
+ jointPast[indexOfNextEmbeddingInterval] = sourceSpikeTimes[mostRecentSourceIndex - sourcePastIntervals[i] + 2]
+ - sourceSpikeTimes[mostRecentSourceIndex - sourcePastIntervals[i] + 1];
+ }
+ indexOfNextEmbeddingInterval++;
+ }
+
+ // Add Gaussian noise, if necessary
+ if (addNoise) {
+ for (int i = 0; i < conditioningPast.length; i++) {
+ //conditioningPast[i] = Math.exp(-conditioningPast[i]);
+ conditioningPast[i] += random.nextGaussian() * noiseLevel;
+ }
+ for (int i = 0; i < jointPast.length; i++) {
+ //jointPast[i] = Math.exp(-jointPast[i]);
+ jointPast[i] += random.nextGaussian() * noiseLevel;
+ }
+ }
+
+ conditioningEmbeddings.add(conditioningPast);
+ jointEmbeddings.add(jointPast);
}
}
- protected void processEventsFromSpikingTimeSeries(double[] sourceSpikeTimes, double[] destSpikeTimes,
- int timeSeriesIndex, Vector[] eventTimings,
- Vector destPastAndNextTimings, Vector eventTypeLocator,
- Vector eventIndexLocator, Vector numEventsPerObservationSet) throws Exception {
- // addObservationsAfterParamsDetermined(sourceSpikeTimes, destSpikeTimes);
-
+
+
+ protected int getFirstDestIndex(double[] sourceSpikeTimes, double[] destSpikeTimes, double[][] conditionalSpikeTimes, Boolean setProcessTimeLengths)
+ throws Exception{
+
// First sort the spike times in case they were not properly in ascending order:
Arrays.sort(sourceSpikeTimes);
Arrays.sort(destSpikeTimes);
- // Scan to find the indices by which we have k and l spikes for dest and source
- // respectively
- int dest_index = k - 1;
- int source_index = l - 1;
- if (sourceSpikeTimes[source_index] > destSpikeTimes[dest_index]) {
- // Minimum required Source spikes are later than the dest.
- // Need to advance dest_index until it's the most recent before source_index
- for(;dest_index < destSpikeTimes.length; dest_index++) {
- if (destSpikeTimes[dest_index] > sourceSpikeTimes[source_index]) {
- // We've gone past the set of source spikes we have, we
- // can move back one in the dest series
- dest_index--;
- break;
- }
- }
- if (dest_index == destSpikeTimes.length) {
- // We didn't have enough spikes in this series to generate any observations
- // TODO work out how to handle this later -- I think this is ok
- numEventsPerObservationSet.add(0);
- return;
- // throw new Exception("Dest spikes stop before enough source spikes in time-series " + timeSeriesIndex);
- }
- } else {
- // Minimum required Dest spikes are later than the source.
- // Need to advance source_index until it's the most recent before dest_index
- for(;source_index < sourceSpikeTimes.length; source_index++) {
- if (sourceSpikeTimes[source_index] > destSpikeTimes[dest_index]) {
- // We've gone past the set of dest spikes we have, we
- // can move back one in the source series
- source_index--;
- break;
- }
- }
- if (source_index == sourceSpikeTimes.length) {
- // We didn't have enough spikes in this series to generate any observations
- // TODO work out how to handle this later -- I think this is ok
- numEventsPerObservationSet.add(0);
- return;
- // throw new Exception("Source spikes stop before enough dest spikes in time-series " + timeSeriesIndex);
+ int firstTargetIndexOfEmbedding = destPastIntervals[destPastIntervals.length - 1];
+
+ int furthestInterval = sourcePastIntervals[sourcePastIntervals.length - 1];
+ while (destSpikeTimes[firstTargetIndexOfEmbedding] <= sourceSpikeTimes[furthestInterval - 1]) {
+ firstTargetIndexOfEmbedding++;
+ }
+ if (conditionalSpikeTimes.length != vectorOfCondPastIntervals.size()) {
+ throw new Exception("Number of conditional embedding lengths does not match the number of conditional processes");
+ }
+ for (int i = 0; i < conditionalSpikeTimes.length; i++) {
+ furthestInterval = vectorOfCondPastIntervals.elementAt(i)[vectorOfCondPastIntervals.elementAt(i).length - 1];
+ while (destSpikeTimes[firstTargetIndexOfEmbedding] <= conditionalSpikeTimes[i][furthestInterval - 1]) {
+ firstTargetIndexOfEmbedding++;
}
}
- // Post-condition: dest_index and source_index are set correctly for the first set of pasts
+
+ // We don't want to reset these lengths when resampling for surrogates
+ if (setProcessTimeLengths) {
+ processTimeLengths.add(destSpikeTimes[destSpikeTimes.length - 1] - destSpikeTimes[firstTargetIndexOfEmbedding]);
+ }
- double timeToNextSpike;
- boolean nextIsDest = false;
- double[] spikeTimesForNextSpiker;
- double timeOfPrevDestSpike = destSpikeTimes[dest_index];
- int numEvents = 0;
- while(true) {
- // 0. Check whether we're finished
- if ((source_index == sourceSpikeTimes.length - 1) &&
- (dest_index == destSpikeTimes.length - 1)) {
- // We have no next spike so we can't take an observation here
- // and we're done
- break;
- }
- // Otherwise:
- // 1. Determine which of source / dest fires next
- if (source_index == sourceSpikeTimes.length - 1) {
- nextIsDest = true;
- } else if (dest_index == destSpikeTimes.length - 1) {
- nextIsDest = false;
- } else if (sourceSpikeTimes[source_index+1] < destSpikeTimes[dest_index+1]) {
- nextIsDest = false;
- } else {
- nextIsDest = true;
- }
- spikeTimesForNextSpiker = nextIsDest ? destSpikeTimes : sourceSpikeTimes;
- int indexForNextSpiker = nextIsDest ? dest_index : source_index;
- timeToNextSpike = spikeTimesForNextSpiker[indexForNextSpiker+1] - timeOfPrevDestSpike;
- // 2. Embed the past spikes
- double[] sourcePast = new double[l];
- double[] destPast = new double[k - 1];
- /* if (debug) {
- System.out.println("previousIsDest = " + previousIsDest + " and nextIsDest = " + nextIsDest);
- }*/
- sourcePast[0] = timeOfPrevDestSpike -
- sourceSpikeTimes[source_index];
- for (int i = 1; i < k; i++) {
- destPast[i - 1] = destSpikeTimes[dest_index - i + 1] -
- destSpikeTimes[dest_index - i];
- }
- for (int i = 1; i < l; i++) {
- sourcePast[i] = sourceSpikeTimes[source_index - i + 1] -
- sourceSpikeTimes[source_index - i];
- }
- // 3. Store these embedded observations
- double[][] observations = new double[][]{sourcePast, destPast,
- new double[] {timeToNextSpike}};
- if (debug) {
- System.out.printf("Adding event %d with: timeToNextSpike=%.4f, sourceSpikeTimes=", numEvents, timeToNextSpike);
- MatrixUtils.printArray(System.out, sourcePast, 3);
- System.out.printf(", destSpikeTimes=");
- MatrixUtils.printArray(System.out, destPast, 3);
- System.out.println();
- }
- // Add the index locator first so it gets the index correct before
- // we add the new event in:
- eventIndexLocator.add(eventTimings[nextIsDest ? NEXT_DEST : NEXT_SOURCE].size());
- eventTimings[nextIsDest ? NEXT_DEST : NEXT_SOURCE].add(observations);
- // TODO Switch eventTypeLocation to be of type Integer rather than int[]
- eventTypeLocator.add(nextIsDest ? NEXT_DEST : NEXT_SOURCE);
- // And finally store the observations for the dest only
- // search structure if required:
- if (nextIsDest) {
- double[][] destOnlyObservations;
- destOnlyObservations = new double[][] {
- destPast,
- new double[] {timeToNextSpike}
- };
- destPastAndNextTimings.add(destOnlyObservations);
- }
- // 4. Reset variables
- if (nextIsDest) {
- dest_index++;
- } else {
- source_index++;
- }
- timeOfPrevDestSpike = destSpikeTimes[dest_index];
- numEvents++;
- }
- numEventsPerObservationSet.add(numEvents);
- if (debug) {
- System.out.printf("Finished processing %d source-target events for observation set %d\n", numEvents, timeSeriesIndex);
+ return firstTargetIndexOfEmbedding;
+ }
+
+ protected double[] generateRandomSampleTimes(double[] sourceSpikeTimes, double[] destSpikeTimes, double[][] conditionalSpikeTimes,
+ double actualNumSamplesMultiplier, int firstTargetIndexOfEmbedding) {
+
+ double sampleLowerBound = destSpikeTimes[firstTargetIndexOfEmbedding];
+ double sampleUpperBound = destSpikeTimes[destSpikeTimes.length - 1];
+ int num_samples = (int) Math.round(actualNumSamplesMultiplier * (destSpikeTimes.length - firstTargetIndexOfEmbedding + 1));
+ double[] randomSampleTimes = new double[num_samples];
+ Random rand = new Random();
+ for (int i = 0; i < randomSampleTimes.length; i++) {
+ randomSampleTimes[i] = sampleLowerBound + rand.nextDouble() * (sampleUpperBound - sampleLowerBound);
}
+ Arrays.sort(randomSampleTimes);
+
+ return randomSampleTimes;
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#getAddedMoreThanOneObservationSet()
- */
- @Override
- public boolean getAddedMoreThanOneObservationSet() {
- return (vectorOfDestinationSpikeTimes != null) &&
- (vectorOfDestinationSpikeTimes.size() > 1);
+ protected void processEventsFromSpikingTimeSeries(double[] sourceSpikeTimes, double[] destSpikeTimes, double[][] conditionalSpikeTimes,
+ Vector conditioningEmbeddingsFromSpikes, Vector jointEmbeddingsFromSpikes,
+ Vector conditioningEmbeddingsFromSamples, Vector jointEmbeddingsFromSamples,
+ double actualNumSamplesMultiplier)
+ throws Exception {
+
+ int firstTargetIndexOfEmbedding = getFirstDestIndex(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes, true);
+ double[] randomSampleTimes = generateRandomSampleTimes(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ actualNumSamplesMultiplier, firstTargetIndexOfEmbedding);
+
+ makeEmbeddingsAtPoints(destSpikeTimes, firstTargetIndexOfEmbedding, sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ conditioningEmbeddingsFromSpikes, jointEmbeddingsFromSpikes);
+ makeEmbeddingsAtPoints(randomSampleTimes, 0, sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ conditioningEmbeddingsFromSamples, jointEmbeddingsFromSamples);
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#computeAverageLocalOfObservations()
- */
- @Override
- public double computeAverageLocalOfObservations() throws Exception {
-
- int numberOfEvents = eventTypeLocator.size();
-
- double contributionFromSpikes = 0;
- double totalTimeLength = 0;
-
- double digammaK = MathsUtils.digamma(Knns);
- double twoInverseKTerm = 2.0 / (double) Knns;
- double inverseKTerm = 1.0 / (double) Knns;
-
- // Create temporary storage for arrays used in the neighbour counting:
- boolean[] isWithinR = new boolean[numberOfEvents]; // dummy, we don't really use this
- int[] indicesWithinR = new int[numberOfEvents];
-
- // Iterate over all the spiking events:
- Iterator eventIndexIterator = eventIndexLocator.iterator();
- int eventIndex = -1;
- int indexForNextIsDest = -1;
- for (Integer eventType : eventTypeLocator) {
- eventIndex++;
- int eventIndexWithinType = eventIndexIterator.next().intValue();
- double[][] thisEventTimings = eventTimings[eventType].elementAt(eventIndexWithinType);
- double timeToNextSpikeSincePreviousDestSpike = thisEventTimings[2][0];
- double timePreviousSourceSpikeBeforePreviousDestSpike = thisEventTimings[0][0];
- totalTimeLength += (timePreviousSourceSpikeBeforePreviousDestSpike < 0) ?
- // Source spike is after previous dest spike
- timeToNextSpikeSincePreviousDestSpike + timePreviousSourceSpikeBeforePreviousDestSpike :
- // Source spike is before previous dest spike
- timeToNextSpikeSincePreviousDestSpike;
- // Pull out the data for this observation:
- if (debug && (eventIndex < 10000)) {
- System.out.print("index = " + eventIndex + ", " +
- eventIndexWithinType + " for ->" +
- (eventType == NEXT_DEST ? "dst" : "src"));
- }
+ protected void processEventsFromSpikingTimeSeries(double[] sourceSpikeTimes, double[] destSpikeTimes, double[][] conditionalSpikeTimes,
+ Vector conditioningEmbeddingsFromSamples, Vector jointEmbeddingsFromSamples,
+ double actualNumSamplesMultiplier)
+ throws Exception {
- // Select only events where the destination spiked next:
- if (eventType == NEXT_DEST) {
- // Find the Knns nearest neighbour matches to this event,
- // with the same previous spiker and the next.
- // TODO Add dynamic exclusion time later
- PriorityQueue nnPQ =
- kdTreesJoint[eventType].findKNearestNeighbours(
- Knns, eventIndexWithinType);
-
- // Find eps_{x,y,z} as the maximum x, y and z norms amongst this set:
- double radius_sourcePast = 0.0;
- double radius_destPast = 0.0;
- double radius_destNext = 0.0;
- int radius_destNext_sampleIndex = -1;
- for (int j = 0; j < Knns; j++) {
- // Take the furthest remaining of the nearest neighbours from the PQ:
- NeighbourNodeData nnData = nnPQ.poll();
- if (nnData.norms[0] > radius_sourcePast) {
- radius_sourcePast = nnData.norms[0];
- }
- if (nnData.norms[1] > radius_destPast) {
- radius_destPast = nnData.norms[1];
- }
- if (nnData.norms[2] > radius_destNext) {
- radius_destNext = nnData.norms[2];
- radius_destNext_sampleIndex = nnData.sampleIndex;
- }
- }
-
- if (debug && (eventIndex < 10000)) {
- System.out.print(", timings: src: ");
- MatrixUtils.printArray(System.out, thisEventTimings[0], 5);
- System.out.print(", dest: ");
- MatrixUtils.printArray(System.out, thisEventTimings[1], 5);
- System.out.print(", time to next: ");
- MatrixUtils.printArray(System.out, thisEventTimings[2], 5);
- System.out.printf("index=%d: K=%d NNs at next_range %.5f (point %d)", eventIndexWithinType, Knns, radius_destNext, radius_destNext_sampleIndex);
- }
-
- indexForNextIsDest++;
-
- // Now find the matching samples in each sub-space;
- // first match dest history and source history, with a next spike in dest:
- kdTreesSourceDestHistories[NEXT_DEST].
- findPointsWithinRs(eventIndexWithinType,
- new double[] {radius_sourcePast, radius_destPast}, 0,
- true, isWithinR, indicesWithinR);
- // And check which of these samples had spike time in dest after ours:
- int countOfDestNextAndGreater = 0;
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[NEXT_DEST].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] > thisEventTimings[2][0] + radius_destNext) {
- // This sample had a matched history and next spike was a destination
- // spike with a longer interval than the current sample
- countOfDestNextAndGreater++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- // And count how many samples with the matching history actually had a
- // *source* spike next, after ours.
- // Note that we now must go to the other kdTree for next source spike
- kdTreesSourceDestHistories[NEXT_SOURCE].
- findPointsWithinRs(
- new double[] {radius_sourcePast, radius_destPast}, thisEventTimings,
- true, isWithinR, indicesWithinR);
- // And check which of these samples had spike time in source at or after ours:
- int countOfSourceNextAndGreater = 0;
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[NEXT_SOURCE].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] >= thisEventTimings[2][0] - radius_destNext) {
- // This sample had a matched history and next spike was a source
- // spike with an interval longer than or considered equal to the current sample.
- // (The "equal to" is why we look for matches within radius_destNext here as well.)
- countOfSourceNextAndGreater++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
-
- // We need to correct radius_destNext to have a different value below, chopping it
- // where it pushes into negative times (i.e. *before* the previous spike).
- // TODO I think this is justified by the approach of Greg Ver Steeg et al. in
- // http://www.jmlr.org/proceedings/papers/v38/gao15.pdf -- it's not the same
- // as that approach, but a principled version where we know the space cannot
- // be explored (so we don't need to only apply the correction where it is large).
- double searchAreaRatio = 0; // Ratio of actual search area for full joint space compared to if the timings were independent
- double prevSource_timing_upper_original = 0, prevSource_timing_lower = 0;
- double destNext_timing_lower_original = timeToNextSpikeSincePreviousDestSpike - radius_destNext;
- double destNext_timing_upper = timeToNextSpikeSincePreviousDestSpike + radius_destNext;
- double radius_destNext_lower = radius_destNext;
- if (trimToPosNextSpikeTimes) {
- if (destNext_timing_lower_original < 0) {
- // This is the range that's actually being searched in the lower
- // dimensional space.
- // (We don't need to adjust margins in lower dimensional space for
- // this because it simply won't get any points below this!)
- destNext_timing_lower_original = 0;
- }
- // timePreviousSourceSpikeBeforePreviousDestSpike is negative if source is after dest.
- prevSource_timing_upper_original = -timePreviousSourceSpikeBeforePreviousDestSpike + radius_sourcePast;
- prevSource_timing_lower = -timePreviousSourceSpikeBeforePreviousDestSpike - radius_sourcePast;
- double destNext_timing_lower = Math.max(destNext_timing_lower_original,
- prevSource_timing_lower);
- double prevSource_timing_upper = Math.min(destNext_timing_upper, prevSource_timing_upper_original);
- // Now look at various cases for the corrective ratio:
- // TODO I think we still need to correct destNext_timing_lower_original to radius_destNext_lower here!
- double denominator = (destNext_timing_upper - destNext_timing_lower_original) *
- (prevSource_timing_upper_original - prevSource_timing_lower);
- if (destNext_timing_upper - destNext_timing_lower >
- prevSource_timing_upper - prevSource_timing_lower) {
- // Case 1:
- if (prevSource_timing_upper < destNext_timing_lower) {
- // Windows do not overlap, so there will be no correction here
- searchAreaRatio = (destNext_timing_upper - destNext_timing_lower) *
- (prevSource_timing_upper - prevSource_timing_lower);
- } else if (destNext_timing_upper < prevSource_timing_lower) {
- // Should never happen, because next spike can't be before previous source spike
- searchAreaRatio = 0;
- throw new RuntimeException("Encountered a next target spike *before* the previous source spike");
- } else if (destNext_timing_lower < prevSource_timing_lower) {
- // Dest next lower bound is below source previous lower bound -- definitely
- // can't get any points in the part below prevSource_timing_lower
- // for dest next.
- searchAreaRatio = (prevSource_timing_upper - prevSource_timing_lower) *
- (destNext_timing_upper - prevSource_timing_upper) +
- 0.5 * (prevSource_timing_upper - prevSource_timing_lower) *
- (prevSource_timing_upper - prevSource_timing_lower);
- } else {
- // destNext_timing_lower >= prevSource_timing_lower
- // Dest next lower bound is within the previous source window
- // but above the source lower bound.
- searchAreaRatio = (destNext_timing_upper - destNext_timing_lower) *
- (prevSource_timing_upper - prevSource_timing_lower) -
- 0.5 * (prevSource_timing_upper - destNext_timing_lower) *
- (prevSource_timing_upper - destNext_timing_lower);
- }
- } else {
- // Case 2:
- if (prevSource_timing_upper < destNext_timing_lower) {
- // Windows do not overlap, so there will be no correction here
- searchAreaRatio = (destNext_timing_upper - destNext_timing_lower) *
- (prevSource_timing_upper - prevSource_timing_lower);
- } else if (destNext_timing_upper < prevSource_timing_lower) {
- // Should never happen, because next spike can't be before previous source spike
- searchAreaRatio = 0;
- throw new RuntimeException("Encountered a next target spike *before* the previous source spike");
- } else if (destNext_timing_lower < prevSource_timing_lower) {
- searchAreaRatio = 0.5 * (destNext_timing_upper - prevSource_timing_lower) *
- (destNext_timing_upper - prevSource_timing_lower);
- } else {
- // destNext_timing_lower >= prevSource_timing_lower
- searchAreaRatio = (destNext_timing_lower - prevSource_timing_lower) *
- (destNext_timing_upper - destNext_timing_lower) +
- 0.5 * (destNext_timing_upper - destNext_timing_lower) *
- (destNext_timing_upper - destNext_timing_lower);
- }
- }
- if (denominator <= 0) {
- // We'll get NaN if we divide by it. No correction is necessary in this
- // case, the margins are too thin to cause a discrepancy anyway.
- searchAreaRatio = 1;
- } else {
- searchAreaRatio /= denominator;
- }
- // And finally fix up the lower radius on destNext if required:
- radius_destNext_lower = timeToNextSpikeSincePreviousDestSpike - destNext_timing_lower;
- }
-
- if (debug && (eventIndex < 10000)) {
- System.out.printf(" of %d + %d + %d points with matching S-D history",
- Knns, countOfSourceNextAndGreater, countOfDestNextAndGreater);
- }
-
- // Now find the matching samples in the dest history and
- // with a next spike timing.
- int countOfDestNextAndGreaterMatchedDest = 0;
- int countOfDestNextMatched = 0;
- if (k > 1) {
- // Search only the space of dest past -- no point
- // searching dest past and next, since we need to run through
- // all matches of dest past to count those with greater next spike
- // times we might as well count those with matching spike times
- // while we're at it.
- kdTreeDestHistory.findPointsWithinR(indexForNextIsDest, radius_destPast,
- true, isWithinR, indicesWithinR);
- // And check which of these samples had next spike time after ours:
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the dest history space
- double[][] matchedHistoryEventTimings = destPastAndNextTimings.elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[1][0] >= timeToNextSpikeSincePreviousDestSpike - radius_destNext_lower) {
- // This sample had a matched history and next spike was a
- // spike with an interval longer than or considered equal to the current sample.
- // (The "equal to" is why we look for matches within kthNnData.distance here as well.)
- countOfDestNextAndGreaterMatchedDest++;
- if (matchedHistoryEventTimings[1][0] <= timeToNextSpikeSincePreviousDestSpike + radius_destNext) {
- // Then we also have a match on the next spike itself
- countOfDestNextMatched++;
- }
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- } else {
- // We don't take any past dest spike ISIs into account, so we just need to look at the proportion of next
- // spike times that match.
- countOfDestNextMatched = nnSearcherDestTimeToNextSpike.countPointsWithinRs(indexForNextIsDest,
- radius_destNext, radius_destNext_lower, true);
- // To check for points matching or larger, we just need to make this call with the
- // revised lower radius, because we don't check the upper one.
- countOfDestNextAndGreaterMatchedDest =
- nnSearcherDestTimeToNextSpike.countPointsWithinROrLarger(indexForNextIsDest, radius_destNext_lower, true);
- }
- double totalSearchTimeWindowCondDestPast = radius_destNext_lower + radius_destNext;
-
- if (debug && (eventIndex < 10000)) {
- System.out.printf(", and %d of %d points for D history only; ",
- countOfDestNextMatched, countOfDestNextAndGreaterMatchedDest);
- }
-
- //============================
- // This code section is if we wish to compute using digamma logs:
- //============================
- // With these neighbours counted, we're ready to compute the probability of the spike given the past
- // of source and dest.
- double logPGivenSourceAndDest;
- double logPGivenDest;
- if (k > 1) {
- // We're handling three variables:
- logPGivenSourceAndDest = digammaK - twoInverseKTerm
- - MathsUtils.digamma(Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater)
- + 1.0 / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater);
- logPGivenDest = MathsUtils.digamma(countOfDestNextMatched)
- - 1.0 / ((double) countOfDestNextMatched)
- - MathsUtils.digamma(countOfDestNextAndGreaterMatchedDest);
- } else {
- // We're really only handling two variables like an MI:
- logPGivenSourceAndDest = digammaK - inverseKTerm
- - MathsUtils.digamma(Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater);
- logPGivenDest = MathsUtils.digamma(countOfDestNextMatched)
- - MathsUtils.digamma(countOfDestNextAndGreaterMatchedDest);
- }
- if (trimToPosNextSpikeTimes) {
- logPGivenSourceAndDest -= Math.log(searchAreaRatio);
- }
- //============================
-
- if (debug && (eventIndex < 10000)) {
- System.out.printf(" te ~~ log (%d/%d)/(%d/%d) = %.4f wc-> %.5f bc-> %.4f (inferred rates %.4f vs %.4f, " +
- "win-cor %.5f vs %.5f, bias-corrected %.5f vs %.5f)\n", Knns,
- Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater,
- countOfDestNextMatched, countOfDestNextAndGreaterMatchedDest,
- Math.log(((double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater)) /
- ((double) (countOfDestNextMatched) / (double) (countOfDestNextAndGreaterMatchedDest))),
- // TE from Window corrected rates:
- Math.log((((double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater)) /
- ((destNext_timing_upper - destNext_timing_lower_original)*searchAreaRatio)) /
- (((double) (countOfDestNextMatched) / (double) (countOfDestNextAndGreaterMatchedDest)) /
- totalSearchTimeWindowCondDestPast)),
- // TE from Bias corrected rates:
- logPGivenSourceAndDest - logPGivenDest,
- // Inferred rates raw:
- (double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater) / (2.0*radius_destNext),
- (double) (countOfDestNextMatched) / (double) (countOfDestNextAndGreaterMatchedDest) / (2.0*radius_destNext),
- // Inferred rates with window correction:
- ((double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater)) /
- ((destNext_timing_upper - destNext_timing_lower_original)*searchAreaRatio),
- ((double) (countOfDestNextMatched) / (double) (countOfDestNextAndGreaterMatchedDest)) /
- totalSearchTimeWindowCondDestPast,
- // Transform the log likelihoods into bias corrected rates:
- Math.exp(logPGivenSourceAndDest) / (2.0*radius_destNext), Math.exp(logPGivenDest) / (2.0*radius_destNext));
- if (trimToPosNextSpikeTimes) {
- System.out.printf("Search area ratio: %.5f, correction %.5f, t_y_upper %.5f, t_y_lower %.5f\n",
- searchAreaRatio, -Math.log(searchAreaRatio), prevSource_timing_upper_original, prevSource_timing_lower);
- }
- }
- // Unexplained case:
- if (countOfDestNextMatched < Knns) {
- // TODO Should not happen, print something!
- System.out.printf("SHOULD NOT HAPPEN!\n");
- // So debug this:
- nnPQ = kdTreesJoint[eventType].findKNearestNeighbours(
- Knns, eventIndexWithinType);
- for (int j = 0; j < Knns; j++) {
- // Take the furthest remaining of the nearest neighbours from the PQ:
- NeighbourNodeData nnData = nnPQ.poll();
- System.out.printf("NN data %d norms: source %.5f, destPast %.5f, destNext %.5f, nextIndex %d\n",
- nnData.norms[0], nnData.norms[1], nnData.norms[2], nnData.sampleIndex);
- }
-
- }
-
- //======================
- // Add the contribution in:
- // a.If we were using digamma logs:
- contributionFromSpikes += logPGivenSourceAndDest - logPGivenDest;
- // b. If we are only using window corrections but actual ratios:
- //contributionFromSpikes +=
- // Math.log((((double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater)) /
- // ((destNext_timing_upper - destNext_timing_lower_original)*searchAreaRatio)) /
- // (((double) (countOfDestNextMatched) / (double) (countOfDestNextAndGreaterMatchedDest)) /
- // totalSearchTimeWindowCondDestPast));
- } else {
- if (debug && (eventIndex < 10000)) {
- System.out.println();
- }
- }
- }
- System.out.println("All done!");
- contributionFromSpikes /= totalTimeLength;
- return contributionFromSpikes;
+ int firstTargetIndexOfEmbedding = getFirstDestIndex(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes, false);
+ double[] randomSampleTimes = generateRandomSampleTimes(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ actualNumSamplesMultiplier, firstTargetIndexOfEmbedding);
+ makeEmbeddingsAtPoints(randomSampleTimes, 0, sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ conditioningEmbeddingsFromSamples, jointEmbeddingsFromSamples);
}
/*
- * This old method is not adjusted for the newer representation yet
- *
- public double computeAverageLocalOfObservationsAlg1() throws Exception {
-
- int numberOfEvents = eventTypeLocator.size();
-
- double te = 0;
- double contributionFromSpikes = 0;
- double contributionFromNonSpikes = 0;
- double contributionFromNonSpikes_destOnly = 0;
- double contributionFromNonSpikes_destAndSource = 0;
- double totalTimeLength = 0;
-
- // Create temporary storage for arrays used in the neighbour counting:
- boolean[] isWithinR = new boolean[numberOfEvents]; // dummy, we don't really use this
- int[] indicesWithinR = new int[numberOfEvents];
-
- // Iterate over all the spiking events:
- Iterator eventIndexIterator = eventIndexLocator.iterator();
- int eventIndex = -1;
- int indexForNextIsDest = -1;
- for (int[] eventType : eventTypeLocator) {
- eventIndex++;
- int eventIndexWithinType = eventIndexIterator.next().intValue();
- double[][] thisEventTimings = eventTimings[eventType[0]][eventType[1]].elementAt(eventIndexWithinType);
- totalTimeLength += thisEventTimings[2][0];
- // Find the Knns nearest neighbour matches to this event,
- // with the same previous spiker and the next.
- // TODO Add dynamic exclusion time later
- PriorityQueue nnPQ =
- kdTreesJoint[eventType[0]][eventType[1]].findKNearestNeighbours(
- Knns, eventIndexWithinType);
- // First element in the PQ is the kth NN,
- // and epsilon = kthNnData.distance
- NeighbourNodeData kthNnData = nnPQ.poll();
- double radiusToKnn = kthNnData.distance;
- if (debug && (eventIndex < 10000)) {
- // Pull out the data for this observation:
- System.out.print("index = " + eventIndex + ", " +
- eventIndexWithinType + " for " +
- (eventType[0] == PREV_DEST ? "dst" : "src") +
- "->" +
- (eventType[1] == NEXT_DEST ? "dst" : "src") +
- ", timings: src: ");
- MatrixUtils.printArray(System.out, thisEventTimings[0], 3);
- System.out.print(", dest: ");
- MatrixUtils.printArray(System.out, thisEventTimings[1], 3);
- System.out.print(", time to next: ");
- MatrixUtils.printArray(System.out, thisEventTimings[2], 3);
- System.out.printf("index=%d: K=%d NNs at range %.5f (point %d)", eventIndexWithinType, Knns, radiusToKnn, kthNnData.sampleIndex);
- }
-
- // Select only events where the destination spiked next:
- if (eventType[1] == NEXT_DEST) {
- indexForNextIsDest++;
-
- // Now find the matching samples in each sub-space;
- // first match dest history and source history, with a next spike in dest:
- kdTreesSourceDestHistories[eventType[0]][NEXT_DEST].
- findPointsWithinR(eventIndexWithinType, radiusToKnn, 0,
- false, isWithinR, indicesWithinR);
- // And check which of these samples had spike time in dest after ours:
- int countOfDestNextAndGreater = 0;
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[eventType[0]][NEXT_DEST].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] >= thisEventTimings[2][0] + radiusToKnn) {
- // This sample had a matched history and next spike was a destination
- // spike with a longer interval than the current sample
- countOfDestNextAndGreater++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- // And count how many samples with the matching history actually had a
- // *source* spike next, after ours.
- // Note that we now must go to the other kdTree for next source spike
- kdTreesSourceDestHistories[eventType[0]][NEXT_SOURCE].
- findPointsWithinR(radiusToKnn, thisEventTimings,
- false, isWithinR, indicesWithinR);
- // And check which of these samples had spike time in source after ours:
- int countOfSourceNextAndGreater = 0;
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[eventType[0]][NEXT_SOURCE].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] > thisEventTimings[2][0] - radiusToKnn) {
- // This sample had a matched history and next spike was a source
- // spike with an interval longer than or considered equal to the current sample.
- // (The "equal to" is why we look for matches within kthNnData.distance here as well.)
- countOfSourceNextAndGreater++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
-
- if (debug && (eventIndex < 10000)) {
- System.out.printf(" of %d + %d + %d points with matching S-D history",
- Knns, countOfSourceNextAndGreater, countOfDestNextAndGreater);
- }
-
- // Now find the matching samples in the dest history and
- // with a next spike timing.
- // Construct the appropriate timings to compare to here:
- double[][] destOnlyObservations;
- double[][] destPastOnlyObservations;
- double timeToNextSpikeSincePreviousDestSpike;
- if (eventType[0] == PREV_DEST) {
- destOnlyObservations = new double[][] {
- thisEventTimings[1], // timing of past dest spikes
- thisEventTimings[2] // time to next spike
- };
- timeToNextSpikeSincePreviousDestSpike = thisEventTimings[2][0];
- destPastOnlyObservations = new double[][] {
- thisEventTimings[1] // timing of past dest spikes
- };
- } else {
- // previous is source:
- // We can take a copy of the dest past timings, removing the first entry
- // (since this only signals time the dest last fired before the source)
- // and add that entry to the timeToNextSpike (which was back to the
- // source firing).
- double[] destPastOnly = Arrays.copyOfRange(
- thisEventTimings[1], 1, thisEventTimings[1].length);
- timeToNextSpikeSincePreviousDestSpike =
- thisEventTimings[1][0] + thisEventTimings[2][0];
- destOnlyObservations = new double[][] {
- destPastOnly,
- new double[] {timeToNextSpikeSincePreviousDestSpike}
- };
- destPastOnlyObservations = new double[][] {
- destPastOnly
- };
- }
- int countOfDestNextAndGreaterMatchedDest = 0;
- int countOfDestNextMatched = 0;
- if (k > 1) {
- // Search only the space of dest past -- no point
- // searching dest past and next, since we need to run through
- // all matches of dest past to count those with greater next spike
- // times we might as well count those with matching spike times
- // while we're at it.
-
- // OLD WAY:
- // NO NO NO -- Can't search for it this way, because it's biased --
- // should search for it by giving the index of this dest past-next
- // observation, so that it doesn't match to this observation.
- // Should be able to use indexForNextIsDest here
- //kdTreeDestHistory.findPointsWithinR(radiusToKnn, destPastOnlyObservations,
- // false, isWithinR, indicesWithinR);
- // Proper way:
- kdTreeDestHistory.findPointsWithinR(indexForNextIsDest, radiusToKnn,
- false, isWithinR, indicesWithinR);
- // And check which of these samples had next spike time after ours:
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the dest history space
- double[][] matchedHistoryEventTimings = destPastAndNextTimings.elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[1][0] > timeToNextSpikeSincePreviousDestSpike - radiusToKnn) {
- // This sample had a matched history and next spike was a
- // spike with an interval longer than or considered equal to the current sample.
- // (The "equal to" is why we look for matches within kthNnData.distance here as well.)
- countOfDestNextAndGreaterMatchedDest++;
- if (matchedHistoryEventTimings[1][0] < timeToNextSpikeSincePreviousDestSpike + radiusToKnn) {
- // Then we also have a match on the next spike itself
- countOfDestNextMatched++;
- }
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- } else {
- // We don't take any past dest spike times into account, so we just need to look at the proportion of next
- // spike times that match.
- countOfDestNextMatched = nnSearcherDestTimeToNextSpike.countPointsStrictlyWithinR(indexForNextIsDest, radiusToKnn);
- countOfDestNextAndGreaterMatchedDest = countOfDestNextMatched +
- nnSearcherDestTimeToNextSpike.countPointsWithinROrLarger(indexForNextIsDest, radiusToKnn, false);
- }
-
- if (debug && (eventIndex < 10000)) {
- System.out.printf(", and %d of %d points for D history only; ",
- countOfDestNextMatched, countOfDestNextAndGreaterMatchedDest);
- }
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * getAddedMoreThanOneObservationSet()
+ */
+ @Override
+ public boolean getAddedMoreThanOneObservationSet() {
+ return (vectorOfDestinationSpikeTimes != null) && (vectorOfDestinationSpikeTimes.size() > 1);
+ }
- // With these neighbours counted, we're ready to compute the probability of the spike given the past
- // of source and dest.
- // Digammas for algorithm 1 include the extra "+1" on all terms except
- // for the full joint space
- double logPGivenSourceAndDest = MathsUtils.digamma(Knns) -
- MathsUtils.digamma(Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater + 1);
- double logPGivenDest = MathsUtils.digamma(countOfDestNextMatched + 1) -
- MathsUtils.digamma(countOfDestNextAndGreaterMatchedDest + 1);
- if (debug && (eventIndex < 10000)) {
- System.out.printf(" te ~~ log (%d/%d)/(%d/%d) = %.4f -> %.4f (inferred rates %.4f vs %.4f)\n", Knns,
- Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater + 1,
- countOfDestNextMatched + 1, countOfDestNextAndGreaterMatchedDest + 1,
- Math.log(((double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater + 1)) /
- ((double) (countOfDestNextMatched + 1) / (double) (countOfDestNextAndGreaterMatchedDest + 1))),
- logPGivenSourceAndDest - logPGivenDest,
- (double) Knns / (double) (Knns + countOfSourceNextAndGreater + countOfDestNextAndGreater + 1) / (2.0*radiusToKnn),
- (double) (countOfDestNextMatched + 1) / (double) (countOfDestNextAndGreaterMatchedDest + 1) / (2.0*radiusToKnn));
- }
- contributionFromSpikes += logPGivenSourceAndDest - logPGivenDest;
- } else {
- if (debug) {
- System.out.println();
- }
- }
-
- // Regardless of which type of event it was, we need to integrate
- // the spiking rates up until the next spiking event
- // Our first attempt at a solution uses the search width defined
- // using the history and the next spike.
-
- // Consider first the destination process only.
- // Match dest history
- double[][] destPastOnlyObservations;
- double timeToNextSpikeSincePreviousDestSpike;
- if (eventType[0] == PREV_DEST) {
- timeToNextSpikeSincePreviousDestSpike = thisEventTimings[2][0];
- destPastOnlyObservations = new double[][] {
- thisEventTimings[1] // timing of past dest spikes
- };
- } else {
- // previous is source:
- // We can take a copy of the dest past timings, removing the first entry
- // (since this only signals time the dest last fired before the source)
- // and add that entry to the timeToNextSpike (which was back to the
- // source firing).
- double[] destPastOnly = Arrays.copyOfRange(
- thisEventTimings[1], 1, thisEventTimings[1].length);
- timeToNextSpikeSincePreviousDestSpike =
- thisEventTimings[1][0] + thisEventTimings[2][0];
- destPastOnlyObservations = new double[][] {
- destPastOnly
- };
- }
- int countOfDestNextEarlier = 0;
- int countOfDestMatches = 0;
- if (k > 1) {
- kdTreeDestHistory.findPointsWithinR(radiusToKnn, destPastOnlyObservations,
- false, isWithinR, indicesWithinR);
- // And check which of these samples had next spike time before ours:
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the dest history space
- double[][] matchedHistoryEventTimings = destPastAndNextTimings.elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[1][0] < timeToNextSpikeSincePreviousDestSpike) {
- // This sample had a matched history and next spike was a
- // spike with an interval shorter than the current sample.
- countOfDestNextEarlier++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- countOfDestMatches++;
- }
- } else {
- // We're not using the past, so we match on everything up to the last spike
- countOfDestMatches = nnSearcherDestTimeToNextSpike.getNumObservations();
- countOfDestNextEarlier = nnSearcherDestTimeToNextSpike.countPointsSmallerAndOutsideR(
- // indexForNextIsDest must point to the next event (possibly this one) where the dest spikes next.
- (eventType[1] == NEXT_DEST) ? indexForNextIsDest : indexForNextIsDest + 1,
- radiusToKnn, false);
+ // Class to allow returning two values in the subsequent method
+ private static class distanceAndNumPoints {
+ public double distance;
+ public int numPoints;
- }
- // And include the contribution for each of these
- double integralForDestHistorySpace = 0;
- for (int hi = 0; hi < countOfDestNextEarlier; hi++) {
- integralForDestHistorySpace += (double) 1 /
- (double) (countOfDestMatches - hi);
- }
-
- // First match dest history and source history, with a next spike in dest:
- kdTreesSourceDestHistories[eventType[0]][NEXT_DEST].
- findPointsWithinR(radiusToKnn, thisEventTimings,
- false, isWithinR, indicesWithinR);
- // And store which of these samples had spike time in dest before ours.
- // Store them in a vector of double arrays, with each array holding the
- // spike time then -1 for a next dest spike and +1 for a source spike
- Vector spikesBeforeOurs = new Vector();
- int countOfSpikesAfterAndIncludingOurs = 0;
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[eventType[0]][NEXT_DEST].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] < thisEventTimings[2][0]) {
- // This sample had a matched history and next spike was a destination
- // spike with a shorted interval than the current sample
- spikesBeforeOurs.add(new double[] {
- matchedHistoryEventTimings[2][0], -1});
- } else {
- countOfSpikesAfterAndIncludingOurs++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- // And store which of these samples had spike time in source before ours.
- // Store them in a vector of double arrays, with each array holding the
- // spike time then -1 for a next dest spike and +1 for a source spike
- // Note that we now must go to the other kdTree for next source spike
- kdTreesSourceDestHistories[eventType[0]][NEXT_SOURCE].
- findPointsWithinR(radiusToKnn, thisEventTimings,
- false, isWithinR, indicesWithinR);
- for (int nIndex = 0; indicesWithinR[nIndex] != -1; nIndex++) {
- // Pull out this matching event from the full joint space
- double[][] matchedHistoryEventTimings = eventTimings[eventType[0]][NEXT_SOURCE].elementAt(indicesWithinR[nIndex]);
- if (matchedHistoryEventTimings[2][0] < thisEventTimings[2][0]) {
- // This sample had a matched history and next spike was a source
- // spike with a shorted interval than the current sample
- spikesBeforeOurs.add(new double[] {
- matchedHistoryEventTimings[2][0], +1});
- } else {
- countOfSpikesAfterAndIncludingOurs++;
- }
- // Reset the isWithinR array while we're here
- isWithinR[indicesWithinR[nIndex]] = false;
- }
- // Now we can sort the spikes which occur before ours and process
- // them in order:
- // Next line doesn't work, so replaced with clunkier code:
- // double[][] nextSpikeTimesAndType = (double[][]) spikesBeforeOurs.toArray();
- double[][] nextSpikeTimesAndType = new double[spikesBeforeOurs.size()][];
- for (int si = 0; si < nextSpikeTimesAndType.length; si++) {
- nextSpikeTimesAndType[si] = spikesBeforeOurs.elementAt(si);
- }
- Arrays.sort(nextSpikeTimesAndType, FirstIndexComparatorDouble.getInstance());
- double integralForJointSpace = 0;
- for (int si = 0; si < nextSpikeTimesAndType.length; si++) {
- if (nextSpikeTimesAndType[si][1] < 0) {
- // We have a next spike from the dest, which is
- // earlier than our spike.
- // Integrated Prob for getting a spike here is 1 / N, where
- // N is the number of properly matched histories (i.e.
- // which don't have a next spike before this one)
- double intPNext = (double) 1 / (double)
- (nextSpikeTimesAndType.length - si + countOfSpikesAfterAndIncludingOurs);
- integralForJointSpace += intPNext;
- }
- // Ignore next spikes on the source, they simply get removed
- // from the matched histories count
- }
-
- // We now have the integral of spike rates given the dest and
- // joint histories, so subtract this out:
- contributionFromNonSpikes += integralForDestHistorySpace - integralForJointSpace;
- contributionFromNonSpikes_destAndSource += integralForJointSpace;
- contributionFromNonSpikes_destOnly += integralForDestHistorySpace;
+ public distanceAndNumPoints(double distance, int numPoints) {
+ this.distance = distance;
+ this.numPoints = numPoints;
}
- contributionFromSpikes /= totalTimeLength;
- contributionFromNonSpikes /= totalTimeLength;
- contributionFromNonSpikes_destAndSource /= totalTimeLength;
- contributionFromNonSpikes_destOnly /= totalTimeLength;
- te = contributionFromSpikes + contributionFromNonSpikes;
- System.out.printf("TE = %.4f (spikes) + %.4f (non-spikes: d:%.4f - s-d:%.4f) = %.4f\n",
- contributionFromSpikes, contributionFromNonSpikes,
- contributionFromNonSpikes_destOnly, contributionFromNonSpikes_destAndSource, te);
- return te;
}
- */
+
+ private distanceAndNumPoints findMaxDistanceAndNumPointsFromIndices(double[] point, int[] indices, Vector setOfPoints) {
+ double maxDistance = 0;
+ int i = 0;
+ for (; indices[i] != -1; i++) {
+ double distance = KdTree.norm(point, setOfPoints.elementAt(indices[i]), normType);
+ if (distance > maxDistance) {
+ maxDistance = distance;
+ }
+ }
+ return new distanceAndNumPoints(maxDistance, i);
+ }
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#computeLocalOfPreviousObservations()
+ @Override
+ public double computeAverageLocalOfObservations() throws Exception {
+ return computeAverageLocalOfObservations(kdTreeJointAtSpikes, jointEmbeddingsFromSpikes);
+ }
+
+
+ /*
+ * We take the actual joint tree at spikes (along with the associated embeddings) as an argument, as we will need to swap these out when
+ * computing surrogates.
+ */
+ public double computeAverageLocalOfObservations(KdTree actualKdTreeJointAtSpikes, Vector actualJointEmbeddingsFromSpikes) throws Exception {
+
+ double currentSum = 0;
+ for (int i = 0; i < conditioningEmbeddingsFromSpikes.size(); i++) {
+
+ double radiusJointSpikes = actualKdTreeJointAtSpikes.findKNearestNeighbours(Knns, i).poll().norms[0];
+ double radiusJointSamples = kdTreeJointAtSamples.findKNearestNeighbours(Knns,
+ new double[][] { actualJointEmbeddingsFromSpikes.elementAt(i) }).poll().norms[0];
+
+ /*
+ The algorithm specified in box 1 of doi.org/10.1371/journal.pcbi.1008054 specifies finding the maximum of the two radii
+ just calculated and then redoing the searches in both sets at this radius. In this implementation, however, we make use
+ of the fact that one radius is equal to the maximum, and so only one search needs to be redone.
+ */
+ double eps = 0.0;
+ // Need variables for the number of neighbours as this is now variable within the maximum radius
+ int kJointSpikes = 0;
+ int kJointSamples = 0;
+ if (radiusJointSpikes >= radiusJointSamples) {
+ /*
+ The maximum was the radius in the set of embeddings at spikes, so redo search in the set of embeddings at randomly
+ sampled points, using this larger radius.
+ */
+ kJointSpikes = Knns;
+ int[] indicesWithinR = new int[jointEmbeddingsFromSamples.size()];
+ boolean[] isWithinR = new boolean[jointEmbeddingsFromSamples.size()];
+ kdTreeJointAtSamples.findPointsWithinR(radiusJointSpikes + eps,
+ new double[][] { actualJointEmbeddingsFromSpikes.elementAt(i) },
+ true,
+ isWithinR,
+ indicesWithinR);
+ distanceAndNumPoints temp = findMaxDistanceAndNumPointsFromIndices(actualJointEmbeddingsFromSpikes.elementAt(i), indicesWithinR,
+ jointEmbeddingsFromSamples);
+ kJointSamples = temp.numPoints;
+ radiusJointSamples = temp.distance;
+ } else {
+ /*
+ The maximum was the radius in the set of embeddings at randomly sampled points, so redo search in the set of embeddings
+ at spikes, using this larger radius.
+ */
+ kJointSamples = Knns;
+ int[] indicesWithinR = new int[jointEmbeddingsFromSamples.size()];
+ boolean[] isWithinR = new boolean[jointEmbeddingsFromSamples.size()];
+ actualKdTreeJointAtSpikes.findPointsWithinR(radiusJointSamples + eps,
+ new double[][] { actualJointEmbeddingsFromSpikes.elementAt(i) },
+ true,
+ isWithinR,
+ indicesWithinR);
+ distanceAndNumPoints temp = findMaxDistanceAndNumPointsFromIndices(actualJointEmbeddingsFromSpikes.elementAt(i), indicesWithinR,
+ actualJointEmbeddingsFromSpikes);
+ // -1 due to the point itself being in the set
+ kJointSpikes = temp.numPoints - 1;
+ radiusJointSpikes = temp.distance;
+ }
+
+ // Repeat the above steps, but in the conditioning (rather than joint) space.
+ double radiusConditioningSpikes = kdTreeConditioningAtSpikes.findKNearestNeighbours(Knns, i).poll().norms[0];
+ double radiusConditioningSamples = kdTreeConditioningAtSamples.findKNearestNeighbours(Knns,
+ new double[][] { conditioningEmbeddingsFromSpikes.elementAt(i) }).poll().norms[0];
+ int kConditioningSpikes = 0;
+ int kConditioningSamples = 0;
+ if (radiusConditioningSpikes >= radiusConditioningSamples) {
+ kConditioningSpikes = Knns;
+ int[] indicesWithinR = new int[conditioningEmbeddingsFromSamples.size()];
+ boolean[] isWithinR = new boolean[conditioningEmbeddingsFromSamples.size()];
+ kdTreeConditioningAtSamples.findPointsWithinR(radiusConditioningSpikes + eps,
+ new double[][] { conditioningEmbeddingsFromSpikes.elementAt(i) },
+ true,
+ isWithinR,
+ indicesWithinR);
+ distanceAndNumPoints temp = findMaxDistanceAndNumPointsFromIndices(conditioningEmbeddingsFromSpikes.elementAt(i), indicesWithinR,
+ conditioningEmbeddingsFromSamples);
+ kConditioningSamples = temp.numPoints;
+ radiusConditioningSamples = temp.distance;
+ } else {
+ kConditioningSamples = Knns;
+ int[] indicesWithinR = new int[conditioningEmbeddingsFromSamples.size()];
+ boolean[] isWithinR = new boolean[conditioningEmbeddingsFromSamples.size()];
+ kdTreeConditioningAtSpikes.findPointsWithinR(radiusConditioningSamples + eps,
+ new double[][] { conditioningEmbeddingsFromSpikes.elementAt(i) },
+ true,
+ isWithinR,
+ indicesWithinR);
+ distanceAndNumPoints temp = findMaxDistanceAndNumPointsFromIndices(conditioningEmbeddingsFromSpikes.elementAt(i), indicesWithinR,
+ conditioningEmbeddingsFromSpikes);
+ // -1 due to the point itself being in the set
+ kConditioningSpikes = temp.numPoints - 1;
+ radiusConditioningSpikes = temp.distance;
+ }
+
+ /*
+ * TODO
+ * The KdTree class defaults to the squared euclidean distance when the euclidean norm is specified. This is fine for Kraskov estimators
+ * (as the radii are never used, just the numbers of points within radii). It causes problems here though, as we do use the radii and the
+ * squared euclidean distance is not a distance metric. We get around this by just taking the square root here, but it might be better to
+ * fix this in the KdTree class.
+ */
+ if (normType == EuclideanUtils.NORM_EUCLIDEAN) {
+ radiusJointSpikes = Math.sqrt(radiusJointSpikes);
+ radiusJointSamples = Math.sqrt(radiusJointSamples);
+ radiusConditioningSpikes = Math.sqrt(radiusConditioningSpikes);
+ radiusConditioningSamples = Math.sqrt(radiusConditioningSamples);
+ }
+
+ currentSum += (MathsUtils.digamma(kJointSpikes) - MathsUtils.digamma(kJointSamples) +
+ ((numDestPastIntervals + numCondPastIntervals + numSourcePastIntervals) * (-Math.log(radiusJointSpikes) + Math.log(radiusJointSamples))) -
+ MathsUtils.digamma(kConditioningSpikes) + MathsUtils.digamma(kConditioningSamples) +
+ + ((numDestPastIntervals + numCondPastIntervals) * (Math.log(radiusConditioningSpikes) - Math.log(radiusConditioningSamples))));
+ if (Double.isNaN(currentSum)) {
+ throw new Exception("NaNs in TE clac");
+ }
+ }
+ // Normalise by time
+ double timeSum = 0;
+ for (Double time : processTimeLengths) {
+ timeSum += time;
+ }
+ currentSum /= timeSum;
+ return currentSum;
+ }
+
+
+ @Override
+ public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck, double estimatedValue) throws Exception {
+ return computeSignificance(numPermutationsToCheck,
+ estimatedValue,
+ System.currentTimeMillis());
+ }
+
+ @Override
+ public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsToCheck,
+ double estimatedValue, long randomSeed) throws Exception{
+
+ Random random = new Random(randomSeed);
+ double[] surrogateTEValues = new double[numPermutationsToCheck];
+
+ for (int permutationNumber = 0; permutationNumber < numPermutationsToCheck; permutationNumber++) {
+ Vector resampledConditioningEmbeddingsFromSamples = new Vector();
+ Vector resampledJointEmbeddingsFromSamples = new Vector();
+
+ // Send all of the observations through:
+ Iterator sourceIterator = vectorOfSourceSpikeTimes.iterator();
+ Iterator conditionalIterator = null;
+ if (vectorOfConditionalSpikeTimes.size() > 0) {
+ conditionalIterator = vectorOfConditionalSpikeTimes.iterator();
+ }
+ int timeSeriesIndex = 0;
+ for (double[] destSpikeTimes : vectorOfDestinationSpikeTimes) {
+ double[] sourceSpikeTimes = sourceIterator.next();
+ double[][] conditionalSpikeTimes = null;
+ if (vectorOfConditionalSpikeTimes.size() > 0) {
+ conditionalSpikeTimes = conditionalIterator.next();
+ } else {
+ conditionalSpikeTimes = new double[][] {};
+ }
+ processEventsFromSpikingTimeSeries(sourceSpikeTimes, destSpikeTimes, conditionalSpikeTimes,
+ resampledConditioningEmbeddingsFromSamples, resampledJointEmbeddingsFromSamples,
+ surrogateNumSamplesMultiplier);
+ }
+ // Convert the vectors to arrays so that they can be put in the trees
+ double[][] arrayedResampledConditioningEmbeddingsFromSamples = new double[resampledConditioningEmbeddingsFromSamples.size()][numDestPastIntervals];
+ for (int i = 0; i < resampledConditioningEmbeddingsFromSamples.size(); i++) {
+ arrayedResampledConditioningEmbeddingsFromSamples[i] = resampledConditioningEmbeddingsFromSamples.elementAt(i);
+ }
+
+ KdTree resampledKdTreeConditioningAtSamples = new KdTree(arrayedResampledConditioningEmbeddingsFromSamples);
+ resampledKdTreeConditioningAtSamples.setNormType(normType);
+
+ Vector conditionallyPermutedJointEmbeddingsFromSpikes = new Vector(jointEmbeddingsFromSpikes);
+
+ Vector usedIndices = new Vector();
+ for (int i = 0; i < conditionallyPermutedJointEmbeddingsFromSpikes.size(); i++) {
+ PriorityQueue neighbours =
+ resampledKdTreeConditioningAtSamples.findKNearestNeighbours(kPerm,
+ new double[][] {conditioningEmbeddingsFromSpikes.elementAt(i)});
+ ArrayList foundIndices = new ArrayList();
+ for (int j = 0; j < kPerm; j++) {
+ foundIndices.add(neighbours.poll().sampleIndex);
+ }
+
+ ArrayList prunedIndices = new ArrayList(foundIndices);
+ prunedIndices.removeAll(usedIndices);
+ int chosenIndex = 0;
+ if (prunedIndices.size() > 0) {
+ chosenIndex = prunedIndices.get(random.nextInt(prunedIndices.size()));
+ } else {
+ chosenIndex = foundIndices.get(random.nextInt(foundIndices.size()));
+ }
+ usedIndices.add(chosenIndex);
+ int embeddingLength = conditionallyPermutedJointEmbeddingsFromSpikes.elementAt(i).length;
+ for(int j = 0; j < numSourcePastIntervals; j++) {
+ conditionallyPermutedJointEmbeddingsFromSpikes.elementAt(i)[embeddingLength - numSourcePastIntervals + j] =
+ resampledJointEmbeddingsFromSamples.elementAt(chosenIndex)[embeddingLength - numSourcePastIntervals + j];
+ }
+
+ }
+
+ double[][] arrayedConditionallyPermutedJointEmbeddingsFromSpikes = new double[conditionallyPermutedJointEmbeddingsFromSpikes.size()][];
+ for (int i = 0; i < conditionallyPermutedJointEmbeddingsFromSpikes.size(); i++) {
+ arrayedConditionallyPermutedJointEmbeddingsFromSpikes[i] = conditionallyPermutedJointEmbeddingsFromSpikes.elementAt(i);
+ }
+ KdTree conditionallyPermutedKdTreeJointFromSpikes = new KdTree(arrayedConditionallyPermutedJointEmbeddingsFromSpikes);
+ conditionallyPermutedKdTreeJointFromSpikes.setNormType(normType);
+ surrogateTEValues[permutationNumber] = computeAverageLocalOfObservations(conditionallyPermutedKdTreeJointFromSpikes,
+ conditionallyPermutedJointEmbeddingsFromSpikes);
+ }
+ return new EmpiricalMeasurementDistribution(surrogateTEValues, estimatedValue);
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#
+ * computeLocalOfPreviousObservations()
*/
@Override
- public SpikingLocalInformationValues computeLocalOfPreviousObservations()
- throws Exception {
+ public SpikingLocalInformationValues computeLocalOfPreviousObservations() throws Exception {
// TODO Auto-generated method stub
return null;
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#computeSignificance(int)
- */
- @Override
- public EmpiricalMeasurementDistribution computeSignificance(
- int numPermutationsToCheck) throws Exception {
- // TODO Auto-generated method stub
- return null;
- }
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#computeSignificance(int[][])
- */
- @Override
- public EmpiricalMeasurementDistribution computeSignificance(
- int[][] newOrderings) throws Exception {
- // TODO Auto-generated method stub
- return null;
- }
-
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#setDebug(boolean)
+ /*
+ * (non-Javadoc)
+ *
+ * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#setDebug(
+ * boolean)
*/
@Override
public void setDebug(boolean debug) {
this.debug = debug;
}
- /* (non-Javadoc)
- * @see infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#getLastAverage()
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * infodynamics.measures.spiking.TransferEntropyCalculatorSpiking#getLastAverage
+ * ()
*/
@Override
public double getLastAverage() {
// TODO Auto-generated method stub
return 0;
}
-
}
diff --git a/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegrationWindows2.java b/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegrationWindows2.java
index 2349b8a..64f25d6 100644
--- a/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegrationWindows2.java
+++ b/java/source/infodynamics/measures/spiking/integration/TransferEntropyCalculatorSpikingIntegrationWindows2.java
@@ -723,33 +723,33 @@ public class TransferEntropyCalculatorSpikingIntegrationWindows2 implements
//kdTreeDestNext.setNormType(3);
- nnPQ =
- kdTreeDestNext.findKNearestNeighbours(
- Knns, eventIndexWithinType);
+ // nnPQ =
+ // kdTreeDestNext.findKNearestNeighbours(
+ // Knns, eventIndexWithinType);
- // Find eps_{x,y,z} as the maximum x, y and z norms amongst this set:
- radius_sourcePast = 0.0;
- radius_destPast = 0.0;
- radius_destNext = 0.0;
- radius_destNext_sampleIndex = -1;
- radius_max = 0;
- for (int j = 0; j < Knns; j++) {
- // Take the furthest remaining of the nearest neighbours from the PQ:
- NeighbourNodeData nnData = nnPQ.poll();
- /*if (nnData.norms[0] > radius_sourcePast) {
- radius_sourcePast = nnData.norms[0];
- }*/
- if (nnData.norms[0] > radius_destPast) {
- radius_destPast = nnData.norms[0];
- }
- if (nnData.norms[1] > radius_destNext) {
- radius_destNext = nnData.norms[1];
- radius_destNext_sampleIndex = nnData.sampleIndex;
- }
- if(nnData.distance > radius_max) {
- radius_max = nnData.distance;
- }
- }
+ // // Find eps_{x,y,z} as the maximum x, y and z norms amongst this set:
+ // radius_sourcePast = 0.0;
+ // radius_destPast = 0.0;
+ // radius_destNext = 0.0;
+ // radius_destNext_sampleIndex = -1;
+ // radius_max = 0.0;
+ // for (int j = 0; j < Knns; j++) {
+ // // Take the furthest remaining of the nearest neighbours from the PQ:
+ // NeighbourNodeData nnData = nnPQ.poll();
+ // /*if (nnData.norms[0] > radius_sourcePast) {
+ // radius_sourcePast = nnData.norms[0];
+ // }*/
+ // if (nnData.norms[0] > radius_destPast) {
+ // radius_destPast = nnData.norms[0];
+ // }
+ // if (nnData.norms[1] > radius_destNext) {
+ // radius_destNext = nnData.norms[1];
+ // radius_destNext_sampleIndex = nnData.sampleIndex;
+ // }
+ // if(nnData.distance > radius_max) {
+ // radius_max = nnData.distance;
+ // }
+ // }
//radius_max = Math.max(radius_destPast, radius_destNext);
@@ -834,8 +834,8 @@ public class TransferEntropyCalculatorSpikingIntegrationWindows2 implements
double rawRateGivenSourceAndDest = (double) Knns / timeInWindowWithMatchingJointHistories;
double rawRateGivenDest = (double) countOfDestNextMatched / timeInWindowWithMatchingDestHistory;
// Attempt at bias correction:
- double logRateGivenSourceAndDestCorrected = -Math.log(timeInWindowWithMatchingJointHistories);
- double logRateGivenDestCorrected = -Math.log(timeInWindowWithMatchingDestHistory);
+ double logRateGivenSourceAndDestCorrected = MathsUtils.digamma(Knns) - Math.log(timeInWindowWithMatchingJointHistories);
+ double logRateGivenDestCorrected = MathsUtils.digamma(countOfDestNextMatched) - Math.log(timeInWindowWithMatchingDestHistory);
//System.out.println(logRateGivenSourceAndDestCorrected + " " + logRateGivenDestCorrected);
//============================
diff --git a/java/source/infodynamics/utils/KdTree.java b/java/source/infodynamics/utils/KdTree.java
old mode 100755
new mode 100644
index 1076af7..a8c8f09
--- a/java/source/infodynamics/utils/KdTree.java
+++ b/java/source/infodynamics/utils/KdTree.java
@@ -275,10 +275,10 @@ public class KdTree extends NearestNeighbourSearcher {
// Could remove these since the code is now functional,
// but may be better to leave them in just in case the code breaks:
if (leftIndex > leftStart + leftNumPoints) {
- throw new RuntimeException("Exceeded expected number of points on left");
+ throw new RuntimeException("Exceeded expected number of points on left - likely had an NaN in the data");
}
if (rightIndex > rightStart + rightNumPoints) {
- throw new RuntimeException("Exceeded expected number of points on right");
+ throw new RuntimeException("Exceeded expected number of points on right - likely had an NaN in the data");
}
// Update the pointer for the sorted indices for this dimension,
// and keep the new temporary array
@@ -1384,19 +1384,19 @@ public class KdTree extends NearestNeighbourSearcher {
}
@Override
- public void findPointsWithinR(int sampleIndex,
+ public int findPointsWithinR(int sampleIndex,
double r, boolean allowEqualToR, boolean[] isWithinR,
int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
- return;
+ return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinR(sampleIndex,
rootNode, 0, r, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
- return;
+ return currentIndexInIndicesWithinR;
}
/**
@@ -2510,20 +2510,21 @@ public class KdTree extends NearestNeighbourSearcher {
* @param allowEqualToR if true, then count points at radius r also, otherwise only those strictly within r
* @param isWithinR the array MUST be passed in with all points set to false initially, and is returned indicating whether each sample was found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices for points marked as true in isWithinR, terminated with a -1 value.
+ * @return the number of points found to match
*/
- public void findPointsWithinRs(int sampleIndex,
+ public int findPointsWithinRs(int sampleIndex,
double[] rs, int dynCorrExclTime, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR) {
if (rootNode == null) {
indicesWithinR[0] = -1;
- return;
+ return 0;
}
int currentIndexInIndicesWithinR =
findPointsWithinRs(sampleIndex,
rootNode, 0, rs, dynCorrExclTime, allowEqualToR, isWithinR,
indicesWithinR, 0);
indicesWithinR[currentIndexInIndicesWithinR] = -1;
- return;
+ return currentIndexInIndicesWithinR;
}
/**
diff --git a/java/source/infodynamics/utils/NearestNeighbourSearcher.java b/java/source/infodynamics/utils/NearestNeighbourSearcher.java
old mode 100755
new mode 100644
index 5f66dcb..3f2c90a
--- a/java/source/infodynamics/utils/NearestNeighbourSearcher.java
+++ b/java/source/infodynamics/utils/NearestNeighbourSearcher.java
@@ -326,8 +326,9 @@ public abstract class NearestNeighbourSearcher {
* found to be within r of that at sampleIndex.
* @param indicesWithinR a list of array indices
* for points marked as true in isWithinR, terminated with a -1 value.
+ * @return the number of matching points found
*/
- public abstract void findPointsWithinR(
+ public abstract int findPointsWithinR(
int sampleIndex, double r,
boolean allowEqualToR, boolean[] isWithinR, int[] indicesWithinR);
diff --git a/java/source/infodynamics/utils/UnivariateNearestNeighbourSearcher.java b/java/source/infodynamics/utils/UnivariateNearestNeighbourSearcher.java
index 38b0731..a06c456 100644
--- a/java/source/infodynamics/utils/UnivariateNearestNeighbourSearcher.java
+++ b/java/source/infodynamics/utils/UnivariateNearestNeighbourSearcher.java
@@ -685,7 +685,7 @@ public class UnivariateNearestNeighbourSearcher extends NearestNeighbourSearcher
*
*
*/
- public void findPointsWithinR(
+ public int findPointsWithinR(
int sampleIndex, double r, boolean allowEqualToR,
boolean[] isWithinR, int[] indicesWithinR) {
int indexInIndicesWithinR = 0;
@@ -718,7 +718,8 @@ public class UnivariateNearestNeighbourSearcher extends NearestNeighbourSearcher
break;
}
// Write the terminating integer into the indicesWithinR array:
- indicesWithinR[indexInIndicesWithinR++] = -1;
+ indicesWithinR[indexInIndicesWithinR] = -1;
+ return indexInIndicesWithinR;
}
/**
diff --git a/java/unittests/infodynamics/measures/continuous/kraskov/GPUPerformanceTester.java b/java/unittests/infodynamics/measures/continuous/kraskov/GPUPerformanceTester.java
index 6be9705..8436052 100644
--- a/java/unittests/infodynamics/measures/continuous/kraskov/GPUPerformanceTester.java
+++ b/java/unittests/infodynamics/measures/continuous/kraskov/GPUPerformanceTester.java
@@ -86,14 +86,14 @@ public class GPUPerformanceTester extends TestCase {
startTime = Calendar.getInstance().getTimeInMillis();
miCalc.initialise(1,1);
miCalc.setObservations(source, dest);
- cpu_val = miCalc.computeSignificance(nb_surrogates).getMeanOfDistribution();
+ cpu_val = miCalc.computeSignificance(nb_surrogates).actualValue;
cpu_duration = Calendar.getInstance().getTimeInMillis() - startTime;
miCalc.setProperty("USE_GPU", "true");
miCalc.initialise(1,1);
startTime = Calendar.getInstance().getTimeInMillis();
miCalc.setObservations(source, dest);
- gpu_val = miCalc.computeSignificance(nb_surrogates).getMeanOfDistribution();
+ gpu_val = miCalc.computeSignificance(nb_surrogates).actualValue;
gpu_duration = Calendar.getInstance().getTimeInMillis() - startTime;
assertEquals(cpu_val, gpu_val, 0.0001);
@@ -124,12 +124,12 @@ public class GPUPerformanceTester extends TestCase {
return;
}
- int timeSteps = 10000;
+ int timeSteps = 1000;
RandomGenerator rg = new RandomGenerator();
double[] source = rg.generateNormalData(timeSteps, 0, 1);
double[] dest = rg.generateNormalData(timeSteps, 0, 1);
- compareGPUPerformance(source, dest, "Random low-dimensional data");
+ compareGPUPerformanceSurrogates(source, dest, 150, "Random low-dimensional data");
return;
}
diff --git a/readme-template.txt b/readme-template.txt
index 44351a6..1a11192 100755
--- a/readme-template.txt
+++ b/readme-template.txt
@@ -134,6 +134,18 @@ Notices for this software are found in the notices/JAMA directory.
Release notes
===============
+v1.5 26/11/2018
+---------------
+(after 753 commits recorded by github, repository as at https://github.com/jlizier/jidt/tree/603445651cc0bf155a42c9ba336141bc7f29bccd save for this file update)
+Added GPU (cuda) capability for KSG Conditional Mutual Information calculator (proper documentation to come), including unit tests and brief wiki page;
+Added auto-embedding for TE/AIS with multivariate KSG, and univariate and multivariate Gaussian estimator (plus unit tests), for Ragwitz criteria and Maximum bias-corrected AIS, and also added Maximum bias corrected AIS and TE to handle source embedding as well;
+Kozachenko entropy estimator adds noise to data by default;
+Added bias-correction property to Gaussian and Kernel estimators for MI and conditional MI, including with surrogates (only option for kernel);
+Enabled use of different bases for different variables in MI discrete estimator;
+All new above features enabled in AutoAnalyser;
+Added drop-down menus for parameters in AutoAnalyser;
+Included long-form lecture slides in course folder;
+
v1.4 26/11/2017
---------------
(after 638 commits recorded by github, repository as at https://github.com/jlizier/jidt/tree/589d51674e6a9cfb569432679e515bea17092876 save for this file update)
diff --git a/tester.py b/tester.py
new file mode 100755
index 0000000..bd8265b
--- /dev/null
+++ b/tester.py
@@ -0,0 +1,186 @@
+##
+## Java Information Dynamics Toolkit (JIDT)
+## Copyright (C) 2012, 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 .
+##
+
+# = Example 4 - Transfer entropy on continuous data using Kraskov estimators =
+
+# Simple transfer entropy (TE) calculation on continuous-valued data using the Kraskov-estimator TE calculator.
+
+from jpype import *
+import random
+import math
+import os
+import numpy as np
+
+
+NUM_REPS = 2
+NUM_SPIKES = int(2e3)
+NUM_OBSERVATIONS = 2
+NUM_SURROGATES = 10
+
+# Params for canonical example generation
+RATE_Y = 1.0
+RATE_X_MAX = 10
+
+def generate_canonical_example_processes(num_y_events):
+ event_train_x = []
+ event_train_x.append(0)
+
+ event_train_y = np.random.uniform(0, int(num_y_events / RATE_Y), int(num_y_events))
+ event_train_y.sort()
+
+ most_recent_y_index = 0
+ previous_x_candidate = 0
+ while most_recent_y_index < (len(event_train_y) - 1):
+
+ this_x_candidate = previous_x_candidate + random.expovariate(RATE_X_MAX)
+
+ while most_recent_y_index < (len(event_train_y) - 1) and this_x_candidate > event_train_y[most_recent_y_index + 1]:
+ most_recent_y_index += 1
+
+ delta_t = this_x_candidate - event_train_y[most_recent_y_index]
+
+ rate = 0
+
+ if delta_t > 1:
+ rate = 0.5
+ else:
+ rate = 0.5 + 5.0 * math.exp(-50 * (delta_t - 0.5)**2) - 5.0 * math.exp(-50 * (0.5)**2)
+ if random.random() < rate/float(RATE_X_MAX):
+ event_train_x.append(this_x_candidate)
+ previous_x_candidate = this_x_candidate
+
+ event_train_x.sort()
+ event_train_y.sort()
+
+ return event_train_x, event_train_y
+
+# Change location of jar to match yours (we assume script is called from demos/python):
+jarLocation = os.path.join(os.getcwd(), "infodynamics.jar");
+if (not(os.path.isfile(jarLocation))):
+ exit("infodynamics.jar not found (expected at " + os.path.abspath(jarLocation) + ") - are you running from demos/python?")
+# Start the JVM (add the "-Xmx" option with say 1024M if you get crashes due to not enough memory space)
+startJVM(getDefaultJVMPath(), "-ea", "-Djava.class.path=" + jarLocation)
+teCalcClass = JPackage("infodynamics.measures.spiking.integration").TransferEntropyCalculatorSpikingIntegration
+
+
+teCalc = teCalcClass()
+teCalc.setProperty("knns", "4")
+print("Independent Poisson Processes")
+teCalc.setProperty("DEST_PAST_INTERVALS", "1,2")
+teCalc.setProperty("SOURCE_PAST_INTERVALS", "1,2")
+teCalc.appendConditionalIntervals(JArray(JInt, 1)([1, 2]))
+teCalc.appendConditionalIntervals(JArray(JInt, 1)([1, 2]))
+teCalc.setProperty("NORM_TYPE", "MAX_NORM")
+
+results_poisson = np.zeros(NUM_REPS)
+for i in range(NUM_REPS):
+ teCalc.startAddObservations()
+ for j in range(NUM_OBSERVATIONS):
+ sourceArray = NUM_SPIKES*np.random.random(NUM_SPIKES)
+ sourceArray.sort()
+ destArray = NUM_SPIKES*np.random.random(NUM_SPIKES)
+ destArray.sort()
+ condArray = NUM_SPIKES*np.random.random((2, NUM_SPIKES))
+ condArray.sort(axis = 1)
+ teCalc.addObservations(JArray(JDouble, 1)(sourceArray), JArray(JDouble, 1)(destArray), JArray(JDouble, 2)(condArray))
+ teCalc.finaliseAddObservations();
+ result = teCalc.computeAverageLocalOfObservations()
+ print("TE result %.4f nats" % (result,))
+ sig = teCalc.computeSignificance(NUM_SURROGATES, result)
+ print(sig.pValue)
+ results_poisson[i] = result
+print("Summary: mean ", np.mean(results_poisson), " std dev ", np.std(results_poisson))
+
+teCalc = teCalcClass()
+teCalc.setProperty("knns", "4")
+print("Noisy copy zero TE")
+#teCalc.appendConditionalIntervals(JArray(JInt, 1)([1]))
+teCalc.setProperty("DEST_PAST_INTERVALS", "1")
+teCalc.setProperty("SOURCE_PAST_INTERVALS", "1")
+#teCalc.setProperty("NORM_TYPE", "MAX_NORM")
+
+results_noisy_zero = np.zeros(NUM_REPS)
+for i in range(NUM_REPS):
+ teCalc.startAddObservations()
+ for j in range(NUM_OBSERVATIONS):
+ condArray = np.ones((1, NUM_SPIKES)) + 0.05 * np.random.random((1, NUM_SPIKES))
+ condArray = np.cumsum(condArray, axis = 1)
+ condArray.sort(axis = 1)
+ sourceArray = condArray[0, :] + 0.25 + 0.05 * np.random.normal(size = condArray.shape[1])
+ sourceArray.sort()
+ destArray = condArray[0, :] + 0.5 + 0.05 * np.random.normal(size = condArray.shape[1])
+ destArray.sort()
+ #teCalc.addObservations(JArray(JDouble, 1)(sourceArray), JArray(JDouble, 1)(destArray), JArray(JDouble, 2)(condArray))
+ teCalc.addObservations(JArray(JDouble, 1)(sourceArray), JArray(JDouble, 1)(destArray))
+ teCalc.finaliseAddObservations();
+ result = teCalc.computeAverageLocalOfObservations()
+ print("TE result %.4f nats" % (result,))
+ sig = teCalc.computeSignificance(NUM_SURROGATES, result)
+ print(sig.pValue)
+ results_noisy_zero[i] = result
+print("Summary: mean ", np.mean(results_noisy_zero), " std dev ", np.std(results_noisy_zero))
+
+
+
+teCalc = teCalcClass()
+teCalc.setProperty("knns", "4")
+print("Noisy copy non-zero TE")
+teCalc.appendConditionalIntervals(JArray(JInt, 1)([1]))
+teCalc.setProperty("DEST_PAST_INTERVALS", "1,2")
+teCalc.setProperty("SOURCE_PAST_INTERVALS", "1")
+#teCalc.setProperty("NORM_TYPE", "MAX_NORM")
+
+results_noisy_non_zero = np.zeros(NUM_REPS)
+for i in range(NUM_REPS):
+ teCalc.startAddObservations()
+ for j in range(NUM_OBSERVATIONS):
+ sourceArray = np.ones((1, NUM_SPIKES)) + 0.05 * np.random.random((1, NUM_SPIKES))
+ sourceArray = np.cumsum(sourceArray)
+ sourceArray.sort()
+ condArray = sourceArray + 0.25 + 0.05 * np.random.normal(size = sourceArray.shape)
+ condArray.sort()
+ condArray = np.expand_dims(condArray, 0)
+ destArray = sourceArray + 0.5 + 0.05 * np.random.normal(size = sourceArray.shape)
+ destArray.sort()
+ teCalc.addObservations(JArray(JDouble, 1)(sourceArray), JArray(JDouble, 1)(destArray), JArray(JDouble, 2)(condArray))
+ teCalc.finaliseAddObservations();
+ result = teCalc.computeAverageLocalOfObservations()
+ print("TE result %.4f nats" % (result,))
+ sig = teCalc.computeSignificance(NUM_SURROGATES, result)
+ print(sig.pValue)
+ results_noisy_non_zero[i] = result
+print("Summary: mean ", np.mean(results_noisy_non_zero), " std dev ", np.std(results_noisy_zero))
+
+print("Canonical example")
+teCalc = teCalcClass()
+teCalc.setProperty("knns", "4")
+teCalc.setProperty("DEST_PAST_INTERVALS", "1,2")
+teCalc.setProperty("SOURCE_PAST_INTERVALS", "1")
+#teCalc.setProperty("NUM_SAMPLES_MULTIPLIER", "1")
+#teCalc.setProperty("NORM_TYPE", "MAX_NORM")
+
+results_canonical = np.zeros(NUM_REPS)
+for i in range(NUM_REPS):
+ event_train_x, event_train_y = generate_canonical_example_processes(NUM_SPIKES)
+ teCalc.setObservations(JArray(JDouble, 1)(event_train_y), JArray(JDouble, 1)(event_train_x))
+ result = teCalc.computeAverageLocalOfObservations()
+ results_canonical[i] = result
+ print("TE result %.4f nats" % (result,))
+ sig = teCalc.computeSignificance(NUM_SURROGATES, result)
+ print(sig.pValue)
+print("Summary: mean ", np.mean(results_canonical), " std dev ", np.std(results_canonical))
diff --git a/tutorial/README-TutorialAndExercise.pdf b/tutorial/README-TutorialAndExercise.pdf
index 7e487b8..596baa3 100644
Binary files a/tutorial/README-TutorialAndExercise.pdf and b/tutorial/README-TutorialAndExercise.pdf differ