diff --git a/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculator.java b/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculator.java index 6bb22cf..782ef30 100755 --- a/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculator.java +++ b/java/source/infodynamics/measures/continuous/ActiveInfoStorageCalculator.java @@ -52,6 +52,8 @@ public interface ActiveInfoStorageCalculator { /** * Allows the user to set properties for the underlying calculator implementation + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * * @param propertyName * @param propertyValue diff --git a/java/source/infodynamics/measures/continuous/ChannelCalculator.java b/java/source/infodynamics/measures/continuous/ChannelCalculator.java index 6ea60f7..15164c1 100755 --- a/java/source/infodynamics/measures/continuous/ChannelCalculator.java +++ b/java/source/infodynamics/measures/continuous/ChannelCalculator.java @@ -12,8 +12,6 @@ package infodynamics.measures.continuous; */ public interface ChannelCalculator extends ChannelCalculatorCommon { - public void initialise() throws Exception; - /** *
Sets the single set of observations to compute the PDFs from. * Cannot be called in conjunction with diff --git a/java/source/infodynamics/measures/continuous/ChannelCalculatorCommon.java b/java/source/infodynamics/measures/continuous/ChannelCalculatorCommon.java index 0259866..f784276 100755 --- a/java/source/infodynamics/measures/continuous/ChannelCalculatorCommon.java +++ b/java/source/infodynamics/measures/continuous/ChannelCalculatorCommon.java @@ -18,7 +18,17 @@ import infodynamics.utils.EmpiricalMeasurementDistribution; public abstract interface ChannelCalculatorCommon { /** - * Allows the user to set properties for the underlying calculator implementation + * Initialise the calculator for re-use with new observations. + * All parameters remain unchanged. + * + * @throws Exception + */ + public void initialise() throws Exception; + + /** + * Allows the user to set properties for the underlying calculator implementation. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * * @param propertyName * @param propertyValue diff --git a/java/source/infodynamics/measures/continuous/ConditionalMutualInfoCalculatorMultiVariate.java b/java/source/infodynamics/measures/continuous/ConditionalMutualInfoCalculatorMultiVariate.java index e16cb18..998ce7e 100755 --- a/java/source/infodynamics/measures/continuous/ConditionalMutualInfoCalculatorMultiVariate.java +++ b/java/source/infodynamics/measures/continuous/ConditionalMutualInfoCalculatorMultiVariate.java @@ -45,6 +45,8 @@ public interface ConditionalMutualInfoCalculatorMultiVariate { /** * Allows the user to set properties for the underlying calculator implementation + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * * @param propertyName * @param propertyValue diff --git a/java/source/infodynamics/measures/continuous/ConditionalMutualInfoMultiVariateCommon.java b/java/source/infodynamics/measures/continuous/ConditionalMutualInfoMultiVariateCommon.java index aeb02cc..2473198 100755 --- a/java/source/infodynamics/measures/continuous/ConditionalMutualInfoMultiVariateCommon.java +++ b/java/source/infodynamics/measures/continuous/ConditionalMutualInfoMultiVariateCommon.java @@ -120,6 +120,8 @@ public abstract class ConditionalMutualInfoMultiVariateCommon implements /** * Sets common properties for the calculator. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * Valid properties include: *
This specifies the interface for implementations of + * the conditional transfer entropy + * and local conditional transfer entropy + * (see Lizier et al. PRE, 2008, and Lizier et al., Chaos 2010). + *
+ * + *Specifically, this specifies the interface for computing + * the transfer entropy for continuous-valued variables.
+ * + * @see "Schreiber, Physical Review Letters 85 (2) pp.461-464 (2000); + * download + * (for definition of transfer entropy)" + * @see "Lizier, Prokopenko and Zomaya, Physical Review E 77, 026110 (2008); + * download + * (for the extension to conditional transfer entropy + * or complete where all other causal sources are conditioned on, + * and local transfer entropy)" + * @see "Lizier, Prokopenko and Zomaya, Chaos 20, 3, 037109 (2010); + * download + * (for further clarification on conditional transfer entropy + * or complete where all other causal sources are conditioned on)" + * + * @author Joseph Lizier, email, + * www + * + */ +public interface ConditionalTransferEntropyCalculator extends ChannelCalculatorCommon { + + /** + * Property name to specify the history length k + */ + public static final String K_PROP_NAME = "k_HISTORY"; + /** + * Embedding delay for the destination past history vector + */ + public static final String K_TAU_PROP_NAME = "k_TAU"; + /** + * Embedding length for the source past history vector + */ + public static final String L_PROP_NAME = "l_HISTORY"; + /** + * Embedding delay for the source past history vector + */ + public static final String L_TAU_PROP_NAME = "l_TAU"; + /** + * Source-destination delay + */ + public static final String DELAY_PROP_NAME = "DELAY"; + /** + * Property name for embedding lengths of conditional variables + */ + public static final String COND_EMBED_LENGTHS_PROP_NAME = "COND_EMBED_LENGTHS"; + /** + * Property name for embedding delays of conditional variables + */ + public static final String COND_EMBED_DELAYS_PROP_NAME = "COND_TAUS"; + /** + * Property name for conditional-destination delays of conditional variables + */ + public static final String COND_DELAYS_PROP_NAME = "COND_DELAYS"; + + /** + * Initialise the calculator for re-use with new observations. + * A new history length k can be supplied here; all other parameters + * remain unchanged. + * + * @param k history length to be considered. + * @throws Exception + */ + public void initialise(int k) throws Exception; + + /** + * Initialise the calculator for a single conditional + * variable, with the given destination, + * source and conditional embedding length, setting all + * embedding delays to 1, and the source-dest and + * conditional-dest delays to 1. + * + * @param k Length of destination past history to consider + * @param l length of source past history to consider + * @param condEmbedDim embedding length for one conditional variable. + * Can be 0 if there are no conditional variables. + * @throws Exception + */ + public void initialise(int k, int l, int condEmbedDim) throws Exception; + + /** + * Initialise the calculator with all required parameters supplied, + * for a single conditional variable. + * + * @param k Length of destination past history to consider + * @param k_tau embedding delay for the destination variable + * @param l length of source past history to consider + * @param l_tau embedding delay for the source variable + * @param delay time lag between last element of source and destination next value + * @param condEmbedDim embedding lengths for one conditional variable. + * Can be 0 if there are no conditional variables. + * @param cond_tau embedding delay for the conditional variable. + * Ignored if condEmbedDim == 0. + * @param condDelay time lags between last element of the conditional variable + * and destination next value. + * Ignored if condEmbedDim == 0. + * @throws Exception for inconsistent arguments, e.g. if array lengths differ between + * condEmbedDims, cond_taus and condDelays. + */ + public void initialise(int k, int k_tau, int l, int l_tau, int delay, + int condEmbedDim, int cond_tau, int condDelay) throws Exception; + /** + * Initialise the calculator with all required parameters supplied. + * + * @param k Length of destination past history to consider + * @param k_tau embedding delay for the destination variable + * @param l length of source past history to consider + * @param l_tau embedding delay for the source variable + * @param delay time lag between last element of source and destination next value + * @param condEmbedDims array of embedding lengths for each conditional variable. + * Can be an empty array or null if there are no conditional variables. + * @param cond_taus array of embedding delays for the conditional variables. + * Must be same length as condEmbedDims array. + * @param condDelays array of time lags between last element of each conditional variable + * and destination next value. + * Must be same length as condEmbedDims array. + * @throws Exception for inconsistent arguments, e.g. if array lengths differ between + * condEmbedDims, cond_taus and condDelays. + */ + public void initialise(int k, int k_tau, int l, int l_tau, int delay, + int[] condEmbedDims, int[] cond_taus, int[] condDelays) throws Exception; + + /** + *Set the given property to the given value. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. + * These can include: + *
Sets the single set of observations to compute the PDFs from. + * Cannot be called in conjunction with + * {@link #startAddObservations()}/{@link #addObservations(double[], double[], double[][])} / + * {@link #finaliseAddObservations()}.
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 2D time series array for the conditional variables + * (first index is time, second index is variable number) + * @throws Exception + */ + public void setObservations(double[] source, double[] destination, double[][] conditionals) throws Exception; + + /** + *Sets the single set of observations to compute the PDFs from. + * Cannot be called in conjunction with + * {@link #startAddObservations()}/{@link #addObservations(double[], double[], double[][])} / + * {@link #finaliseAddObservations()}.
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 1D time series array for the conditional variables + * -- valid only if the calculator was initialised for a single + * conditional variable. + * @throws Exception for example if the calculator was not initialised for + * a single conditional variable + */ + public void setObservations(double[] source, double[] destination, double[] conditionals) throws Exception; + + /** + *Adds a new set of observations to update the PDFs with - is + * intended to be called multiple times. + * Must be called after {@link #startAddObservations()}; call + * {@link #finaliseAddObservations()} once all observations have + * been supplied.
+ * + *Important: this does not append these observations to the previously + * supplied observations, but treats them independently - i.e. measurements + * such as the transfer entropy will not join them up to examine k + * consecutive values in time.
+ * + *Note that the arrays source, destination and conditionals must not be over-written by the user + * until after finaliseAddObservations() has been called + * (they are not copied by this method necessarily, but the method + * may simply hold a pointer to them).
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 2D time series array for the conditional variables + * (first index is time, second index is variable number) + * @throws Exception + */ + public void addObservations(double[] source, double[] destination, double[][] conditionals) throws Exception; + + /** + *Adds a new set of observations to update the PDFs with - is + * intended to be called multiple times. + * Must be called after {@link #startAddObservations()}; call + * {@link #finaliseAddObservations()} once all observations have + * been supplied.
+ * + *Important: this does not append these observations to the previously + * supplied observations, but treats them independently - i.e. measurements + * such as the transfer entropy will not join them up to examine k + * consecutive values in time.
+ * + *Note that the arrays source, destination and conditionals must not be over-written by the user + * until after finaliseAddObservations() has been called + * (they are not copied by this method necessarily, but the method + * may simply hold a pointer to them).
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 1D time series array for the conditional variables + * -- valid only if the calculator was initialised for a single + * conditional variable. + * @throws Exception for example if the calculator was not initialised for + * a single conditional variable + */ + public void addObservations(double[] source, double[] destination, double[] conditionals) throws Exception; + + /** + *Adds a new set of observations to update the PDFs with - is + * intended to be called multiple times. + * Must be called after {@link #startAddObservations()}; call + * {@link #finaliseAddObservations()} once all observations have + * been supplied.
+ * + *Important: this does not append these observations to the previously + * supplied observations, but treats them independently - i.e. measurements + * such as the transfer entropy will not join them up to examine k + * consecutive values in time.
+ * + *Note that the arrays source, destination and conditionals must not be over-written by the user + * until after finaliseAddObservations() has been called + * (they are not copied by this method necessarily, but the method + * may simply hold a pointer to them).
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 2D time series array for the conditional variables + * (first index is time, second index is variable number) + * @param startTime first time index to take observations on + * @param numTimeSteps number of time steps to use + * @throws Exception + */ + public void addObservations(double[] source, double[] destination, + double[][] conditionals, + int startTime, int numTimeSteps) throws Exception ; + + /** + *Adds a new set of observations to update the PDFs with - is + * intended to be called multiple times. + * Must be called after {@link #startAddObservations()}; call + * {@link #finaliseAddObservations()} once all observations have + * been supplied.
+ * + *Important: this does not append these observations to the previously + * supplied observations, but treats them independently - i.e. measurements + * such as the transfer entropy will not join them up to examine k + * consecutive values in time.
+ * + *Note that the arrays source, destination and conditionals must not be over-written by the user + * until after finaliseAddObservations() has been called + * (they are not copied by this method necessarily, but the method + * may simply hold a pointer to them).
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 1D time series array for the conditional variables + * -- valid only if the calculator was initialised for a single + * conditional variable. + * @param startTime first time index to take observations on + * @param numTimeSteps number of time steps to use + * @throws Exception for example if the calculator was not initialised for + * a single conditional variable + */ + public void addObservations(double[] source, double[] destination, + double[] conditionals, + int startTime, int numTimeSteps) throws Exception ; + + /** + *Sets the single set of observations to compute the PDFs from. + * Cannot be called in conjunction with + * {@link #startAddObservations()}/{@link #addObservations(double[], double[])} / + * {@link #finaliseAddObservations()}.
+ * + * @param source observations for the source variable + * @param destination observations for the destination variable + * @param conditionals 2D time series array for the conditional variables + * (first index is time, second index is variable number) + * @param sourceValid time series (with time indices the same as source) + * indicating whether the source at that point is valid. + * @param destValid time series (with time indices the same as destination) + * indicating whether the destination at that point is valid. + * @param conditionalsValid 2D time series (with time indices the same as conditionals) + * indicating whether the conditional variables at that point are valid. + * @throws Exception + */ + public void setObservations(double[] source, double[] destination, + double[][] conditionals, + boolean[] sourceValid, boolean[] destValid, boolean[][] conditionalsValid) throws Exception; + + /** + * Compute local conditional transfer entropy values for the + * observations in the given parameters, + * using the PDFs computed from the previously supplied method calls. + * + * @param newSourceObservations new observations for the source variable + * @param newDestObservations new observations for the destination variable + * @param newCondObservations new observations for the conditional variables + * @return + * @throws Exception + */ + public double[] computeLocalUsingPreviousObservations( + double[] newSourceObservations, double[] newDestObservations, + double[][] newCondObservations) throws Exception; + + /** + * Compute local conditional transfer entropy values for the + * observations in the given parameters, + * using the PDFs computed from the previously supplied method calls. + * + * @param newSourceObservations new observations for the source variable + * @param newDestObservations new observations for the destination variable + * @param newCondObservations 1D time series array for the conditional variables + * -- valid only if the calculator was initialised for a single + * conditional variable. + * @return + * @throws Exception for example if the calculator was not initialised for + * a single conditional variable + */ + public double[] computeLocalUsingPreviousObservations( + double[] newSourceObservations, double[] newDestObservations, + double[] newCondObservations) throws Exception; +} diff --git a/java/source/infodynamics/measures/continuous/ConditionalTransferEntropyCalculatorViaCondMutualInfo.java b/java/source/infodynamics/measures/continuous/ConditionalTransferEntropyCalculatorViaCondMutualInfo.java new file mode 100755 index 0000000..7b01364 --- /dev/null +++ b/java/source/infodynamics/measures/continuous/ConditionalTransferEntropyCalculatorViaCondMutualInfo.java @@ -0,0 +1,803 @@ +package infodynamics.measures.continuous; + +import infodynamics.utils.EmpiricalMeasurementDistribution; +import infodynamics.utils.MatrixUtils; +import infodynamics.utils.ParsedProperties; + +import java.util.Vector; + +/** + *An abstract Conditional Transfer entropy calculator which is implemented using a + * Conditional Mutual Information calculator. + * The Conditional Mutual Information calculator must be supplied at construction time. + *
+ * + *There are no abstract methods of this class, and conceivably it could be constructed + * with a {@link ConditionalMutualInfoCalculatorMultiVariate} class supplied, + * however there are typically extra considerations for each estimator type. + * As such, the children of this abstract class provide concrete implementations using various + * estimator types; see e.g. {@link infodynamics.continuous.gaussian.ConditionalTransferEntropyCalculatorGaussian}. + *
+ * + * @see "Schreiber, Physical Review Letters 85 (2) pp.461-464 (2000); + * download + * (for definition of transfer entropy)" + * @see "Lizier, Prokopenko and Zomaya, Physical Review E 77, 026110 (2008); + * download + * (for the extension to conditional transfer entropy + * or complete where all other causal sources are conditioned on, + * and local transfer entropy)" + * @see "Lizier, Prokopenko and Zomaya, Chaos 20, 3, 037109 (2010); + * download + * (for further clarification on conditional transfer entropy + * or complete where all other causal sources are conditioned on)" + * + * @author Joseph Lizier, email, + * www + */ +public abstract class ConditionalTransferEntropyCalculatorViaCondMutualInfo implements + ConditionalTransferEntropyCalculator { + + /** + * Underlying conditional mutual information calculator + */ + protected ConditionalMutualInfoCalculatorMultiVariate condMiCalc; + /** + * Length of past destination history to consider (embedding length) + */ + protected int k = 1; + /** + * Embedding delay to use between elements of the destination embeding vector. + * We're hard-coding a delay of 1 between the history vector and the next + * observation however. + */ + protected int k_tau = 1; + /** + * Length of past source history to consider (embedding length) + */ + protected int l = 1; + /** + * Embedding delay to use between elements of the source embeding vector. + */ + protected int l_tau = 1; + /** + * Source-destination next observation delay + */ + protected int delay = 1; + /** + * Array of embedding lengths for each conditional variable. + * Can be an empty array or null if there are no conditional variables. + */ + protected int[] condEmbedDims = null; + /** + * Array of embedding delays for the conditional variables. + * Must be same length as condEmbedDims array. + */ + protected int[] cond_taus = null; + /** + * Array of time lags between last element of each conditional variable + * and destination next value. + */ + protected int[] condDelays = null; + + /** + * Time index of the last point in the destination embedding of the first + * (destination past, source past, destination next) tuple than can be + * taken from any set of time-series observations. + */ + protected int startTimeForFirstDestEmbedding; + + /** + * The total dimensionality of our embedded conditional values + * (sum of condEmbedDims) + */ + protected int dimOfConditionals = 0; + + protected boolean debug = false; + + /** + * Construct a conditional transfer entropy calculator using an instance of + * condMiCalculatorClassName as the underlying conditional mutual information calculator. + * + * @param condMiCalculatorClassName name of the class which must implement + * {@link ConditionalMutualInfoCalculatorMultiVariate} + * @throws InstantiationException if the given class cannot be instantiated + * @throws IllegalAccessException if illegal access occurs while trying to create an instance + * of the class + * @throws ClassNotFoundException if the given class is not found + */ + public ConditionalTransferEntropyCalculatorViaCondMutualInfo(String condMiCalculatorClassName) + throws InstantiationException, IllegalAccessException, ClassNotFoundException { + @SuppressWarnings("unchecked") + ClassSet the given property to the given value. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. + * These can include the following properties from {@link ConditionalTransferEntropyCalculator}: + *
Otherwise, it is assumed the property + * is for the underlying {@link ConditionalMutualInfoCalculatorMultiVariate#setProperty(String, String)} implementation. + *
+ * + * @param propertyName name of the property + * @param propertyValue value of the property. + * @throws Exception if there is a problem with the supplied value + */ + 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(K_TAU_PROP_NAME)) { + k_tau = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(L_PROP_NAME)) { + l = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(L_TAU_PROP_NAME)) { + l_tau = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(DELAY_PROP_NAME)) { + delay = Integer.parseInt(propertyValue); + } else if (propertyName.equalsIgnoreCase(COND_EMBED_LENGTHS_PROP_NAME)) { + condEmbedDims = ParsedProperties.parseStringArrayOfInts(propertyValue); + } else if (propertyName.equalsIgnoreCase(COND_EMBED_DELAYS_PROP_NAME)) { + cond_taus = ParsedProperties.parseStringArrayOfInts(propertyValue); + } else if (propertyName.equalsIgnoreCase(COND_DELAYS_PROP_NAME)) { + condDelays = ParsedProperties.parseStringArrayOfInts(propertyValue); + } else { + // No property was set on this class, assume it is for the underlying + // conditional MI calculator + condMiCalc.setProperty(propertyName, propertyValue); + propertySet = false; + } + if (debug && propertySet) { + System.out.println(this.getClass().getSimpleName() + ": Set property " + propertyName + + " to " + propertyValue); + } + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#setObservations(double[], double[], double[][]) + */ + public void setObservations(double[] source, double[] destination, + double[][] conditionals) throws Exception { + startAddObservations(); + addObservations(source, destination, conditionals); + finaliseAddObservations(); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#setObservations(double[], double[], double[]) + */ + public void setObservations(double[] source, double[] destination, + double[] conditionals) throws Exception { + if (condEmbedDims.length != 1) { + throw new Exception("Cannot call setObservations(double[], double[], double[]) when the " + + "conditional TE calculator was not initialised for one conditional variable"); + } + startAddObservations(); + addObservations(source, destination, conditionals); + finaliseAddObservations(); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#startAddObservations() + */ + public void startAddObservations() { + condMiCalc.startAddObservations(); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#addObservations(double[], double[], double[][]) + */ + public void addObservations(double[] source, double[] destination, + double[][] conditionals) 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 (conditionals == null) { + if (condEmbedDims.length > 0) { + throw new Exception(String.format("No conditionals supplied (expected %d-dimensional conditionals", condEmbedDims.length)); + } else { + // This is allowed; make a dummy set of conditionals + conditionals = new double[destination.length][0]; + } + } + if (conditionals.length != destination.length) { + throw new Exception(String.format("Conditionals and destination lengths (%d and %d) must match!", + conditionals.length, destination.length)); + } + // Postcondition -- all time series have same 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; + } + if (conditionals[0].length != condEmbedDims.length) { + throw new Exception(String.format("Number of conditional variables %d does not " + + "match the initialised number %d", conditionals[0].length, condEmbedDims.length)); + } + // All parameters are as expected + double[][][] embeddedVectorsForCondMI = + embedSourceDestAndConditionalsForCondMI(source, destination, conditionals); + + condMiCalc.addObservations(embeddedVectorsForCondMI[0], + embeddedVectorsForCondMI[1], embeddedVectorsForCondMI[2]); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#addObservations(double[], double[], double[]) + */ + public void addObservations(double[] source, double[] destination, + double[] conditionals) throws Exception { + if (condEmbedDims.length != 1) { + throw new Exception("Cannot call addObservations(double[], double[], double[]) when the " + + "conditional TE calculator was not initialised for one conditional variable"); + } + double[][] conditionalsIn2D = null; + if (conditionals != null) { + // This isn't incredibly efficient, but is easy to code and doesn't cost more + // than an increase in the linear time multiplier. + conditionalsIn2D = new double[conditionals.length][1]; + MatrixUtils.copyIntoColumn(conditionalsIn2D, 0, conditionals); + } + addObservations(source, destination, conditionalsIn2D); + } + + /** + * Internal method to take (pre-screened) vectors for a source, destination + * and conditional variables, and embed them using the given embedding + * parameters, as well as combining the destination past and conditionals, + * making all ready for a conditional MI calculation. + * + * @param source source time series observations + * @param destination destination time series observations + * @param conditionals 2D array of conditional time series observations. + * @return double[][][] returnValue; where returnValue[0] is the embedded + * source vectors, returnValue[1] is the destination next values, + * and returnValue[2] is the joined embedded destination past + * and conditionals. + * @throws Exception + */ + protected double[][][] embedSourceDestAndConditionalsForCondMI(double[] source, double[] destination, + double[][] conditionals) throws Exception { + 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); + // Now combine the destination past vectors with the conditionals: + double[][] currentCombinedConditionalVectors = + new double[currentSourcePastVectors.length][k + dimOfConditionals]; + MatrixUtils.arrayCopy(currentDestPastVectors, 0, 0, + currentCombinedConditionalVectors, 0, 0, + currentDestPastVectors.length, k); + int nextColumnToCopyInto = k; + for (int i = 0; i < condEmbedDims.length; i++) { + // Extract the embedding for conditional variable i + double[][] currentThisConditonalVectors = + MatrixUtils.makeDelayEmbeddingVector(conditionals, i, + condEmbedDims[i], this.cond_taus[i], + startTimeForFirstDestEmbedding + 1 - condDelays[i], + conditionals.length - startTimeForFirstDestEmbedding - 1); + // And add this embedding to our set of conditional variables + MatrixUtils.arrayCopy(currentThisConditonalVectors, 0, 0, + currentCombinedConditionalVectors, 0, nextColumnToCopyInto, + currentThisConditonalVectors.length, condEmbedDims[i]); + nextColumnToCopyInto += condEmbedDims[i]; + } + + double[][][] returnSet = new double[3][][]; + returnSet[0] = currentSourcePastVectors; + returnSet[1] = currentDestNextVectors; + returnSet[2] = currentCombinedConditionalVectors; + return returnSet; + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#addObservations(double[], double[], double[][], int, int) + */ + public void addObservations(double[] source, double[] destination, + double[][] conditionals, int startTime, int numTimeSteps) + 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 (conditionals == null) { + if (condEmbedDims.length > 0) { + throw new Exception(String.format("No conditionals supplied (expected %d-dimensional conditionals", condEmbedDims.length)); + } else { + // This is allowed; make a dummy set of conditionals + conditionals = new double[destination.length][0]; + } + } + if (conditionals.length != destination.length) { + throw new Exception(String.format("Conditionals and destination lengths (%d and %d) must match!", + conditionals.length, destination.length)); + } + // Postcondition -- all time series have same length + if (source.length < startTime + numTimeSteps) { + // There are not enough observations given the arguments here + throw new Exception("Not enough observations to set here given startTime and numTimeSteps parameters"); + } + addObservations(MatrixUtils.select(source, startTime, numTimeSteps), + MatrixUtils.select(destination, startTime, numTimeSteps), + MatrixUtils.selectRows(conditionals, startTime, numTimeSteps)); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#addObservations(double[], double[], double[], int, int) + */ + public void addObservations(double[] source, double[] destination, + double[] conditionals, int startTime, int numTimeSteps) throws Exception { + if (condEmbedDims.length != 1) { + throw new Exception("Cannot call addObservations(double[], double[], double[], int, int) when the " + + "conditional TE calculator was not initialised for one conditional variable"); + } + double[][] conditionalsIn2D = null; + if (conditionals != null) { + // This isn't incredibly efficient, but is easy to code and doesn't cost more + // than an increase in the linear time multiplier. + conditionalsIn2D = new double[conditionals.length][1]; + MatrixUtils.copyIntoColumn(conditionalsIn2D, 0, conditionals); + } + addObservations(source, destination, conditionalsIn2D, startTime, numTimeSteps); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#finaliseAddObservations() + */ + public void finaliseAddObservations() throws Exception { + condMiCalc.finaliseAddObservations(); + } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ConditionalTransferEntropyCalculator#setObservations(double[], double[], double[][], boolean[], boolean[], boolean[][]) + */ + public void setObservations(double[] source, double[] destination, + double[][] conditionals, boolean[] sourceValid, + boolean[] destValid, boolean[][] conditionalsValid) + throws Exception { + + VectorSet the required property of the calculator to the given value.
+ *Set the required property of the calculator to the given value. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. + *
* *There are no general properties settable on all child classes; * each child class may define their own properties.
diff --git a/java/source/infodynamics/measures/continuous/MutualInfoMultiVariateCommon.java b/java/source/infodynamics/measures/continuous/MutualInfoMultiVariateCommon.java index dfa79ee..01a12f6 100755 --- a/java/source/infodynamics/measures/continuous/MutualInfoMultiVariateCommon.java +++ b/java/source/infodynamics/measures/continuous/MutualInfoMultiVariateCommon.java @@ -84,6 +84,13 @@ public abstract class MutualInfoMultiVariateCommon implements protected boolean addedMoreThanOneObservationSet; + /* (non-Javadoc) + * @see infodynamics.measures.continuous.ChannelCalculatorCommon#initialise() + */ + public void initialise() throws Exception { + initialise(dimensionsSource, dimensionsDest); + } + /** * Clear any previously supplied probability distributions and prepare * the calculator to be used again. @@ -103,6 +110,8 @@ public abstract class MutualInfoMultiVariateCommon implements /** *Set the given property to the given value. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * These can include: *
Set the given property to the given value. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. * These can include: *
+ * Implements a conditional transfer entropy calculator using model of + * Gaussian variables with linear interactions. + * This is equivalent (up to a multiplicative constant) to + * (a conditional) Granger causality (see Barnett et al., below). + * This is achieved by plugging in {@link ConditionalMutualInfoCalculatorMultiVariateGaussian} + * as the calculator into {@link ConditionalTransferEntropyCalculatorViaCondMutualInfo}. + *
+ * + *+ * Usage: + *
- * Implements a transfer entropy calculator using kernel estimation. + * Implements a multivariate transfer entropy calculator using kernel estimation. * (see Schreiber, PRL 85 (2) pp.461-464, 2000)
* *diff --git a/java/source/infodynamics/measures/continuous/kozachenko/EntropyCalculatorMultiVariateKozachenko.java b/java/source/infodynamics/measures/continuous/kozachenko/EntropyCalculatorMultiVariateKozachenko.java index 98f919a..1871758 100755 --- a/java/source/infodynamics/measures/continuous/kozachenko/EntropyCalculatorMultiVariateKozachenko.java +++ b/java/source/infodynamics/measures/continuous/kozachenko/EntropyCalculatorMultiVariateKozachenko.java @@ -52,6 +52,14 @@ public class EntropyCalculatorMultiVariateKozachenko isComputed = false; lastLocalEntropy = null; } + + /* (non-Javadoc) + * @see infodynamics.measures.continuous.EntropyCalculatorMultiVariate#setProperty(java.lang.String, java.lang.String) + */ + public void setProperty(String propertyName, String propertyValue) + throws Exception { + // No properties here to set + } public void setObservations(double[][] observations) { rawData = observations; diff --git a/java/source/infodynamics/measures/continuous/kraskov/ConditionalTransferEntropyCalculatorKraskov.java b/java/source/infodynamics/measures/continuous/kraskov/ConditionalTransferEntropyCalculatorKraskov.java new file mode 100755 index 0000000..ebcf2c5 --- /dev/null +++ b/java/source/infodynamics/measures/continuous/kraskov/ConditionalTransferEntropyCalculatorKraskov.java @@ -0,0 +1,186 @@ +package infodynamics.measures.continuous.kraskov; + +import java.util.Hashtable; + +import infodynamics.measures.continuous.ConditionalMutualInfoCalculatorMultiVariate; +import infodynamics.measures.continuous.ConditionalTransferEntropyCalculator; +import infodynamics.measures.continuous.ConditionalTransferEntropyCalculatorViaCondMutualInfo; + +/** + * + *
+ * Implements a conditional transfer entropy calculator using a conditional MI calculator + * implementing the Kraskov-Grassberger estimator. + * This is achieved by plugging in a {@link ConditionalMutualInfoCalculatorMultiVariateKraskov} + * as the calculator into {@link TransferEntropyCalculatorViaCondMutualInfo}. + *
+ * + *+ * Usage: + *