Altered interal names of kernel width variables to be kernel width, instead of epsilon. This is more intuitive for users.

This commit is contained in:
joseph.lizier 2012-08-07 07:49:23 +00:00
parent 7159ecd4cc
commit 12e6f12147
5 changed files with 91 additions and 70 deletions

View File

@ -8,17 +8,24 @@ import java.util.Hashtable;
/**
* Class to maintain probability distribution function for
* a single variable, using kernel estimates.
* <p>Class to maintain probability distribution function for
* a multivariate set, using kernel estimates.</p>
*
* <p>
* For more details on kernel estimation for computing probability distribution functions,
* see Kantz and Schreiber (below).
* </p>
*
* @author Joseph Lizier
* @see KernelEstimatorSingleVariate
* @see "H. Kantz and T. Schreiber, 'Nonlinear Time Series Analysis'.
* Cambridge, MA: Cambridge University Press, 1997"
* @author Joseph Lizier, <a href="mailto:joseph.lizier at gmail.com">joseph.lizier at gmail.com</>
*
*/
public class KernelEstimatorMultiVariate implements Cloneable {
protected double[] epsilon = null;
protected double[] epsilonInUse = null;
protected double[] suppliedKernelWidths = null;
protected double[] kernelWidthsInUse = null;
protected int dimensions = 1;
private int[] bins = null;
private boolean usingIntegerIndexBins = true;
@ -107,9 +114,9 @@ public class KernelEstimatorMultiVariate implements Cloneable {
*/
public void initialise(int dimensions, double epsilon) {
this.dimensions = dimensions;
this.epsilon = new double[dimensions];
this.suppliedKernelWidths = new double[dimensions];
for (int d = 0; d < dimensions; d++) {
this.epsilon[d] = epsilon;
this.suppliedKernelWidths[d] = epsilon;
}
finishInitialisation();
}
@ -121,9 +128,9 @@ public class KernelEstimatorMultiVariate implements Cloneable {
*/
public void initialise(double[] epsilon) {
dimensions = epsilon.length;
this.epsilon = new double[dimensions];
this.suppliedKernelWidths = new double[dimensions];
for (int d = 0; d < dimensions; d++) {
this.epsilon[d] = epsilon[d];
this.suppliedKernelWidths[d] = epsilon[d];
}
finishInitialisation();
}
@ -142,7 +149,7 @@ public class KernelEstimatorMultiVariate implements Cloneable {
multipliers = null;
rawData = null;
totalObservations = 0;
epsilonInUse = new double[dimensions];
kernelWidthsInUse = new double[dimensions];
}
/**
@ -172,11 +179,11 @@ public class KernelEstimatorMultiVariate implements Cloneable {
// it should expand with the standard deviation.
// This saves us from normalising all of the incoming data points!
std = MatrixUtils.stdDev(data, d);
epsilonInUse[d] = epsilon[d] * std;
kernelWidthsInUse[d] = suppliedKernelWidths[d] * std;
} else {
epsilonInUse[d] = epsilon[d];
kernelWidthsInUse[d] = suppliedKernelWidths[d];
}
bins[d] = (int) Math.ceil((max - mins[d]) / epsilonInUse[d]);
bins[d] = (int) Math.ceil((max - mins[d]) / kernelWidthsInUse[d]);
if (bins[d] == 0) {
// This means the min and max are exactly the same:
// for our purposes this is akin to requiring one bin here.
@ -196,7 +203,7 @@ public class KernelEstimatorMultiVariate implements Cloneable {
System.out.println("Dim: " + d + " => Max: " + max + ", min: " + mins[d] +
", bins: " + bins[d] +
(normalise ? ", std: " + std : "") +
", eps: " + epsilonInUse[d]);
", eps: " + kernelWidthsInUse[d]);
}
}
@ -774,7 +781,7 @@ public class KernelEstimatorMultiVariate implements Cloneable {
}
private int getBinIndex(double value, int dimension) {
int bin = (int) Math.floor((value - mins[dimension]) / epsilonInUse[dimension]);
int bin = (int) Math.floor((value - mins[dimension]) / kernelWidthsInUse[dimension]);
// Check for any rounding errors on the bin assignment:
if (bin >= bins[dimension]) {
bin = bins[dimension] - 1;
@ -915,7 +922,7 @@ public class KernelEstimatorMultiVariate implements Cloneable {
*/
public int stepKernel(double[] observation, double[] candidate) {
for (int d = 0; d < dimensions; d++) {
if (Math.abs(observation[d] - candidate[d]) > epsilonInUse[d]) {
if (Math.abs(observation[d] - candidate[d]) > kernelWidthsInUse[d]) {
return 0;
}
}

View File

@ -6,17 +6,23 @@ import java.util.Vector;
import java.util.Arrays;
/**
* Class to maintain probability distribution function for
* a single variable, using kernel estimates.
* <p>Class to maintain probability distribution function for
* a single variable, using kernel estimates.</p>
*
* <p>
* For more details on kernel estimation for computing probability distribution functions,
* see Kantz and Schreiber (below).
* </p>
*
* @author Joseph Lizier
* @see "H. Kantz and T. Schreiber, 'Nonlinear Time Series Analysis'.
* Cambridge, MA: Cambridge University Press, 1997"
* @author Joseph Lizier, <a href="mailto:joseph.lizier at gmail.com">joseph.lizier at gmail.com</>
*
*/
public class KernelEstimatorSingleVariate {
private double epsilon = 0.1;
private double epsilonInUse;
private double suppliedKernelWidth = 0.1;
private double kernelWidthInUse;
private double min = 0;
private double max = 0;
private int bins = 0;
@ -72,7 +78,7 @@ public class KernelEstimatorSingleVariate {
* @param epsilon
*/
public void initialise(double epsilon) {
this.epsilon = epsilon;
this.suppliedKernelWidth = epsilon;
sortedObservations = null;
}
@ -90,14 +96,14 @@ public class KernelEstimatorSingleVariate {
// it should expand with the standard deviation.
// This saves us from normalising all of the incoming data points!
double std = MatrixUtils.stdDev(data);
epsilonInUse = epsilon * std;
kernelWidthInUse = suppliedKernelWidth * std;
} else {
epsilonInUse = epsilon;
kernelWidthInUse = suppliedKernelWidth;
}
// Create the bins
Vector<TimeStampedObservation>[] observations = null;
bins = (int) Math.ceil((max - min) / epsilonInUse);
bins = (int) Math.ceil((max - min) / kernelWidthInUse);
if (bins == 0) {
// The max and min are the same.
// Should still have one bin here to put all the data in,
@ -207,7 +213,7 @@ public class KernelEstimatorSingleVariate {
// are no longer within epsilon of the given value.
int topIndex;
for (topIndex = sortedObservations[bin-1].length;
(topIndex > 0) && (sortedObservations[bin-1][topIndex-1].observation > observation - epsilonInUse);
(topIndex > 0) && (sortedObservations[bin-1][topIndex-1].observation > observation - kernelWidthInUse);
topIndex--) {
// This observation is within epsilon.
// Before adding to the count just check if it's a dynamic correlation if required:
@ -233,7 +239,7 @@ public class KernelEstimatorSingleVariate {
int bottomIndex;
for (bottomIndex = 0;
(bottomIndex < sortedObservations[bin+1].length) &&
(sortedObservations[bin+1][bottomIndex].observation < observation + epsilonInUse);
(sortedObservations[bin+1][bottomIndex].observation < observation + kernelWidthInUse);
bottomIndex++) {
// This observation is within epsilon.
// Before adding to the count just check if it's a dynamic correlation if required:
@ -256,7 +262,7 @@ public class KernelEstimatorSingleVariate {
}
private int getBinIndex(double value) {
int bin = (int) Math.floor((value - min) / epsilonInUse);
int bin = (int) Math.floor((value - min) / kernelWidthInUse);
// Check for any rounding errors on the bin assignment:
if (bin >= bins) {
bin = bins - 1;

View File

@ -13,13 +13,16 @@ import infodynamics.utils.MatrixUtils;
* function on the joint history.
* </p>
*
* @author Joseph Lizier joseph.lizier at gmail.com
* @see KernelEstimatorMultiVariate
* @see "H. Kantz and T. Schreiber, 'Nonlinear Time Series Analysis'.
* Cambridge, MA: Cambridge University Press, 1997"
* @author Joseph Lizier, <a href="mailto:joseph.lizier at gmail.com">joseph.lizier at gmail.com</>
*
*/
public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate {
private double epsilonSource;
private double epsilonSourceInUse;
private double suppliedKernelWidthSource;
private double kernelWidthSourceInUse;
private double[] destNext;
private double[] source;
@ -42,13 +45,13 @@ public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate
public void initialise(int dimensions, double epsilon) {
super.initialise(dimensions, epsilon);
this.epsilonSource = epsilon;
this.suppliedKernelWidthSource = epsilon;
}
public void initialise(int dimensions, double epsilonDest,
double epsilonSource) {
super.initialise(dimensions, epsilonDest);
this.epsilonSource = epsilonSource;
this.suppliedKernelWidthSource = epsilonSource;
}
public void setObservations(double[][] destPastVectors,
@ -61,9 +64,9 @@ public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate
if (normalise) {
double std = MatrixUtils.stdDev(source);
epsilonSourceInUse = epsilonSource * std;
kernelWidthSourceInUse = suppliedKernelWidthSource * std;
} else {
epsilonSourceInUse = epsilonSource;
kernelWidthSourceInUse = suppliedKernelWidthSource;
}
this.source = source;
@ -107,7 +110,7 @@ public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate
}
public void setEpsSource(double epsilonSource) {
this.epsilonSource = epsilonSource;
this.suppliedKernelWidthSource = epsilonSource;
}
/**
@ -118,13 +121,13 @@ public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate
*/
protected void correlatedPointAddedCallback(int correlatedTimeStep) {
boolean sourceMatches = false;
if (Math.abs(sourceObs - source[correlatedTimeStep]) <= epsilonSourceInUse) {
if (Math.abs(sourceObs - source[correlatedTimeStep]) <= kernelWidthSourceInUse) {
countPastSource++;
sourceMatches = true;
}
// The epsilons across the destination variables should all be approximately
// equal, so just use the first one.
if (Math.abs(destNextObs - destNext[correlatedTimeStep]) <= epsilonInUse[0]) {
if (Math.abs(destNextObs - destNext[correlatedTimeStep]) <= kernelWidthsInUse[0]) {
countNextPast++;
if (sourceMatches) {
countNextPastSource++;
@ -139,13 +142,13 @@ public class KernelEstimatorTransferEntropy extends KernelEstimatorMultiVariate
*/
protected void correlatedPointRemovedCallback(int removedCorrelatedTimeStep) {
boolean sourceMatches = false;
if (Math.abs(sourceObs - source[removedCorrelatedTimeStep]) <= epsilonSourceInUse) {
if (Math.abs(sourceObs - source[removedCorrelatedTimeStep]) <= kernelWidthSourceInUse) {
countPastSource--;
sourceMatches = true;
}
// The epsilons across the destination variables should all be approximately
// equal, so just use the first one.
if (Math.abs(destNextObs - destNext[removedCorrelatedTimeStep]) <= epsilonInUse[0]) {
if (Math.abs(destNextObs - destNext[removedCorrelatedTimeStep]) <= kernelWidthsInUse[0]) {
countNextPast--;
if (sourceMatches) {
countNextPastSource--;

View File

@ -6,28 +6,33 @@ package infodynamics.measures.continuous.kernel;
import infodynamics.utils.MatrixUtils;
/**
* <p>Kernel estimator for use with the transfer entropy.</p>
* <p>Kernel estimator for use with the transfer entropy on
* multivariate source and destination.</p>
*
* <p>Extends KernelEstimatorMultiVariate, using the super class to manage the history of the destination
* variable, and adds the next state and source on top of this. Any calls to the super class methods will only
* function on the joint history.
* </p>
*
* @author Joseph Lizier joseph.lizier at gmail.com
* @see KernelEstimatorMultiVariate
* @see KernelEstimatorTransferEntropy
* @see "H. Kantz and T. Schreiber, 'Nonlinear Time Series Analysis'.
* Cambridge, MA: Cambridge University Press, 1997"
* @author Joseph Lizier, <a href="mailto:joseph.lizier at gmail.com">joseph.lizier at gmail.com</>
*
*/
public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorMultiVariate {
private int sourceDimensions;
private double[] epsilonSource;
private double[] epsilonSourceInUse;
private double[] suppliedKernelWidthSource;
private double[] kernelWidthSourceInUse;
// Keep a separate epsilon for the destination next state, just in case
// we're doing something funky and using different variables to track
// the destination's past and next state (e.g. in swarm analysis).
private double epsilonDestNextFixed;
private double[] epsilonDestNext;
private double[] epsilonDestNextInUse;
private double suppliedKernelWidthDestNextFixed;
private double[] suppliedKernelWidthDestNext;
private double[] kernelWidthDestNextInUse;
private double[][] destNext;
private double[][] source;
@ -56,24 +61,24 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
double epsilonDest, double epsilonSource) {
super.initialise(destDimensionsWithPast, epsilonDest);
this.sourceDimensions = sourceDimensions;
this.epsilonSource = new double[sourceDimensions];
this.suppliedKernelWidthSource = new double[sourceDimensions];
for (int d = 0; d < sourceDimensions; d++) {
this.epsilonSource[d] = epsilonSource;
this.suppliedKernelWidthSource[d] = epsilonSource;
}
epsilonSourceInUse = new double[sourceDimensions];
kernelWidthSourceInUse = new double[sourceDimensions];
// Track that we're using a fixed epsilon for destination next
epsilonDestNext = null;
epsilonDestNextFixed = epsilonDest;
suppliedKernelWidthDestNext = null;
suppliedKernelWidthDestNextFixed = epsilonDest;
}
public void initialise(double[] epsilonDest,
double[] epsilonSource) {
super.initialise(epsilonDest);
this.epsilonSource = epsilonSource;
epsilonSourceInUse = new double[sourceDimensions];
this.suppliedKernelWidthSource = epsilonSource;
kernelWidthSourceInUse = new double[sourceDimensions];
// Assume that we are doing an ordinary TE computation (dest past
// and next state are the same variable)
epsilonDestNext = epsilonDest;
suppliedKernelWidthDestNext = epsilonDest;
}
/**
@ -96,11 +101,11 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
if (normalise) {
for (int d = 0; d < sourceDimensions; d++) {
double std = MatrixUtils.stdDev(source, d);
epsilonSourceInUse[d] = epsilonSource[d] * std;
kernelWidthSourceInUse[d] = suppliedKernelWidthSource[d] * std;
}
} else {
for (int d = 0; d < sourceDimensions; d++) {
epsilonSourceInUse[d] = epsilonSource[d];
kernelWidthSourceInUse[d] = suppliedKernelWidthSource[d];
}
}
@ -109,22 +114,22 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
// we're doing something funky with different dest past and next
// state variables).
int destNextDimensions = destNext[0].length;
if (epsilonDestNext == null) {
if (suppliedKernelWidthDestNext == null) {
// We must be using a fixed epsilon
epsilonDestNext = new double[destNextDimensions];
suppliedKernelWidthDestNext = new double[destNextDimensions];
for (int d = 0; d < destNextDimensions; d++) {
epsilonDestNext[d] = epsilonDestNextFixed;
suppliedKernelWidthDestNext[d] = suppliedKernelWidthDestNextFixed;
}
}
epsilonDestNextInUse = new double[destNextDimensions];
kernelWidthDestNextInUse = new double[destNextDimensions];
if (normalise) {
for (int d = 0; d < destNextDimensions; d++) {
double std = MatrixUtils.stdDev(destNext, d);
epsilonDestNextInUse[d] = epsilonDestNext[d] * std;
kernelWidthDestNextInUse[d] = suppliedKernelWidthDestNext[d] * std;
}
} else {
for (int d = 0; d < destNextDimensions; d++) {
epsilonDestNextInUse[d] = epsilonDestNext[d];
kernelWidthDestNextInUse[d] = suppliedKernelWidthDestNext[d];
}
}
@ -169,7 +174,7 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
}
public void setEpsSource(double[] epsilonSource) {
this.epsilonSource = epsilonSource;
this.suppliedKernelWidthSource = epsilonSource;
}
/**
@ -180,7 +185,7 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
*/
protected void correlatedPointAddedCallback(int correlatedTimeStep) {
boolean sourceMatches = false;
if (stepKernel(sourceObs, source[correlatedTimeStep], epsilonSourceInUse) > 0) {
if (stepKernel(sourceObs, source[correlatedTimeStep], kernelWidthSourceInUse) > 0) {
countPastSource++;
sourceMatches = true;
}
@ -191,7 +196,7 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
// is achieved simply by passing in epsilon, since
// stepKernel just uses the first dimensions elements
// of the widths argument).
if (stepKernel(destNextObs, destNext[correlatedTimeStep], epsilonDestNextInUse) > 0) {
if (stepKernel(destNextObs, destNext[correlatedTimeStep], kernelWidthDestNextInUse) > 0) {
countNextPast++;
if (sourceMatches) {
countNextPastSource++;
@ -206,7 +211,7 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
*/
protected void correlatedPointRemovedCallback(int removedCorrelatedTimeStep) {
boolean sourceMatches = false;
if (stepKernel(sourceObs, source[removedCorrelatedTimeStep], epsilonSourceInUse) > 0) {
if (stepKernel(sourceObs, source[removedCorrelatedTimeStep], kernelWidthSourceInUse) > 0) {
countPastSource--;
sourceMatches = true;
}
@ -217,7 +222,7 @@ public class KernelEstimatorTransferEntropyMultiVariate extends KernelEstimatorM
// is achieved simply by passing in epsilon, since
// stepKernel just uses the first dimensions elements
// of the widths argument).
if (stepKernel(destNextObs, destNext[removedCorrelatedTimeStep], epsilonDestNextInUse) > 0) {
if (stepKernel(destNextObs, destNext[removedCorrelatedTimeStep], kernelWidthDestNextInUse) > 0) {
countNextPast--;
if (sourceMatches) {
countNextPastSource--;

View File

@ -143,7 +143,7 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKernel implements
double[][] obsForThisDiscValue = MatrixUtils.extractSelectedPointsMatchingCondition(
continuousObservations, discreteObservations, i, discCounts[i]);
// Set the kernel width for the relevant kernel estimator:
mvkeForEachDiscrete[i].initialise(mvke.epsilonInUse);
mvkeForEachDiscrete[i].initialise(mvke.kernelWidthsInUse);
// Set these observations for the relevant kernel estimator:
mvkeForEachDiscrete[i].setObservations(obsForThisDiscValue);
}
@ -599,6 +599,6 @@ public class MutualInfoCalculatorMultiVariateWithDiscreteKernel implements
*/
public double[] getKernelWidthsInUse() {
// Return a copy so that the user can't mess with it
return Arrays.copyOf(mvke.epsilonInUse, mvke.epsilonInUse.length);
return Arrays.copyOf(mvke.kernelWidthsInUse, mvke.kernelWidthsInUse.length);
}
}