jidt/java/source/infodynamics/measures/continuous/ConditionalTransferEntropyC...

537 lines
25 KiB
Java
Executable File

/*
* 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 <http://www.gnu.org/licenses/>.
*/
package infodynamics.measures.continuous;
/**
* <p>Interface for implementations of the <b>conditional transfer entropy</b>
* (conditional TE), which may be applied to univariate continuous
* time-series data.
* That is, it is applied to <code>double[]</code> data, indexed
* by time.
* See Schreiber below for the definition of transfer entropy,
* and Lizier et al (2008, 2010). for the definition of local transfer entropy
* and conditional TE, which is TE conditioned on one or
* more other potential sources.
* This is also called complete TE when all other causal sources
* are conditioned on.
* </p>
*
* <p>
* Usage of the child classes implementing this interface is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator;</li>
* <li>Set properties using {@link #setProperty(String, String)}
* which may now include properties describing
* the source and destination embedding;</li>
* <li>Initialise the calculator using
* {@link #initialise()}, {@link #initialise(int)}
* {@link #initialise(int, int, int)},
* {@link #initialise(int, int, int, int, int, int, int, int)} or
* {@link #initialise(int, int, int, int, int, int[], int[], int[])};</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using:
* <ul>
* <li>{@link #setObservations(double[], double[], double[])},
* {@link #setObservations(double[], double[], double[][])} or
* {@link #setObservations(double[], double[], double[][], boolean[], boolean[], boolean[][])}
* for calculations based on single time-series, OR</li>
* <li>The following sequence:<ol>
* <li>{@link #startAddObservations()}, then</li>
* <li>One or more calls to
* {@link #addObservations(double[], double[], double[])},
* {@link #addObservations(double[], double[], double[][])},
* {@link #addObservations(double[], double[], double[], int, int)} or
* {@link #addObservations(double[], double[], double[][], int, int)}, then</li>
* <li>{@link #finaliseAddObservations()};</li>
* </ol></li>
* </ul>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average conditional TE: {@link #computeAverageLocalOfObservations()};</li>
* <li>the local TE values for these samples: {@link #computeLocalOfPreviousObservations()};</li>
* <li>local TE values for a specific set of samples:
* {@link #computeLocalUsingPreviousObservations(double[], double[], double[])} or
* {@link #computeLocalUsingPreviousObservations(double[], double[], double[][])};</li>
* <li>the distribution of MI values under the null hypothesis
* of no relationship between source and
* destination values: {@link #computeSignificance(int)} or
* {@link #computeSignificance(int[][])}.</li>
* </ul>
* </li>
* <li>
* Return to step 2 or 3 to re-use the calculator on a new data set.
* </li>
* </ol>
* </p>
*
* <p><b>References:</b><br/>
* <ul>
* <li>T. Schreiber, <a href="http://dx.doi.org/10.1103/PhysRevLett.85.461">
* "Measuring information transfer"</a>,
* Physical Review Letters 85 (2) pp.461-464, 2000.</li>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href="http://dx.doi.org/10.1103/PhysRevE.77.026110">
* "Local information transfer as a spatiotemporal filter for complex systems"</a>
* Physical Review E 77, 026110, 2008.</li>
* <li>J. T. Lizier, M. Prokopenko and A. Zomaya,
* <a href=http://dx.doi.org/10.1063/1.3486801">
* "Information modification and particle collisions in distributed computation"</a>
* Chaos 20, 3, 037109 (2010).</li>
* </ul>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public interface ConditionalTransferEntropyCalculator extends ChannelCalculatorCommon {
/**
* Property name to specify the history length k
*/
public static final String K_PROP_NAME = "k_HISTORY";
/**
* Property name for embedding delay for the destination past history vector
*/
public static final String K_TAU_PROP_NAME = "k_TAU";
/**
* Property name for embedding length for the source past history vector
*/
public static final String L_PROP_NAME = "l_HISTORY";
/**
* Property name for embedding delay for the source past history vector
*/
public static final String L_TAU_PROP_NAME = "l_TAU";
/**
* Property name for 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 destination history embedding length to be considered.
* @throws Exception
*/
public void initialise(int k) throws Exception;
/**
* Initialise the calculator for re-use with 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.
* All other parameters remain unchanged.
*
* @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 for
* embeddings and delays supplied,
* for a single conditional variable.
* All other parameters remain unchanged.
*
* @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
*/
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 for
* embeddings and delays supplied.
* All other parameters remain unchanged.
*
* @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;
// Overriding the javadocs here, the method is already defined on ChannelCalculatorCommon
/**
* Sets properties for the conditional TE calculator.
* New property values are not guaranteed to take effect until the next call
* to an initialise method.
*
* <p>Valid property names, and what their
* values should represent, include:</p>
* <ul>
* <li>{@link #K_PROP_NAME} -- destination history embedding length k
* (default value 1)</li>
* <li>{@link #K_TAU_PROP_NAME} -- embedding delay for the destination past history vector
* (default value 1)</li>
* <li>{@link #L_PROP_NAME} -- embedding length for the source past history vector
* (default value 1)</li>
* <li>{@link #L_TAU_PROP_NAME} -- embedding delay for the source past history vector
* (default value 1)</li>
* <li>{@link #DELAY_PROP_NAME} -- source-destination delay (default value is 1)</li>
* <li>{@link #COND_EMBED_LENGTHS_PROP_NAME} -- conditional variables embedding lengths
* as a comma separated integer list (default value "1" for a single variable)</li>
* <li>{@link #COND_EMBED_DELAYS_PROP_NAME} -- conditional variables embedding delays
* as a comma separated integer list (default value "1" for a single variable)</li>
* <li>{@link #COND_DELAYS_PROP_NAME} -- delays from the conditional variables to the destination
* as a comma separated integer list (default value "1" for a single variable)</li>
* </ul>
*
* <p>While {@link #COND_EMBED_LENGTHS_PROP_NAME},
* {@link #COND_EMBED_DELAYS_PROP_NAME} and {@link #COND_DELAYS_PROP_NAME}
* may be set separately and may therefore be arrays of different
* lengths, {@link #COND_EMBED_LENGTHS_PROP_NAME} acts as the master
* from which we determine the number of conditional variables that we have,
* and we will check that they have the same lengths at the next initialisation.</p>
*
* <p><b>Note:</b> further properties may be defined by child classes.</p>
*
* <p>Unknown property values are ignored.</p>
*
* @param propertyName name of the property
* @param propertyValue value of the property.
* @throws Exception if there is a problem with the supplied value,
* or if the property is recognised but unsupported.
*/
@Override
public void setProperty(String propertyName, String propertyValue) throws Exception;
/**
* Sets the single time-series from which to compute the PDFs.
* Cannot be called in conjunction with other methods for setting/adding
* observations.
*
* @param source time-series of observations for the source variable.
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @throws Exception
*/
public void setObservations(double[] source, double[] destination, double[][] conditionals) throws Exception;
/**
* Sets the single time-series from which to compute the PDFs.
* Cannot be called in conjunction with other methods for setting/adding
* observations.
*
* @param source time-series of observations for the source variable.
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 1D time series array for the conditional variables
* (indexed by time only) -- valid only if the calculator
* was initialised for a single conditional variable.
* Length must match <code>source</code>.
* @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;
/**
* <p>Adds a new set of observations to update the PDFs with - is
* intended to be called multiple times.
* Must be called after {@link #startAddObservations()}; call
* {@link #finaliseAddObservations()} once all observations have
* been supplied.</p>
*
* <p><b>Important:</b> this does not append/concatenate these observations to the previously
* supplied observations, but treats them independently.</p>
*
* <p>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).</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @throws Exception
*/
public void addObservations(double[] source, double[] destination, double[][] conditionals) throws Exception;
/**
* <p>Adds a new set of observations to update the PDFs with - is
* intended to be called multiple times.
* Must be called after {@link #startAddObservations()}; call
* {@link #finaliseAddObservations()} once all observations have
* been supplied.</p>
*
* <p><b>Important:</b> this does not append/concatenate these observations to the previously
* supplied observations, but treats them independently.</p>
*
* <p>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).</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 1D time series array for the conditional variables
* (indexed by time only) -- valid only if the calculator
* was initialised for a single conditional variable.
* Length must match <code>source</code>.
* @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;
/**
* <p>Adds a new sub-series of observations to update the PDFs with - is
* intended to be called multiple times.
* Must be called after {@link #startAddObservations()}; call
* {@link #finaliseAddObservations()} once all observations have
* been supplied.</p>
*
* <p><b>Important:</b> this does not append/concatenate these observations to the previously
* supplied observations, but treats them independently.</p>
*
* <p>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).</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @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 ;
/**
* <p>Adds a new sub-series of observations to update the PDFs with - is
* intended to be called multiple times.
* Must be called after {@link #startAddObservations()}; call
* {@link #finaliseAddObservations()} once all observations have
* been supplied.</p>
*
* <p><b>Important:</b> this does not append/concatenate these observations to the previously
* supplied observations, but treats them independently.</p>
*
* <p>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).</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 1D time-series array for the conditional variables
* (indexed by time only) -- valid only if the calculator
* was initialised for a single conditional variable.
* Length must match <code>source</code>.
* @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 ;
/**
* <p>Adds a set of observations to compute the PDFs from,
* but only where these observations are indicated to be valid.
* Cannot be called in conjunction with
* {@link #startAddObservations()}/{@link #addObservations(double[], double[])} /
* {@link #finaliseAddObservations()}.</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @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 addObservations(double[] source, double[] destination,
double[][] conditionals,
boolean[] sourceValid, boolean[] destValid, boolean[][] conditionalsValid) throws Exception;
/**
* <p>Adds a set of univariate observations to compute the PDFs from,
* but only where these observations are indicated to be valid.
* Cannot be called in conjunction with
* {@link #startAddObservations()}/{@link #addObservations(double[], double[])} /
* {@link #finaliseAddObservations()}.</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals time-series array for the conditional variable.
* Length must match <code>source</code>.
* @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 time-series
* indicating whether the conditional variable at that point is valid.
* @throws Exception
*/
public void addObservations(double[] source, double[] destination,
double[] conditionals,
boolean[] sourceValid, boolean[] destValid, boolean[] conditionalsValid) throws Exception;
/**
* <p>Sets the single set of observations to compute the PDFs from,
* but only where these observations are indicated to be valid.
* Cannot be called in conjunction with
* {@link #startAddObservations()}/{@link #addObservations(double[], double[])} /
* {@link #finaliseAddObservations()}.</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @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;
/**
* <p>Sets the single set of observations to compute the PDFs from,
* but only where these observations are indicated to be valid.
* Cannot be called in conjunction with
* {@link #startAddObservations()}/{@link #addObservations(double[], double[])} /
* {@link #finaliseAddObservations()}.</p>
*
* @param source time-series observations for the source variable
* @param destination time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param conditionals time-series array for the conditional variable.
* Length must match <code>source</code>.
* @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 time-series (with time indices the same as conditional)
* indicating whether the conditional variable at that point is 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 time-series,
* using the PDFs computed from the previously supplied method calls.
*
* @param newSourceObservations new time-series observations for the source variable
* @param newDestObservations new time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param newCondObservations new 2D time-series array for the conditional variables
* (first index is time, second index is variable number).
* Length must match <code>source</code>.
* @return the time-series of local conditional TE values.
* First values will be set to zero until enough samples are
* accumulated to embed the variables as per the parameter settings.
* @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 time-series observations for the source variable
* @param newDestObservations new time-series of observations for the destination
* variable. Length must match <code>source</code>.
* @param newCondObservations new 1D time-series array for the conditional variables
* (indexed by time only) -- valid only if the calculator
* was initialised for a single conditional variable.
* Length must match <code>source</code>.
* @return the time-series of local conditional TE values.
* First values will be set to zero until enough samples are
* accumulated to embed the variables as per the parameter settings.
* @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;
}