From 75bb2c814ae65db2952efdc62bb3c6052593d64d Mon Sep 17 00:00:00 2001 From: Pedro Martinez Mediano Date: Sun, 25 Feb 2018 18:32:14 +0000 Subject: [PATCH] Added addObservations-related methods to multivariate TE calculators. This is needed to implement auto-embedding methods later. --- ...lculatorMultiVariateViaCondMutualInfo.java | 171 ++++++++++++++---- 1 file changed, 133 insertions(+), 38 deletions(-) diff --git a/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorMultiVariateViaCondMutualInfo.java b/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorMultiVariateViaCondMutualInfo.java index 7d4e654..5356b75 100755 --- a/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorMultiVariateViaCondMutualInfo.java +++ b/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorMultiVariateViaCondMutualInfo.java @@ -18,10 +18,11 @@ package infodynamics.measures.continuous; -import java.util.Vector; - import infodynamics.utils.MatrixUtils; +import java.util.Iterator; +import java.util.Vector; + /** * A Multivariate Transfer Entropy (TE) calculator (implementing * {@link TransferEntropyCalculatorMultiVariate}) @@ -85,6 +86,15 @@ public class TransferEntropyCalculatorMultiVariateViaCondMutualInfo * Number of dimensions of the source */ protected int sourceDimensions = 1; + /** + * Storage for source observations supplied via {@link #addObservations(double[][], double[][])} etc. + */ + protected Vector vectorOfMultiVariateSourceTimeSeries; + + /** + * Storage for destination observations supplied via {@link #addObservations(double[][], double[][])} etc. + */ + protected Vector vectorOfMultiVariateDestinationTimeSeries; /** * Construct a transfer entropy calculator using an instance of @@ -239,7 +249,10 @@ public class TransferEntropyCalculatorMultiVariateViaCondMutualInfo return; } // Otherwise initialise ourselves: - condMiCalc.startAddObservations(); + vectorOfMultiVariateSourceTimeSeries = new Vector(); + vectorOfMultiVariateDestinationTimeSeries = new Vector(); + vectorOfValidityOfSource = new Vector(); + vectorOfValidityOfDestination = new Vector(); } /** @@ -263,43 +276,92 @@ public class TransferEntropyCalculatorMultiVariateViaCondMutualInfo } super.addObservations(source, destination); } - + /* (non-Javadoc) * @see infodynamics.measures.continuous.ChannelCalculatorMultiVariate#addObservations(double[][], double[][]) */ - @Override - public void addObservations(double[][] source, double[][] destination) - throws Exception { - if (source.length != destination.length) { - throw new Exception(String.format("Source and destination lengths (%d and %d) must match!", - source.length, destination.length)); - } - if ((sourceDimensions == 1) && (destDimensions == 1)) { - // We'll be using the superclass for the computation - super.addObservations(MatrixUtils.selectColumn(source, 0), - MatrixUtils.selectColumn(destination, 0)); - return; - } - if (source.length < startTimeForFirstDestEmbedding + 2) { - // There are no observations to add here, the time series is too short - // Don't throw an exception, do nothing since more observations - // can be added later. - return; - } - double[][] currentDestPastVectors = - MatrixUtils.makeDelayEmbeddingVector(destination, k, k_tau, - startTimeForFirstDestEmbedding, - destination.length - startTimeForFirstDestEmbedding - 1); - double[][] currentDestNextVectors = - MatrixUtils.makeDelayEmbeddingVector(destination, 1, - startTimeForFirstDestEmbedding + 1, - destination.length - startTimeForFirstDestEmbedding - 1); - double[][] currentSourcePastVectors = - MatrixUtils.makeDelayEmbeddingVector(source, l, l_tau, - startTimeForFirstDestEmbedding + 1 - delay, - source.length - startTimeForFirstDestEmbedding - 1); - condMiCalc.addObservations(currentSourcePastVectors, currentDestNextVectors, currentDestPastVectors); - } + @Override + public void addObservations(double[][] source, double[][] destination) + throws Exception { + // Store these observations in our vector for now + vectorOfMultiVariateSourceTimeSeries.add(source); + vectorOfMultiVariateDestinationTimeSeries.add(destination); + vectorOfValidityOfSource.add(null); // All observations were valid + vectorOfValidityOfDestination.add(null); // All observations were valid + } + + /** + * Protected method to internally parse and submit observations through + * to the underlying conditional MI calculator once any internal parameter settings + * have been finalised (in the case of automatically determining the embedding + * parameters) + * + * @param source time series of source observations + * @param destination time series of destination observations + * @return the number of observations added + * @throws Exception + */ + protected int addObservationsAfterParamsDetermined(double[][] source, double[][] destination) throws Exception { + if (source.length != destination.length) { + throw new Exception(String.format("Source and destination lengths (%d and %d) must match!", + source.length, destination.length)); + } + if (source.length < startTimeForFirstDestEmbedding + 2) { + // There are no observations to add here, the time series is too short + // Don't throw an exception, do nothing since more observations + // can be added later. + return 0; + } + double[][] currentDestPastVectors = + MatrixUtils.makeDelayEmbeddingVector(destination, k, k_tau, + startTimeForFirstDestEmbedding, + destination.length - startTimeForFirstDestEmbedding - 1); + double[][] currentDestNextVectors = + MatrixUtils.makeDelayEmbeddingVector(destination, 1, + startTimeForFirstDestEmbedding + 1, + destination.length - startTimeForFirstDestEmbedding - 1); + double[][] currentSourcePastVectors = + MatrixUtils.makeDelayEmbeddingVector(source, l, l_tau, + startTimeForFirstDestEmbedding + 1 - delay, + source.length - startTimeForFirstDestEmbedding - 1); + condMiCalc.addObservations(currentSourcePastVectors, currentDestNextVectors, currentDestPastVectors); + return destination.length - startTimeForFirstDestEmbedding - 1; + } + + /** + * Protected method to internally parse and submit observations through + * to the underlying conditional MI calculator once any internal parameter settings + * have been finalised (in the case of automatically determining the embedding + * parameters) + * This is done given time-series of booleans indicating whether each entry + * is valid + * + * @param source time series of source observations + * @param destination time series of destination observations + * @param sourceValid array (with indices the same as source) indicating whether + * the source at that index is valid. + * @param destValid array (with indices the same as destination) indicating whether + * the destination at that index is valid. + * @return total number of observations added + * @throws Exception + */ + protected int addObservationsAfterParamsDetermined(double[][] source, double[][] destination, + boolean[] sourceValid, boolean[] destValid) throws Exception { + + // Compute the start and end time pairs using our embedding parameters: + Vector startAndEndTimePairs = computeStartAndEndTimePairs(sourceValid, destValid); + + int totalObservationsAdded = 0; + for (int[] timePair : startAndEndTimePairs) { + int startTime = timePair[0]; + int endTime = timePair[1]; + totalObservationsAdded += addObservationsAfterParamsDetermined( + MatrixUtils.selectRows(source, startTime, endTime - startTime + 1), + MatrixUtils.selectRows(destination, startTime, endTime - startTime + 1)); + } + return totalObservationsAdded; + } + /** *

Adds a new sub-series of univariate observations to compute the PDFs from. @@ -384,7 +446,7 @@ public class TransferEntropyCalculatorMultiVariateViaCondMutualInfo sourceValid, destValid); return; } - + Vector startAndEndTimePairs = computeStartAndEndTimePairs(sourceValid, destValid); // We've found the set of start and end times for this pair @@ -425,6 +487,39 @@ public class TransferEntropyCalculatorMultiVariateViaCondMutualInfo return; } // Otherwise finalise ourselves: + + // Auto embed if required + preFinaliseAddObservations(); + + // Initialise the conditional MI calculator, including any auto-embedding length + condMiCalc.initialise(l*sourceDimensions, destDimensions, k*destDimensions); + condMiCalc.startAddObservations(); + // Send all of the observations through: + Iterator destIterator = vectorOfMultiVariateDestinationTimeSeries.iterator(); + Iterator sourceValidityIterator = vectorOfValidityOfSource.iterator(); + Iterator destValidityIterator = vectorOfValidityOfDestination.iterator(); + separateNumObservations = new int[vectorOfMultiVariateDestinationTimeSeries.size()]; + int setNum = 0; + for (double[][] source : vectorOfMultiVariateSourceTimeSeries) { + double[][] destination = destIterator.next(); + boolean[] sourceValidity = sourceValidityIterator.next(); + boolean[] destValidity = destValidityIterator.next(); + int observationsAddedThisTime = 0; + if (sourceValidity == null) { + // Add the whole time-series + observationsAddedThisTime = addObservationsAfterParamsDetermined(source, destination); + } else { + observationsAddedThisTime = addObservationsAfterParamsDetermined(source, destination, + sourceValidity, destValidity); + } + separateNumObservations[setNum++] = observationsAddedThisTime; + } + vectorOfMultiVariateSourceTimeSeries = null; // No longer required + vectorOfMultiVariateDestinationTimeSeries = null; // No longer required + vectorOfValidityOfSource = null; + vectorOfValidityOfDestination = null; + + // TODO do we need to throw an exception if there are no observations to add? condMiCalc.finaliseAddObservations(); }