Updating all discrete calculators (except SeparableInfo, since it's outmoded) to have a default empty constructor, with an overloaded initialise method added that provides all relevant properties. Fixes #77, and provides a first step for #66. Also aligned PredictiveInfo and ConditionalTE calculators to use the appropriate super classes.

This commit is contained in:
Joseph Lizier 2019-07-25 14:29:51 +10:00
parent d545793bc5
commit 1dc2dc8b86
15 changed files with 657 additions and 544 deletions

View File

@ -90,6 +90,13 @@ public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
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 class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscr
@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;

View File

@ -82,6 +82,13 @@ public class BlockEntropyCalculatorDiscrete extends EntropyCalculatorDiscrete {
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 class BlockEntropyCalculatorDiscrete extends EntropyCalculatorDiscrete {
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 class BlockEntropyCalculatorDiscrete extends EntropyCalculatorDiscrete {
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 class BlockEntropyCalculatorDiscrete extends EntropyCalculatorDiscrete {
}
}
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

View File

@ -72,9 +72,16 @@ public class CombinedActiveEntRateCalculatorDiscrete {
}
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.

View File

@ -98,6 +98,15 @@ public class ConditionalMutualInformationCalculatorDiscrete
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 class ConditionalMutualInformationCalculatorDiscrete
// 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;
return basesChanged;
}
@Override
public void 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);
// 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);
condMiComputed = false;
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);
}
}
@Override
public void initialise(){
super.initialise();
condMiComputed = false;
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

View File

@ -77,12 +77,6 @@ import infodynamics.utils.RandomGenerator;
* be incorrect.
* </p>
*
* <p><i>Note for developers</i>: 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.
* </p>
*
* TODO Add methods for passing in single time series.
* This is done for addObservations, but not other routines.
*
@ -111,19 +105,17 @@ import infodynamics.utils.RandomGenerator;
*
*/
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);
}
/**

View File

@ -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 abstract class ContextOfPastMeasureCalculatorDiscrete extends
*/
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 @@ public abstract class ContextOfPastMeasureCalculatorDiscrete extends
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);
}
}
}

View File

@ -83,6 +83,13 @@ public class EntropyCalculatorDiscrete extends InfoMeasureCalculatorDiscrete
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 class EntropyCalculatorDiscrete extends InfoMeasureCalculatorDiscrete
* E.g. binary variables are in base-2.
*/
public EntropyCalculatorDiscrete(int base) {
super(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);
}
}
/**
* Initialise with new base
*
* @param base
*/
public void initialise(int base){
boolean baseChanged = (this.base != base);
super.initialise(base);
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

View File

@ -77,6 +77,13 @@ public class EntropyRateCalculatorDiscrete extends SingleAgentMeasureDiscreteInC
return new EntropyRateCalculatorDiscrete(base, history);
}
/**
* Construct a new instance with default base 2 and history 1
*/
public EntropyRateCalculatorDiscrete() {
super();
}
/**
* Construct a new instance
*

View File

@ -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 @@ public abstract class InfoMeasureCalculatorDiscrete {
/**
* 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()}

View File

@ -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);
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);
}
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);
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);
}
/**

View File

@ -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 class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi
// 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;
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);
}
return basesChanged;
}
@Override
public void initialise(){
super.initialise();
miComputed = false;
MatrixUtils.fill(iCount, 0);
MatrixUtils.fill(jCount, 0);
MatrixUtils.fill(jointCount, 0);
public void initialise() {
try {
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);
}
}
/**
* 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}
*

View File

@ -18,7 +18,6 @@
package infodynamics.measures.discrete;
import infodynamics.utils.MathsUtils;
import infodynamics.utils.MatrixUtils;
/**
@ -63,8 +62,7 @@ import infodynamics.utils.MatrixUtils;
* </li>
* </ol>
*
* <p>TODO Inherit from {@link SingleAgentMeasureDiscreteInContextOfPastCalculator}
* as {@link ActiveInformationCalculatorDiscrete} does; Tidy up the Javadocs for
* <p>TODO Tidy up the Javadocs for
* the methods, which are somewhat preliminary</p>
*
* <p><b>References:</b><br/>
@ -82,22 +80,8 @@ import infodynamics.utils.MatrixUtils;
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
* 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);
}
/**
* 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(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);
}
}
@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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
* @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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
}
// 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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
}
// 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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
}
// 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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
}
// 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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
}
// 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 class PredictiveInformationCalculatorDiscrete {
}
/**
* 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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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 class PredictiveInformationCalculatorDiscrete {
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;

View File

@ -42,6 +42,13 @@ package infodynamics.measures.discrete;
*/
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.
*

View File

@ -46,6 +46,13 @@ package infodynamics.measures.discrete;
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 abstract class SingleAgentMeasureDiscreteInContextOfPastCalculator extend
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();

View File

@ -173,6 +173,15 @@ public class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
*/
}
/**
* 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 class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
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 class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
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 class TransferEntropyCalculatorDiscrete extends ContextOfPastMeasureCalcu
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