diff --git a/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculatorViaMutualInfo.java b/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculatorViaMutualInfo.java index 03cf050..5fe813a 100755 --- a/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculatorViaMutualInfo.java +++ b/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculatorViaMutualInfo.java @@ -18,6 +18,7 @@ package infodynamics.measures.continuous; +import java.util.Iterator; import java.util.Vector; import infodynamics.utils.EmpiricalMeasurementDistribution; @@ -79,10 +80,15 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements protected boolean debug = false; /** - * Storage for source observations supplied via {@link #addObservations(double[])} + * Storage for observations supplied via {@link #addObservations(double[])} * type calls */ protected Vector vectorOfObservationTimeSeries; + /** + * Storage for validity arrays for supplied observations. + * Entries are null where the whole corresponding observation time-series is valid + */ + protected Vector vectorOfValidityOfObservations; /** * Construct using an instantiation of the named MI calculator @@ -164,6 +170,8 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements public void initialise(int k, int tau) throws Exception { this.k = k; this.tau = tau; + vectorOfObservationTimeSeries = null; + vectorOfValidityOfObservations = null; } /** @@ -248,6 +256,7 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements @Override public void startAddObservations() { vectorOfObservationTimeSeries = new Vector(); + vectorOfValidityOfObservations = new Vector(); } /* (non-Javadoc) @@ -257,6 +266,7 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements public void addObservations(double[] observations) throws Exception { // Store these observations in our vector for now vectorOfObservationTimeSeries.add(observations); + vectorOfValidityOfObservations.add(null); // All observations were valid } /** @@ -282,6 +292,33 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements miCalc.addObservations(currentDestPastVectors, currentDestNextVectors); } + /** + * Protected method to internally parse and submit observations through + * to the underlying MI calculator once any internal parameter settings + * have been finalised (in the case of automatically determining the embedding + * parameters). + * This is done given a time-series of booleans indicating whether each entry + * is valid + * + * @param observations time series of observations + * @param valid a time series (with indices the same as observations) indicating + * whether the entry in observations at that index is valid; we only take vectors + * as samples to add to the observation set where all points in the time series + * (even between points in the embedded k-vector with embedding delays) are valid. + * @throws Exception + */ + protected void addObservationsAfterParamsDetermined(double[] observations, boolean[] valid) throws Exception { + + // compute the start and end times using our determined embedding parameters: + Vector startAndEndTimePairs = computeStartAndEndTimePairs(valid); + + for (int[] timePair : startAndEndTimePairs) { + int startTime = timePair[0]; + int endTime = timePair[1]; + addObservationsAfterParamsDetermined(MatrixUtils.select(observations, startTime, endTime - startTime + 1)); + } + } + /* (non-Javadoc) * @see infodynamics.measures.continuous.ActiveInfoStorageCalculator#addObservations(double[], int, int) */ @@ -300,7 +337,7 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements * Child implementations do not need to override this default empty implementation * if no new functionality is required. */ - public void preFinaliseAddObservations() throws Exception { + protected void preFinaliseAddObservations() throws Exception { // Empty implementation supplied by default. } @@ -316,10 +353,18 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements miCalc.initialise(k, 1); miCalc.startAddObservations(); // Send all of the observations through: + Iterator validityIterator = vectorOfValidityOfObservations.iterator(); for (double[] observations : vectorOfObservationTimeSeries) { - addObservationsAfterParamsDetermined(observations); + boolean[] validity = validityIterator.next(); + if (validity == null) { + // Add the whole time-series + addObservationsAfterParamsDetermined(observations); + } else { + addObservationsAfterParamsDetermined(observations, validity); + } } vectorOfObservationTimeSeries = null; // No longer required + vectorOfValidityOfObservations = null; // TODO do we need to throw an exception if there are no observations to add? miCalc.finaliseAddObservations(); @@ -331,15 +376,10 @@ public class ActiveInfoStorageCalculatorViaMutualInfo implements @Override public void setObservations(double[] observations, boolean[] valid) throws Exception { - Vector startAndEndTimePairs = computeStartAndEndTimePairs(valid); - - // We've found the set of start and end times for this pair startAddObservations(); - for (int[] timePair : startAndEndTimePairs) { - int startTime = timePair[0]; - int endTime = timePair[1]; - addObservations(observations, startTime, endTime - startTime + 1); - } + // Add these observations and the indication of their validity + vectorOfObservationTimeSeries.add(observations); + vectorOfValidityOfObservations.add(valid); finaliseAddObservations(); } diff --git a/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorViaCondMutualInfo.java b/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorViaCondMutualInfo.java index f94a0cc..f6da00c 100755 --- a/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorViaCondMutualInfo.java +++ b/java/source/infodynamics/measures/continuous/TransferEntropyCalculatorViaCondMutualInfo.java @@ -21,6 +21,7 @@ package infodynamics.measures.continuous; import infodynamics.utils.EmpiricalMeasurementDistribution; import infodynamics.utils.MatrixUtils; +import java.util.Iterator; import java.util.Vector; /** @@ -102,6 +103,28 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements */ protected boolean debug = false; + /** + * Storage for source observations supplied via {@link #addObservations(double[], double[])} etc. + */ + protected Vector vectorOfSourceTimeSeries; + + /** + * Storage for destination observations supplied via {@link #addObservations(double[], double[])} etc. + */ + protected Vector vectorOfDestinationTimeSeries; + + /** + * Storage for validity arrays for supplied source observations. + * Entries are null where the whole corresponding observation time-series is valid + */ + protected Vector vectorOfValidityOfSource; + + /** + * Storage for validity arrays for supplied destination observations. + * Entries are null where the whole corresponding observation time-series is valid + */ + protected Vector vectorOfValidityOfDestination; + /** * Construct a transfer entropy calculator using an instance of * condMiCalculatorClassName as the underlying conditional mutual information calculator. @@ -199,19 +222,30 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements this.l_tau = l_tau; this.delay = delay; - // Now check which point we can start taking observations from in any - // addObservations call. These two integers represent the last - // point of the destination embedding, in the cases where the destination - // embedding itself determines where we can start taking observations, or - // the case where the source embedding plus delay is longer and so determines - // where we can start taking observations. + setStartTimeForFirstDestEmbedding(); + + vectorOfSourceTimeSeries = null; + vectorOfDestinationTimeSeries = null; + vectorOfValidityOfSource = null; + vectorOfValidityOfDestination = null; + } + + /** + * Protected internal method to + * set the point at which we can start taking observations from in any + * addObservations call. + */ + protected void setStartTimeForFirstDestEmbedding() { + // These two integers represent the last + // point of the destination embedding, in the cases where the destination + // embedding itself determines where we can start taking observations, or + // the case where the source embedding plus delay is longer and so determines + // where we can start taking observations. int startTimeBasedOnDestPast = (k-1)*k_tau; int startTimeBasedOnSourcePast = (l-1)*l_tau + delay - 1; startTimeForFirstDestEmbedding = Math.max(startTimeBasedOnDestPast, startTimeBasedOnSourcePast); - - condMiCalc.initialise(l, 1, k); } - + /** * Sets properties for the TE calculator. * New property values are not guaranteed to take effect until the next call @@ -277,37 +311,40 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements @Override public void setObservations(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 - throw new Exception("Not enough observations to set here given k, k_tau, l, l_tau and delay parameters"); - } - 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.setObservations(currentSourcePastVectors, currentDestNextVectors, currentDestPastVectors); + startAddObservations(); + addObservations(source, destination); + finaliseAddObservations(); } @Override public void startAddObservations() { - condMiCalc.startAddObservations(); + vectorOfSourceTimeSeries = new Vector(); + vectorOfDestinationTimeSeries = new Vector(); + vectorOfValidityOfSource = new Vector(); + vectorOfValidityOfDestination = new Vector(); } @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 + vectorOfSourceTimeSeries.add(source); + vectorOfDestinationTimeSeries.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 + * @throws Exception + */ + protected void 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)); @@ -333,6 +370,37 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements condMiCalc.addObservations(currentSourcePastVectors, currentDestNextVectors, currentDestPastVectors); } + /** + * 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. + * @throws Exception + */ + protected void 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); + + for (int[] timePair : startAndEndTimePairs) { + int startTime = timePair[0]; + int endTime = timePair[1]; + addObservationsAfterParamsDetermined( + MatrixUtils.select(source, startTime, endTime - startTime + 1), + MatrixUtils.select(destination, startTime, endTime - startTime + 1)); + } + } + @Override public void addObservations(double[] source, double[] destination, int startTime, int numTimeSteps) throws Exception { @@ -348,8 +416,49 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements MatrixUtils.select(destination, startTime, numTimeSteps)); } + /** + * Hook in case child implementations need to perform any processing on the + * observation time series prior to their being processed and supplied + * to the underlying MI calculator. + * Primarily this is to allow the child implementation to automatically determine + * embedding parameters if desired. + * Child implementations do not need to override this default empty implementation + * if no new functionality is required. + */ + protected void preFinaliseAddObservations() throws Exception { + // Empty implementation supplied by default. + } + @Override public void finaliseAddObservations() throws Exception { + // Auto embed if required + preFinaliseAddObservations(); + + // Initialise the conditional MI calculator, including any auto-embedding length + condMiCalc.initialise(l, 1, k); + condMiCalc.startAddObservations(); + // Send all of the observations through: + Iterator destIterator = vectorOfDestinationTimeSeries.iterator(); + Iterator sourceValidityIterator = vectorOfValidityOfSource.iterator(); + Iterator destValidityIterator = vectorOfValidityOfDestination.iterator(); + for (double[] source : vectorOfSourceTimeSeries) { + double[] destination = destIterator.next(); + boolean[] sourceValidity = sourceValidityIterator.next(); + boolean[] destValidity = destValidityIterator.next(); + if (sourceValidity == null) { + // Add the whole time-series + addObservationsAfterParamsDetermined(source, destination); + } else { + addObservationsAfterParamsDetermined(source, destination, + sourceValidity, destValidity); + } + } + vectorOfSourceTimeSeries = null; // No longer required + vectorOfDestinationTimeSeries = 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(); } @@ -357,15 +466,13 @@ public class TransferEntropyCalculatorViaCondMutualInfo implements public void setObservations(double[] source, double[] destination, boolean[] sourceValid, boolean[] destValid) throws Exception { - Vector startAndEndTimePairs = computeStartAndEndTimePairs(sourceValid, destValid); - - // We've found the set of start and end times for this pair startAddObservations(); - for (int[] timePair : startAndEndTimePairs) { - int startTime = timePair[0]; - int endTime = timePair[1]; - addObservations(source, destination, startTime, endTime - startTime + 1); - } + // Add these observations and the indication of their validity + // for later analysis: + vectorOfSourceTimeSeries.add(source); + vectorOfDestinationTimeSeries.add(destination); + vectorOfValidityOfSource.add(sourceValid); + vectorOfValidityOfDestination.add(destValid); finaliseAddObservations(); } diff --git a/java/source/infodynamics/measures/continuous/kraskov/ActiveInfoStorageCalculatorKraskov.java b/java/source/infodynamics/measures/continuous/kraskov/ActiveInfoStorageCalculatorKraskov.java index 22edcdf..163f845 100755 --- a/java/source/infodynamics/measures/continuous/kraskov/ActiveInfoStorageCalculatorKraskov.java +++ b/java/source/infodynamics/measures/continuous/kraskov/ActiveInfoStorageCalculatorKraskov.java @@ -202,7 +202,10 @@ public class ActiveInfoStorageCalculatorKraskov * and embedding delay ({@link #TAU_PROP_NAME}). Default is {@link #AUTO_EMBED_METHOD_NONE} meaning * values are set manually; other accepted values include: {@link #AUTO_EMBED_METHOD_RAGWITZ} for use * of the Ragwitz criteria (searching up to {@link #PROP_K_SEARCH_MAX} and - * {@link #PROP_TAU_SEARCH_MAX}) + * {@link #PROP_TAU_SEARCH_MAX}). Use of any value other than {@link #AUTO_EMBED_METHOD_NONE} + * will lead to any previous settings for k and tau (via e.g. {@link #initialise(int, int)} or + * auto-embedding during previous calculations) will be overwritten after observations + * are supplied. *
  • {@link #PROP_K_SEARCH_MAX} -- maximum embedded history length to search * up to if automatically determining the embedding parameters (as set by * {@link #PROP_AUTO_EMBED_METHOD}); default is 1
  • diff --git a/java/source/infodynamics/measures/continuous/kraskov/TransferEntropyCalculatorKraskov.java b/java/source/infodynamics/measures/continuous/kraskov/TransferEntropyCalculatorKraskov.java index 467f817..4de878c 100755 --- a/java/source/infodynamics/measures/continuous/kraskov/TransferEntropyCalculatorKraskov.java +++ b/java/source/infodynamics/measures/continuous/kraskov/TransferEntropyCalculatorKraskov.java @@ -20,6 +20,7 @@ package infodynamics.measures.continuous.kraskov; import java.util.Hashtable; +import infodynamics.measures.continuous.ActiveInfoStorageCalculator; import infodynamics.measures.continuous.ConditionalMutualInfoCalculatorMultiVariate; import infodynamics.measures.continuous.TransferEntropyCalculator; import infodynamics.measures.continuous.TransferEntropyCalculatorViaCondMutualInfo; @@ -49,7 +50,12 @@ import infodynamics.measures.continuous.TransferEntropyCalculatorViaCondMutualIn * {@link ConditionalMutualInfoCalculatorMultiVariateKraskov#setProperty(String, String)} * as outlined * in {@link TransferEntropyCalculatorViaCondMutualInfo#setProperty(String, String)}); - * as well as for {@link #PROP_KRASKOV_ALG_NUM}. + * as well as for {@link #PROP_KRASKOV_ALG_NUM}. + * Embedding parameters may be automatically determined as per the Ragwitz criteria + * by setting the property {@link #PROP_AUTO_EMBED_METHOD} to {@link #AUTO_EMBED_METHOD_RAGWITZ} + * or {@link #AUTO_EMBED_METHOD_RAGWITZ_DEST_ONLY} + * (plus additional parameter settings for this). + * *
  • Computed values are in nats, not bits!
  • * *

    @@ -73,6 +79,8 @@ import infodynamics.measures.continuous.TransferEntropyCalculatorViaCondMutualIn * * "Local information transfer as a spatiotemporal filter for complex systems" * Physical Review E 77, 026110, 2008. + *
  • Ragwitz and Kantz, "Markov models from data by simple nonlinear time series + * predictors in delay embedding spaces", Physical Review E, vol 65, 056201 (2002).
  • * * * @author Joseph Lizier (email, @@ -108,6 +116,71 @@ public class TransferEntropyCalculatorKraskov */ protected Hashtable props; + /** + * Property name for the auto-embedding method. Defaults to {@link #AUTO_EMBED_METHOD_NONE} + */ + public static final String PROP_AUTO_EMBED_METHOD = "AUTO_EMBED_METHOD"; + /** + * Valid value for the property {@link #PROP_AUTO_EMBED_METHOD} indicating that + * no auto embedding should be done (i.e. to use manually supplied parameters) + */ + public static final String AUTO_EMBED_METHOD_NONE = "NONE"; + /** + * Valid value for the property {@link #PROP_AUTO_EMBED_METHOD} indicating that + * the Ragwitz optimisation technique should be used for automatic embedding + * for both source and destination time-series + */ + public static final String AUTO_EMBED_METHOD_RAGWITZ = "RAGWITZ"; + /** + * Valid value for the property {@link #PROP_AUTO_EMBED_METHOD} indicating that + * the Ragwitz optimisation technique should be used for automatic embedding + * for the destination time-series only + */ + public static final String AUTO_EMBED_METHOD_RAGWITZ_DEST_ONLY = "RAGWITZ_DEST_ONLY"; + /** + * Internal variable tracking what type of auto embedding (if any) + * we are using + */ + protected String autoEmbeddingMethod = AUTO_EMBED_METHOD_NONE; + + /** + * Property name for maximum embedding lengths (i.e. k for destination, and l for source if we're auto-embedding + * the source as well) for the auto-embedding search. Defaults to 1 + */ + public static final String PROP_K_SEARCH_MAX = "AUTO_EMBED_K_SEARCH_MAX"; + /** + * Internal variable for storing the maximum embedding length to search up to for + * automating the parameters. + */ + protected int k_search_max = 1; + + /** + * Property name for maximum embedding delay (i.e. k_tau for destination, and l_tau for source if we're auto-embedding + * the source as well) for the auto-embedding search. Defaults to 1 + */ + public static final String PROP_TAU_SEARCH_MAX = "AUTO_EMBED_TAU_SEARCH_MAX"; + /** + * Internal variable for storing the maximum embedding delay to search up to for + * automating the parameters. + */ + protected int tau_search_max = 1; + + /** + * Property name for the number of nearest neighbours to use for the auto-embedding search (Ragwitz criteria). + * Defaults to match the value in use for {@link MutualInfoCalculatorMultiVariateKraskov#PROP_K} + */ + public static final String PROP_RAGWITZ_NUM_NNS = "AUTO_EMBED_RAGWITZ_NUM_NNS"; + /** + * Internal variable for storing the number of nearest neighbours to use for the + * auto embedding search (Ragwitz criteria) + */ + protected int ragwitz_num_nns = 1; + /** + * Internal variable to track whether the property {@link #PROP_RAGWITZ_NUM_NNS} has been + * set yet + */ + protected boolean ragwitz_num_nns_set = false; + /** * Creates a new instance of the Kraskov-estimate style transfer entropy calculator * @@ -185,6 +258,29 @@ public class TransferEntropyCalculatorKraskov * values should represent, include:

    *
      *
    • {@link #PROP_KRASKOV_ALG_NUM} -- which Kraskov algorithm number to use (1 or 2).
    • + *
    • {@link #PROP_AUTO_EMBED_METHOD} -- method by which the calculator + * automatically determines the embedding history length ({@link #K_PROP_NAME}) + * and embedding delay ({@link #TAU_PROP_NAME}) for destination and potentially source. + * Default is {@link #AUTO_EMBED_METHOD_NONE} meaning + * values are set manually; other accepted values include: {@link #AUTO_EMBED_METHOD_RAGWITZ} for use + * of the Ragwitz criteria for both source and destination (searching up to {@link #PROP_K_SEARCH_MAX} and + * {@link #PROP_TAU_SEARCH_MAX}), and {@link #AUTO_EMBED_METHOD_RAGWITZ_DEST_ONLY} for use + * of the Ragwitz criteria for the destination only. + * Use of any value other than {@link #AUTO_EMBED_METHOD_NONE} + * will lead to previous settings for embedding lengths and delays (via e.g. {@link #initialise(int, int)} or + * auto-embedding during previous calculations) for the destination and perhaps source to + * be overwritten after observations are supplied.
    • + *
    • {@link #PROP_K_SEARCH_MAX} -- maximum embedded history length to search + * up to if automatically determining the embedding parameters (as set by + * {@link #PROP_AUTO_EMBED_METHOD}) for the time-series to be embedded; default is 1
    • + *
    • {@link #PROP_TAU_SEARCH_MAX} -- maximum embedded history length to search + * up to if automatically determining the embedding parameters (as set by + * {@link #PROP_AUTO_EMBED_METHOD}) for the time-series to be embedded; default is 1
    • + *
    • {@link #PROP_RAGWITZ_NUM_NNS} -- number of nearest neighbours to use + * in the auto-embedding if the property {@link #PROP_AUTO_EMBED_METHOD} + * has been set to {@link #AUTO_EMBED_METHOD_RAGWITZ} or {@link #AUTO_EMBED_METHOD_RAGWITZ_DEST_ONLY}. + * Defaults to the property value + * set for {@link ConditionalMutualInfoCalculatorMultiVariateKraskov#PROP_K}
    • *
    • Any properties accepted by {@link TransferEntropyCalculatorViaCondMutualInfo#setProperty(String, String)}
    • *
    • Or properties accepted by the underlying * {@link ConditionalMutualInfoCalculatorMultiVariateKraskov#setProperty(String, String)} implementation.
    • @@ -215,6 +311,19 @@ public class TransferEntropyCalculatorKraskov System.out.println(this.getClass().getSimpleName() + ": Set property " + propertyName + " to " + propertyValue); } + } else if (propertyName.equalsIgnoreCase(PROP_AUTO_EMBED_METHOD)) { + // New method set for determining the embedding parameters + autoEmbeddingMethod = propertyValue; + } else if (propertyName.equalsIgnoreCase(PROP_K_SEARCH_MAX)) { + // Set max embedding history length for auto determination of embedding + k_search_max = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(PROP_TAU_SEARCH_MAX)) { + // Set maximum embedding delay for auto determination of embedding + tau_search_max = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(PROP_RAGWITZ_NUM_NNS)) { + // Set the number of nearest neighbours to use in case of Ragwitz auto embedding: + ragwitz_num_nns = Integer.parseInt(propertyValue); + ragwitz_num_nns_set = true; } else { // Assume it was a property for the parent class or underlying conditional MI calculator super.setProperty(propertyName, propertyValue); @@ -226,10 +335,94 @@ public class TransferEntropyCalculatorKraskov public String getProperty(String propertyName) throws Exception { if (propertyName.equalsIgnoreCase(PROP_KRASKOV_ALG_NUM)) { return Integer.toString(kraskovAlgorithmNumber); + } else if (propertyName.equalsIgnoreCase(PROP_AUTO_EMBED_METHOD)) { + return autoEmbeddingMethod; + } else if (propertyName.equalsIgnoreCase(PROP_K_SEARCH_MAX)) { + return Integer.toString(k_search_max); + } else if (propertyName.equalsIgnoreCase(PROP_TAU_SEARCH_MAX)) { + return Integer.toString(tau_search_max); + } else if (propertyName.equalsIgnoreCase(PROP_RAGWITZ_NUM_NNS)) { + if (ragwitz_num_nns_set) { + return Integer.toString(ragwitz_num_nns); + } else { + return condMiCalc.getProperty(ConditionalMutualInfoCalculatorMultiVariateKraskov.PROP_K); + } } else { // Assume it was a property for the parent class or underlying conditional MI calculator return super.getProperty(propertyName); } } + @Override + public void preFinaliseAddObservations() throws Exception { + // Automatically determine the embedding parameters for the given time series + + if (autoEmbeddingMethod.equalsIgnoreCase(AUTO_EMBED_METHOD_NONE)) { + return; + } + // Else we need to auto embed + + // If we need to check which embedding method later: + // if (autoEmbeddingMethod.equalsIgnoreCase(AUTO_EMBED_METHOD_RAGWITZ) || + // autoEmbeddingMethod.equalsIgnoreCase(AUTO_EMBED_METHOD_RAGWITZ_DEST_ONLY)) { + + // Use a Kraskov AIS calculator to embed both time-series individually: + ActiveInfoStorageCalculatorKraskov aisCalc = new ActiveInfoStorageCalculatorKraskov(); + // Set the properties for the underlying MI Kraskov calculator here to match ours: + for (String key : props.keySet()) { + aisCalc.setProperty(key, props.get(key)); + } + // Set the auto-embedding properties as we require: + aisCalc.setProperty(ActiveInfoStorageCalculatorKraskov.PROP_AUTO_EMBED_METHOD, + ActiveInfoStorageCalculatorKraskov.AUTO_EMBED_METHOD_RAGWITZ); + aisCalc.setProperty(ActiveInfoStorageCalculatorKraskov.PROP_K_SEARCH_MAX, + Integer.toString(k_search_max)); + aisCalc.setProperty(ActiveInfoStorageCalculatorKraskov.PROP_TAU_SEARCH_MAX, + Integer.toString(tau_search_max)); + // In case !ragwitz_num_nns_set and our condMiCalc has a different default number of + // kNNs for Kraskov search than miCalc, we had best supply the number directly here: + aisCalc.setProperty(ActiveInfoStorageCalculatorKraskov.PROP_RAGWITZ_NUM_NNS, + getProperty(PROP_RAGWITZ_NUM_NNS)); + + // Embed the destination with the Ragwitz criteria: + if (debug) { + System.out.println("Starting embedding of destination:"); + } + aisCalc.initialise(); + aisCalc.startAddObservations(); + for (double[] destination : vectorOfDestinationTimeSeries) { + aisCalc.addObservations(destination); + } + aisCalc.finaliseAddObservations(); + // Set the auto-embedding parameters for the destination: + k = Integer.parseInt(aisCalc.getProperty(ActiveInfoStorageCalculator.K_PROP_NAME)); + k_tau = Integer.parseInt(aisCalc.getProperty(ActiveInfoStorageCalculator.TAU_PROP_NAME)); + if (debug) { + System.out.printf("Embedding parameters for destination set to k=%d,k_tau=%d\n", + k, k_tau); + } + + if (autoEmbeddingMethod.equalsIgnoreCase(AUTO_EMBED_METHOD_RAGWITZ)) { + // Embed the source also with the Ragwitz criteria: + if (debug) { + System.out.println("Starting embedding of source:"); + } + aisCalc.initialise(); + aisCalc.startAddObservations(); + for (double[] source : vectorOfSourceTimeSeries) { + aisCalc.addObservations(source); + } + aisCalc.finaliseAddObservations(); + // Set the auto-embedding parameters for the source: + l = Integer.parseInt(aisCalc.getProperty(ActiveInfoStorageCalculator.K_PROP_NAME)); + l_tau = Integer.parseInt(aisCalc.getProperty(ActiveInfoStorageCalculator.TAU_PROP_NAME)); + if (debug) { + System.out.printf("Embedding parameters for source set to l=%d,l_tau=%d\n", + l, l_tau); + } + } + + // Now that embedding parameters are finalised: + setStartTimeForFirstDestEmbedding(); + } }