diff --git a/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java index 7511ea25..c8d8aefa 100755 --- a/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java @@ -90,6 +90,13 @@ public static ActiveInformationCalculatorDiscrete newInstance(int base, int hist return new ActiveInformationCalculatorDiscrete(base, history); } + /** + * Construct a new instance with default base 2 and history 1 + */ + public ActiveInformationCalculatorDiscrete() { + super(); + } + /** * Construct a new instance * @@ -104,10 +111,14 @@ public ActiveInformationCalculatorDiscrete(int base, int history) { @Override public void initialise() { - super.initialise(); - aisComputed = false; + initialise(base, k); } + public void initialise(int base, int history) { + super.initialise(base, history); + aisComputed = false; + } + @Override public void addObservations(int states[]) { int timeSteps = states.length; diff --git a/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java index f7d9e28e..40bdda3d 100755 --- a/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java @@ -82,6 +82,13 @@ public static EntropyCalculatorDiscrete newInstance(int blocksize, int base) { return new BlockEntropyCalculatorDiscrete(blocksize, base); } + /** + * Construct an instance with default blocksize 1 and base 2 + */ + public BlockEntropyCalculatorDiscrete() { + this(1, 2); + } + /** * Construct a new instance * @@ -92,7 +99,10 @@ public static EntropyCalculatorDiscrete newInstance(int blocksize, int base) { public BlockEntropyCalculatorDiscrete(int blocksize, int base) { super(base); - + resetBlocksize(blocksize); + } + + private void resetBlocksize(int blocksize) { this.blocksize = blocksize; base_power_blocksize = MathsUtils.power(base, blocksize); @@ -102,19 +112,7 @@ public BlockEntropyCalculatorDiscrete(int blocksize, int base) { if (blocksize > Math.log(Integer.MAX_VALUE) / log_base) { throw new RuntimeException("Base and blocksize combination too large"); } - - // Create storage for counts of observations. - // We're recreating stateCount, which was created in super() - // however the super() didn't create it for blocksize > 1 - try { - stateCount = new int[MathsUtils.power(base, blocksize)]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a Runtimexception - throw new RuntimeException("Requested memory for the base " + - base + " and k=" + blocksize+ " is too large for the JVM at this time", e); - } - + // Create constants for tracking stateValues maxShiftedValue = new int[base]; for (int v = 0; v < base; v++) { @@ -122,10 +120,35 @@ public BlockEntropyCalculatorDiscrete(int blocksize, int base) { } } + public void initialise(int blocksize, int base) { + boolean baseOrBlocksizeChanged = (this.blocksize != blocksize) || (this.base != base); + + super.initialise(base); + if (baseOrBlocksizeChanged) { + resetBlocksize(blocksize); + } + + if (baseOrBlocksizeChanged || (stateCount == null)) { + // Create storage for counts of observations. + // We're recreating stateCount, which was created in super() + // however the super() didn't create it for blocksize > 1 + try { + stateCount = new int[MathsUtils.power(base, blocksize)]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a Runtimexception + throw new RuntimeException("Requested memory for the base " + + base + " and k=" + blocksize+ " is too large for the JVM at this time", e); + } + } else { + // Storage for sample counts exists and needs to be reset + MatrixUtils.fill(stateCount, 0); + } + } + @Override - public void initialise(){ - super.initialise(); - MatrixUtils.fill(stateCount, 0); + public void initialise() { + initialise(blocksize, base); } @Override diff --git a/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java index 66a02a19..56ee18d5 100755 --- a/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java @@ -72,9 +72,16 @@ public class CombinedActiveEntRateLocalResults { } public CombinedActiveEntRateCalculatorDiscrete() { - super(); + // TODO Make this inherit from InfoMeasureCalculatorDiscrete } + /** + * Initialise with the existing history and base + */ + public void initialise() { + initialise(k, base); + } + /** * Initialise calculator, preparing to take observation sets in * Should be called prior to any of the addObservations() methods. diff --git a/java/source/infodynamics/measures/discrete/ConditionalMutualInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ConditionalMutualInformationCalculatorDiscrete.java index a86bf680..0f144e07 100755 --- a/java/source/infodynamics/measures/discrete/ConditionalMutualInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ConditionalMutualInformationCalculatorDiscrete.java @@ -98,6 +98,15 @@ public static ConditionalMutualInformationCalculatorDiscrete newInstance(int bas return new ConditionalMutualInformationCalculatorDiscrete(base1, base2, condBase); } + /** + * Construct a new instance with default bases + */ + public ConditionalMutualInformationCalculatorDiscrete() { + + // Create super object, just with first base defaulting to 2 + this(2, 2, 2); + } + /** * Construct a new instance * @@ -110,39 +119,68 @@ public ConditionalMutualInformationCalculatorDiscrete(int base1, int base2, int // Create super object, just with first base super(base1); - + changeBases(base1, base2, condBase); + } + + /** + * Common code to be called when bases are changed (does not update arrays though) + * + * @param base1 + * @param base2 + * @param condBase + */ + private boolean changeBases(int base1, int base2, int condBase) { + boolean basesChanged = false; + if ((this.base1 != base1) || (this.base2 != base2) || (this.condBase != condBase)) { + basesChanged = true; + } // Store the bases this.base1 = base1; this.base2 = base2; this.condBase = condBase; - - // Create storage for extra counts of observations - try { - firstSecondCondCount = new int[base1][base2][condBase]; - firstCondCount = new int[base1][condBase]; - secondCondCount = new int[base2][condBase]; - condCount = new int[condBase]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as an Exception - throw new RuntimeException("Requested memory for the MI bases (" + - base1 + ", " + base2 + ", " + condBase + - ") is too large for the JVM at this time", e); - } + return basesChanged; } - + @Override public void initialise(){ - super.initialise(); + initialise(base1, base2, condBase); + } + + /** + * Initialise with new bases + * + * @param base1 + * @param base2 + * @param condBase + */ + public void initialise(int base1, int base2, int condBase){ + boolean basesChanged = changeBases(base1, base2, condBase); + super.initialise(base1); condMiComputed = false; - - MatrixUtils.fill(firstSecondCondCount, 0); - MatrixUtils.fill(firstCondCount, 0); - MatrixUtils.fill(secondCondCount, 0); - MatrixUtils.fill(condCount,0); + + if (basesChanged || (firstSecondCondCount == null)) { + // Create storage for extra counts of observations + try { + firstSecondCondCount = new int[base1][base2][condBase]; + firstCondCount = new int[base1][condBase]; + secondCondCount = new int[base2][condBase]; + condCount = new int[condBase]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as an Exception + throw new RuntimeException("Requested memory for the MI bases (" + + base1 + ", " + base2 + ", " + condBase + + ") is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(firstSecondCondCount, 0); + MatrixUtils.fill(firstCondCount, 0); + MatrixUtils.fill(secondCondCount, 0); + MatrixUtils.fill(condCount,0); + } } - + /** * Add observations for the given var1,var2,cond tuples * of the variables diff --git a/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java index fbdc12fb..e593b7aa 100644 --- a/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java @@ -77,12 +77,6 @@ * be incorrect. *

* - *

Note for developers: Ideally, this class would extend ContextOfPastMeasure, however - * by conditioning on other info contributors, we need to alter - * the arrays pastCount and nextPastCount to consider all - * conditioned variables (i.e. other sources) also. - *

- * * TODO Add methods for passing in single time series. * This is done for addObservations, but not other routines. * @@ -111,19 +105,17 @@ * */ public class ConditionalTransferEntropyCalculatorDiscrete - extends InfoMeasureCalculatorDiscrete + extends ContextOfPastMeasureCalculatorDiscrete implements EmpiricalNullDistributionComputer { - protected int k = 0; // history length k. protected int base_others = 0; // base of the conditional variables - protected int base_power_k = 0; protected int base_power_num_others = 0; protected int numOtherInfoContributors = 0; + protected int[][][][] sourceDestPastOthersCount = null; // count for (i-j[n],i[n+1],i[n]^k,others) tuples protected int[][][] sourcePastOthersCount = null; // count for (i-j[n],i[n]^k,others) tuples protected int[][][] destPastOthersCount = null; // Count for (i[n+1], i[n]^k,others) tuples protected int[][] pastOthersCount = null; // Count for (i[n]^k,others) - protected int[] maxShiftedValue = null; // states * (base^(k-1)) /** * First time step at which we can take an observation @@ -159,6 +151,13 @@ public class ConditionalTransferEntropyCalculatorDiscrete */ } + /** + * Construct with default parameters of base 2, history 1 and 1 other contributor + */ + public ConditionalTransferEntropyCalculatorDiscrete() { + this(2, 1, 1, 2); + } + /** * Construct a new instance. This constructor allows the source and * destination variables to have a different base from the other contributors @@ -177,53 +176,8 @@ public class ConditionalTransferEntropyCalculatorDiscrete public ConditionalTransferEntropyCalculatorDiscrete (int base, int history, int numOtherInfoContributors, int base_others) { - super(base); - - k = history; - this.base_others = base_others; - this.numOtherInfoContributors = numOtherInfoContributors; - base_power_k = MathsUtils.power(base, k); - base_power_num_others = MathsUtils.power(base_others, numOtherInfoContributors); - - // Relaxing this assumption so we can use this calculation as - // a time-lagged conditional MI at will: - //if (k < 1) { - // throw new RuntimeException("History k " + history + " is not >= 1 a ContextOfPastMeasureCalculator"); - //} - - // Which time step do we start taking observations from? - // Normally this is k (to allow k previous time steps) - // but if k==0 (becoming a lagged MI), it's 1. - startObservationTime = Math.max(k, 1); - - // check that we can convert the base tuple into an integer ok - if (k > Math.log(Integer.MAX_VALUE) / log_base) { - throw new RuntimeException("Base and history combination too large"); - } - if (numOtherInfoContributors < 1) { - throw new RuntimeException("Number of other info contributors < 1 for CompleteTECalculator"); - } - - // Create storage for counts of observations - try { - sourceDestPastOthersCount = new int[base][base][base_power_k][base_power_num_others]; - sourcePastOthersCount = new int[base][base_power_k][base_power_num_others]; - destPastOthersCount = new int [base][base_power_k][base_power_num_others]; - pastOthersCount = new int[base_power_k][base_power_num_others]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base " + - base + ", k=" + k + " num of others " + numOtherInfoContributors + - " with base " + base_others + ") is too large for the JVM at this time", e); - } - - // Create constants for tracking prevValues - maxShiftedValue = new int[base]; - for (int v = 0; v < base; v++) { - maxShiftedValue[v] = v * MathsUtils.power(base, k-1); - } - + super(base, history, true); + updateParameters(base, history, numOtherInfoContributors, base_others); } /** @@ -243,14 +197,78 @@ public class ConditionalTransferEntropyCalculatorDiscrete this(base, history, numOtherInfoContributors, base); } + /** + * Private method to update the parameters, + * save for parts only concerning base and history + * which are to be handled before this by the super + * + * @param base + * @param history + * @param numOtherInfoContributors + * @param base_others + */ + private void updateParameters(int base, int history, + int numOtherInfoContributors, int base_others) { + + this.base_others = base_others; + this.numOtherInfoContributors = numOtherInfoContributors; + base_power_num_others = MathsUtils.power(base_others, numOtherInfoContributors); + + // Which time step do we start taking observations from? + // Normally this is k (to allow k previous time steps) + // but if k==0 (becoming a lagged MI), it's 1. + startObservationTime = Math.max(k, 1); + + if (numOtherInfoContributors < 1) { + throw new RuntimeException("Number of other info contributors < 1 for CompleteTECalculator"); + } + + } + + /** + * Initialise with (potentially) new parameters + * + * @param base + * @param history + * @param numOtherInfoContributors + * @param base_others + */ + public void initialise(int base, int history, + int numOtherInfoContributors, int base_others) { + + boolean paramsChanged = (this.base != base) || (k != history) || + (this.numOtherInfoContributors != numOtherInfoContributors) || + (this.base_others != base_others); + super.initialise(base, history); + if (paramsChanged) { + updateParameters(base, history, numOtherInfoContributors, base_others); + } + + if (paramsChanged || (sourceDestPastOthersCount == null)) { + // Create new storage for counts of observations + try { + sourceDestPastOthersCount = new int[base][base][base_power_k][base_power_num_others]; + sourcePastOthersCount = new int[base][base_power_k][base_power_num_others]; + destPastOthersCount = new int [base][base_power_k][base_power_num_others]; + pastOthersCount = new int[base_power_k][base_power_num_others]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + ", k=" + k + " num of others " + numOtherInfoContributors + + " with base " + base_others + ") is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(sourceDestPastOthersCount, 0); + MatrixUtils.fill(sourcePastOthersCount, 0); + MatrixUtils.fill(destPastOthersCount, 0); + MatrixUtils.fill(pastOthersCount, 0); + } + } + @Override public void initialise(){ - super.initialise(); - - MatrixUtils.fill(sourceDestPastOthersCount, 0); - MatrixUtils.fill(sourcePastOthersCount, 0); - MatrixUtils.fill(destPastOthersCount, 0); - MatrixUtils.fill(pastOthersCount, 0); + initialise(base, k, numOtherInfoContributors, base_others); } /** diff --git a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java index cad6374e..2ec38e35 100755 --- a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java @@ -65,6 +65,14 @@ public abstract class ContextOfPastMeasureCalculatorDiscrete extends */ protected int base_power_k = 0; + /** + * Construct an instance with default base 2 and history 1 + * and not keeping sample counts + */ + public ContextOfPastMeasureCalculatorDiscrete() { + this(2, 1, false); + } + /** * Construct an instance * @@ -90,7 +98,18 @@ public ContextOfPastMeasureCalculatorDiscrete(int base, int history) { */ protected ContextOfPastMeasureCalculatorDiscrete(int base, int history, boolean dontCreateObsStorage) { super(base); + resetHistory(history); + + noObservationStorage = dontCreateObsStorage; + } + /** + * Should be called after {@link #resetBase(int)} has just been called. + * + * @param history + */ + private void resetHistory(int history) { + k = history; base_power_k = MathsUtils.power(base, k); @@ -108,31 +127,51 @@ protected ContextOfPastMeasureCalculatorDiscrete(int base, int history, boolean for (int v = 0; v < base; v++) { maxShiftedValue[v] = v * MathsUtils.power(base, k-1); } - - noObservationStorage = dontCreateObsStorage; - if (!dontCreateObsStorage) { - // Create storage for counts of observations - try { - nextPastCount = new int[base][base_power_k]; - pastCount = new int[base_power_k]; - nextCount = new int[base]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base " + - base + " and k=" + k + " is too large for the JVM at this time", e); - } - } } @Override public void initialise() { - super.initialise(); + initialise(base, k); + } + + /** + * Initialise the calculator with (potentially new) + * specified base and history + * + * @param base + * @param history + */ + public void initialise(int base, int history) { + boolean baseOrHistoryChanged = false; + if ((this.base != base) || (k != history)) { + baseOrHistoryChanged = true; + } + + super.initialise(base); + if (baseOrHistoryChanged) { + resetHistory(history); + } if (!noObservationStorage) { - MatrixUtils.fill(nextPastCount, 0); - MatrixUtils.fill(pastCount, 0); - MatrixUtils.fill(nextCount, 0); + // We manage storage for relevant sample counts here: + if (baseOrHistoryChanged || (nextPastCount == null)) { + // Create new storage for counts of observations + try { + nextPastCount = new int[base][base_power_k]; + pastCount = new int[base_power_k]; + nextCount = new int[base]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + " and k=" + k + " is too large for the JVM at this time", e); + } + } else { + // Storage for sample counts exists and needs to be reset + MatrixUtils.fill(nextPastCount, 0); + MatrixUtils.fill(pastCount, 0); + MatrixUtils.fill(nextCount, 0); + } } } diff --git a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java index d650080b..1577ffde 100755 --- a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java @@ -83,6 +83,13 @@ public static EntropyCalculatorDiscrete newInstance(int base) { return new EntropyCalculatorDiscrete(base); } + /** + * Construct a new instance with default base of 2 + */ + public EntropyCalculatorDiscrete() { + this(2); + } + /** * Contruct a new instance * @@ -90,25 +97,36 @@ public static EntropyCalculatorDiscrete newInstance(int base) { * E.g. binary variables are in base-2. */ public EntropyCalculatorDiscrete(int base) { - super(base); + } + + /** + * Initialise with new base + * + * @param base + */ + public void initialise(int base){ + boolean baseChanged = (this.base != base); + super.initialise(base); - // Create storage for counts of observations - try { - stateCount = new int[base]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base (" + - base + ") is too large for the JVM at this time", e); + if (baseChanged || (stateCount == null)) { + // Create storage for counts of observations + try { + stateCount = new int[base]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base (" + + base + ") is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(stateCount, 0); } - } - + @Override public void initialise(){ - super.initialise(); - MatrixUtils.fill(stateCount, 0); + initialise(base); } @Override diff --git a/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java index 4fd84e2e..9080c30a 100755 --- a/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java @@ -77,6 +77,13 @@ public static EntropyRateCalculatorDiscrete newInstance(int base, int history) { return new EntropyRateCalculatorDiscrete(base, history); } + /** + * Construct a new instance with default base 2 and history 1 + */ + public EntropyRateCalculatorDiscrete() { + super(); + } + /** * Construct a new instance * diff --git a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java index 2825b39b..2a0ecf42 100755 --- a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java @@ -97,6 +97,13 @@ public abstract class InfoMeasureCalculatorDiscrete { */ protected boolean debug = false; + /** + * Construct an instance with default base of 2 + */ + protected InfoMeasureCalculatorDiscrete() { + this(2); + } + /** * Construct an instance * @@ -104,7 +111,10 @@ public abstract class InfoMeasureCalculatorDiscrete { * E.g. binary variables are in base-2. */ protected InfoMeasureCalculatorDiscrete(int base) { - + resetBase(base); + } + + protected void resetBase(int base) { this.base = base; log_base = Math.log(base); @@ -122,15 +132,26 @@ protected InfoMeasureCalculatorDiscrete(int base) { /** * Initialise the calculator for re-use with new observations. * (Child classes should clear the existing PDFs) + * @throws Exception */ - public void initialise(){ + public void initialise() { + initialise(base); + } + + /** + * Initialise the calculator for re-use with new observations, + * and a new base. + * (Child classes should clear the existing PDFs) + */ + public void initialise(int base){ + resetBase(base); average = 0.0; max = 0.0; min = 0.0; std = 0.0; observations = 0; } - + /** * Return the measure last calculated in a call to * {@link #computeAverageLocalOfObservations()} diff --git a/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java index fe53bcbd..56fadac3 100755 --- a/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java @@ -68,6 +68,13 @@ public class MultiInformationCalculatorDiscrete extends InfoMeasureCalculatorDis private int jointStates; private boolean checkedFirst = false; + /** + * Construct new instance with default base 2 and numVars 2 + */ + public MultiInformationCalculatorDiscrete() { + this(2, 2); + } + /** * Construct an instance * @@ -78,25 +85,55 @@ public class MultiInformationCalculatorDiscrete extends InfoMeasureCalculatorDis */ public MultiInformationCalculatorDiscrete(int base, int numVars) { super(base); + changeParams(base, numVars); + } + + /** + * Update the parameters using numVars + * + * @param base + * @param numVars + * @return + */ + private boolean changeParams(int base, int numVars) { + boolean paramsChanged = (this.base != base) || (this.numVars != numVars); + this.numVars = numVars; jointStates = MathsUtils.power(base, numVars); - try { - jointCount = new int[jointStates]; - marginalCounts = new int[numVars][base]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base " + - base + " with " + numVars + - " variables is too large for the JVM at this time", e); - } + return paramsChanged; } + + /** + * Initialise (possible) updating the base and number of variables + * + * @param base + * @param numVars + */ + public void initialise(int base, int numVars) { + boolean paramsChanged = changeParams(base, numVars); + super.initialise(base); + if(paramsChanged || (jointCount == null)) { + // Create storage for counts of observations + try { + jointCount = new int[jointStates]; + marginalCounts = new int[numVars][base]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + " with " + numVars + + " variables is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(jointCount, 0); + MatrixUtils.fill(marginalCounts, 0); + } + } + @Override public void initialise(){ - super.initialise(); - MatrixUtils.fill(jointCount, 0); - MatrixUtils.fill(marginalCounts, 0); + initialise(base, numVars); } /** diff --git a/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java index 0a20ccfe..7cb0c4e7 100755 --- a/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java @@ -79,6 +79,16 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi protected boolean miComputed = false; + /** + * Construct a new MI calculator with default bases of 2 and time difference of 0 + * between the variables + * + * @throws Exception + */ + public MutualInformationCalculatorDiscrete() throws Exception { + this(2); + } + /** * Construct a new MI calculator with default time difference of 0 * between the variables @@ -109,35 +119,78 @@ public MutualInformationCalculatorDiscrete(int base1, int base2, int timeDiff) t // Create super object, just with first base super(base1); + changeBases(base1, base2, timeDiff); + } + + /** + * Common code to be called when bases are changed (does not update arrays though) + * + * @param base1 + * @param base2 + * @param timeDiff + * @throws Exception + */ + private boolean changeBases(int base1, int base2, int timeDiff) throws Exception { + boolean basesChanged = false; + if ((this.base1 != base1) || (this.base2 != base2)) { + basesChanged = true; + } + // Store the bases this.base1 = base1; this.base2 = base2; - + if (timeDiff < 0) { throw new Exception("timeDiff must be >= 0"); } this.timeDiff = timeDiff; + + return basesChanged; + } + + @Override + public void initialise() { try { - jointCount = new int[base1][base2]; - iCount = new int[base1]; - jCount = new int[base2]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the MI bases (" + - base1 + ", " + base2 + ") is too large for the JVM at this time", e); + initialise(base1, base2, timeDiff); + } catch (Exception e) { + // The only possible (non runtime) exception here is that the timeDiff was < 0 + // which we've already checked, so we can cast this as a Runtime Exception + throw new RuntimeException(e); } } - @Override - public void initialise(){ - super.initialise(); - miComputed = false; - MatrixUtils.fill(iCount, 0); - MatrixUtils.fill(jCount, 0); - MatrixUtils.fill(jointCount, 0); + /** + * Initialise with new bases and time diff + * + * @param base1 + * @param base2 + * @param timeDiff + * @throws Exception + */ + public void initialise(int base1, int base2, int timeDiff) throws Exception { + boolean basesChanged = changeBases(base1, base2, timeDiff); + super.initialise(base1); + + if (basesChanged || (jointCount == null)) { + try { + jointCount = new int[base1][base2]; + iCount = new int[base1]; + jCount = new int[base2]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the MI bases (" + + base1 + ", " + base2 + ") is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(iCount, 0); + MatrixUtils.fill(jCount, 0); + MatrixUtils.fill(jointCount, 0); + } + miComputed = false; + } - + /** * {@inheritDoc} * diff --git a/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java index fc55bc2c..1d46f7f5 100755 --- a/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java @@ -18,7 +18,6 @@ package infodynamics.measures.discrete; -import infodynamics.utils.MathsUtils; import infodynamics.utils.MatrixUtils; /** @@ -63,8 +62,7 @@ * * * - *

TODO Inherit from {@link SingleAgentMeasureDiscreteInContextOfPastCalculator} - * as {@link ActiveInformationCalculatorDiscrete} does; Tidy up the Javadocs for + *

TODO Tidy up the Javadocs for * the methods, which are somewhat preliminary

* *

References:
@@ -82,22 +80,8 @@ * @author Joseph Lizier (email, * www) */ -public class PredictiveInformationCalculatorDiscrete { +public class PredictiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator { - private double average = 0.0; - private double max = 0.0; - private double min = 0.0; - private int observations = 0; - private int k = 0; // history/future block length k. - private int numDiscreteValues = 0; // number of individual states. - private int[][] jointCount = null; // Count for (x[t+1], x[t]) tuples - private int[] prevCount = null; // Count for x[t] - private int[] nextCount = null; // Count for x[t+1] - private int[] maxShiftedValue = null; // states * (base^(history-1)) - - private int base_power_k = 0; - private double log_base = 0; - /** * User was formerly forced to create new instances through this factory method. * Retained for backwards compatibility. @@ -111,6 +95,13 @@ public static PredictiveInformationCalculatorDiscrete newInstance(int numDiscret return new PredictiveInformationCalculatorDiscrete(numDiscreteValues, blockLength); } + /** + * Construct a new instance with default base 2 and history 1 + */ + public PredictiveInformationCalculatorDiscrete() { + this(2, 1); + } + /** * Construct a new instance * @@ -120,55 +111,38 @@ public static PredictiveInformationCalculatorDiscrete newInstance(int numDiscret * this is k in Schreiber's notation. */ public PredictiveInformationCalculatorDiscrete(int numDiscreteValues, int blockLength) { - super(); - - this.numDiscreteValues = numDiscreteValues; - k = blockLength; - base_power_k = MathsUtils.power(numDiscreteValues, k); - log_base = Math.log(numDiscreteValues); - - if (blockLength < 1) { - throw new RuntimeException("History k " + blockLength + " is not >= 1 for Predictive information calculator"); - } - if (k > Math.log(Integer.MAX_VALUE) / log_base) { - throw new RuntimeException("Base and history combination too large"); - } - - // Create storage for counts of observations - try { - jointCount = new int[base_power_k][base_power_k]; - prevCount = new int[base_power_k]; - nextCount = new int[base_power_k]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base " + - numDiscreteValues + " with k=" + blockLength + - ") is too large for the JVM at this time", e); - } - - // Create constants for tracking prevValues and nextValues - maxShiftedValue = new int[numDiscreteValues]; - for (int v = 0; v < numDiscreteValues; v++) { - maxShiftedValue[v] = v * MathsUtils.power(numDiscreteValues, k-1); + super(numDiscreteValues, blockLength, true); + } + + @Override + public void initialise(int base, int blockLength) { + boolean baseOrHistoryChanged = (this.base != base) || (k != blockLength); + super.initialise(base, blockLength); + + if (baseOrHistoryChanged || (nextPastCount == null)) { + // Create new storage for counts of observations. + // Need to manage this here since next is now a block variable rather than univariate in the super. + try { + nextPastCount = new int[base_power_k][base_power_k]; + pastCount = new int[base_power_k]; + nextCount = new int[base_power_k]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + " with k=" + blockLength + + ") is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(nextPastCount, 0); + MatrixUtils.fill(pastCount, 0); + MatrixUtils.fill(nextCount, 0); } } - - /** - * Initialise calculator, preparing to take observation sets in - * Should be called prior to any of the addObservations() methods. - * You can reinitialise without needing to create a new object. - * - */ + + @Override public void initialise(){ - average = 0.0; - max = 0.0; - min = 0.0; - observations = 0; - - MatrixUtils.fill(jointCount, 0); - MatrixUtils.fill(prevCount, 0); - MatrixUtils.fill(nextCount, 0); + initialise(base, k); } /** @@ -191,9 +165,9 @@ public void addObservations(int timeSeries[]) { int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[p]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+p]; } @@ -201,15 +175,15 @@ public void addObservations(int timeSeries[]) { for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[timeSeries[t-1]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+t]; // Update the counts - jointCount[nextVal][prevVal]++; - prevCount[prevVal]++; + nextPastCount[nextVal][prevVal]++; + pastCount[prevVal]++; nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[t]; } } @@ -239,9 +213,9 @@ public void addObservations(int states[][]) { prevVal[c] = 0; nextVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= numDiscreteValues; + prevVal[c] *= base; prevVal[c] += states[p][c]; - nextVal[c] *= numDiscreteValues; + nextVal[c] *= base; nextVal[c] += states[k-1+p][c]; } } @@ -251,15 +225,15 @@ public void addObservations(int states[][]) { for (int c = 0; c < columns; c++) { // Update the next value: nextVal[c] -= maxShiftedValue[states[r-1][c]]; - nextVal[c] *= numDiscreteValues; + nextVal[c] *= base; nextVal[c] += states[k-1+r][c]; // Update the counts - jointCount[nextVal[c]][prevVal[c]]++; - prevCount[prevVal[c]]++; + nextPastCount[nextVal[c]][prevVal[c]]++; + pastCount[prevVal[c]]++; nextCount[nextVal[c]]++; // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= numDiscreteValues; + prevVal[c] *= base; prevVal[c] += states[r][c]; } } @@ -296,9 +270,9 @@ public void addObservations(int states[][][]) { prevVal[r][c] = 0; nextVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= numDiscreteValues; + prevVal[r][c] *= base; prevVal[r][c] += states[p][r][c]; - nextVal[r][c] *= numDiscreteValues; + nextVal[r][c] *= base; nextVal[r][c] += states[k-1+p][r][c]; } } @@ -310,15 +284,15 @@ public void addObservations(int states[][][]) { for (int c = 0; c < agentColumns; c++) { // Update the next value: nextVal[r][c] -= maxShiftedValue[states[t-1][r][c]]; - nextVal[r][c] *= numDiscreteValues; + nextVal[r][c] *= base; nextVal[r][c] += states[k-1+t][r][c]; // Update the counts - jointCount[nextVal[r][c]][prevVal[r][c]]++; - prevCount[prevVal[r][c]]++; + nextPastCount[nextVal[r][c]][prevVal[r][c]]++; + pastCount[prevVal[r][c]]++; nextCount[nextVal[r][c]]++; // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= numDiscreteValues; + prevVal[r][c] *= base; prevVal[r][c] += states[t][r][c]; } } @@ -347,9 +321,9 @@ public void addObservations(int states[][], int col) { int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[p][col]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+p][col]; } @@ -357,16 +331,16 @@ public void addObservations(int states[][], int col) { for (int r = k; r < rows - (k-1); r++) { // Update the next value: nextVal -= maxShiftedValue[states[r-1][col]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+r][col]; // Add to the count for this particular transition: // (cell's assigned as above) - jointCount[nextVal][prevVal]++; - prevCount[prevVal]++; + nextPastCount[nextVal][prevVal]++; + pastCount[prevVal]++; nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[r][col]; } } @@ -394,9 +368,9 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[p][agentIndex1][agentIndex2]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+p][agentIndex1][agentIndex2]; } @@ -404,26 +378,21 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[states[t-1][agentIndex1][agentIndex2]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+t][agentIndex1][agentIndex2]; // Add to the count for this particular transition: // (cell's assigned as above) - jointCount[nextVal][prevVal]++; - prevCount[prevVal]++; + nextPastCount[nextVal][prevVal]++; + pastCount[prevVal]++; nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[t][agentIndex1][agentIndex2]; } } - /** - * Returns the average predictive information from - * the observed values which have been passed in previously. - * - * @return - */ + @Override public double computeAverageLocalOfObservations() { double mi = 0.0; double miCont = 0.0; @@ -435,9 +404,9 @@ public double computeAverageLocalOfObservations() { double p_next = (double) nextCount[nextVal] / (double) observations; for (int prevVal = 0; prevVal < base_power_k; prevVal++) { // compute p_prev - double p_prev = (double) prevCount[prevVal] / (double) observations; + double p_prev = (double) pastCount[prevVal] / (double) observations; // compute p(prev, next) - double p_joint = (double) jointCount[nextVal][prevVal] / (double) observations; + double p_joint = (double) nextPastCount[nextVal][prevVal] / (double) observations; // Compute MI contribution: if (p_joint > 0.0) { double logTerm = p_joint / (p_next * p_prev); @@ -469,9 +438,9 @@ public double computeAverageLocalOfObservations() { * @return */ public double computeLocalFromPreviousObservations(int next, int past){ - double logTerm = ( (double) jointCount[next][past] ) / + double logTerm = ( (double) nextPastCount[next][past] ) / ( (double) nextCount[next] * - (double) prevCount[past] ); + (double) pastCount[past] ); logTerm *= (double) observations; return Math.log(logTerm) / log_base; } @@ -503,9 +472,9 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[p]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+p]; } @@ -513,11 +482,11 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[timeSeries[t-1]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+t]; - logTerm = ( (double) jointCount[nextVal][prevVal] ) / + logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * - (double) prevCount[prevVal] ); + (double) pastCount[prevVal] ); // Now account for the fact that we've // just used counts rather than probabilities, // and we've got two counts on the bottom @@ -532,7 +501,7 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ } // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[t]; } average = average/(double) (timeSteps - k - (k-1)); @@ -574,9 +543,9 @@ public double[][] computeLocalFromPreviousObservations(int timeSeries[][]){ prevVal[c] = 0; nextVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= numDiscreteValues; + prevVal[c] *= base; prevVal[c] += timeSeries[p][c]; - nextVal[c] *= numDiscreteValues; + nextVal[c] *= base; nextVal[c] += timeSeries[k-1+p][c]; } } @@ -585,11 +554,11 @@ public double[][] computeLocalFromPreviousObservations(int timeSeries[][]){ for (int c = 0; c < columns; c++) { // Update the next value: nextVal[c] -= maxShiftedValue[timeSeries[r-1][c]]; - nextVal[c] *= numDiscreteValues; + nextVal[c] *= base; nextVal[c] += timeSeries[k-1+r][c]; - logTerm = ( (double) jointCount[nextVal[c]][prevVal[c]] ) / + logTerm = ( (double) nextPastCount[nextVal[c]][prevVal[c]] ) / ( (double) nextCount[nextVal[c]] * - (double) prevCount[prevVal[c]] ); + (double) pastCount[prevVal[c]] ); // Now account for the fact that we've // just used counts rather than probabilities, // and we've got two counts on the bottom @@ -604,7 +573,7 @@ public double[][] computeLocalFromPreviousObservations(int timeSeries[][]){ } // Update the previous value: prevVal[c] -= maxShiftedValue[timeSeries[r-k][c]]; - prevVal[c] *= numDiscreteValues; + prevVal[c] *= base; prevVal[c] += timeSeries[r][c]; } } @@ -649,9 +618,9 @@ public double[][][] computeLocalFromPreviousObservations(int timeSeries[][][]){ prevVal[r][c] = 0; nextVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= numDiscreteValues; + prevVal[r][c] *= base; prevVal[r][c] += timeSeries[p][r][c]; - nextVal[r][c] *= numDiscreteValues; + nextVal[r][c] *= base; nextVal[r][c] += timeSeries[k-1+p][r][c]; } } @@ -662,11 +631,11 @@ public double[][][] computeLocalFromPreviousObservations(int timeSeries[][][]){ for (int c = 0; c < agentColumns; c++) { // Update the next value: nextVal[r][c] -= maxShiftedValue[timeSeries[t-1][r][c]]; - nextVal[r][c] *= numDiscreteValues; + nextVal[r][c] *= base; nextVal[r][c] += timeSeries[k-1+t][r][c]; - logTerm = ( (double) jointCount[nextVal[r][c]][prevVal[r][c]] ) / + logTerm = ( (double) nextPastCount[nextVal[r][c]][prevVal[r][c]] ) / ( (double) nextCount[nextVal[r][c]] * - (double) prevCount[prevVal[r][c]] ); + (double) pastCount[prevVal[r][c]] ); // Now account for the fact that we've // just used counts rather than probabilities, // and we've got two counts on the bottom @@ -681,7 +650,7 @@ public double[][][] computeLocalFromPreviousObservations(int timeSeries[][][]){ } // Update the previous value: prevVal[r][c] -= maxShiftedValue[timeSeries[t-k][r][c]]; - prevVal[r][c] *= numDiscreteValues; + prevVal[r][c] *= base; prevVal[r][c] += timeSeries[t][r][c]; } } @@ -721,20 +690,20 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[p][col]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+p][col]; } double logTerm = 0.0; for (int r = k; r < rows - (k-1); r++) { // Update the next value: nextVal -= maxShiftedValue[states[r-1][col]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += states[k-1+r][col]; - logTerm = ( (double) jointCount[nextVal][prevVal] ) / + logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * - (double) prevCount[prevVal] ); + (double) pastCount[prevVal] ); // Now account for the fact that we've // just used counts rather than probabilities, // and we've got two counts on the bottom @@ -749,7 +718,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += states[r][col]; } average = average/(double) (rows - k - (k-1)); @@ -789,20 +758,20 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[][][], int a int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[p][agentIndex1][agentIndex2]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+p][agentIndex1][agentIndex2]; } double logTerm = 0.0; for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[timeSeries[t-1][agentIndex1][agentIndex2]]; - nextVal *= numDiscreteValues; + nextVal *= base; nextVal += timeSeries[k-1+t][agentIndex1][agentIndex2]; - logTerm = ( (double) jointCount[nextVal][prevVal] ) / + logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * - (double) prevCount[prevVal] ); + (double) pastCount[prevVal] ); // Now account for the fact that we've // just used counts rather than probabilities, // and we've got two counts on the bottom @@ -817,7 +786,7 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[][][], int a } // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k][agentIndex1][agentIndex2]]; - prevVal *= numDiscreteValues; + prevVal *= base; prevVal += timeSeries[t][agentIndex1][agentIndex2]; } average = average/(double) (timeSteps - k - (k-1)); @@ -826,215 +795,6 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[][][], int a } - /** - * Standalone routine to - * compute local predictive information from a time series. - * Return an array of local values. - * First k and last k-1 rows are zeros. - * - * @param timeSeries time series array of states - * @return time series of local predictive information values - */ - public double[] computeLocal(int timeSeries[]) { - - initialise(); - addObservations(timeSeries); - return computeLocalFromPreviousObservations(timeSeries); - } - - /** - * Standalone routine to - * compute local predictive information across a 2D spatiotemporal - * time series of the observations of homogeneous agents. - * This method to be called for homogeneous agents only, as all - * observations are pooled together. - * First k and last k-1 rows are zeros. - * - * @param timeSeries 2D array of states (first index is time, second is variable id) - * @return multivariate time series of local predictive information values - * (1st index is time, second is variable id) - */ - public double[][] computeLocal(int timeSeries[][]) { - - initialise(); - addObservations(timeSeries); - return computeLocalFromPreviousObservations(timeSeries); - } - - /** - * Standalone routine to - * compute local predictive information across a 3D spatiotemporal - * array of the states of homogeneous agents - * This method to be called for homogeneous agents only, as all - * observations are pooled together. - * First k and last k-1 rows are zeros. - * - * @param timeSeries 2D array of states (first index is time, second - * and third are variable ids) - * @return multivariate time series of local predictive information values - * (1st index is time, second and third are variable ids) - */ - public double[][][] computeLocal(int timeSeries[][][]) { - - initialise(); - addObservations(timeSeries); - return computeLocalFromPreviousObservations(timeSeries); - } - - /** - * Standalone routine to - * compute average predictive information across a - * time series. - * - * @param timeSeries time series array of states - * @return average predictive information values - */ - public double computeAverageLocal(int timeSeries[]) { - - initialise(); - addObservations(timeSeries); - return computeAverageLocalOfObservations(); - } - - /** - * Standalone routine to - * compute average predictive information across a 2D spatiotemporal - * array of the states of homogeneous agents. - * This method to be called for homogeneous agents only, as all - * observations are pooled together. - * - * @param timeSeries 2D array of states (first index is time, second is variable id) - * @return average predictive information value - */ - public double computeAverageLocal(int timeSeries[][]) { - - initialise(); - addObservations(timeSeries); - return computeAverageLocalOfObservations(); - } - - /** - * Standalone routine to - * compute average predictive information across a 3D spatiotemporal - * array of the states of homogeneous agents - * This method to be called for homogeneous agents only, as all - * observations are pooled together. - * - * @param timeSeries 2D array of states (first index is time, second - * and third are variable ids) - * @return average predictive information value - */ - public double computeAverageLocal(int timeSeries[][][]) { - - initialise(); - addObservations(timeSeries); - return computeAverageLocalOfObservations(); - } - - /** - * Standalone routine to - * compute local predictive information for one agent in a 2D spatiotemporal - * array of the states of agents - * First k and last k-1 rows are zeros. - * - * This method should be used for heterogeneous agents. - * - * @param timeSeries 2D array of states (first index is time, second is variable id) - * @param col index of the agent whose local values should be calculated - * @return time series of local predictive information values for the given agent id - */ - public double[] computeLocal(int timeSeries[][], int col) { - - initialise(); - addObservations(timeSeries, col); - return computeLocalFromPreviousObservations(timeSeries, col); - } - - /** - * Standalone routine to - * compute local predictive information for one agent in a 3D spatiotemporal - * array of the states of agents - * First k and last k-1 rows are zeros. - * This method should be used for heterogeneous agents. - * - * @param timeSeries 3D array of states (first index is time, second - * and third are variable id) - * @param agentIndex1 row index of agent - * @param agentIndex2 column index of agent - * @return time series of local predictive information values for the given agent id - */ - public double[] computeLocal(int timeSeries[][][], int agentIndex1, int agentIndex2) { - - initialise(); - addObservations(timeSeries, agentIndex1, agentIndex2); - return computeLocalFromPreviousObservations(timeSeries, agentIndex1, agentIndex2); - } - - /** - * Standalone routine to - * compute average local predictive information - * for a single agent in a 2D spatiotemporal time series. - * This method suitable for heterogeneous agents, since the agent - * to be investigated is named. - * - * @param timeSeries 2D array of states (first index is time, second is variable id) - * @param col index of the agent whose local values should be calculated - * @return average predictive info for the given agent id - */ - public double computeAverageLocal(int timeSeries[][], int col) { - - initialise(); - addObservations(timeSeries, col); - return computeAverageLocalOfObservations(); - } - - /** - * Standalone routine to - * compute average local active information storage - * for a single agent in a 3D spatiotemporal time series. - * This method suitable for heterogeneous agents, since the agent - * to be investigated is named. - * - * @param timeSeries 3D array of states (first index is time, second - * and third are variable id) - * @param agentIndex1 row index of agent - * @param agentIndex2 column index of agent - * @return average predictive info for the given agent id - */ - public double computeAverageLocal(int timeSeries[][][], int agentIndex1, int agentIndex2) { - - initialise(); - addObservations(timeSeries, agentIndex1, agentIndex2); - return computeAverageLocalOfObservations(); - } - - /** - * - * @return the last predictive information computed since the - * previous {@link #initialise()} call. - */ - public double getLastAverage() { - return average; - } - - /** - * - * @return the last maximum local predictive information computed since the - * previous {@link #initialise()} call. - */ - public double getLastMax() { - return max; - } - - /** - * - * @return the last minimum local predictive information computed since the - * previous {@link #initialise()} call. - */ - public double getLastMin() { - return min; - } - /** * Writes the current probability distribution functions * @@ -1050,9 +810,9 @@ public void writePdfs() { double p_next = (double) nextCount[nextVal] / (double) observations; for (int prevVal = 0; prevVal < base_power_k; prevVal++) { // compute p_prev - double p_prev = (double) prevCount[prevVal] / (double) observations; + double p_prev = (double) pastCount[prevVal] / (double) observations; // compute p(prev, next) - double p_joint = (double) jointCount[nextVal][prevVal] / (double) observations; + double p_joint = (double) nextPastCount[nextVal][prevVal] / (double) observations; // Compute MI contribution: if (p_joint > 0.0) { double logTerm = p_joint / (p_next * p_prev); @@ -1084,7 +844,7 @@ public void writePdfs() { public int computePastValue(int[] x, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= numDiscreteValues; + pastVal *= base; pastVal += x[t - k + 1 + p]; } return pastVal; @@ -1103,7 +863,7 @@ public int computePastValue(int[] x, int t) { public int computePastValue(int[][] x, int i, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= numDiscreteValues; + pastVal *= base; pastVal += x[t - k + 1 + p][i]; } return pastVal; @@ -1123,7 +883,7 @@ public int computePastValue(int[][] x, int i, int t) { public int computePastValue(int[][][] x, int i, int j, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= numDiscreteValues; + pastVal *= base; pastVal += x[t - k + 1 + p][i][j]; } return pastVal; diff --git a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java index f02af641..896b5e4e 100755 --- a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java +++ b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java @@ -42,6 +42,13 @@ */ public interface SingleAgentMeasureDiscrete { + /** + * Initialise the calculator with (potentially) a new base + * + * @param base + */ + public void initialise(int base); + /** * Add observations in to our estimates of the pdfs. * diff --git a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java index e6051616..d9af5939 100755 --- a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java +++ b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java @@ -46,6 +46,13 @@ public abstract class SingleAgentMeasureDiscreteInContextOfPastCalculator extends ContextOfPastMeasureCalculatorDiscrete implements SingleAgentMeasureDiscrete { + /** + * Construct the calculator with default base of 2 and history 1 + */ + public SingleAgentMeasureDiscreteInContextOfPastCalculator() { + super(2, 1); + } + /** * Construct the calculator * @@ -57,6 +64,20 @@ public SingleAgentMeasureDiscreteInContextOfPastCalculator(int base, int history super(base, history); } + /** + * Construct the calculator + * + * @param base number of quantisation levels for each variable. + * E.g. binary variables are in base-2. + * @param history embedding length + * @param dontCreateObsStorage do not create storage + * for observations of the embedded past (as the child + * class is signalling that it does not need it) + */ + public SingleAgentMeasureDiscreteInContextOfPastCalculator(int base, int history, boolean dontCreateObsStorage) { + super(base, history, dontCreateObsStorage); + } + @Override public final double[] computeLocal(int[] states) { initialise(); diff --git a/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java index cea5a172..a178b1ac 100755 --- a/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java @@ -173,6 +173,15 @@ public static TransferEntropyCalculatorDiscrete newInstance(int base, int destHi */ } + /** + * Create a new TE calculator with all parameters as default + * (base 2 and all embedding/delay parameters 1) + * + */ + public TransferEntropyCalculatorDiscrete() { + this(2, 1, 1, 1, 1, 1); + } + /** * Create a new TE calculator for the given base and destination history embedding length * (leave the other embedding parameters as default) @@ -223,6 +232,25 @@ public TransferEntropyCalculatorDiscrete(int base, int destHistoryEmbedLength, i int sourceHistoryEmbeddingLength, int sourceEmbeddingDelay, int delay) { super(base, destHistoryEmbedLength); + updateParameters(base, destHistoryEmbedLength, destEmbeddingDelay, + sourceHistoryEmbeddingLength, sourceEmbeddingDelay, delay); + } + + /** + * Private method to update the estimators parameters, + * save for parts only concerning base and destHistoryEmbedLength + * which are to be handled before this by the super + * + * @param base + * @param destHistoryEmbedLength + * @param destEmbeddingDelay + * @param sourceHistoryEmbeddingLength + * @param sourceEmbeddingDelay + * @param delay + */ + private void updateParameters(int base, int destHistoryEmbedLength, int destEmbeddingDelay, + int sourceHistoryEmbeddingLength, int sourceEmbeddingDelay, int delay) { + this.destEmbeddingDelay = destEmbeddingDelay; if (sourceHistoryEmbeddingLength <= 0) { throw new RuntimeException("Cannot have source embedding length of zero or less"); @@ -243,18 +271,6 @@ public TransferEntropyCalculatorDiscrete(int base, int destHistoryEmbedLength, i maxShiftedSourceValue[v] = v * MathsUtils.power(base, sourceHistoryEmbedLength-1); } - // Create storage for extra counts of observations - try { - sourceNextPastCount = new int[base_power_l][base][base_power_k]; - sourcePastCount = new int[base_power_l][base_power_k]; - } catch (OutOfMemoryError e) { - // Allow any Exceptions to be thrown, but catch and wrap - // Error as a RuntimeException - throw new RuntimeException("Requested memory for the base " + - base + ", k=" + k + ", l=" + sourceHistoryEmbedLength + - " is too large for the JVM at this time", e); - } - // Which time step do we start taking observations from? // These two integers represent the earliest next time step, in the cases where the destination // embedding itself determines where we can start taking observations, or @@ -263,16 +279,53 @@ public TransferEntropyCalculatorDiscrete(int base, int destHistoryEmbedLength, i int startTimeBasedOnDestPast = (k-1)*destEmbeddingDelay + 1; int startTimeBasedOnSourcePast = (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + delay; startObservationTime = Math.max(startTimeBasedOnDestPast, startTimeBasedOnSourcePast); - } - + + /** + * Initialise with (potentially) new parameters + * + * @param base + * @param destHistoryEmbedLength + * @param destEmbeddingDelay + * @param sourceHistoryEmbeddingLength + * @param sourceEmbeddingDelay + * @param delay + */ + public void initialise(int base, int destHistoryEmbedLength, int destEmbeddingDelay, + int sourceHistoryEmbeddingLength, int sourceEmbeddingDelay, int delay) { + + boolean paramsChanged = (this.base != base) || (k != destHistoryEmbedLength) || + (this.destEmbeddingDelay != destEmbeddingDelay) || (this.sourceHistoryEmbedLength != sourceHistoryEmbeddingLength) || + (this.sourceEmbeddingDelay != sourceEmbeddingDelay) || (this.delay != delay); + super.initialise(base, destHistoryEmbedLength); + if (paramsChanged) { + updateParameters(base, destHistoryEmbedLength, destEmbeddingDelay, + sourceHistoryEmbeddingLength, sourceEmbeddingDelay, delay); + } + + if (paramsChanged || (sourceNextPastCount == null)) { + // Create new storage for extra counts of observations + try { + sourceNextPastCount = new int[base_power_l][base][base_power_k]; + sourcePastCount = new int[base_power_l][base_power_k]; + } catch (OutOfMemoryError e) { + // Allow any Exceptions to be thrown, but catch and wrap + // Error as a RuntimeException + throw new RuntimeException("Requested memory for the base " + + base + ", k=" + k + ", l=" + sourceHistoryEmbedLength + + " is too large for the JVM at this time", e); + } + } else { + MatrixUtils.fill(sourceNextPastCount, 0); + MatrixUtils.fill(sourcePastCount, 0); + } + estimateComputed = false; + } + @Override public void initialise(){ - super.initialise(); - estimateComputed = false; - - MatrixUtils.fill(sourceNextPastCount, 0); - MatrixUtils.fill(sourcePastCount, 0); + initialise(base, k, destEmbeddingDelay, sourceHistoryEmbedLength, + sourceEmbeddingDelay, delay); } @Override