From ac8326aecfe9f26e64d2b4a5bf79d26fd6c8c6b7 Mon Sep 17 00:00:00 2001 From: Brad Wootton Date: Thu, 22 Aug 2024 13:52:19 +1000 Subject: [PATCH 1/2] Discrete entropy is complete I think. Maybe a few more things moved around. No tests yet. --- ...ontextOfPastMeasureCalculatorDiscrete.java | 2 +- .../discrete/EntropyCalculatorDiscrete.java | 472 ++++++++++++------ .../InfoMeasureCalculatorDiscrete.java | 116 ++++- .../discrete/SingleAgentMeasureDiscrete.java | 165 ++++-- 4 files changed, 537 insertions(+), 218 deletions(-) diff --git a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java index 2ec38e35..1b198013 100755 --- a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java @@ -104,7 +104,7 @@ protected ContextOfPastMeasureCalculatorDiscrete(int base, int history, boolean } /** - * Should be called after {@link #resetBase(int)} has just been called. + * Should be called after {@link #resetAlphabetSize(int)} has just been called. * * @param history */ diff --git a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java index 1577ffde..f2efc79b 100755 --- a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java @@ -18,6 +18,8 @@ package infodynamics.measures.discrete; +import java.util.Hashtable; + import infodynamics.utils.MatrixUtils; /** @@ -61,63 +63,43 @@ public class EntropyCalculatorDiscrete extends InfoMeasureCalculatorDiscrete protected int[] stateCount = null; // Count for i[t] - /** - * User was formerly forced to create new instances through this factory method. - * Retained for backwards compatibility. - * - * @param base number of symbols for each variable. - * E.g. binary variables are in base-2. - * @param blocksize number of consecutive joint values to include - * in the calculation. - * @deprecated - * @return a new EntropyCalculator - */ - public static EntropyCalculatorDiscrete newInstance(int base, int blocksize) { - if (blocksize > 1) { - return BlockEntropyCalculatorDiscrete.newInstance(blocksize, base); - } else { - return EntropyCalculatorDiscrete.newInstance(base); - } - } - public static EntropyCalculatorDiscrete newInstance(int base) { - return new EntropyCalculatorDiscrete(base); - } + protected Hashtable hashedStateCount = null; /** - * Construct a new instance with default base of 2 + * Construct a new instance with no specified alphabet size. */ public EntropyCalculatorDiscrete() { - this(2); + this(-1); } /** * Contruct a new instance * - * @param base number of quantisation levels for each variable. + * @param alphabetSize number of quantisation levels for each variable. * E.g. binary variables are in base-2. */ - public EntropyCalculatorDiscrete(int base) { - super(base); + public EntropyCalculatorDiscrete(int alphabetSize) { + super(alphabetSize); } /** - * Initialise with new base + * Initialise with new alphabet size * - * @param base + * @param alphabetSize */ - public void initialise(int base){ - boolean baseChanged = (this.base != base); - super.initialise(base); + public void initialise(int alphabetSize){ + boolean sizeChange = (this.alphabetSize != alphabetSize); + super.initialise(alphabetSize); - if (baseChanged || (stateCount == null)) { + if (sizeChange || (stateCount == null)) { // Create storage for counts of observations try { - stateCount = new int[base]; + stateCount = new int[alphabetSize]; } 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); + throw new RuntimeException("Requested memory for the alphabet size (" + + alphabetSize + ") is too large for the JVM at this time", e); } } else { MatrixUtils.fill(stateCount, 0); @@ -126,65 +108,65 @@ public void initialise(int base){ @Override public void initialise(){ - initialise(base); + initialise(alphabetSize); } - + @Override - public void addObservations(int states[]) { + public void addObservations(Object[] states) { int rows = states.length; // increment the count of observations: - observations += rows; + observations += rows; // 1. Count the tuples observed for (int r = 0; r < rows; r++) { // Add to the count for this particular state: - stateCount[states[r]]++; - } - } - @Override - public void addObservations(int states[][]) { - int rows = states.length; - int columns = states[0].length; - // increment the count of observations: - observations += rows * columns; - - // 1. Count the tuples observed - for (int r = 0; r < rows; r++) { - for (int c = 0; c < columns; c++) { - // Add to the count for this particular state: - stateCount[states[r][c]]++; + if (!knownIntegerRange) { + Object key = states[r]; + Integer value = hashedStateCount.getOrDefault(key, 0) + 1; + hashedStateCount.put(key, value); + continue; } - } - } - - @Override - public void addObservations(int states[][][]) { - int timeSteps = states.length; - if (timeSteps == 0) { - return; - } - int agentRows = states[0].length; - if (agentRows == 0) { - return; - } - int agentColumns = states[0][0].length; - // increment the count of observations: - observations += timeSteps * agentRows * agentColumns; - - // 1. Count the tuples observed - for (int t = 0; t < timeSteps; t++) { - for (int i = 0; i < agentRows; i++) { - for (int j = 0; j < agentColumns; j++) { - // Add to the count for this particular state: - stateCount[states[t][i][j]]++; - } + + // TODO better way to do this?? + // obviously we can do .getClass() twice in the if statements, I think this is + // slightly more readable though. + // joe's input: + @SuppressWarnings("rawtypes") + Class objectClass = states[r].getClass(); + Integer index; + if (objectClass == String.class) { + index = Integer.parseInt((String) states[r]); + } else if (objectClass == Integer.class) { + index = (Integer) states[r]; + } else { + throw new NumberFormatException(String.format( + "Cannot parse %s as an Integer for indexing", + states[r])); + } + + // valid index, check to make sure user didn't break the alphabet size they provided. + if (index >= stateCount.length) { + // runtime exception I guess...? + // TODO joe's input: + throw new RuntimeException(String.format( + "Observation %s exceeds provided alphabet size %d", + states[r], this.alphabetSize)); } + stateCount[index]++; } } - @Override - public void addObservations(int states[][], int agentNumber) { + // I added this so that computeAverageLocal works, which we were considering deprecating + // Though i think keeping the old int[] format is worthwhile for backwards compat. + // TODO joe's input: + // maybe deprecate (with computeAverageLocal) + /** + * Add observations in to our estimates of the pdfs. + * + * @param states + */ + public void addObservations(int[] states) { int rows = states.length; // increment the count of observations: observations += rows; @@ -192,23 +174,10 @@ public void addObservations(int states[][], int agentNumber) { // 1. Count the tuples observed for (int r = 0; r < rows; r++) { // Add to the count for this particular state: - stateCount[states[r][agentNumber]]++; - } - } - - @Override - public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { - int timeSteps = states.length; - // increment the count of observations: - observations += timeSteps; - - // 1. Count the tuples observed - for (int r = 0; r < timeSteps; r++) { - // Add to the count for this particular state: - stateCount[states[r][agentIndex1][agentIndex2]]++; + stateCount[states[r]]++; } } - + /** * Return the current count for the given value * @@ -236,7 +205,7 @@ public double computeAverageLocalOfObservations() { max = 0; min = 0; - for (int stateVal = 0; stateVal < base; stateVal++) { + for (int stateVal = 0; stateVal < alphabetSize; stateVal++) { // compute p_state double p_state = (double) stateCount[stateVal] / (double) observations; if (p_state > 0.0) { @@ -277,13 +246,224 @@ public double[] computeLocalFromPreviousObservations(int states[]){ min = localEntropy[r]; } } + average = average/(double) rows; - return localEntropy; + } + + @Override // maybe deprecate + public final double computeAverageLocal(int states[]) throws Exception { + initialise(); + startAddObservations(); + addObservations(states); + finaliseAddObservations(); + return computeAverageLocalOfObservations(); + } + + + @Override + public void setProperty(String propertyName, String propertyValue) throws Exception { + // TODO - I think this should just be the super call, as this class has no new properties. + // joe's input: + + switch(propertyName.toLowerCase()) { + case ALPHABET_SIZE: + this.alphabetSize = Integer.parseInt(propertyValue); + break; + case MAX_ALPHA_SIZE_TO_STORE: + this.maxAlphabetSize = Integer.parseInt(propertyValue); + break; + case KNOWN_INTEGER_RANGE: + this.knownIntegerRange = Boolean.parseBoolean(propertyValue); + break; + default: + // Assume it's a property of parent class + super.setProperty(propertyName, propertyValue); + break; + } + + } + + @Override + public void setObservations(Object[] observations) throws Exception { + this.observations = observations.length; + this.addObservations(observations); + this.finaliseAddObservations(); + } + + /* + *********************************************************************** + ******************* ALL METHODS BELOW ARE DEPRECATED ****************** + ******************* DON'T USE THEM ****************** + *********************************************************************** + */ + + @Override + @Deprecated + public void startAddObservations() { + + // reinitialise if already finalised + if (currentState == State.COMPUTING) { + initialise(); + } + this.currentState = State.ADDING_OBSERVATIONS; + + } + + @Override + @Deprecated + public void finaliseAddObservations() throws Exception { + + if (observations == 0) { + // TODO better error message. maybe not runtimeExc? + // joe's input: + throw new RuntimeException("0 observations not allowed."); + } + + // I actually think this is useless? It shouldn't ever get here without + // throwing the error above. + if (currentState == State.SETTING_PROPERTIES) { + throw new RuntimeException("Estimator should be initialised before finalised..."); + } + + this.currentState = State.COMPUTING; + } + + @Override + @Deprecated + public void addObservations(int states[][]) { + int rows = states.length; + int columns = states[0].length; + // increment the count of observations: + observations += rows * columns; + // 1. Count the tuples observed + for (int r = 0; r < rows; r++) { + for (int c = 0; c < columns; c++) { + // Add to the count for this particular state: + stateCount[states[r][c]]++; + } + } } @Override + @Deprecated + public void addObservations(int states[][][]) { + int timeSteps = states.length; + if (timeSteps == 0) { + return; + } + int agentRows = states[0].length; + if (agentRows == 0) { + return; + } + int agentColumns = states[0][0].length; + // increment the count of observations: + observations += timeSteps * agentRows * agentColumns; + + // 1. Count the tuples observed + for (int t = 0; t < timeSteps; t++) { + for (int i = 0; i < agentRows; i++) { + for (int j = 0; j < agentColumns; j++) { + // Add to the count for this particular state: + stateCount[states[t][i][j]]++; + } + } + } + } + + @Override + @Deprecated + public void addObservations(int states[][], int agentNumber) { + int rows = states.length; + // increment the count of observations: + observations += rows; + + // 1. Count the tuples observed + for (int r = 0; r < rows; r++) { + // Add to the count for this particular state: + stateCount[states[r][agentNumber]]++; + } + } + + @Override + @Deprecated + public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { + int timeSteps = states.length; + // increment the count of observations: + observations += timeSteps; + + // 1. Count the tuples observed + for (int r = 0; r < timeSteps; r++) { + // Add to the count for this particular state: + stateCount[states[r][agentIndex1][agentIndex2]]++; + } + } + + @Override + @Deprecated + public final double computeAverageLocal(int states[][]) { + + initialise(); + startAddObservations(); + addObservations(states); + this.currentState = State.COMPUTING; + return computeAverageLocalOfObservations(); + } + + @Override + @Deprecated + public final double computeAverageLocal(int states[][][]) { + initialise(); + startAddObservations(); + addObservations(states); + this.currentState = State.COMPUTING; + return computeAverageLocalOfObservations(); + } + + @Override + @Deprecated + public final double[] computeLocal(int states[][], int col) { + initialise(); + startAddObservations(); + addObservations(states, col); + this.currentState = State.COMPUTING; + return computeLocalFromPreviousObservations(states, col); + } + + @Override + @Deprecated + public final double[] computeLocal(int states[][][], + int agentIndex1, int agentIndex2) { + initialise(); + startAddObservations(); + addObservations(states, agentIndex1, agentIndex2); + this.currentState = State.COMPUTING; + return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2); + } + + @Override + @Deprecated + public final double computeAverageLocal(int states[][], int col) { + initialise(); + startAddObservations(); + addObservations(states, col); + this.currentState = State.COMPUTING; + return computeAverageLocalOfObservations(); + } + + @Override + @Deprecated + public final double computeAverageLocal(int states[][][], int agentIndex1, int agentIndex2) { + initialise(); + startAddObservations(); + addObservations(states, agentIndex1, agentIndex2); + this.currentState = State.COMPUTING; + return computeAverageLocalOfObservations(); + } + + @Override + @Deprecated public double[][] computeLocalFromPreviousObservations(int states[][]){ int rows = states.length; int columns = states[0].length; @@ -312,6 +492,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ } @Override + @Deprecated public double[][][] computeLocalFromPreviousObservations(int states[][][]){ int timeSteps = states.length; int agentRows, agentColumns; @@ -326,7 +507,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ agentColumns = states[0][0].length; } } - + double[][][] localEntropy = new double[timeSteps][agentRows][agentColumns]; average = 0; max = 0; @@ -351,12 +532,13 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ return localEntropy; } - + @Override + @Deprecated public double[] computeLocalFromPreviousObservations(int states[][], int agentNumber){ int rows = states.length; //int columns = states[0].length; - + double[] localEntropy = new double[rows]; average = 0; max = 0; @@ -379,10 +561,11 @@ public double[] computeLocalFromPreviousObservations(int states[][], int agentNu } @Override + @Deprecated public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){ int timeSteps = states.length; //int columns = states[0].length; - + // Allocate for all rows even though we'll leave the first ones as zeros double[] localEntropy = new double[timeSteps]; average = 0; @@ -404,80 +587,59 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent return localEntropy; } - + @Override + @Deprecated public final double[] computeLocal(int states[]) { - initialise(); + startAddObservations(); addObservations(states); + this.currentState = State.COMPUTING; return computeLocalFromPreviousObservations(states); } - + @Override + @Deprecated public final double[][] computeLocal(int states[][]) { - initialise(); + startAddObservations(); addObservations(states); + this.currentState = State.COMPUTING; return computeLocalFromPreviousObservations(states); } - + @Override + @Deprecated public final double[][][] computeLocal(int states[][][]) { - initialise(); + startAddObservations(); addObservations(states); + this.currentState = State.COMPUTING; return computeLocalFromPreviousObservations(states); } - @Override - public final double computeAverageLocal(int states[]) { - - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double computeAverageLocal(int states[][]) { - - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double computeAverageLocal(int states[][][]) { - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double[] computeLocal(int states[][], int col) { - initialise(); - addObservations(states, col); - return computeLocalFromPreviousObservations(states, col); - } - - @Override - public final double[] computeLocal(int states[][][], - int agentIndex1, int agentIndex2) { - initialise(); - addObservations(states, agentIndex1, agentIndex2); - return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2); + /** + * User was formerly forced to create new instances through this factory method. + * Retained for backwards compatibility. + * + * @param alphabetSize number of symbols for each variable. + * E.g. binary variables are in base-2. + * @param blocksize number of consecutive joint values to include + * in the calculation. + * @deprecated + * @return a new EntropyCalculator + */ + @Deprecated + public static EntropyCalculatorDiscrete newInstance(int alphabetSize, int blocksize) { + if (blocksize > 1) { + return BlockEntropyCalculatorDiscrete.newInstance(blocksize, alphabetSize); + } else { + return EntropyCalculatorDiscrete.newInstance(alphabetSize); + } } - - @Override - public final double computeAverageLocal(int states[][], int col) { - initialise(); - addObservations(states, col); - return computeAverageLocalOfObservations(); + @Deprecated + public static EntropyCalculatorDiscrete newInstance(int alphabetSize) { + return new EntropyCalculatorDiscrete(alphabetSize); } - @Override - public final double computeAverageLocal(int states[][][], int agentIndex1, int agentIndex2) { - initialise(); - addObservations(states, agentIndex1, agentIndex2); - return computeAverageLocalOfObservations(); - } } diff --git a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java index 2a0ecf42..132beda3 100755 --- a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java @@ -74,7 +74,23 @@ public abstract class InfoMeasureCalculatorDiscrete { * Number of available quantised states for each variable * (ie binary is base-2). */ - protected int base = 0; // number of individual states. Need initialised to 0 for changedSizes + protected int alphabetSize = -1; // number of individual states. -1 for unknown number + /** + * Size of the largest alphabet to support in array based memory management before + * switching to Hashtable implementation. + */ + protected int maxAlphabetSize = 100; // largest alphabet size before moving to Hashtable memory management TODO some default value + /** + * TODO -- make better comments :) + * State indicator of what the estimator is expecting to happen next. + */ + protected State currentState = State.SETTING_PROPERTIES; + + /** + * Boolean indicating if the size of the alphabet is known to determine the use of a + * hash table implementation for memory management. + */ + protected boolean knownIntegerRange = false; /** * Cached value of ln(base) @@ -96,36 +112,49 @@ public abstract class InfoMeasureCalculatorDiscrete { * Whether we're in debug mode */ protected boolean debug = false; + + protected enum State { + SETTING_PROPERTIES, + INITIALISED, + ADDING_OBSERVATIONS, + COMPUTING + } /** - * Construct an instance with default base of 2 + * Construct an instance with no specified alphabet size. */ protected InfoMeasureCalculatorDiscrete() { - this(2); + this(-1); } /** * Construct an instance * - * @param base number of quantisation levels for each variable. + * @param alphabetSize number of quantisation levels for each variable. * E.g. binary variables are in base-2. */ - protected InfoMeasureCalculatorDiscrete(int base) { - resetBase(base); + protected InfoMeasureCalculatorDiscrete(int alphabetSize) { + resetAlphabetSize(alphabetSize); } - protected void resetBase(int base) { - this.base = base; - log_base = Math.log(base); + protected void resetAlphabetSize(int alphabetSize) { + this.alphabetSize = alphabetSize; + + // indicator of unknown alphabet size + if (alphabetSize == -1) { + return; + } + + log_base = Math.log(alphabetSize); - if (base < 2) { - throw new RuntimeException("Can't calculate info theoretic measures for base " + base); + if (alphabetSize < 2) { + throw new RuntimeException("Can't calculate info theoretic measures for alphabet size " + alphabetSize); } // Check if we've got a power of 2 - power_of_2_base = isPowerOf2(base); + power_of_2_base = isPowerOf2(alphabetSize); if (power_of_2_base) { - log_2_base = (int) Math.round(Math.log(base) / Math.log(2)); + log_2_base = (int) Math.round(Math.log(alphabetSize) / Math.log(2)); } } @@ -135,21 +164,74 @@ protected void resetBase(int base) { * @throws Exception */ public void initialise() { - initialise(base); + initialise(-1); } /** * Initialise the calculator for re-use with new observations, - * and a new base. + * and a new alphabet size. * (Child classes should clear the existing PDFs) */ - public void initialise(int base){ - resetBase(base); + public void initialise(int alphabetSize){ + resetAlphabetSize(alphabetSize); average = 0.0; max = 0.0; min = 0.0; std = 0.0; observations = 0; + currentState = State.INITIALISED; + } + + /** + * Set properties for the underlying calculator implementation. + * New property values are not guaranteed to take effect until the next call + * to an initialise method. + * + * TODO -- word the property descr. nicely + * + *

Property names defined at the interface level, and what their + * values should represent, include:

+ * + * + *

Unknown property values are ignored.

+ * + *

Note that implementing classes may defined additional properties.

+ * + * @param propertyName name of the property + * @param propertyValue value of the property + * @throws Exception for invalid property names or values + */ + public void setProperty(String propertyName, String propertyValue) throws Exception { + + if (currentState != State.SETTING_PROPERTIES) { + currentState = State.SETTING_PROPERTIES; + // TODO I think there are things here to do to go back to this state but I don't remember them rn + // Actually do we want this here...? it should definitely be in the lower levels, so it should be + // caught then... + // joe's input: + } + + switch(propertyName.toLowerCase()) { + case "ALPHABET_SIZE": + this.alphabetSize = Integer.parseInt(propertyValue); + break; + case "MAX_ALPHA_SIZE_TO_STORE": + this.maxAlphabetSize = Integer.parseInt(propertyValue); + break; + case "KNOWN_INTEGER_RANGE": + this.knownIntegerRange = Boolean.parseBoolean(propertyValue); + break; + default: + // This is the highest level in which this method should get called, if the property + // hasn't been recognised yet, it doesn't exist, throw an exception + throw new IllegalArgumentException(String.format("Property name: %s was not recognised", propertyName)); + // break; (but it's unreachable...) + } } /** diff --git a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java index 896b5e4e..507e7f62 100755 --- a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java +++ b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java @@ -43,66 +43,75 @@ public interface SingleAgentMeasureDiscrete { /** - * Initialise the calculator with (potentially) a new base + * Property name for definite alphabet size. Attempting + * to use observations beyond this property will throw + * errors. + */ + public static final String ALPHABET_SIZE = "ALPHABET_SIZE"; + + /** + * Property name for determining when to switch to HashTable + * memory management system. (default is TODO...) + */ + public static final String MAX_ALPHA_SIZE_TO_STORE = "MAX_ALPHA_SIZE_TO_STORE"; + + /** + * Property name for whether or not the calculator is using + * the HashTable memory management. * - * @param base + * TODO -- maybe this shouldn't be a property... it should def be + * mutually exclusive with alphaSize. */ - public void initialise(int base); + public static final String KNOWN_INTEGER_RANGE = "KNOWN_INTEGER_RANGE"; + /** - * Add observations in to our estimates of the pdfs. - * - * @param states series of samples + * Property name for determining whether or not the calculator + * is still adding observations. + * + * TODO -- I think this is the nicest way to do this, though I'd rather a private + * boolean not set by setProperties. This probs shouldn't be a property at all... */ - public void addObservations(int states[]); - + public static final String EXPECTING_OBSERVATIONS = "EXPECTING_OBSERVATIONS"; + /** - * Add observations in to our estimates of the pdfs. - * This call suitable only for homogeneous agents, as all - * agents will contribute to the PDFs. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) + * Initialise the calculator with same or unknown alphabet size */ - public void addObservations(int states[][]); + public void initialise(); /** - * Add observations for a single variable of the multi-agent system - * to our estimates of the pdfs. - * This call should be made as opposed to {@link #addObservations(int[][])} - * for computing active info for heterogeneous agents. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @param col index of agent + * Sets the samples from which to compute the PDF for the entropy. + * Should only be called once, the last call contains the + * observations that are used (they are not accumulated). + * + * @param observations array of (univariate) samples + * @throws Exception */ - public void addObservations(int states[][], int col); + public void setObservations(Object[] observations) throws Exception; /** - * Add observations in to our estimates of the pdfs. - * This call suitable only for homogeneous agents, as all - * agents will contribute to single pdfs. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) + * Signal that we will add in the samples for computing the PDF + * from several disjoint time-series or trials via calls to + * "addObservations" rather than "setObservations" type methods + * (defined by the child interfaces and classes). */ - public void addObservations(int states[][][]); + public void startAddObservations(); /** - * Add observations for a single agent of the multi-agent system - * to our estimates of the pdfs. - * This call should be made as opposed to {@link #addObservations(int[][][])} - * for computing active info for heterogeneous agents. + * Signal that the observations are now all added, PDFs can now be constructed. + * + * @throws Exception when the estimator has no observations. + */ + public void finaliseAddObservations() throws Exception; + + /** + * Add observations in to our estimates of the pdfs. * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @param index1 row index index the variable - * @param index2 column index of the variable + * @param states series of samples */ - public void addObservations(int states[][][], int index1, int index2); - + public void addObservations(Object states[]); + + /** * Compute the average value of the measure * from the previously-supplied samples. @@ -233,7 +242,7 @@ public interface SingleAgentMeasureDiscrete { * @return 3D time-series of local values (indexed as per states) */ public double[][][] computeLocal(int states[][][]); - + /** * Standalone routine to * compute the average information theoretic measure across a time-series @@ -242,8 +251,9 @@ public interface SingleAgentMeasureDiscrete { * * @param states time series of samples * @return average of the information-theoretic measure. + * @throws Exception if states parameter is empty. */ - public double computeAverageLocal(int states[]); + public double computeAverageLocal(int states[]) throws Exception; /** * Standalone routine to @@ -341,4 +351,69 @@ public interface SingleAgentMeasureDiscrete { * @return average of the measure for the given variable. */ public double computeAverageLocal(int states[][][], int index1, int index2); + + /** + * Add observations in to our estimates of the pdfs. + * This call suitable only for homogeneous agents, as all + * agents will contribute to the PDFs. + * + * @param states multivariate time series + * (1st index is time, 2nd index is variable number) + */ + @Deprecated + public void addObservations(int states[][]); + + /** + * Add observations for a single variable of the multi-agent system + * to our estimates of the pdfs. + * This call should be made as opposed to {@link #addObservations(int[][])} + * for computing active info for heterogeneous agents. + * + * @deprecated + * @param states multivariate time series + * (1st index is time, 2nd index is variable number) + * @param col index of agent + */ + @Deprecated + public void addObservations(int states[][], int col); + + /** + * Add observations in to our estimates of the pdfs. + * This call suitable only for homogeneous agents, as all + * agents will contribute to single pdfs. + * + * @deprecated + * @param states multivariate time series + * (1st index is time, 2nd index is variable row number, + * 3rd is variable column number) + */ + @Deprecated + public void addObservations(int states[][][]); + + /** + * Add observations for a single agent of the multi-agent system + * to our estimates of the pdfs. + * This call should be made as opposed to {@link #addObservations(int[][][])} + * for computing active info for heterogeneous agents. + * + * @deprecated + * @param states multivariate time series + * (1st index is time, 2nd index is variable row number, + * 3rd is variable column number) + * @param index1 row index index the variable + * @param index2 column index of the variable + */ + @Deprecated + public void addObservations(int states[][][], int index1, int index2); + + /** + * Initialise the calculator with (potentially) a new alphabet size + * + * TODO - I don't think so anymore... this should be done with set property + * Deprecated, use initialise() and setProperty("ALPHABET_SIZE", "n") instead. + * @deprecated + * @param alphabetSize + */ + @Deprecated + public void initialise(int alphabetSize); } From de44f67d46ccfa452a7ace646c8217575fcdac9b Mon Sep 17 00:00:00 2001 From: Brad Wootton Date: Tue, 24 Dec 2024 20:40:51 +1000 Subject: [PATCH 2/2] Final commit for Brad's thesis. All remaining TODO comments are real TODOs. Support for larger alphabets has been added for entropy and mutual information, lightly tested. The old misleading 'alphabet size' has refactored to 'base'. --- .../ActiveInformationCalculatorDiscrete.java | 90 +- .../BlockEntropyCalculatorDiscrete.java | 62 +- ...mbinedActiveEntRateCalculatorDiscrete.java | 2 +- ...onalTransferEntropyCalculatorDiscrete.java | 50 +- ...ontextOfPastMeasureCalculatorDiscrete.java | 18 +- .../discrete/EntropyCalculatorDiscrete.java | 813 +++++++++--------- .../EntropyRateCalculatorDiscrete.java | 76 +- .../InfoMeasureCalculatorDiscrete.java | 32 +- .../MultiInformationCalculatorDiscrete.java | 48 +- ...iVariateInfoMeasureCalculatorDiscrete.java | 10 +- .../MutualInformationCalculatorDiscrete.java | 359 ++++---- ...edictiveInformationCalculatorDiscrete.java | 110 ++- .../SeparableInfoCalculatorDiscrete.java | 46 +- ...rableInfoCalculatorDiscreteByAddition.java | 18 +- .../discrete/SingleAgentMeasureDiscrete.java | 419 --------- .../TransferEntropyCalculatorDiscrete.java | 104 +-- .../discrete/UnivariateMeasureDiscrete.java | 150 ++++ ...ureDiscreteInContextOfPastCalculator.java} | 340 ++++---- .../measures/discrete/EntropyTester.java | 196 +++++ 19 files changed, 1444 insertions(+), 1499 deletions(-) delete mode 100755 java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java create mode 100644 java/source/infodynamics/measures/discrete/UnivariateMeasureDiscrete.java rename java/source/infodynamics/measures/discrete/{SingleAgentMeasureDiscreteInContextOfPastCalculator.java => UnivariateMeasureDiscreteInContextOfPastCalculator.java} (61%) mode change 100755 => 100644 create mode 100644 java/unittests/infodynamics/measures/discrete/EntropyTester.java diff --git a/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java index c8d8aefa..e5653522 100755 --- a/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ActiveInformationCalculatorDiscrete.java @@ -72,7 +72,7 @@ * @author Joseph Lizier (email, * www) */ -public class ActiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator +public class ActiveInformationCalculatorDiscrete extends UnivariateMeasureDiscreteInContextOfPastCalculator implements EmpiricalNullDistributionComputer, AnalyticNullDistributionComputer { protected boolean aisComputed = false; @@ -111,7 +111,7 @@ public ActiveInformationCalculatorDiscrete(int base, int history) { @Override public void initialise() { - initialise(base, k); + initialise(alphabetSize, k); } public void initialise(int base, int history) { @@ -128,7 +128,7 @@ public void addObservations(int states[]) { // Initialise and store the current previous value for each column int prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p]; } @@ -142,7 +142,7 @@ public void addObservations(int states[]) { nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[t-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t]; } } @@ -159,7 +159,7 @@ public void addObservations(int states[][]) { for (int c = 0; c < columns; c++) { prevVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[p][c]; } } @@ -176,13 +176,12 @@ public void addObservations(int states[][]) { nextCount[nextVal]++; // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[r][c]; } } } - @Override public void addObservations(int states[][][]) { int timeSteps = states.length; if (timeSteps == 0) { @@ -202,7 +201,7 @@ public void addObservations(int states[][][]) { for (int c = 0; c < agentColumns; c++) { prevVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[p][r][c]; } } @@ -221,14 +220,13 @@ public void addObservations(int states[][][]) { nextCount[nextVal]++; // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[t][r][c]; } } } } - @Override public void addObservations(int states[][], int col) { int rows = states.length; // increment the count of observations: @@ -238,7 +236,7 @@ public void addObservations(int states[][], int col) { int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; } @@ -253,12 +251,11 @@ public void addObservations(int states[][], int col) { nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } } - @Override public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { int timeSteps = states.length; // increment the count of observations: @@ -268,7 +265,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][agentIndex1][agentIndex2]; } @@ -283,7 +280,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t][agentIndex1][agentIndex2]; } } @@ -295,7 +292,7 @@ public double computeAverageLocalOfObservations() { max = 0; min = 0; - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < alphabetSize; nextVal++) { // compute p_next double p_next = (double) nextCount[nextVal] / (double) observations; for (int prevVal = 0; prevVal < base_power_k; prevVal++) { @@ -335,7 +332,7 @@ public double computeAverageLocalEntropyRateOfObservations() { double entRate = 0.0; double entRateCont = 0.0; - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < alphabetSize; nextVal++) { for (int prevVal = 0; prevVal < base_power_k; prevVal++) { // compute p_prev double p_prev = (double) pastCount[prevVal] / (double) observations; @@ -389,7 +386,7 @@ public double[] computeLocalFromPreviousObservations(int states[]){ // Initialise and store the current previous value for each column int prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p]; } @@ -414,7 +411,7 @@ public double[] computeLocalFromPreviousObservations(int states[]){ } // Update the previous value: prevVal -= maxShiftedValue[states[t-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t]; } average = average/(double) (timeSteps - k); @@ -423,7 +420,6 @@ public double[] computeLocalFromPreviousObservations(int states[]){ } - @Override public double[][] computeLocalFromPreviousObservations(int states[][]){ int rows = states.length; int columns = states[0].length; @@ -439,7 +435,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ for (int c = 0; c < columns; c++) { prevVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[p][c]; } } @@ -465,7 +461,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ } // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[r][c]; } } @@ -475,7 +471,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ } - @Override + public double[][][] computeLocalFromPreviousObservations(int states[][][]){ int timeSteps = states.length; int agentRows = states[0].length; @@ -493,7 +489,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ for (int c = 0; c < agentColumns; c++) { prevVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[p][r][c]; } } @@ -521,7 +517,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ } // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[t][r][c]; } } @@ -531,7 +527,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ return localActive; } - @Override + public double[] computeLocalFromPreviousObservations(int states[][], int col){ int rows = states.length; //int columns = states[0].length; @@ -546,7 +542,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; } int nextVal; @@ -570,7 +566,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } average = average/(double) (rows - k); @@ -579,7 +575,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } - @Override + public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){ int timeSteps = states.length; @@ -595,7 +591,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][agentIndex1][agentIndex2]; } int nextVal; @@ -619,7 +615,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], } // Update the previous value: prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t][agentIndex1][agentIndex2]; } average = average/(double) (timeSteps - k); @@ -709,14 +705,14 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings MatrixUtils.fill(prevValues, prevVal, t_prev, numberOfSamplesPrev); t_prev += numberOfSamplesPrev; } - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < alphabetSize; nextVal++) { int numberOfSamplesNext = nextCount[nextVal]; MatrixUtils.fill(nextValues, nextVal, t_next, numberOfSamplesNext); t_next += numberOfSamplesNext; } ActiveInformationCalculatorDiscrete ais2; - ais2 = new ActiveInformationCalculatorDiscrete(base, k); + ais2 = new ActiveInformationCalculatorDiscrete(alphabetSize, k); ais2.initialise(); ais2.observations = observations; ais2.pastCount = pastCount; @@ -754,7 +750,7 @@ public AnalyticMeasurementDistribution computeSignificance() } return new ChiSquareMeasurementDistribution(average, observations, - (base - 1) * (base_power_k - 1)); + (alphabetSize - 1) * (base_power_k - 1)); } /** @@ -767,7 +763,7 @@ public void writePdfs() { double miCont = 0.0; System.out.println("nextVal p(next) prevVal p(prev) p(joint) logTerm localVal"); - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < alphabetSize; nextVal++) { // compute p_next double p_next = (double) nextCount[nextVal] / (double) observations; for (int prevVal = 0; prevVal < base_power_k; prevVal++) { @@ -811,7 +807,7 @@ public void writePdfs() { public int computePastValue(int[] x, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += x[t - k + 1 + p]; } return pastVal; @@ -835,7 +831,7 @@ public int computePastValue(int[] x, int t) { public int computePastValue(int[][] data, int column, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += data[t - k + 1 + p][column]; } return pastVal; @@ -863,9 +859,27 @@ public int computePastValue(int[][][] data, int agentRow, int agentColumn, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += data[t - k + 1 + p][agentRow][agentColumn]; } return pastVal; } + + @Override + public void setObservations(Object[] observations) throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObservations'"); + } + + @Override + public void startAddObservations() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'startAddObservations'"); + } + + @Override + public void finaliseAddObservations() throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'finaliseAddObservations'"); + } } diff --git a/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java index 40bdda3d..29eb1fb1 100755 --- a/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/BlockEntropyCalculatorDiscrete.java @@ -104,7 +104,7 @@ public BlockEntropyCalculatorDiscrete(int blocksize, int base) { private void resetBlocksize(int blocksize) { this.blocksize = blocksize; - base_power_blocksize = MathsUtils.power(base, blocksize); + base_power_blocksize = MathsUtils.power(alphabetSize, blocksize); if (blocksize <= 1) { throw new RuntimeException("Blocksize " + blocksize + " is not > 1 for Block Entropy Calculator"); @@ -114,14 +114,14 @@ private void resetBlocksize(int blocksize) { } // Create constants for tracking stateValues - maxShiftedValue = new int[base]; - for (int v = 0; v < base; v++) { - maxShiftedValue[v] = v * MathsUtils.power(base, blocksize-1); + maxShiftedValue = new int[alphabetSize]; + for (int v = 0; v < alphabetSize; v++) { + maxShiftedValue[v] = v * MathsUtils.power(alphabetSize, blocksize-1); } } public void initialise(int blocksize, int base) { - boolean baseOrBlocksizeChanged = (this.blocksize != blocksize) || (this.base != base); + boolean baseOrBlocksizeChanged = (this.blocksize != blocksize) || (this.alphabetSize != base); super.initialise(base); if (baseOrBlocksizeChanged) { @@ -148,7 +148,7 @@ public void initialise(int blocksize, int base) { @Override public void initialise() { - initialise(blocksize, base); + initialise(blocksize, alphabetSize); } @Override @@ -163,7 +163,7 @@ public void addObservations(int states[]) { // Add the contribution from this observation stateVal += states[p]; // And shift up - stateVal *= base; + stateVal *= alphabetSize; } // 1. Now count the tuples observed from the next row onwards @@ -176,7 +176,7 @@ public void addObservations(int states[]) { // Remove the oldest observation from the state value stateVal -= maxShiftedValue[states[r-blocksize+1]]; - stateVal *= base; + stateVal *= alphabetSize; } } @@ -195,7 +195,7 @@ public void addObservations(int states[][]) { // Add the contribution from this observation stateVal[c] += states[p][c]; // And shift up - stateVal[c] *= base; + stateVal[c] *= alphabetSize; } } @@ -210,12 +210,12 @@ public void addObservations(int states[][]) { // Remove the oldest observation from the state value stateVal[c] -= maxShiftedValue[states[r-blocksize+1][c]]; - stateVal[c] *= base; + stateVal[c] *= alphabetSize; } } } - @Override + public void addObservations(int states[][][]) { int timeSteps = states.length; if (timeSteps == 0) { @@ -238,7 +238,7 @@ public void addObservations(int states[][][]) { // Add the contribution from this observation stateVal[r][c] += states[p][r][c]; // And shift up - stateVal[r][c] *= base; + stateVal[r][c] *= alphabetSize; } } } @@ -255,13 +255,13 @@ public void addObservations(int states[][][]) { // Remove the oldest observation from the state value stateVal[r][c] -= maxShiftedValue[states[t-blocksize+1][r][c]]; - stateVal[r][c] *= base; + stateVal[r][c] *= alphabetSize; } } } } - @Override + public void addObservations(int states[][], int col) { int rows = states.length; // increment the count of observations: @@ -273,7 +273,7 @@ public void addObservations(int states[][], int col) { // Add the contribution from this observation stateVal += states[p][col]; // And shift up - stateVal *= base; + stateVal *= alphabetSize; } // 1. Count the tuples observed @@ -286,11 +286,11 @@ public void addObservations(int states[][], int col) { // Remove the oldest observation from the state value stateVal -= maxShiftedValue[states[r-blocksize+1][col]]; - stateVal *= base; + stateVal *= alphabetSize; } } - @Override + public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { int timeSteps = states.length; // increment the count of observations: @@ -302,7 +302,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) // Add the contribution from this observation stateVal += states[p][agentIndex1][agentIndex2]; // And shift up - stateVal *= base; + stateVal *= alphabetSize; } // 1. Count the tuples observed @@ -315,7 +315,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) // Remove the oldest observation from the state value stateVal -= maxShiftedValue[states[t-blocksize+1][agentIndex1][agentIndex2]]; - stateVal *= base; + stateVal *= alphabetSize; } } @@ -348,7 +348,7 @@ public double computeAverageLocalOfObservations() { return ent; } - @Override + public double[][] computeLocalFromPreviousObservations(int states[][]){ int rows = states.length; int columns = states[0].length; @@ -365,7 +365,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ stateVal[c] = 0; for (int p = 0; p < blocksize - 1; p++) { stateVal[c] += states[p][c]; - stateVal[c] *= base; + stateVal[c] *= alphabetSize; } } // StateVal just needs the next value put in before processing @@ -387,7 +387,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ // Subtract out the oldest part of the state value: stateVal[c] -= maxShiftedValue[states[r-blocksize+1][c]]; // And shift all upwards - stateVal[c] *= base; + stateVal[c] *= alphabetSize; } } average = average/(double) (columns * (rows - blocksize + 1)); @@ -396,7 +396,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ } - @Override + public double[][][] computeLocalFromPreviousObservations(int states[][][]){ int timeSteps = states.length; int agentRows, agentColumns; @@ -425,7 +425,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ stateVal[r][c] = 0; for (int p = 0; p < blocksize - 1; p++) { stateVal[r][c] += states[p][r][c]; - stateVal[r][c] *= base; + stateVal[r][c] *= alphabetSize; } } } @@ -449,7 +449,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ // Subtract out the oldest part of the state value: stateVal[r][c] -= maxShiftedValue[states[t-blocksize+1][r][c]]; // And shift all upwards - stateVal[r][c] *= base; + stateVal[r][c] *= alphabetSize; } } } @@ -459,7 +459,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ } - @Override + public double[] computeLocalFromPreviousObservations(int states[][], int col){ int rows = states.length; //int columns = states[0].length; @@ -476,7 +476,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ int stateVal = 0; for (int p = 0; p < blocksize - 1; p++) { stateVal += states[p][col]; - stateVal *= base; + stateVal *= alphabetSize; } // StateVal just needs the next value put in before processing @@ -496,7 +496,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ // Subtract out the oldest part of the state value: stateVal -= maxShiftedValue[states[r-blocksize+1][col]]; // And shift all upwards - stateVal *= base; + stateVal *= alphabetSize; } average = average/(double) (rows - blocksize + 1); @@ -504,7 +504,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } - @Override + public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){ int timeSteps = states.length; //int columns = states[0].length; @@ -521,7 +521,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent int stateVal = 0; for (int p = 0; p < blocksize - 1; p++) { stateVal += states[p][agentIndex1][agentIndex2]; - stateVal *= base; + stateVal *= alphabetSize; } // StateVal just needs the next value put in before processing @@ -541,7 +541,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent // Subtract out the oldest part of the state value: stateVal -= maxShiftedValue[states[t-blocksize+1][agentIndex1][agentIndex2]]; // And shift all upwards - stateVal *= base; + stateVal *= alphabetSize; } average = average/(double) (timeSteps - blocksize + 1); diff --git a/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java index 56ee18d5..d59505cd 100755 --- a/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/CombinedActiveEntRateCalculatorDiscrete.java @@ -32,7 +32,7 @@ * and typical usage pattern. *

* - * TODO Make this inherit from {@link SingleAgentMeasureDiscreteInContextOfPastCalculator} + * TODO Make this inherit from {@link UnivariateMeasureDiscreteInContextOfPastCalculator} * like {@link ActiveInformationCalculatorDiscrete} and fix the Javadocs * * @author Joseph Lizier (email, diff --git a/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java index c9e8aef1..9f8d13b6 100644 --- a/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ConditionalTransferEntropyCalculatorDiscrete.java @@ -242,7 +242,7 @@ private void updateParameters(int base, int history, public void initialise(int base, int history, int numOtherInfoContributors, int base_others) { - boolean paramsChanged = (this.base != base) || (k != history) || + boolean paramsChanged = (this.alphabetSize != base) || (k != history) || (this.numOtherInfoContributors != numOtherInfoContributors) || (this.base_others != base_others); super.initialise(base, history); @@ -275,7 +275,7 @@ public void initialise(int base, int history, @Override public void initialise(){ - initialise(base, k, numOtherInfoContributors, base_others); + initialise(alphabetSize, k, numOtherInfoContributors, base_others); } /** @@ -313,7 +313,7 @@ public void addObservations(int[] source, int[] dest, int[][] conditionals) // Initialise and store the current previous value int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[p]; } @@ -336,7 +336,7 @@ public void addObservations(int[] source, int[] dest, int[][] conditionals) // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[dest[r-k]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[r]; } } @@ -386,7 +386,7 @@ public void addObservations(int[] source, int[] dest, int[] conditionals) // Initialise and store the current previous value int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[p]; } @@ -404,7 +404,7 @@ public void addObservations(int[] source, int[] dest, int[] conditionals) // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[dest[r-k]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[r]; } } @@ -468,7 +468,7 @@ private void addObservations(int states[][], int j, int otherSourcesToDestOffset for (int c = 0; c < columns; c++) { pastVal[c] = 0; for (int p = 0; p < k; p++) { - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[p][c]; } } @@ -494,7 +494,7 @@ private void addObservations(int states[][], int j, int otherSourcesToDestOffset // Update the previous value: if (k > 0) { pastVal[c] -= maxShiftedValue[states[r-k][c]]; - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[r][c]; } } @@ -551,7 +551,7 @@ private void addObservations(int states[][], int sourceCol, int destCol, int[] o // Initialise and store the current previous value for each column int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destCol]; } @@ -575,7 +575,7 @@ private void addObservations(int states[][], int sourceCol, int destCol, int[] o // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[states[r-k][destCol]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[r][destCol]; } } @@ -591,8 +591,8 @@ public double computeAverageLocalOfObservations() { double meanSqLocals = 0; for (int othersVal = 0; othersVal < this.base_power_num_others; othersVal++) { for (int pastVal = 0; pastVal < base_power_k; pastVal++) { - for (int destVal = 0; destVal < base; destVal++) { - for (int sourceVal = 0; sourceVal < base; sourceVal++) { + for (int destVal = 0; destVal < alphabetSize; destVal++) { + for (int sourceVal = 0; sourceVal < alphabetSize; sourceVal++) { // Compute TE contribution: if (sourceDestPastOthersCount[sourceVal][destVal][pastVal][othersVal] != 0) { /* Double check: should never happen @@ -650,7 +650,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings // Reconstruct the source values (not necessarily in order) int[] sourceValues = new int[observations]; int t_s = 0; - for (int sourceVal = 0; sourceVal < base; sourceVal++) { + for (int sourceVal = 0; sourceVal < alphabetSize; sourceVal++) { // Count up the number of times this source value was observed: int numberOfSamples = 0; for (int pastVal = 0; pastVal < base_power_k; pastVal++) { @@ -683,7 +683,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings MatrixUtils.fill(othersValues, othersVal, t_o, pastOthersCount[pastVal][othersVal]); t_o += pastOthersCount[pastVal][othersVal]; - for (int destVal = 0; destVal < base; destVal++) { + for (int destVal = 0; destVal < alphabetSize; destVal++) { MatrixUtils.fill(destValues, destVal, t_d, destPastOthersCount[destVal][pastVal][othersVal]); t_d += destPastOthersCount[destVal][pastVal][othersVal]; @@ -692,7 +692,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings } // TODO stop using deprecated method - ConditionalTransferEntropyCalculatorDiscrete cte = newInstance(base, k, numOtherInfoContributors); + ConditionalTransferEntropyCalculatorDiscrete cte = newInstance(alphabetSize, k, numOtherInfoContributors); cte.initialise(); cte.observations = observations; cte.pastOthersCount = pastOthersCount; @@ -733,7 +733,7 @@ public AnalyticMeasurementDistribution computeSignificance() } return new ChiSquareMeasurementDistribution(average, observations, - (base - 1)*(base - 1)*(base_power_k*base_power_num_others)); + (alphabetSize - 1)*(alphabetSize - 1)*(base_power_k*base_power_num_others)); } /** @@ -804,7 +804,7 @@ public AnalyticMeasurementDistribution computeSignificance() for (int c = 0; c < columns; c++) { pastVal[c] = 0; for (int p = 0; p < k; p++) { - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[p][c]; } } @@ -834,7 +834,7 @@ public AnalyticMeasurementDistribution computeSignificance() // Update the previous value: if (k > 0) { pastVal[c] -= maxShiftedValue[states[r-k][c]]; - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[r][c]; } } @@ -905,7 +905,7 @@ public AnalyticMeasurementDistribution computeSignificance() int pastVal = 0; pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destCol]; } int destVal, sourceVal, othersVal; @@ -932,7 +932,7 @@ public AnalyticMeasurementDistribution computeSignificance() // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[states[r-k][destCol]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[r][destCol]; } } @@ -975,7 +975,7 @@ public AnalyticMeasurementDistribution computeSignificance() int pastVal = 0; pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[p]; } int destVal, sourceVal, conditionalVal; @@ -997,7 +997,7 @@ public AnalyticMeasurementDistribution computeSignificance() // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[dest[r-k]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[r]; } } @@ -1033,7 +1033,7 @@ public AnalyticMeasurementDistribution computeSignificance() int pastVal = 0; pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[p]; } int destVal, sourceVal, conditionalsVal; @@ -1044,7 +1044,7 @@ public AnalyticMeasurementDistribution computeSignificance() conditionalsVal = 0; for (int o = 0; o < conditionals[r-1].length; o++) { // Include this other contributor - conditionalsVal *= base; + conditionalsVal *= alphabetSize; conditionalsVal += conditionals[r-1][o]; } // Now compute the local value @@ -1060,7 +1060,7 @@ public AnalyticMeasurementDistribution computeSignificance() // Update the previous value: if (k > 0) { pastVal -= maxShiftedValue[dest[r-k]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += dest[r]; } } diff --git a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java index 1b198013..5e095e26 100755 --- a/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/ContextOfPastMeasureCalculatorDiscrete.java @@ -111,7 +111,7 @@ protected ContextOfPastMeasureCalculatorDiscrete(int base, int history, boolean private void resetHistory(int history) { k = history; - base_power_k = MathsUtils.power(base, k); + base_power_k = MathsUtils.power(alphabetSize, k); if (k < 0) { throw new RuntimeException("History k " + history + " is not >= 0 for a ContextOfPastMeasureCalculator"); @@ -123,15 +123,15 @@ private void resetHistory(int history) { } // Create constants for tracking prevValues - maxShiftedValue = new int[base]; - for (int v = 0; v < base; v++) { - maxShiftedValue[v] = v * MathsUtils.power(base, k-1); + maxShiftedValue = new int[alphabetSize]; + for (int v = 0; v < alphabetSize; v++) { + maxShiftedValue[v] = v * MathsUtils.power(alphabetSize, k-1); } } @Override public void initialise() { - initialise(base, k); + initialise(alphabetSize, k); } /** @@ -143,7 +143,7 @@ public void initialise() { */ public void initialise(int base, int history) { boolean baseOrHistoryChanged = false; - if ((this.base != base) || (k != history)) { + if ((this.alphabetSize != base) || (k != history)) { baseOrHistoryChanged = true; } @@ -189,7 +189,7 @@ public void initialise(int base, int history) { public int computePastValue(int[] x, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += x[t - k + 1 + p]; } return pastVal; @@ -212,7 +212,7 @@ public int computePastValue(int[] x, int t) { public int computePastValue(int[][] data, int columnNumber, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += data[t - k + 1 + p][columnNumber]; } return pastVal; @@ -238,7 +238,7 @@ public int computePastValue(int[][] data, int columnNumber, int t) { public int computePastValue(int[][][] data, int rowNumber, int columnNumber, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += data[t - k + 1 + p][rowNumber][columnNumber]; } return pastVal; diff --git a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java index f2efc79b..6dfe84d4 100755 --- a/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/EntropyCalculatorDiscrete.java @@ -19,6 +19,9 @@ package infodynamics.measures.discrete; import java.util.Hashtable; +import java.util.Map; +import java.util.List; +import java.util.Arrays; import infodynamics.utils.MatrixUtils; @@ -58,12 +61,27 @@ * www) */ public class EntropyCalculatorDiscrete extends InfoMeasureCalculatorDiscrete - implements SingleAgentMeasureDiscrete + implements UnivariateMeasureDiscrete { + // TODO -- find or calculate reasonable max value. Currently 100. protected int[] stateCount = null; // Count for i[t] + /** + * State counts in a hashtable for observations other than ints + * as well as for sparse observations + */ protected Hashtable hashedStateCount = null; + + /** + * Number of dimensions for multi-dimensional calculations + */ + protected int numDimensions = 1; + + /** + * Sizes of each dimension's alphabet + */ + protected int[] alphabetSizes = null; /** * Construct a new instance with no specified alphabet size. @@ -80,6 +98,49 @@ public EntropyCalculatorDiscrete() { */ public EntropyCalculatorDiscrete(int alphabetSize) { super(alphabetSize); + currentState = State.SETTING_PROPERTIES; + } + + /** + * Return the current count for the given value + * + * @param stateVal given value + * @return count of observations of the given state + */ + public int getStateCount(int stateVal) { + // stateCount could have overflowed + if (stateCount == null) { + return hashedStateCount.get((Integer) stateVal); + } + return stateCount[stateVal]; + } + + /** + * Return the current count for the given value + * + * @param stateVal given value + * @return count of observations of the given state + */ + public int getStateCount(Object stateVal) { + if (stateCount == null) { + return hashedStateCount.get(stateVal); + } else if (stateVal instanceof String) { + int index = Integer.parseInt((String) stateVal); + return stateCount[index]; + } else { + int index = (Integer) stateVal; + return stateCount[index]; + } + } + + /** + * Return the current probability for the given value + * + * @param stateVal given value + * @return probability of the given state + */ + public double getStateProbability(int stateVal) { + return (double) stateCount[stateVal] / (double) observations; } /** @@ -88,13 +149,22 @@ public EntropyCalculatorDiscrete(int alphabetSize) { * @param alphabetSize */ public void initialise(int alphabetSize){ + boolean sizeChange = (this.alphabetSize != alphabetSize); super.initialise(alphabetSize); - - if (sizeChange || (stateCount == null)) { + + // if alphabet size is unknown, use large but managable array + // only moving to hash table for arrays exceeding this temp limit. + int temp = alphabetSize; + if (temp == -1) { + // TODO -- This is where we set the reasonable max value. See top of class. + temp = 100; + } + + if (sizeChange || stateCount == null) { // Create storage for counts of observations try { - stateCount = new int[alphabetSize]; + stateCount = new int[temp]; } catch (OutOfMemoryError e) { // Allow any Exceptions to be thrown, but catch and wrap // Error as a RuntimeException @@ -104,15 +174,83 @@ public void initialise(int alphabetSize){ } else { MatrixUtils.fill(stateCount, 0); } + hashedStateCount = new Hashtable<>(); + currentState = State.INITIALISED; } @Override public void initialise(){ initialise(alphabetSize); } - + @Override - public void addObservations(Object[] states) { + public void setProperty(String propertyName, String propertyValue) throws Exception { + // TODO -- set states here too, enforce correct workflow + // I believe this has been done + if (currentState != State.SETTING_PROPERTIES) { + stateCount = null; + hashedStateCount = new Hashtable<>(); + knownIntegerRange = false; + alphabetSizes = null; + numDimensions = 1; + currentState = State.SETTING_PROPERTIES; + + // TODO -- This is where we set the reasonable max value. See top of class. + alphabetSize = 100; + } + + switch(propertyName.toUpperCase()) { + case "NUM_DIMENSIONS": + try { + this.numDimensions = Integer.parseInt(propertyValue); + if (numDimensions < 0) { + throw new NumberFormatException("propertyValue for NUM_DIMENSIONS must be at least 0."); + } + } catch (NumberFormatException e) { + throw new NumberFormatException("propertyValue for NUM_DIMENSIONS must be interpretable as an integer."); + } + break; + case "ALPHABET_SIZES": + /* + * Currently accepts '1,2,3' or '[1,2,3]' formats, as this seemed + * easiest for importing data as well as manual input. + * '1, 2, 3' and '[1, 2, 3]' should also work with parseInt, though I'm unsure. + */ + propertyValue.replaceFirst("[", ""); + propertyValue.replaceFirst("]", ""); + String[] temp = propertyValue.split(","); + this.alphabetSizes = new int[temp.length]; + for (int i = 0; i < temp.length; i++) { + try { + alphabetSizes[i] = Integer.parseInt(temp[i]); + } catch (NumberFormatException e) { + throw new NumberFormatException("propertyValue for ALPHABET_SIZES must be interpretable as an integer array.\n" + + "Input should look like [1,2,3]"); + } + } + break; + default: + super.setProperty(propertyName, propertyValue); + break; + } + } + + public void addObservations(Object[] states) throws NumberFormatException, RuntimeException { + // if user has set numDimensions, they intend for multi-dimensional, and + // should not be using this function + if (numDimensions != 1) { + throw new RuntimeException( + "numDimensions was not 1. " + + "If you intend to use multi-dimensional observations, use addObservations(Object[][] states)." + ); + } + + if (currentState == State.COMPUTING || + currentState == State.SETTING_PROPERTIES) { + initialise(); + } + currentState = State.ADDING_OBSERVATIONS; + int rows = states.length; // increment the count of observations: observations += rows; @@ -121,430 +259,310 @@ public void addObservations(Object[] states) { for (int r = 0; r < rows; r++) { // Add to the count for this particular state: - if (!knownIntegerRange) { + if (!hashedStateCount.isEmpty()) { Object key = states[r]; Integer value = hashedStateCount.getOrDefault(key, 0) + 1; hashedStateCount.put(key, value); continue; } - - // TODO better way to do this?? - // obviously we can do .getClass() twice in the if statements, I think this is - // slightly more readable though. - // joe's input: - @SuppressWarnings("rawtypes") - Class objectClass = states[r].getClass(); - Integer index; - if (objectClass == String.class) { - index = Integer.parseInt((String) states[r]); - } else if (objectClass == Integer.class) { + + Integer index = this.alphabetSize; + if (states[r] instanceof String ) { + if (((String) states[r]).matches("[0-9]+")) { + index = Integer.parseInt((String) states[r]); + } else if (!knownIntegerRange) { + // First observation ever is non-integer string, right to hashtable + hashedStateCount.put((Object) states[r], 1); + stateCount = null; + continue; + } + } else if (states[r] instanceof Integer) { index = (Integer) states[r]; } else { - throw new NumberFormatException(String.format( - "Cannot parse %s as an Integer for indexing", - states[r])); + throw new NumberFormatException( + "Cannot parse " + states[r] + " as an Integer for indexing."); } // valid index, check to make sure user didn't break the alphabet size they provided. - if (index >= stateCount.length) { - // runtime exception I guess...? - // TODO joe's input: - throw new RuntimeException(String.format( - "Observation %s exceeds provided alphabet size %d", - states[r], this.alphabetSize)); + if (knownIntegerRange && index >= alphabetSize) { + throw new RuntimeException("Observation " + states[r] + + " exceeds provided alphabet size " + alphabetSize); + } + + // if array is still a managable size... + if (index < stateCount.length) { + stateCount[index]++; + } else { + // otherwise array has grown too large, move to hash table. + for (int i = 0; i < stateCount.length; i++) { + hashedStateCount.put(i, stateCount[i]); + } + hashedStateCount.put(index, 1); + stateCount = null; } - stateCount[index]++; } } - // I added this so that computeAverageLocal works, which we were considering deprecating - // Though i think keeping the old int[] format is worthwhile for backwards compat. - // TODO joe's input: - // maybe deprecate (with computeAverageLocal) /** * Add observations in to our estimates of the pdfs. * * @param states */ public void addObservations(int[] states) { + + // if user has set numDimensions, they intend for multi-dimensional, and + // should not be using this function + if (numDimensions != 1) { + throw new RuntimeException( + "numDimensions was not 1. " + + "If you intend to use multi-dimensional observations, use addObservations(int[][] states)." + ); + } + + if (currentState == State.COMPUTING || + currentState == State.SETTING_PROPERTIES) { + initialise(); + } + currentState = State.ADDING_OBSERVATIONS; + int rows = states.length; // increment the count of observations: observations += rows; // 1. Count the tuples observed for (int r = 0; r < rows; r++) { - // Add to the count for this particular state: - stateCount[states[r]]++; - } - } - - /** - * Return the current count for the given value - * - * @param stateVal given value - * @return count of observations of the given state - */ - public int getStateCount(int stateVal) { - return stateCount[stateVal]; - } - - /** - * Return the current probability for the given value - * - * @param stateVal given value - * @return probability of the given state - */ - public double getStateProbability(int stateVal) { - return (double) stateCount[stateVal] / (double) observations; - } - @Override - public double computeAverageLocalOfObservations() { - double ent = 0.0; - double entCont = 0.0; + if (!hashedStateCount.isEmpty()) { + Object key = states[r]; + Integer value = hashedStateCount.getOrDefault(key, 0) + 1; + hashedStateCount.put(key, value); + continue; + } - max = 0; - min = 0; - for (int stateVal = 0; stateVal < alphabetSize; stateVal++) { - // compute p_state - double p_state = (double) stateCount[stateVal] / (double) observations; - if (p_state > 0.0) { - // Entropy takes the negative log: - double localValue = - Math.log(p_state) / log_2; - entCont = p_state * localValue; - if (localValue > max) { - max = localValue; - } else if (localValue < min) { - min = localValue; - } + Integer index = states[r]; + // check to make sure user didn't break the alphabet size they provided. + if (knownIntegerRange && index >= this.alphabetSize) { + throw new RuntimeException("Observation " + states[r] + + " exceeds provided alphabet size " + alphabetSize); + } + + // if array is still a managable size... + if (index < alphabetSize) { + stateCount[index]++; } else { - entCont = 0.0; + // Otherwise array has grown too large, move to hash table. + hashedStateCount = new Hashtable<>(); + for (int i = 0; i < stateCount.length; i++) { + if (stateCount[i] != 0) { + hashedStateCount.put(i, stateCount[i]); + } + } + hashedStateCount.put(index, 1); + stateCount = null; } - ent += entCont; } - - average = ent; - return ent; } - + + /** + * This method is intended for multi-dimensional use, and adds the observations + * into our estimates of the pdfs. + * + * @param states - the observations of all dimensions at each time step + * states[i][j] represents the ith observed state of the jth dimension. + * states[i] is the array of observations at time step i. + */ @Override - public double[] computeLocalFromPreviousObservations(int states[]){ - int rows = states.length; - - double[] localEntropy = new double[rows]; - average = 0; - max = 0; - min = 0; - for (int r = 0; r < rows; r++) { - double p_state = (double) stateCount[states[r]] / (double) observations; - // Entropy takes the negative log: - localEntropy[r] = - Math.log(p_state) / log_2; - average += localEntropy[r]; - if (localEntropy[r] > max) { - max = localEntropy[r]; - } else if (localEntropy[r] < min) { - min = localEntropy[r]; + public void addObservations(int[][] states) throws RuntimeException { + + // ensure correct number of dimensions are provided + for (int i = 0; i < states.length; i++) { + if (states[i].length != numDimensions) { + throw new RuntimeException(String.format( + "Incorrect number of dimensions were given. Expected %d got %d.", numDimensions, states[i].length)); } } - average = average/(double) rows; - return localEntropy; - } - - @Override // maybe deprecate - public final double computeAverageLocal(int states[]) throws Exception { - initialise(); - startAddObservations(); - addObservations(states); - finaliseAddObservations(); - return computeAverageLocalOfObservations(); - } + if (currentState == State.COMPUTING || + currentState == State.SETTING_PROPERTIES) { + initialise(); + } + currentState = State.ADDING_OBSERVATIONS; - - @Override - public void setProperty(String propertyName, String propertyValue) throws Exception { - // TODO - I think this should just be the super call, as this class has no new properties. - // joe's input: - switch(propertyName.toLowerCase()) { - case ALPHABET_SIZE: - this.alphabetSize = Integer.parseInt(propertyValue); - break; - case MAX_ALPHA_SIZE_TO_STORE: - this.maxAlphabetSize = Integer.parseInt(propertyValue); - break; - case KNOWN_INTEGER_RANGE: - this.knownIntegerRange = Boolean.parseBoolean(propertyValue); - break; - default: - // Assume it's a property of parent class - super.setProperty(propertyName, propertyValue); - break; + // Unwrap unecessary single dimensional arrays, allows additional calls + // to the single dimensional version without issue. + if (numDimensions == 1) { + for (int i = 0; i < states.length; i++) { + this.stateCount[i] = states[i][0]; + } + return; } - } - - @Override - public void setObservations(Object[] observations) throws Exception { - this.observations = observations.length; - this.addObservations(observations); - this.finaliseAddObservations(); - } - - /* - *********************************************************************** - ******************* ALL METHODS BELOW ARE DEPRECATED ****************** - ******************* DON'T USE THEM ****************** - *********************************************************************** - */ - - @Override - @Deprecated - public void startAddObservations() { + int[] result; + if (alphabetSizes != null) { + result = computeCombinedValues(states, alphabetSizes, alphabetSize); + } else { + result = computeCombinedValues(states, alphabetSize); + } - // reinitialise if already finalised - if (currentState == State.COMPUTING) { - initialise(); + for (int i = 0; i < result.length; i++) { + this.stateCount[i] += result[i]; } - this.currentState = State.ADDING_OBSERVATIONS; - } - @Override - @Deprecated - public void finaliseAddObservations() throws Exception { + /** + * This method is intended for multi-dimensional use, and adds the observations + * into our estimates of the pdfs. + * + * @param states - the observations of all dimensions at each time step + */ + public void addObservations(Object[][] states) { - if (observations == 0) { - // TODO better error message. maybe not runtimeExc? - // joe's input: - throw new RuntimeException("0 observations not allowed."); + // ensure correct number of dimensions are provided + for (int i = 0; i < states.length; i++) { + if (states[i].length != numDimensions) { + throw new RuntimeException(String.format( + "Incorrect number of dimensions were given. Expected %d got %d.", numDimensions, states[i].length)); + } } - // I actually think this is useless? It shouldn't ever get here without - // throwing the error above. - if (currentState == State.SETTING_PROPERTIES) { - throw new RuntimeException("Estimator should be initialised before finalised..."); + if (currentState == State.COMPUTING || + currentState == State.SETTING_PROPERTIES) { + initialise(); } + currentState = State.ADDING_OBSERVATIONS; - this.currentState = State.COMPUTING; - } - - @Override - @Deprecated - public void addObservations(int states[][]) { - int rows = states.length; - int columns = states[0].length; - // increment the count of observations: - observations += rows * columns; - - // 1. Count the tuples observed - for (int r = 0; r < rows; r++) { - for (int c = 0; c < columns; c++) { - // Add to the count for this particular state: - stateCount[states[r][c]]++; + // Unwrap unecessary single dimensional arrays, allows additional calls + // to the single dimensional version without issue. + if (numDimensions == 1) { + Object[] temp = new Object[states.length]; + for (int i = 0; i < states.length; i++) { + temp[i] = states[i][0]; } - } - } - - @Override - @Deprecated - public void addObservations(int states[][][]) { - int timeSteps = states.length; - if (timeSteps == 0) { + addObservations(temp); return; } - int agentRows = states[0].length; - if (agentRows == 0) { - return; + + for (int i = 0; i < states.length; i++) { + List key = Arrays.asList(states[i]); + Integer value = hashedStateCount.getOrDefault(key, 0) + 1; + hashedStateCount.put(key, value); } - int agentColumns = states[0][0].length; - // increment the count of observations: - observations += timeSteps * agentRows * agentColumns; - - // 1. Count the tuples observed - for (int t = 0; t < timeSteps; t++) { - for (int i = 0; i < agentRows; i++) { - for (int j = 0; j < agentColumns; j++) { - // Add to the count for this particular state: - stateCount[states[t][i][j]]++; - } - } - } } - @Override - @Deprecated - public void addObservations(int states[][], int agentNumber) { - int rows = states.length; - // increment the count of observations: - observations += rows; - - // 1. Count the tuples observed - for (int r = 0; r < rows; r++) { - // Add to the count for this particular state: - stateCount[states[r][agentNumber]]++; - } + protected int[] computeCombinedValues(int[][] separatedValues, int maxSize) throws RuntimeException { + int[] sizes = new int[numDimensions]; + MatrixUtils.fill(sizes, alphabetSize); + return computeCombinedValues(separatedValues, sizes, maxSize); } - @Override - @Deprecated - public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { - int timeSteps = states.length; - // increment the count of observations: - observations += timeSteps; - - // 1. Count the tuples observed - for (int r = 0; r < timeSteps; r++) { - // Add to the count for this particular state: - stateCount[states[r][agentIndex1][agentIndex2]]++; + // TODO -- move to MatrixUtils. + protected int[] computeCombinedValues(int[][] separateValues, int[] alphabetSizes, int maxSize) throws RuntimeException { + // Make sure we won't get any overflow here + if (MatrixUtils.combinedValuesOverflow(numDimensions, alphabetSize)) { + // multiplier has overflown + throw new RuntimeException("Too many numDimensions " + numDimensions + " for the given alphabetSize " + alphabetSize + + " for this call to computeCombinedValues"); } - } - - @Override - @Deprecated - public final double computeAverageLocal(int states[][]) { - initialise(); - startAddObservations(); - addObservations(states); - this.currentState = State.COMPUTING; - return computeAverageLocalOfObservations(); - } - - @Override - @Deprecated - public final double computeAverageLocal(int states[][][]) { - initialise(); - startAddObservations(); - addObservations(states); - this.currentState = State.COMPUTING; - return computeAverageLocalOfObservations(); - } - - @Override - @Deprecated - public final double[] computeLocal(int states[][], int col) { - initialise(); - startAddObservations(); - addObservations(states, col); - this.currentState = State.COMPUTING; - return computeLocalFromPreviousObservations(states, col); - } - - @Override - @Deprecated - public final double[] computeLocal(int states[][][], - int agentIndex1, int agentIndex2) { - initialise(); - startAddObservations(); - addObservations(states, agentIndex1, agentIndex2); - this.currentState = State.COMPUTING; - return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2); - } - - @Override - @Deprecated - public final double computeAverageLocal(int states[][], int col) { - initialise(); - startAddObservations(); - addObservations(states, col); - this.currentState = State.COMPUTING; - return computeAverageLocalOfObservations(); + int size = 1; + for (int s : alphabetSizes) { + size *= s; + } + + if (size >= maxSize) { + throw new RuntimeException("Size of combined observations exceeds capacity."); + } + + int[] combinedValues = new int[size]; + for (int r = 0; r < separateValues.length; r++) { + if (separateValues[r].length != alphabetSizes.length) { + throw new RuntimeException("alphabetSize and given input size do not match"); + } + int combinedRowValue = 0; + int multiplier = 1; + for (int c = numDimensions - 1; c >= 0; c--) { + // check alphabet size restriction is maintained. + if (separateValues[r][c] >= alphabetSizes[r]) { + throw new RuntimeException("input value " + separateValues[r][c] + + " exceeded alphabet size" + alphabetSizes[c]); + } + combinedRowValue += separateValues[r][c] * multiplier; + multiplier *= alphabetSizes[r]; + } + combinedValues[r] = combinedRowValue; + } + return combinedValues; } @Override - @Deprecated - public final double computeAverageLocal(int states[][][], int agentIndex1, int agentIndex2) { - initialise(); - startAddObservations(); - addObservations(states, agentIndex1, agentIndex2); - this.currentState = State.COMPUTING; - return computeAverageLocalOfObservations(); + public void setObservations(Object[] observations) throws Exception { + this.observations = observations.length; + this.addObservations(observations); + this.finaliseAddObservations(); } @Override - @Deprecated - public double[][] computeLocalFromPreviousObservations(int states[][]){ - int rows = states.length; - int columns = states[0].length; - - double[][] localEntropy = new double[rows][columns]; - average = 0; + public double computeAverageLocalOfObservations() { + double ent = 0.0; + double p_state; max = 0; min = 0; - for (int r = 0; r < rows; r++) { - for (int c = 0; c < columns; c++) { - double p_state = (double) stateCount[states[r][c]] / (double) observations; - // Entropy takes the negative log: - localEntropy[r][c] = - Math.log(p_state) / log_2; - average += localEntropy[r][c]; - if (localEntropy[r][c] > max) { - max = localEntropy[r][c]; - } else if (localEntropy[r][c] < min) { - min = localEntropy[r][c]; - } + currentState = State.COMPUTING; + + if (hashedStateCount.isEmpty()) { + for (int count: stateCount) { + // compute p_state + p_state = (double) count / (double) observations; + ent += computeEntropyCont(p_state); + } + } else { + for (Map.Entry entry : hashedStateCount.entrySet()) { + p_state = (double) entry.getValue() / (double) observations; + ent += computeEntropyCont(p_state); } } - average = average/(double) (columns * rows); - - return localEntropy; + average = ent; + return ent; } @Override - @Deprecated - public double[][][] computeLocalFromPreviousObservations(int states[][][]){ - int timeSteps = states.length; - int agentRows, agentColumns; - if (timeSteps == 0) { - agentRows = 0; - agentColumns = 0; - } else { - agentRows = states[0].length; - if (agentRows == 0) { - agentColumns = 0; - } else { - agentColumns = states[0][0].length; - } + public final double computeAverageLocal(int states[]) throws Exception { + if (states.length == 0) { + throw new RuntimeException("States cannot be empty."); } - - double[][][] localEntropy = new double[timeSteps][agentRows][agentColumns]; - average = 0; - max = 0; - min = 0; - for (int r = 0; r < timeSteps; r++) { - for (int i = 0; i < agentRows; i++) { - for (int j = 0; j < agentColumns; j++) { - double p_state = (double) stateCount[states[r][i][j]] / (double) observations; - // Entropy takes the negative log: - localEntropy[r][i][j] = - Math.log(p_state) / log_2; - average += localEntropy[r][i][j]; - if (localEntropy[r][i][j] > max) { - max = localEntropy[r][i][j]; - } else if (localEntropy[r][i][j] < min) { - min = localEntropy[r][i][j]; - } - } + addObservations(states); + return computeAverageLocalOfObservations(); + } + + protected double computeEntropyCont(double p_state) { + double entCont = 0.0; + if (p_state > 0.0) { + // Entropy takes the negative log: + double localValue = - Math.log(p_state) / log_2; + entCont = p_state * localValue; + if (localValue > max) { + max = localValue; + } else if (localValue < min) { + min = localValue; } } - average = average/(double) (agentRows * agentColumns * timeSteps); - - return localEntropy; - + return entCont; } @Override - @Deprecated - public double[] computeLocalFromPreviousObservations(int states[][], int agentNumber){ + public double[] computeLocalFromPreviousObservations(int states[]) { + + this.currentState = State.COMPUTING; int rows = states.length; - //int columns = states[0].length; - + double[] localEntropy = new double[rows]; average = 0; max = 0; min = 0; for (int r = 0; r < rows; r++) { - double p_state = (double) stateCount[states[r][agentNumber]] / (double) observations; + double p_state = (double) stateCount[states[r]] / (double) observations; // Entropy takes the negative log: localEntropy[r] = - Math.log(p_state) / log_2; average += localEntropy[r]; @@ -554,92 +572,41 @@ public double[] computeLocalFromPreviousObservations(int states[][], int agentNu min = localEntropy[r]; } } - average = average/(double) (rows); - + + average = average/(double) rows; return localEntropy; - } - + + /* + *********************************************************************** + ******************* ALL METHODS BELOW ARE DEPRECATED ****************** + ******************* DON'T USE THEM ****************** + *********************************************************************** + */ + + // TODO protected duplicates, keep public call, that does the same as private. @Override @Deprecated - public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){ - int timeSteps = states.length; - //int columns = states[0].length; - - // Allocate for all rows even though we'll leave the first ones as zeros - double[] localEntropy = new double[timeSteps]; - average = 0; - max = 0; - min = 0; - for (int r = 0; r < timeSteps; r++) { - double p_state = (double) stateCount[states[r][agentIndex1][agentIndex2]] / (double) observations; - // Entropy takes the negative log: - localEntropy[r] = - Math.log(p_state) / log_2; - average += localEntropy[r]; - if (localEntropy[r] > max) { - max = localEntropy[r]; - } else if (localEntropy[r] < min) { - min = localEntropy[r]; - } + public void startAddObservations() { + // reinitialise if already finalised + if (currentState == State.COMPUTING) { + initialise(); } - average = average/(double) (timeSteps); - - return localEntropy; - - } - - @Override - @Deprecated - public final double[] computeLocal(int states[]) { - initialise(); - startAddObservations(); - addObservations(states); - this.currentState = State.COMPUTING; - return computeLocalFromPreviousObservations(states); - } - - @Override - @Deprecated - public final double[][] computeLocal(int states[][]) { - initialise(); - startAddObservations(); - addObservations(states); - this.currentState = State.COMPUTING; - return computeLocalFromPreviousObservations(states); + this.currentState = State.ADDING_OBSERVATIONS; } - + @Override @Deprecated - public final double[][][] computeLocal(int states[][][]) { - initialise(); - startAddObservations(); - addObservations(states); - this.currentState = State.COMPUTING; - return computeLocalFromPreviousObservations(states); - } + public void finaliseAddObservations() throws Exception { - /** - * User was formerly forced to create new instances through this factory method. - * Retained for backwards compatibility. - * - * @param alphabetSize number of symbols for each variable. - * E.g. binary variables are in base-2. - * @param blocksize number of consecutive joint values to include - * in the calculation. - * @deprecated - * @return a new EntropyCalculator - */ - @Deprecated - public static EntropyCalculatorDiscrete newInstance(int alphabetSize, int blocksize) { - if (blocksize > 1) { - return BlockEntropyCalculatorDiscrete.newInstance(blocksize, alphabetSize); - } else { - return EntropyCalculatorDiscrete.newInstance(alphabetSize); + if (currentState == State.SETTING_PROPERTIES) { + throw new RuntimeException("Estimator should be initialised before finalised..."); + } + + if (observations == 0) { + throw new RuntimeException("Must have some observations to finalise."); } - } - @Deprecated - public static EntropyCalculatorDiscrete newInstance(int alphabetSize) { - return new EntropyCalculatorDiscrete(alphabetSize); - } -} + this.currentState = State.COMPUTING; + } +} \ No newline at end of file diff --git a/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java index 9080c30a..f090aa29 100755 --- a/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/EntropyRateCalculatorDiscrete.java @@ -62,7 +62,7 @@ * @author Joseph Lizier (email, * www) */ -public class EntropyRateCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator { +public class EntropyRateCalculatorDiscrete extends UnivariateMeasureDiscreteInContextOfPastCalculator { /** * User was formerly forced to create new instances through this factory method. @@ -105,7 +105,7 @@ public void addObservations(int[] states) { // Initialise and store the current previous value for each column int prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p]; } @@ -119,7 +119,7 @@ public void addObservations(int[] states) { pastCount[prevVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r]; } } @@ -136,7 +136,7 @@ public void addObservations(int states[][]) { for (int c = 0; c < columns; c++) { prevVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[p][c]; } } @@ -152,13 +152,13 @@ public void addObservations(int states[][]) { pastCount[prevVal[c]]++; // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[r][c]; } } } - @Override + public void addObservations(int states[][][]) { int timeSteps = states.length; if (timeSteps == 0) { @@ -178,7 +178,7 @@ public void addObservations(int states[][][]) { for (int c = 0; c < agentColumns; c++) { prevVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[p][r][c]; } } @@ -196,14 +196,14 @@ public void addObservations(int states[][][]) { pastCount[prevVal[r][c]]++; // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[t][r][c]; } } } } - @Override + public void addObservations(int states[][], int col) { int rows = states.length; // increment the count of observations: @@ -213,7 +213,7 @@ public void addObservations(int states[][], int col) { int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; } @@ -227,12 +227,12 @@ public void addObservations(int states[][], int col) { pastCount[prevVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } } - @Override + public void addObservations(int states[][][], int agentIndex1, int agentIndex2) { int timeSteps = states.length; // increment the count of observations: @@ -242,7 +242,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][agentIndex1][agentIndex2]; } @@ -256,7 +256,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) pastCount[prevVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][agentIndex1][agentIndex2]; } } @@ -269,7 +269,7 @@ public double computeAverageLocalOfObservations() { max = 0; min = 0; double logTerm = 0; - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < alphabetSize; nextVal++) { for (int prevVal = 0; prevVal < base_power_k; prevVal++) { // compute p_prev double p_prev = (double) pastCount[prevVal] / (double) observations; @@ -310,7 +310,7 @@ public double[] computeLocalFromPreviousObservations(int[] states) { // Initialise and store the current previous value for each column int prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p]; } @@ -330,7 +330,7 @@ public double[] computeLocalFromPreviousObservations(int[] states) { } // Update the previous value: prevVal -= maxShiftedValue[states[r-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r]; } average = average/(double) (rows - k); @@ -338,7 +338,7 @@ public double[] computeLocalFromPreviousObservations(int[] states) { return localEntRate; } - @Override + public double[][] computeLocalFromPreviousObservations(int states[][]){ int rows = states.length; int columns = states[0].length; @@ -354,7 +354,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ for (int c = 0; c < columns; c++) { prevVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[p][c]; } } @@ -375,7 +375,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ } // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[r][c]; } } @@ -384,7 +384,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][]){ return localEntRate; } - @Override + public double[][][] computeLocalFromPreviousObservations(int states[][][]){ int timeSteps = states.length; int agentRows = states[0].length; @@ -402,7 +402,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ for (int c = 0; c < agentColumns; c++) { prevVal[r][c] = 0; for (int p = 0; p < k; p++) { - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[p][r][c]; } } @@ -426,7 +426,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ } // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[t][r][c]; } } @@ -436,7 +436,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][]){ return localEntRate; } - @Override + public double[] computeLocalFromPreviousObservations(int states[][], int col){ int rows = states.length; //int columns = states[0].length; @@ -451,7 +451,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; } int nextVal; @@ -470,7 +470,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } average = average/(double) (rows - k); @@ -479,7 +479,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } - @Override + public double[] computeLocalFromPreviousObservations(int states[][][], int agentIndex1, int agentIndex2){ int timeSteps = states.length; //int columns = states[0].length; @@ -494,7 +494,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent int prevVal = 0; prevVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][agentIndex1][agentIndex2]; } int nextVal; @@ -513,7 +513,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent } // Update the previous value: prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t][agentIndex1][agentIndex2]; } average = average/(double) (timeSteps - k); @@ -521,4 +521,22 @@ public double[] computeLocalFromPreviousObservations(int states[][][], int agent return localEntRate; } + + @Override + public void setObservations(Object[] observations) throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObservations'"); + } + + @Override + public void startAddObservations() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'startAddObservations'"); + } + + @Override + public void finaliseAddObservations() throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'finaliseAddObservations'"); + } } diff --git a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java index 132beda3..e4386eb3 100755 --- a/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscrete.java @@ -71,24 +71,21 @@ public abstract class InfoMeasureCalculatorDiscrete { */ protected int observations = 0; /** - * Number of available quantised states for each variable - * (ie binary is base-2). + * Number of available quantised states for each variable. + * (ie binary is 2). */ - protected int alphabetSize = -1; // number of individual states. -1 for unknown number - /** - * Size of the largest alphabet to support in array based memory management before - * switching to Hashtable implementation. - */ - protected int maxAlphabetSize = 100; // largest alphabet size before moving to Hashtable memory management TODO some default value + protected int alphabetSize = 100; + /** - * TODO -- make better comments :) - * State indicator of what the estimator is expecting to happen next. + * Variable to keep track of where the estimator is within its workflow. + * The estimator can be in four states; Setting Properties, Initialised, + * Adding Observations, and Computing. */ protected State currentState = State.SETTING_PROPERTIES; /** - * Boolean indicating if the size of the alphabet is known to determine the use of a - * hash table implementation for memory management. + * Boolean indicating if the size of the alphabet is known to determine the + * potnetial use of a hash table implementation for memory management. */ protected boolean knownIntegerRange = false; @@ -113,6 +110,9 @@ public abstract class InfoMeasureCalculatorDiscrete { */ protected boolean debug = false; + /** + * Enum for the states of an estimator + */ protected enum State { SETTING_PROPERTIES, INITIALISED, @@ -213,15 +213,13 @@ public void setProperty(String propertyName, String propertyValue) throws Except // TODO I think there are things here to do to go back to this state but I don't remember them rn // Actually do we want this here...? it should definitely be in the lower levels, so it should be // caught then... - // joe's input: + // joe's input: yes, check this at all levels. } - switch(propertyName.toLowerCase()) { + switch(propertyName.toUpperCase()) { case "ALPHABET_SIZE": this.alphabetSize = Integer.parseInt(propertyValue); - break; - case "MAX_ALPHA_SIZE_TO_STORE": - this.maxAlphabetSize = Integer.parseInt(propertyValue); + this.knownIntegerRange = true; break; case "KNOWN_INTEGER_RANGE": this.knownIntegerRange = Boolean.parseBoolean(propertyValue); diff --git a/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java index 56fadac3..32855f55 100755 --- a/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/MultiInformationCalculatorDiscrete.java @@ -78,51 +78,51 @@ public MultiInformationCalculatorDiscrete() { /** * Construct an instance * - * @base number of symbols for each variable. - * E.g. binary variables are in base-2. + * @alphabetSize number of symbols for each variable. + * E.g. binary variables are in base-2, so alphabetSize would be 2. * @numVars numbers of joint variables that multi-info * will be computed over. */ - public MultiInformationCalculatorDiscrete(int base, int numVars) { - super(base); - changeParams(base, numVars); + public MultiInformationCalculatorDiscrete(int alphabetSize, int numVars) { + super(alphabetSize); + changeParams(alphabetSize, numVars); } /** * Update the parameters using numVars * - * @param base + * @param alphabetSize * @param numVars * @return */ - private boolean changeParams(int base, int numVars) { - boolean paramsChanged = (this.base != base) || (this.numVars != numVars); + private boolean changeParams(int alphabetSize, int numVars) { + boolean paramsChanged = (this.alphabetSize != alphabetSize) || (this.numVars != numVars); this.numVars = numVars; - jointStates = MathsUtils.power(base, numVars); + jointStates = MathsUtils.power(alphabetSize, numVars); return paramsChanged; } /** - * Initialise (possible) updating the base and number of variables + * Initialise (possible) updating the alphabetSize and number of variables * - * @param base + * @param alphabetSize * @param numVars */ - public void initialise(int base, int numVars) { - boolean paramsChanged = changeParams(base, numVars); - super.initialise(base); + public void initialise(int alphabetSize, int numVars) { + boolean paramsChanged = changeParams(alphabetSize, numVars); + super.initialise(alphabetSize); if(paramsChanged || (jointCount == null)) { // Create storage for counts of observations try { jointCount = new int[jointStates]; - marginalCounts = new int[numVars][base]; + marginalCounts = new int[numVars][alphabetSize]; } 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 + + throw new RuntimeException("Requested memory for the alphabetSize " + + alphabetSize + " with " + numVars + " variables is too large for the JVM at this time", e); } } else { @@ -133,7 +133,7 @@ public void initialise(int base, int numVars) { @Override public void initialise(){ - initialise(base, numVars); + initialise(alphabetSize, numVars); } /** @@ -152,7 +152,7 @@ public void addObservations(int[][] states) { for (int i = 0; i < numVars; i++) { int thisValue = states[t][i]; marginalCounts[i][thisValue]++; - jointValue *= base; + jointValue *= alphabetSize; jointValue += thisValue; } jointCount[jointValue]++; @@ -176,7 +176,7 @@ public void addObservations(int[] states, int[] groupOffsets) { for (int i = 0; i < numVars; i++) { int thisValue = states[(c + groupOffsets[i] + states.length) % states.length]; marginalCounts[i][thisValue]++; - jointValue *= base; + jointValue *= alphabetSize; jointValue += thisValue; } jointCount[jointValue]++; @@ -201,7 +201,7 @@ public void addObservations(int[] states, int destinationIndex, int[] groupOffse for (int i = 0; i < numVars; i++) { int thisValue = states[(destinationIndex + groupOffsets[i] + states.length) % states.length]; marginalCounts[i][thisValue]++; - jointValue *= base; + jointValue *= alphabetSize; jointValue += thisValue; } jointCount[jointValue]++; @@ -227,7 +227,7 @@ public void addObservations(int[][] states, int[] groupOffsets) { for (int i = 0; i < numVars; i++) { int thisValue = states[t][(c + groupOffsets[i] + states.length) % states.length]; marginalCounts[i][thisValue]++; - jointValue *= base; + jointValue *= alphabetSize; jointValue += thisValue; } jointCount[jointValue]++; @@ -261,7 +261,7 @@ public double computeMiOfGivenTupleFromVarIndex(int[] tuple, int fromIndex) { int jointValue = 0; for (int i = 0; i < numVars; i++) { prodMarginalProbs *= (double) marginalCounts[i][tuple[i]] / (double) observations; - jointValue *= base; + jointValue *= alphabetSize; jointValue += tuple[i]; } if (jointCount[jointValue] == 0) { @@ -288,7 +288,7 @@ public double computeMiOfGivenTupleFromVarIndex(int[] tuple, int fromIndex) { miCont = jointProb * localValue; } else { // Fill out the next part of the tuple and make the recursive calls - for (int v = 0; v < base; v++) { + for (int v = 0; v < alphabetSize; v++) { tuple[fromIndex] = v; miCont += computeMiOfGivenTupleFromVarIndex(tuple, fromIndex + 1); } diff --git a/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java index 1d3f8262..82ff22b1 100755 --- a/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/MultiVariateInfoMeasureCalculatorDiscrete.java @@ -144,7 +144,7 @@ public void initialise(){ * at many observations (first index is time, second is variable index) */ public void addObservations(int[][] states) throws Exception { - int[] jointStates = MatrixUtils.computeCombinedValues(states, base); + int[] jointStates = MatrixUtils.computeCombinedValues(states, alphabetSize); for (int t = 0; t < states.length; t++) { for (int i = 0; i < numVars; i++) { // Extract values of the 1D and the (N-1)D marginals @@ -187,7 +187,7 @@ public double computeForGivenTupleFromVarIndex(int[] tuple, int fromIndex) throw double miCont = 0; if (fromIndex == numVars) { // The whole tuple is filled in, so compute the contribution to the MI from this tuple - int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, base)[0]; + int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, alphabetSize)[0]; if (jointCount[jointValue] == 0) { // This joint state does not occur, so it makes no contribution here @@ -200,7 +200,7 @@ public double computeForGivenTupleFromVarIndex(int[] tuple, int fromIndex) throw } else { // Fill out the next part of the tuple and make the recursive calls - for (int v = 0; v < base; v++) { + for (int v = 0; v < alphabetSize; v++) { tuple[fromIndex] = v; miCont += computeForGivenTupleFromVarIndex(tuple, fromIndex + 1); } @@ -259,7 +259,7 @@ protected abstract double computeLocalValueForTuple(int[] tuple, int jointValue) * @param tuple state of the system at a given time (index is variable number) */ protected double computeLocalValueForTuple(int[] tuple) throws Exception { - int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, base)[0]; + int jointValue = MatrixUtils.computeCombinedValues(new int[][] {tuple}, alphabetSize)[0]; return computeLocalValueForTuple(tuple, jointValue); } @@ -271,7 +271,7 @@ protected double computeLocalValueForTuple(int[] tuple) throws Exception { * @param varValue value of the variable in question in the system state */ protected int computeBigMarginalState(int jointState, int varIdx, int varValue) { - int bigMarginalState = jointState - varValue*MathsUtils.power(base, numVars - varIdx - 1); + int bigMarginalState = jointState - varValue*MathsUtils.power(alphabetSize, numVars - varIdx - 1); return bigMarginalState; } diff --git a/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java index 7cb0c4e7..2a2d4172 100755 --- a/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/MutualInformationCalculatorDiscrete.java @@ -69,15 +69,16 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi /** * Store the number of symbols for each variable */ - protected int base1; - protected int base2; + protected int alphabetSize1; + protected int alphabetSize2; protected int timeDiff = 0; - protected int[][] jointCount = null; // Count for (i[t-timeDiff], j[t]) tuples - protected int[] iCount = null; // Count for i[t-timeDiff] - protected int[] jCount = null; // Count for j[t] + private EntropyCalculatorDiscrete jointEntropyCalc; + private EntropyCalculatorDiscrete entropyCalc2; + private EntropyCalculatorDiscrete entropyCalc1; - protected boolean miComputed = false; + private boolean miComputed = false; + /** * Construct a new MI calculator with default bases of 2 and time difference of 0 @@ -86,7 +87,7 @@ public class MutualInformationCalculatorDiscrete extends InfoMeasureCalculatorDi * @throws Exception */ public MutualInformationCalculatorDiscrete() throws Exception { - this(2); + this(-1); } /** @@ -97,48 +98,60 @@ public MutualInformationCalculatorDiscrete() throws Exception { * E.g. binary variables are in base-2. * @throws Exception */ - public MutualInformationCalculatorDiscrete(int base) throws Exception { - this(base, base, 0); + public MutualInformationCalculatorDiscrete(int alphabetSize) throws Exception { + this(alphabetSize, alphabetSize, 0); } - // TODO Bring in a MutualInformationCalculatorDiscrete(int base1, int base2) constructor + // TODO Bring in a MutualInformationCalculatorDiscrete(int alphabetSize1, int alphabetSize2) constructor // but don't do this yet since it will override the previous MutualInformationCalculatorDiscrete(int base, int timeDiff) // constructor present up until v1.4 and that may lead to errors. /** * Create a new mutual information calculator * - * @param base1 number of symbols for first variable. + * @param alphabetSize1 number of symbols for first variable. * E.g. binary variables are in base-2. - * @param base2 number of symbols for second variable. + * @param alphabetSize2 number of symbols for second variable. * @param timeDiff number of time steps across which to compute * MI for given time series * @throws Exception when timeDiff < 0 */ - public MutualInformationCalculatorDiscrete(int base1, int base2, int timeDiff) throws Exception { + public MutualInformationCalculatorDiscrete(int alphabetSize1, int alphabetSize2, int timeDiff) throws Exception { // Create super object, just with first base - super(base1); + super(alphabetSize1); + + // For unknown alphabet sizes + if (alphabetSize1 == -1 || alphabetSize2 == -1) { + this.entropyCalc1 = new EntropyCalculatorDiscrete(); + this.entropyCalc2 = new EntropyCalculatorDiscrete(); + } + // For known alphabet sizes + else { + this.entropyCalc1 = new EntropyCalculatorDiscrete(alphabetSize1); + this.entropyCalc2 = new EntropyCalculatorDiscrete(alphabetSize2); + } + this.jointEntropyCalc = new EntropyCalculatorDiscrete(); - changeBases(base1, base2, timeDiff); + changeBases(alphabetSize1, alphabetSize2, timeDiff); } /** * Common code to be called when bases are changed (does not update arrays though) * - * @param base1 - * @param base2 + * @param alphabetSize1 + * @param alphabetSize2 * @param timeDiff * @throws Exception */ - private boolean changeBases(int base1, int base2, int timeDiff) throws Exception { + private boolean changeBases(int alphabetSize1, int alphabetSize2, int timeDiff) throws Exception { boolean basesChanged = false; - if ((this.base1 != base1) || (this.base2 != base2)) { + if ((this.alphabetSize1 != alphabetSize1) || (this.alphabetSize2 != alphabetSize2)) { basesChanged = true; } // Store the bases - this.base1 = base1; - this.base2 = base2; + this.alphabetSize1 = alphabetSize1; + this.alphabetSize2 = alphabetSize2; if (timeDiff < 0) { throw new Exception("timeDiff must be >= 0"); @@ -151,7 +164,7 @@ private boolean changeBases(int base1, int base2, int timeDiff) throws Exception @Override public void initialise() { try { - initialise(base1, base2, timeDiff); + initialise(alphabetSize1, alphabetSize2, 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 @@ -162,33 +175,17 @@ public void initialise() { /** * Initialise with new bases and time diff * - * @param base1 - * @param base2 + * @param alphabetSize1 + * @param alphabetSize2 * @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; - + public void initialise(int alphabetSize1, int alphabetSize2, int timeDiff) { + super.initialise(alphabetSize1); + + this.entropyCalc1.initialise(alphabetSize1); + this.entropyCalc2.initialise(alphabetSize2); + this.jointEntropyCalc.initialise(); + jointEntropyCalc.alphabetSizes = new int[] {alphabetSize1, alphabetSize2}; } /** @@ -200,95 +197,91 @@ public void initialise(int base1, int base2, int timeDiff) throws Exception { */ @Override public void addObservations(int[] var1, int[] var2) { - int timeSteps = var1.length; - // int columns = states[0].length; - // increment the count of observations: - observations += (timeSteps - timeDiff); - - // 1. Count the tuples observed - int iVal, jVal; - for (int r = timeDiff; r < timeSteps; r++) { - // Add to the count for this particular pair: - iVal = var1[r-timeDiff]; - jVal = var2[r]; - jointCount[iVal][jVal]++; - iCount[iVal]++; - jCount[jVal]++; + + // I think this won't work if these aren't the same length... + // Could make it work with different lengths, unsure if this + // is ever useful. + if (var1.length != var2.length) { + throw new RuntimeException("Array lengths do not match. " + + "var1: " + var1.length + ", var2: " + var2.length); + } + + // adjust var1 observations for the time difference + int[] temp1 = new int[var1.length - timeDiff]; + for (int i = 0; i < var1.length; i++) { + if (i < timeDiff) { + continue; + } + temp1[i-timeDiff] = var1[i]; + } + + // adjust var2 osbervations for the time difference + int[] temp2 = new int[var2.length - timeDiff]; + for (int i = 0; i < var2.length - timeDiff; i++) { + temp2[i-timeDiff] = var2[i]; } + + entropyCalc1.addObservations(temp1); + entropyCalc2.addObservations(temp2); + + // Create the joint observations + int[][] jointObservations = new int[temp1.length][2]; + for (int i = 0; i < temp1.length; i++) { + jointObservations[i][0] = temp1[i]; + jointObservations[i][1] = temp2[i]; + } + + // This will throw a runtime error if format is somehow incorrect + jointEntropyCalc.addObservations(jointObservations); + observations += (var1.length - timeDiff); } - /** - * {@inheritDoc} - * - * Pairs for MI are between columns iCol and jCol, separated in time by timeDiff (i is first). - * - */ - @Override - public void addObservations(int states[][], int iCol, int jCol) { - int rows = states.length; - // int columns = states[0].length; - // increment the count of observations: - observations += (rows - timeDiff); - - // 1. Count the tuples observed - int iVal, jVal; - for (int r = timeDiff; r < rows; r++) { - // Add to the count for this particular pair: - iVal = states[r-timeDiff][iCol]; - jVal = states[r][jCol]; - jointCount[iVal][jVal]++; - iCount[iVal]++; - jCount[jVal]++; + public void addObservations(Object[] var1, Object[] var2) { + // I think this won't work if these aren't the same length... + if (var1.length != var2.length) { + throw new RuntimeException("Array lengths do not match. " + + "var1: " + var1.length + ", var2: " + var2.length); + } + + // adjust var1 observations for the time difference + Object[] temp1 = new Object[var1.length - timeDiff]; + for (int i = 0; i < var1.length; i++) { + if (i < timeDiff) { + continue; + } + temp1[i-timeDiff] = var1[i]; + } + + // adjust var2 osbervations for the time difference + Object[] temp2 = new Object[var2.length - timeDiff]; + for (int i = 0; i < var2.length - timeDiff; i++) { + temp2[i-timeDiff] = var2[i]; + } + + entropyCalc1.addObservations(temp1); + entropyCalc2.addObservations(temp2); + + // Create the joint observations + Object[][] jointObservations = new Object[temp1.length][2]; + for (int i = 0; i < temp1.length; i++) { + jointObservations[i][0] = temp1[i]; + jointObservations[i][1] = temp2[i]; } + + // This will throw a runtime error if format is somehow incorrect + jointEntropyCalc.addObservations(jointObservations); + observations += (var1.length - timeDiff); } @Override public double computeAverageLocalOfObservations() { - double mi = 0.0; - double miCont = 0.0; - - max = 0; - min = 0; - double meanSqLocals = 0; - if (debug) { - System.out.println("i\tj\tp_i\tp_j\tp_joint\tlocal"); - } - for (int i = 0; i < base1; i++) { - // compute p_i - double probi = (double) iCount[i] / (double) observations; - for (int j = 0; j < base2; j++) { - // compute p_j - double probj = (double) jCount[j] / (double) observations; - // compute p(veci=i, vecj=j) - double jointProb = (double) jointCount[i][j] / (double) observations; - // Compute MI contribution: - if (jointProb * probi * probj > 0.0) { - double localValue = Math.log(jointProb / (probi * probj)) / log_2; - miCont = jointProb * localValue; - if (debug) { - System.out.printf("%d\t%d\t%.4f\t%.4f\t%.4f\t%.4f\n", - i, j, probi, probj, jointProb, localValue); - } - if (localValue > max) { - max = localValue; - } else if (localValue < min) { - min = localValue; - } - // Add this contribution to the mean - // of the squared local values - meanSqLocals += miCont * localValue; - } else { - miCont = 0.0; - } - mi += miCont; - } - } - - average = mi; - miComputed = true; - std = Math.sqrt(meanSqLocals - average * average); + double entropy1 = entropyCalc1.computeAverageLocalOfObservations(); + double entropy2 = entropyCalc2.computeAverageLocalOfObservations(); + double jointEntropy = jointEntropyCalc.computeAverageLocalOfObservations(); - return mi; + // computeFromPreviousObservations relies on average, so I've kept that being set. + average = entropy1 + entropy2 - jointEntropy; + return average; } @Override @@ -299,8 +292,23 @@ public EmpiricalMeasurementDistribution computeSignificance(int numPermutationsT return computeSignificance(newOrderings); } + // TODO Implement this method. Old code has been left for reference. + /* + * For Brad's understanding, free to delete this comment. + * This is: + * > calculate MI + * > shuffle all observations + * > recalculate MI for each permutation + * > count number of recalcs > original MI + * > p = count / numPerms + */ @Override public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings) { + + // 1/3 temporary fix to get it to compile + // all original code is commented out below + return null; + /* double actualMI = computeAverageLocalOfObservations(); int numPermutationsToCheck = newOrderings.length; @@ -308,14 +316,14 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings // Reconstruct the values of the first and second variables (not necessarily in order) int[] iValues = new int[observations]; int t_i = 0; - for (int iVal = 0; iVal < base1; iVal++) { + for (int iVal = 0; iVal < alphabetSize1; iVal++) { int numberOfSamplesI = iCount[iVal]; MatrixUtils.fill(iValues, iVal, t_i, numberOfSamplesI); t_i += numberOfSamplesI; } int[] jValues = new int[observations]; int t_j = 0; - for (int jVal = 0; jVal < base2; jVal++) { + for (int jVal = 0; jVal < alphabetSize2; jVal++) { int numberOfSamplesJ = jCount[jVal]; MatrixUtils.fill(jValues, jVal, t_j, numberOfSamplesJ); t_j += numberOfSamplesJ; @@ -323,7 +331,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings MutualInformationCalculatorDiscrete mi2; try { - mi2 = new MutualInformationCalculatorDiscrete(base1, base2, timeDiff); + mi2 = new MutualInformationCalculatorDiscrete(alphabetSize1, alphabetSize2, timeDiff); } catch (Exception e) { // The only possible exception is if timeDiff < 0, which // it cannot be. Shut down the JVM @@ -356,6 +364,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings measDistribution.pValue = (double) countWhereMIIsMoreSignificantThanOriginal / (double) numPermutationsToCheck; measDistribution.actualValue = actualMI; return measDistribution; + */ } @Override @@ -363,9 +372,17 @@ public AnalyticMeasurementDistribution computeSignificance() { if (!miComputed) { computeAverageLocalOfObservations(); } + + int a1 = alphabetSize1; + int a2 = alphabetSize2; + if (alphabetSize1 <= -1 || alphabetSize2 == -1) { + a1 = entropyCalc1.hashedStateCount.keySet().size(); + a2 = entropyCalc2.hashedStateCount.keySet().size(); + } + return new ChiSquareMeasurementDistribution(average, observations, - (base1 - 1) * (base2 - 1)); + (a1 - 1) * (a2 - 1)); } /** @@ -381,6 +398,9 @@ public AnalyticMeasurementDistribution computeSignificance() { */ public double computeLocalFromPreviousObservations(int val1, int val2) throws Exception{ + // 2/3 temporary fix to get this to compile + return 0; + /* double logTerm = ( (double) jointCount[val1][val2] ) / ( (double) jCount[val2] * (double) iCount[val1] ); @@ -392,6 +412,7 @@ public double computeLocalFromPreviousObservations(int val1, int val2) throws Ex double localMI = Math.log(logTerm) / log_2; return localMI; + */ } /** @@ -407,6 +428,9 @@ public double computeLocalFromPreviousObservations(int val1, int val2) throws Ex */ public double[] computeLocalFromPreviousObservations(int[] var1, int[] var2) throws Exception{ + // 3/3 temporary fix to get it to compile + return null; + /* if (var1.length != var2.length) { throw new Exception("var1 and var2 must have the same number of observations"); } @@ -435,74 +459,13 @@ public double[] computeLocalFromPreviousObservations(int[] var1, int[] var2) thr average = average/(double) observations; return localMI; + */ } - - /** - * Computes local mutual information (or pointwise mutual information) - * for the given states, using pdfs built up from observations previously - * sent in via the addObservations method - * - * @param states 2D time series of observations (first index time, - * second is variable index) - * @param iCol column number for first variable - * @param jCol column number for second variable - * @return array of local mutual information values for each - * observation of (var1, var2). Note - if timeDiff > 0, then the - * return length will be var1.length - timeDiff. - */ - public double[] computeLocalFromPreviousObservations(int states[][], int iCol, int jCol){ - int rows = states.length; - //int columns = states[0].length; - // Allocate for all rows even though we'll leave the first ones as zeros - double[] localMI = new double[rows]; - int iVal, jVal; - double logTerm = 0.0; - for (int r = timeDiff; r < rows; r++) { - iVal = states[r-timeDiff][iCol]; - jVal = states[r][jCol]; - logTerm = ( (double) jointCount[iVal][jVal] ) / - ( (double) jCount[jVal] * - (double) iCount[iVal] ); - // Now account for the fact that we've - // just used counts rather than probabilities, - // and we've got two counts on the bottom - // but one count on the top: - logTerm *= (double) observations; - localMI[r] = Math.log(logTerm) / log_2; - average += localMI[r]; - if (localMI[r] > max) { - max = localMI[r]; - } else if (localMI[r] < min) { - min = localMI[r]; - } - } - average = average/(double) observations; - - return localMI; - - } - - /** - * Standalone routine to - * compute local mutual information (or pointwise mutual information) - * across a 2D spatiotemporal - * array of the states of homogeneous agents - * Return a 2D spatiotemporal array of local values. - * First history rows are zeros - * - * @param states 2D time series of observations (first index time, - * second is variable index) - * @param iCol column number for first variable - * @param jCol column number for second variable - * @return array of local mutual information values for each - * observation of (var1, var2). Note - if timeDiff > 0, then the - * return length will be var1.length - timeDiff. - */ - public double[] computeLocal(int states[][], int iCol, int jCol) { - initialise(); - addObservations(states, iCol, jCol); - return computeLocalFromPreviousObservations(states, iCol, jCol); - + // TODO -- do not implement, just remove this from the interface + @Override + public void addObservations(int[][] states, int sourceIndex, int destIndex) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addObservations'"); } } diff --git a/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java index 1d46f7f5..b1802856 100755 --- a/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/PredictiveInformationCalculatorDiscrete.java @@ -80,7 +80,7 @@ * @author Joseph Lizier (email, * www) */ -public class PredictiveInformationCalculatorDiscrete extends SingleAgentMeasureDiscreteInContextOfPastCalculator { +public class PredictiveInformationCalculatorDiscrete extends UnivariateMeasureDiscreteInContextOfPastCalculator { /** * User was formerly forced to create new instances through this factory method. @@ -116,7 +116,7 @@ public PredictiveInformationCalculatorDiscrete(int numDiscreteValues, int blockL @Override public void initialise(int base, int blockLength) { - boolean baseOrHistoryChanged = (this.base != base) || (k != blockLength); + boolean baseOrHistoryChanged = (this.alphabetSize != base) || (k != blockLength); super.initialise(base, blockLength); if (baseOrHistoryChanged || (nextPastCount == null)) { @@ -142,7 +142,7 @@ public void initialise(int base, int blockLength) { @Override public void initialise(){ - initialise(base, k); + initialise(alphabetSize, k); } /** @@ -165,9 +165,9 @@ public void addObservations(int timeSeries[]) { int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[p]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += timeSeries[k-1+p]; } @@ -175,7 +175,7 @@ public void addObservations(int timeSeries[]) { for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[timeSeries[t-1]]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += timeSeries[k-1+t]; // Update the counts nextPastCount[nextVal][prevVal]++; @@ -183,7 +183,7 @@ public void addObservations(int timeSeries[]) { nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[t]; } } @@ -213,9 +213,9 @@ public void addObservations(int states[][]) { prevVal[c] = 0; nextVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[p][c]; - nextVal[c] *= base; + nextVal[c] *= alphabetSize; nextVal[c] += states[k-1+p][c]; } } @@ -225,7 +225,7 @@ 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] *= base; + nextVal[c] *= alphabetSize; nextVal[c] += states[k-1+r][c]; // Update the counts nextPastCount[nextVal[c]][prevVal[c]]++; @@ -233,7 +233,7 @@ public void addObservations(int states[][]) { nextCount[nextVal[c]]++; // Update the previous value: prevVal[c] -= maxShiftedValue[states[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += states[r][c]; } } @@ -270,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] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[p][r][c]; - nextVal[r][c] *= base; + nextVal[r][c] *= alphabetSize; nextVal[r][c] += states[k-1+p][r][c]; } } @@ -284,7 +284,7 @@ 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] *= base; + nextVal[r][c] *= alphabetSize; nextVal[r][c] += states[k-1+t][r][c]; // Update the counts nextPastCount[nextVal[r][c]][prevVal[r][c]]++; @@ -292,7 +292,7 @@ public void addObservations(int states[][][]) { nextCount[nextVal[r][c]]++; // Update the previous value: prevVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += states[t][r][c]; } } @@ -321,9 +321,9 @@ public void addObservations(int states[][], int col) { int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += states[k-1+p][col]; } @@ -331,7 +331,7 @@ 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 *= base; + nextVal *= alphabetSize; nextVal += states[k-1+r][col]; // Add to the count for this particular transition: // (cell's assigned as above) @@ -340,7 +340,7 @@ public void addObservations(int states[][], int col) { nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } } @@ -368,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 *= base; + prevVal *= alphabetSize; prevVal += states[p][agentIndex1][agentIndex2]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += states[k-1+p][agentIndex1][agentIndex2]; } @@ -378,7 +378,7 @@ 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 *= base; + nextVal *= alphabetSize; nextVal += states[k-1+t][agentIndex1][agentIndex2]; // Add to the count for this particular transition: // (cell's assigned as above) @@ -387,7 +387,7 @@ public void addObservations(int states[][][], int agentIndex1, int agentIndex2) nextCount[nextVal]++; // Update the previous value: prevVal -= maxShiftedValue[states[t-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[t][agentIndex1][agentIndex2]; } } @@ -472,9 +472,9 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[p]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += timeSeries[k-1+p]; } @@ -482,7 +482,7 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ for (int t = k; t < timeSteps - (k-1); t++) { // Update the next value: nextVal -= maxShiftedValue[timeSeries[t-1]]; - nextVal *= base; + nextVal *= alphabetSize; nextVal += timeSeries[k-1+t]; logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * @@ -501,7 +501,7 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[]){ } // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[t]; } average = average/(double) (timeSteps - k - (k-1)); @@ -543,9 +543,9 @@ public double[][] computeLocalFromPreviousObservations(int timeSeries[][]){ prevVal[c] = 0; nextVal[c] = 0; for (int p = 0; p < k; p++) { - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += timeSeries[p][c]; - nextVal[c] *= base; + nextVal[c] *= alphabetSize; nextVal[c] += timeSeries[k-1+p][c]; } } @@ -554,7 +554,7 @@ 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] *= base; + nextVal[c] *= alphabetSize; nextVal[c] += timeSeries[k-1+r][c]; logTerm = ( (double) nextPastCount[nextVal[c]][prevVal[c]] ) / ( (double) nextCount[nextVal[c]] * @@ -573,7 +573,7 @@ public double[][] computeLocalFromPreviousObservations(int timeSeries[][]){ } // Update the previous value: prevVal[c] -= maxShiftedValue[timeSeries[r-k][c]]; - prevVal[c] *= base; + prevVal[c] *= alphabetSize; prevVal[c] += timeSeries[r][c]; } } @@ -618,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] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += timeSeries[p][r][c]; - nextVal[r][c] *= base; + nextVal[r][c] *= alphabetSize; nextVal[r][c] += timeSeries[k-1+p][r][c]; } } @@ -631,7 +631,7 @@ 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] *= base; + nextVal[r][c] *= alphabetSize; nextVal[r][c] += timeSeries[k-1+t][r][c]; logTerm = ( (double) nextPastCount[nextVal[r][c]][prevVal[r][c]] ) / ( (double) nextCount[nextVal[r][c]] * @@ -650,7 +650,7 @@ public double[][][] computeLocalFromPreviousObservations(int timeSeries[][][]){ } // Update the previous value: prevVal[r][c] -= maxShiftedValue[timeSeries[t-k][r][c]]; - prevVal[r][c] *= base; + prevVal[r][c] *= alphabetSize; prevVal[r][c] += timeSeries[t][r][c]; } } @@ -690,16 +690,16 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[p][col]; - nextVal *= base; + nextVal *= alphabetSize; 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 *= base; + nextVal *= alphabetSize; nextVal += states[k-1+r][col]; logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * @@ -718,7 +718,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int col){ } // Update the previous value: prevVal -= maxShiftedValue[states[r-k][col]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += states[r][col]; } average = average/(double) (rows - k - (k-1)); @@ -758,16 +758,16 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[][][], int a int prevVal = 0; int nextVal = 0; for (int p = 0; p < k; p++) { - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[p][agentIndex1][agentIndex2]; - nextVal *= base; + nextVal *= alphabetSize; 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 *= base; + nextVal *= alphabetSize; nextVal += timeSeries[k-1+t][agentIndex1][agentIndex2]; logTerm = ( (double) nextPastCount[nextVal][prevVal] ) / ( (double) nextCount[nextVal] * @@ -786,7 +786,7 @@ public double[] computeLocalFromPreviousObservations(int timeSeries[][][], int a } // Update the previous value: prevVal -= maxShiftedValue[timeSeries[t-k][agentIndex1][agentIndex2]]; - prevVal *= base; + prevVal *= alphabetSize; prevVal += timeSeries[t][agentIndex1][agentIndex2]; } average = average/(double) (timeSteps - k - (k-1)); @@ -844,7 +844,7 @@ public void writePdfs() { public int computePastValue(int[] x, int t) { int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += x[t - k + 1 + p]; } return pastVal; @@ -863,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 *= base; + pastVal *= alphabetSize; pastVal += x[t - k + 1 + p][i]; } return pastVal; @@ -883,9 +883,27 @@ 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 *= base; + pastVal *= alphabetSize; pastVal += x[t - k + 1 + p][i][j]; } return pastVal; } + + @Override + public void setObservations(Object[] observations) throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObservations'"); + } + + @Override + public void startAddObservations() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'startAddObservations'"); + } + + @Override + public void finaliseAddObservations() throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'finaliseAddObservations'"); + } } diff --git a/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscrete.java index 2593484b..766cfe65 100755 --- a/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscrete.java @@ -255,7 +255,7 @@ private void addObservations(int states[][], int offsetOfDestFromSources[], bool for (int c = 0; c < numAgents; c++) { pastVal[c] = 0; for (int p = 0; p < k; p++) { - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[p][c]; } } @@ -277,13 +277,13 @@ private void addObservations(int states[][], int offsetOfDestFromSources[], bool sourceVal = states[t-1][(c-cleanedSourcesOffsets[sIndex]+numAgents) % numAgents]; sourceNumValueNextPastCount[sIndex][sourceVal][destVal][pastVal[c]]++; sourceNumValuePastCount[sIndex][sourceVal][pastVal[c]]++; - jointSourcesVal *= base; + jointSourcesVal *= alphabetSize; jointSourcesVal += sourceVal; } sourcesNextPastCount[jointSourcesVal][destVal][pastVal[c]]++; // Update the previous value: pastVal[c] -= maxShiftedValue[states[t-k][c]]; - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[t][c]; } } @@ -346,7 +346,7 @@ private void addObservations(int states[][][], int offsetOfDestFromSources[][], for (int c = 0; c < agentColumns; c++) { pastVal[r][c] = 0; for (int p = 0; p < k; p++) { - pastVal[r][c] *= base; + pastVal[r][c] *= alphabetSize; pastVal[r][c] += states[p][r][c]; } } @@ -377,13 +377,13 @@ private void addObservations(int states[][][], int offsetOfDestFromSources[][], [(c-cleanedSourcesOffsets[sIndex][COLUMN_INDEX]+agentColumns) % agentColumns]; sourceNumValueNextPastCount[sIndex][sourceVal][destVal][pastVal[r][c]]++; sourceNumValuePastCount[sIndex][sourceVal][pastVal[r][c]]++; - jointSourcesVal *= base; + jointSourcesVal *= alphabetSize; jointSourcesVal += sourceVal; } sourcesNextPastCount[jointSourcesVal][destVal][pastVal[r][c]]++; // Update the previous value: pastVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - pastVal[r][c] *= base; + pastVal[r][c] *= alphabetSize; pastVal[r][c] += states[t][r][c]; } } @@ -421,7 +421,7 @@ private void addObservations(int states[][], int destCol, int[] sourcesAbsolute, // Initialise and store the current previous value for each column int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destCol]; } @@ -439,13 +439,13 @@ private void addObservations(int states[][], int destCol, int[] sourcesAbsolute, sourceVal = states[r-1][cleanedSourcesAbsolute[sIndex]]; sourceNumValueNextPastCount[sIndex][sourceVal][destVal][pastVal]++; sourceNumValuePastCount[sIndex][sourceVal][pastVal]++; - jointSourcesVal *= base; + jointSourcesVal *= alphabetSize; jointSourcesVal += sourceVal; } sourcesNextPastCount[jointSourcesVal][destVal][pastVal]++; // Update the previous value: pastVal -= maxShiftedValue[states[r-k][destCol]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[r][destCol]; } } @@ -483,7 +483,7 @@ private void addObservations(int states[][][], int destAgentRow, int destAgentCo // Initialise and store the current previous value for each column int pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destAgentRow][destAgentColumn]; } @@ -502,13 +502,13 @@ private void addObservations(int states[][][], int destAgentRow, int destAgentCo [cleanedSourcesAbsolute[sIndex][COLUMN_INDEX]]; sourceNumValueNextPastCount[sIndex][sourceVal][destVal][pastVal]++; sourceNumValuePastCount[sIndex][sourceVal][pastVal]++; - jointSourcesVal *= base; + jointSourcesVal *= alphabetSize; jointSourcesVal += sourceVal; } sourcesNextPastCount[jointSourcesVal][destVal][pastVal]++; // Update the previous value: pastVal -= maxShiftedValue[states[t-k][destAgentRow][destAgentColumn]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[t][destAgentRow][destAgentColumn]; } } @@ -560,7 +560,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo if (indexToModify < sourceValues.length) { // Assign values to our variables and make the recursive call - for (int s = 0; s < base; s++) { + for (int s = 0; s < alphabetSize; s++) { sourceValues[indexToModify] = s; computeAverageLocalOfObservations(sourceValues, indexToModify + 1); } @@ -575,7 +575,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo // Compute the joint source value first: jointSourcesVal = 0; for (int sIndex = 0; sIndex < numSources; sIndex++) { - jointSourcesVal *= base; + jointSourcesVal *= alphabetSize; jointSourcesVal += sourceValues[sIndex]; } @@ -584,7 +584,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo // contributions for each tuple. double[] localActAndTes = new double[numSources + 1]; for (int pastVal = 0; pastVal < base_power_k; pastVal++) { - for (int destVal = 0; destVal < base; destVal++) { + for (int destVal = 0; destVal < alphabetSize; destVal++) { if (sourcesNextPastCount[jointSourcesVal][destVal][pastVal] != 0) { // Add in the local active information storage first: logTerm = ( (double) nextPastCount[destVal][pastVal] ) / @@ -692,7 +692,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo for (int c = 0; c < numAgents; c++) { pastVal[c] = 0; for (int p = 0; p < k; p++) { - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[p][c]; } } @@ -751,7 +751,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo } // Update the previous value: pastVal[c] -= maxShiftedValue[states[t-k][c]]; - pastVal[c] *= base; + pastVal[c] *= alphabetSize; pastVal[c] += states[t][c]; } } @@ -838,7 +838,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo for (int c = 0; c < numAgentColumns; c++) { pastVal[r][c] = 0; for (int p = 0; p < k; p++) { - pastVal[r][c] *= base; + pastVal[r][c] *= alphabetSize; pastVal[r][c] += states[p][r][c]; } } @@ -906,7 +906,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo } // Update the previous value: pastVal[r][c] -= maxShiftedValue[states[t-k][r][c]]; - pastVal[r][c] *= base; + pastVal[r][c] *= alphabetSize; pastVal[r][c] += states[t][r][c]; } } @@ -983,7 +983,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo int pastVal = 0; pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destCol]; } @@ -1038,7 +1038,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo } // Update the previous value: pastVal -= maxShiftedValue[states[r-k][destCol]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[r][destCol]; } @@ -1107,7 +1107,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo int pastVal = 0; pastVal = 0; for (int p = 0; p < k; p++) { - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[p][destAgentRow][destAgentColumn]; } @@ -1163,7 +1163,7 @@ protected void computeAverageLocalOfObservations(int[] sourceValues, int indexTo } // Update the previous value: pastVal -= maxShiftedValue[states[t-k][destAgentRow][destAgentColumn]]; - pastVal *= base; + pastVal *= alphabetSize; pastVal += states[t][destAgentRow][destAgentColumn]; } diff --git a/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscreteByAddition.java b/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscreteByAddition.java index 2579b2a5..37f16774 100755 --- a/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscreteByAddition.java +++ b/java/source/infodynamics/measures/discrete/SeparableInfoCalculatorDiscreteByAddition.java @@ -66,7 +66,7 @@ public SeparableInfoCalculatorDiscreteByAddition(int base, int history, private void createAppTeCalculators() { ateCalcs = new TransferEntropyCalculatorDiscrete[numSources]; for (int i = 0; i < numSources; i++) { - ateCalcs[i] = TransferEntropyCalculatorDiscrete.newInstance(base, k); + ateCalcs[i] = TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalcs[i].setPeriodicBoundaryConditions(periodicBoundaryConditions); } } @@ -276,7 +276,7 @@ public double[][] computeLocal(int states[][], int[] offsetOfDestFromSources) { } int[] cleanedOffsets = cleanOffsetOfDestFromSources(offsetOfDestFromSources); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); double[][] temp; for (int i = 0; i < numSources; i++) { @@ -312,7 +312,7 @@ public double[][][] computeLocal(int states[][][], int[][] offsetOfDestFromSourc } int[][] cleanedOffsets = cleanOffsetOfDestFromSources(offsetOfDestFromSources); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); double[][][] temp; for (int i = 0; i < numSources; i++) { @@ -346,7 +346,7 @@ public double computeAverageLocal(int[][] states, int[] sourceOffsets) { average = aiCalc.computeAverageLocal(states); int[] cleanedOffsets = cleanOffsetOfDestFromSources(sourceOffsets); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); for (int i = 0; i < numSources; i++) { average += ateCalc.computeAverageLocal(states, cleanedOffsets[i]); @@ -363,7 +363,7 @@ public double computeAverageLocal(int[][][] states, int[][] sourceOffsets) { average = aiCalc.computeAverageLocal(states); int[][] cleanedOffsets = cleanOffsetOfDestFromSources(sourceOffsets); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); for (int i = 0; i < numSources; i++) { average += ateCalc.computeAverageLocal(states, @@ -385,7 +385,7 @@ public double[] computeLocal(int states[][], int destCol, int[] sourcesAbsolute) } int[] cleanedOffsets = cleanAbsoluteSources(sourcesAbsolute, destCol); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); double[] temp; for (int i = 0; i < numSources; i++) { @@ -421,7 +421,7 @@ public double[] computeLocal(int states[][][], int destAgentRow, } int[][] cleanedSourcesAbsolute = cleanAbsoluteSources(sourcesAbsolute, destAgentRow, destAgentColumn); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); double[] temp; for (int i = 0; i < numSources; i++) { @@ -454,7 +454,7 @@ public double computeAverageLocal(int[][] states, int destCol, int[] sourcesAbso average = aiCalc.computeAverageLocal(states, destCol); int[] cleanedSourcesAbsolute = cleanAbsoluteSources(sourcesAbsolute, destCol); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); for (int i = 0; i < numSources; i++) { average += ateCalc.computeAverageLocal(states, cleanedSourcesAbsolute[i], destCol); @@ -472,7 +472,7 @@ public double computeAverageLocal(int states[][][], int destAgentRow, average = aiCalc.computeAverageLocal(states); int[][] cleanedSourcesAbsolute = cleanAbsoluteSources(sourcesAbsolute, destAgentRow, destAgentColumn); TransferEntropyCalculatorDiscrete ateCalc = - TransferEntropyCalculatorDiscrete.newInstance(base, k); + TransferEntropyCalculatorDiscrete.newInstance(alphabetSize, k); ateCalc.setPeriodicBoundaryConditions(periodicBoundaryConditions); for (int i = 0; i < numSources; i++) { average += ateCalc.computeAverageLocal(states, diff --git a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java b/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java deleted file mode 100755 index 507e7f62..00000000 --- a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscrete.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Java Information Dynamics Toolkit (JIDT) - * Copyright (C) 2012, Joseph T. Lizier - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package infodynamics.measures.discrete; - -/** - * Interface for calculators of information-theoretic measures - * for single variables (e.g. entropy, active information storage). - * The interface defines common operations such as - * adding observations and calculating - * local and average values, etc. - * - *

Usage is as per {@link InfoMeasureCalculatorDiscrete}, with - * many methods for supplying observations and making - * calculations defined here.

- * - *

It would ideally be an abstract class to be inherited from, but - * it's more important for some of our calculators to have inheritance from - * ContextOfPastCalculator, and since java doesn't allow multiple - * inheritance, one of them has to miss out. - * To get around this, we combine the two in - * {@link SingleAgentMeasureDiscreteInContextOfPastCalculator}. - *

- * - * @author Joseph Lizier (email, - * www) - */ -public interface SingleAgentMeasureDiscrete { - - /** - * Property name for definite alphabet size. Attempting - * to use observations beyond this property will throw - * errors. - */ - public static final String ALPHABET_SIZE = "ALPHABET_SIZE"; - - /** - * Property name for determining when to switch to HashTable - * memory management system. (default is TODO...) - */ - public static final String MAX_ALPHA_SIZE_TO_STORE = "MAX_ALPHA_SIZE_TO_STORE"; - - /** - * Property name for whether or not the calculator is using - * the HashTable memory management. - * - * TODO -- maybe this shouldn't be a property... it should def be - * mutually exclusive with alphaSize. - */ - public static final String KNOWN_INTEGER_RANGE = "KNOWN_INTEGER_RANGE"; - - - /** - * Property name for determining whether or not the calculator - * is still adding observations. - * - * TODO -- I think this is the nicest way to do this, though I'd rather a private - * boolean not set by setProperties. This probs shouldn't be a property at all... - */ - public static final String EXPECTING_OBSERVATIONS = "EXPECTING_OBSERVATIONS"; - - /** - * Initialise the calculator with same or unknown alphabet size - */ - public void initialise(); - - /** - * Sets the samples from which to compute the PDF for the entropy. - * Should only be called once, the last call contains the - * observations that are used (they are not accumulated). - * - * @param observations array of (univariate) samples - * @throws Exception - */ - public void setObservations(Object[] observations) throws Exception; - - /** - * Signal that we will add in the samples for computing the PDF - * from several disjoint time-series or trials via calls to - * "addObservations" rather than "setObservations" type methods - * (defined by the child interfaces and classes). - */ - public void startAddObservations(); - - /** - * Signal that the observations are now all added, PDFs can now be constructed. - * - * @throws Exception when the estimator has no observations. - */ - public void finaliseAddObservations() throws Exception; - - /** - * Add observations in to our estimates of the pdfs. - * - * @param states series of samples - */ - public void addObservations(Object states[]); - - - /** - * Compute the average value of the measure - * from the previously-supplied samples. - * - * Must set average, min and max - * - * @return the estimate of the measure - */ - public double computeAverageLocalOfObservations(); - - /** - * Computes local information theoretic measure for the given - * states, using pdfs built up from observations previously - * sent in via the addObservations method. - * - * Must set average, min and max - * - * @param states time series of samples - * @return time-series of local values (indexed as per states) - */ - public double[] computeLocalFromPreviousObservations(int states[]); - - /** - * Computes local information theoretic measure for the given - * states, using pdfs built up from observations previously - * sent in via the addObservations method. - * This method to be used for homogeneous agents only, - * since the local values will be computed for all variables. - * - * Must set average, min and max - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @return 2D time-series of local values (indexed as per states) - */ - public double[][] computeLocalFromPreviousObservations(int states[][]); - - /** - * Computes local information theoretic measure for the given - * variable in the 2D time-series - * states, using pdfs built up from observations previously - * sent in via the addObservations method. - * This method is suitable for heterogeneous agents, since - * the specific variable is identified. - * - * Must set average, min and max. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @param col index of the given variable - * @return time-series of local values for the variable - */ - public double[] computeLocalFromPreviousObservations(int states[][], int col); - - /** - * Computes local information theoretic measure for the given - * states, using pdfs built up from observations previously - * sent in via the addObservations method - * This method to be used for homogeneous agents only, - * since the local values will be computed for all variables. - * - * Must set average, min and max - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @return 3D time-series of local values (indexed as per states) - */ - public double[][][] computeLocalFromPreviousObservations(int states[][][]); - - /** - * Computes the local information theoretic measure for the given - * variable in the 3D time-series - * states, using pdfs built up from observations previously - * sent in via the addObservations method - * This method is suitable for heterogeneous agents, since - * the specific variable is identified. - * - * Must set average, min and max - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @param index1 row index of the given variable - * @param index2 column index of the given variable - * @return time-series of local values for the variable - */ - public double[] computeLocalFromPreviousObservations(int states[][][], int index1, int index2); - - /** - * Standalone routine to - * compute the local information-theoretic measure across a - * time-series of states. - * Return a time-series array of local values. - * First history rows are zeros when the measure must build up - * embedded history of the variable. - * - * @param states time series of samples - * @return time-series of local values (indexed as per states) - */ - public double[] computeLocal(int states[]); - - /** - * Standalone routine to - * compute the local information-theoretic measure across a 2D spatiotemporal - * array of the states of homogeneous agents, - * Return a 2D spatiotemporal array of local values. - * First history rows are zeros when the measure must build up - * embedded history of the variable. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @return 2D time-series of local values (indexed as per states) - */ - public double[][] computeLocal(int states[][]); - - /** - * Standalone routine to - * compute the local information theoretic measure across a 3D spatiotemporal - * array of the states of homogeneous agents - * Return a 3D spatiotemporal array of local values. - * First history rows are zeros when the measure must build up - * embedded history of the variable. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @return 3D time-series of local values (indexed as per states) - */ - public double[][][] computeLocal(int states[][][]); - - /** - * Standalone routine to - * compute the average information theoretic measure across a time-series - * of states. - * Return the average. - * - * @param states time series of samples - * @return average of the information-theoretic measure. - * @throws Exception if states parameter is empty. - */ - public double computeAverageLocal(int states[]) throws Exception; - - /** - * Standalone routine to - * compute the average information theoretic measure across a 2D spatiotemporal - * array of the states of homogeneous agents. - * Return the average. - * This method to be called for homogeneous agents only, - * since all variables are used in the PDFs. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @return average of the information-theoretic measure. - */ - public double computeAverageLocal(int states[][]); - - /** - * Standalone routine to - * compute the average information theoretic measure across a 3D spatiotemporal - * array of the states of homogeneous agents. - * Return the average. - * This method to be called for homogeneous agents only, - * since all variables are used in the PDFs. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @return average of the information-theoretic measure. - */ - public double computeAverageLocal(int states[][][]); - - /** - * Standalone routine to - * compute local information theoretic measure for one variable - * in a 2D spatiotemporal - * multivariate array. - * Return a time-series array of local values. - * First history rows are zeros when the measure must build up - * embedded history of the variable. - * This method should be used for heterogeneous agents - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @param col index of the given variable - * @return time-series of local values of the measure for - * the given variable - */ - public double[] computeLocal(int states[][], int col); - - /** - * Standalone routine to - * compute local information theoretic measure for one variable - * in a 3D spatiotemporal - * multivariate array. - * Return a time-series array of local values. - * First history rows are zeros when the measure must build up - * embedded history of the variable. - * This method should be used for heterogeneous agents - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @param index1 row index of the given variable - * @param index2 column index of the given variable - * @return time-series of local values of the measure for - * the given variable - */ - public double[] computeLocal(int states[][][], int index1, int index2); - - /** - * Standalone routine to - * compute the average information theoretic measure - * for a single agent in a multivariate time series. - * Returns the average. - * This method suitable for heterogeneous agents. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @param col index of the given variable - * @return average of the measure for the given variable. - */ - public double computeAverageLocal(int states[][], int col); - - /** - * Standalone routine to - * compute the average information theoretic measure - * for a single agent in a multivariate time series. - * Returns the average. - * This method suitable for heterogeneous agents. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @param index1 row index of the given variable - * @param index2 column index of the given variable - * @return average of the measure for the given variable. - */ - public double computeAverageLocal(int states[][][], int index1, int index2); - - /** - * Add observations in to our estimates of the pdfs. - * This call suitable only for homogeneous agents, as all - * agents will contribute to the PDFs. - * - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - */ - @Deprecated - public void addObservations(int states[][]); - - /** - * Add observations for a single variable of the multi-agent system - * to our estimates of the pdfs. - * This call should be made as opposed to {@link #addObservations(int[][])} - * for computing active info for heterogeneous agents. - * - * @deprecated - * @param states multivariate time series - * (1st index is time, 2nd index is variable number) - * @param col index of agent - */ - @Deprecated - public void addObservations(int states[][], int col); - - /** - * Add observations in to our estimates of the pdfs. - * This call suitable only for homogeneous agents, as all - * agents will contribute to single pdfs. - * - * @deprecated - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - */ - @Deprecated - public void addObservations(int states[][][]); - - /** - * Add observations for a single agent of the multi-agent system - * to our estimates of the pdfs. - * This call should be made as opposed to {@link #addObservations(int[][][])} - * for computing active info for heterogeneous agents. - * - * @deprecated - * @param states multivariate time series - * (1st index is time, 2nd index is variable row number, - * 3rd is variable column number) - * @param index1 row index index the variable - * @param index2 column index of the variable - */ - @Deprecated - public void addObservations(int states[][][], int index1, int index2); - - /** - * Initialise the calculator with (potentially) a new alphabet size - * - * TODO - I don't think so anymore... this should be done with set property - * Deprecated, use initialise() and setProperty("ALPHABET_SIZE", "n") instead. - * @deprecated - * @param alphabetSize - */ - @Deprecated - public void initialise(int alphabetSize); -} diff --git a/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java b/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java index a178b1ac..93fa00ec 100755 --- a/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java +++ b/java/source/infodynamics/measures/discrete/TransferEntropyCalculatorDiscrete.java @@ -294,7 +294,7 @@ private void updateParameters(int base, int destHistoryEmbedLength, int destEmbe public void initialise(int base, int destHistoryEmbedLength, int destEmbeddingDelay, int sourceHistoryEmbeddingLength, int sourceEmbeddingDelay, int delay) { - boolean paramsChanged = (this.base != base) || (k != destHistoryEmbedLength) || + boolean paramsChanged = (this.alphabetSize != base) || (k != destHistoryEmbedLength) || (this.destEmbeddingDelay != destEmbeddingDelay) || (this.sourceHistoryEmbedLength != sourceHistoryEmbeddingLength) || (this.sourceEmbeddingDelay != sourceEmbeddingDelay) || (this.delay != delay); super.initialise(base, destHistoryEmbedLength); @@ -324,7 +324,7 @@ public void initialise(int base, int destHistoryEmbedLength, int destEmbeddingDe @Override public void initialise(){ - initialise(base, k, destEmbeddingDelay, sourceHistoryEmbedLength, + initialise(this.alphabetSize, k, destEmbeddingDelay, sourceHistoryEmbedLength, sourceEmbeddingDelay, delay); } @@ -374,7 +374,7 @@ public void addObservations(int[] source, int[] dest, int startTime, int endTime pastVal[d] += dest[startTime + startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -389,7 +389,7 @@ public void addObservations(int[] source, int[] dest, int startTime, int endTime sourcePastVal[d] += source[startTime + startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -416,12 +416,12 @@ public void addObservations(int[] source, int[] dest, int startTime, int endTime // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[dest[r-1-(k-1)*destEmbeddingDelay]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ source[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -463,7 +463,7 @@ public void addObservations(int[] source, int[] dest, boolean[] valid) { pastVal[d] += dest[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // We can take an observation if timeSinceLastDestInvalid >= minDestLengthRequired @@ -489,7 +489,7 @@ public void addObservations(int[] source, int[] dest, boolean[] valid) { sourcePastVal[d] += source[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } // We can take an observation if timeSinceLastSourceInvalid >= minSourceLengthRequired @@ -551,12 +551,12 @@ public void addObservations(int[] source, int[] dest, boolean[] valid) { // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[dest[r-1-(k-1)*destEmbeddingDelay]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ source[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -606,7 +606,7 @@ public void addObservations(int states[][], int j) { pastVal[c][d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][c]; - pastVal[c][d] *= base; + pastVal[c][d] *= this.alphabetSize; } } } @@ -633,7 +633,7 @@ public void addObservations(int states[][], int j) { sourcePastVal[c][d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceVariable]; - sourcePastVal[c][d] *= base; + sourcePastVal[c][d] *= this.alphabetSize; } } } @@ -674,12 +674,12 @@ public void addObservations(int states[][], int j) { // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[c][destEmbeddingPhase] -= maxShiftedValue[states[r-1-(k-1)*destEmbeddingDelay][c]]; - pastVal[c][destEmbeddingPhase] *= base; // and shift the others up + pastVal[c][destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[c][sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceVariable]]; - sourcePastVal[c][sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[c][sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up } // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; @@ -737,7 +737,7 @@ public void addObservations(int states[][][], int h, int j) { pastVal[r][c][d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][r][c]; - pastVal[r][c][d] *= base; + pastVal[r][c][d] *= this.alphabetSize; } } } @@ -777,7 +777,7 @@ public void addObservations(int states[][][], int h, int j) { sourcePastVal[r][c][d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceAgentRow][sourceAgentColumn]; - sourcePastVal[r][c][d] *= base; + sourcePastVal[r][c][d] *= this.alphabetSize; } } } @@ -830,12 +830,12 @@ public void addObservations(int states[][][], int h, int j) { // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[r][c][destEmbeddingPhase] -= maxShiftedValue[states[t-1-(k-1)*destEmbeddingDelay][r][c]]; - pastVal[r][c][destEmbeddingPhase] *= base; // and shift the others up + pastVal[r][c][destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[r][c][sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[t-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceAgentRow][sourceAgentColumn]]; - sourcePastVal[r][c][sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[r][c][sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up } } // then update the phase @@ -879,7 +879,7 @@ public void addObservations(int states[][], int sourceIndex, int destIndex) { pastVal[d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][destIndex]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -894,7 +894,7 @@ public void addObservations(int states[][], int sourceIndex, int destIndex) { sourcePastVal[d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceIndex]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -921,12 +921,12 @@ public void addObservations(int states[][], int sourceIndex, int destIndex) { // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[states[r-1-(k-1)*destEmbeddingDelay][destIndex]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceIndex]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -972,7 +972,7 @@ public void addObservations(int states[][][], int sourceRowIndex, int sourceColu pastVal[d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][destRowIndex][destColumnIndex]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -987,7 +987,7 @@ public void addObservations(int states[][][], int sourceRowIndex, int sourceColu sourcePastVal[d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceRowIndex][sourceColumnIndex]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -1014,12 +1014,12 @@ public void addObservations(int states[][][], int sourceRowIndex, int sourceColu // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[states[r-1-(k-1)*destEmbeddingDelay][destRowIndex][destColumnIndex]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceRowIndex][sourceColumnIndex]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -1171,7 +1171,7 @@ public double computeAverageLocalOfObservations() { if (pastCount[pastVal] == 0) { continue; } - for (int destVal = 0; destVal < base; destVal++) { + for (int destVal = 0; destVal < this.alphabetSize; destVal++) { // compute p(dest,past) // double p_dest_past = (double) destPastCount[destVal][pastVal] / (double) observations; if (nextPastCount[destVal][pastVal] == 0) { @@ -1234,7 +1234,7 @@ public double computeAverageActiveInfoStorageOfObservations() { double active = 0.0; double activeCont = 0.0; - for (int nextVal = 0; nextVal < base; nextVal++) { + for (int nextVal = 0; nextVal < this.alphabetSize; nextVal++) { // compute p_next double p_next = (double) nextCount[nextVal] / (double) observations; for (int prevVal = 0; prevVal < base_power_k; prevVal++) { @@ -1263,7 +1263,7 @@ public void debugPrintObservations() { System.out.println("Src\tDst\tPast\tc(s,d,p)\tc(s,p)\tc(d,p)\tc(p)"); for (int pastVal = 0; pastVal < base_power_k; pastVal++) { - for (int destVal = 0; destVal < base; destVal++) { + for (int destVal = 0; destVal < this.alphabetSize; destVal++) { for (int sourceVal = 0; sourceVal < base_power_l; sourceVal++) { // Compute TE contribution: System.out.println(sourceVal + "\t" + destVal + "\t" + pastVal + "\t" + @@ -1315,7 +1315,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings for (int pastVal = 0; pastVal < base_power_k; pastVal++) { MatrixUtils.fill(pastValues, pastVal, t_p, pastCount[pastVal]); t_p += pastCount[pastVal]; - for (int destVal = 0; destVal < base; destVal++) { + for (int destVal = 0; destVal < this.alphabetSize; destVal++) { MatrixUtils.fill(destValues, destVal, t_d, nextPastCount[destVal][pastVal]); t_d += nextPastCount[destVal][pastVal]; } @@ -1324,7 +1324,7 @@ public EmpiricalMeasurementDistribution computeSignificance(int[][] newOrderings // If we want a calculator just like this one, we should provide all of // the same parameters: TransferEntropyCalculatorDiscrete ate2 = - new TransferEntropyCalculatorDiscrete(base, k, destEmbeddingDelay, + new TransferEntropyCalculatorDiscrete(this.alphabetSize, k, destEmbeddingDelay, sourceHistoryEmbedLength, sourceEmbeddingDelay, delay); ate2.initialise(); ate2.observations = observations; @@ -1366,7 +1366,7 @@ public AnalyticMeasurementDistribution computeSignificance() } return new ChiSquareMeasurementDistribution(average, observations, - (base_power_l - 1)*(base - 1)*(base_power_k)); + (base_power_l - 1)*(this.alphabetSize - 1)*(base_power_k)); } /** @@ -1425,7 +1425,7 @@ public double[] computeLocalFromPreviousObservations(int source[], int dest[]){ pastVal[d] += dest[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -1440,7 +1440,7 @@ public double[] computeLocalFromPreviousObservations(int source[], int dest[]){ sourcePastVal[d] += source[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -1472,12 +1472,12 @@ public double[] computeLocalFromPreviousObservations(int source[], int dest[]){ // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[dest[t-1-(k-1)*destEmbeddingDelay]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ source[t-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -1537,7 +1537,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][], int j){ pastVal[c][d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][c]; - pastVal[c][d] *= base; + pastVal[c][d] *= this.alphabetSize; } } } @@ -1564,7 +1564,7 @@ public double[][] computeLocalFromPreviousObservations(int states[][], int j){ sourcePastVal[c][d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceVariable]; - sourcePastVal[c][d] *= base; + sourcePastVal[c][d] *= this.alphabetSize; } } } @@ -1608,12 +1608,12 @@ public double[][] computeLocalFromPreviousObservations(int states[][], int j){ // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[c][destEmbeddingPhase] -= maxShiftedValue[states[t-1-(k-1)*destEmbeddingDelay][c]]; - pastVal[c][destEmbeddingPhase] *= base; // and shift the others up + pastVal[c][destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[c][sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[t-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceVariable]]; - sourcePastVal[c][sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[c][sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up } // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; @@ -1689,7 +1689,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][], int h pastVal[r][c][d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][r][c]; - pastVal[r][c][d] *= base; + pastVal[r][c][d] *= this.alphabetSize; } } } @@ -1729,7 +1729,7 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][], int h sourcePastVal[r][c][d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceAgentRow][sourceAgentColumn]; - sourcePastVal[r][c][d] *= base; + sourcePastVal[r][c][d] *= this.alphabetSize; } } } @@ -1787,12 +1787,12 @@ public double[][][] computeLocalFromPreviousObservations(int states[][][], int h // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[r][c][destEmbeddingPhase] -= maxShiftedValue[states[t-1-(k-1)*destEmbeddingDelay][r][c]]; - pastVal[r][c][destEmbeddingPhase] *= base; // and shift the others up + pastVal[r][c][destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[r][c][sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[t-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceAgentRow][sourceAgentColumn]]; - sourcePastVal[r][c][sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[r][c][sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up } } // then update the phase @@ -1852,7 +1852,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int sourceI pastVal[d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][destIndex]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -1867,7 +1867,7 @@ public double[] computeLocalFromPreviousObservations(int states[][], int sourceI sourcePastVal[d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceIndex]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -1899,12 +1899,12 @@ public double[] computeLocalFromPreviousObservations(int states[][], int sourceI // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[states[r-1-(k-1)*destEmbeddingDelay][destIndex]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceIndex]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; @@ -1958,7 +1958,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], pastVal[d] += states[startObservationTime + d - 1 - (k-1)*destEmbeddingDelay + p*destEmbeddingDelay][destRowIndex][destColumnIndex]; - pastVal[d] *= base; + pastVal[d] *= this.alphabetSize; } } // Next for the source: @@ -1973,7 +1973,7 @@ public double[] computeLocalFromPreviousObservations(int states[][][], sourcePastVal[d] += states[startObservationTime + d - delay - (sourceHistoryEmbedLength-1)*sourceEmbeddingDelay + p*sourceEmbeddingDelay][sourceRowIndex][sourceColumnIndex]; - sourcePastVal[d] *= base; + sourcePastVal[d] *= this.alphabetSize; } } @@ -2005,12 +2005,12 @@ public double[] computeLocalFromPreviousObservations(int states[][][], // for this phase we back out the oldest value which we'll no longer need: if (k > 0) { pastVal[destEmbeddingPhase] -= maxShiftedValue[states[r-1-(k-1)*destEmbeddingDelay][destRowIndex][destColumnIndex]]; - pastVal[destEmbeddingPhase] *= base; // and shift the others up + pastVal[destEmbeddingPhase] *= this.alphabetSize; // and shift the others up } sourcePastVal[sourceEmbeddingPhase] -= maxShiftedSourceValue[ states[r-delay-(sourceHistoryEmbedLength-1)*sourceEmbeddingDelay][sourceRowIndex][sourceColumnIndex]]; - sourcePastVal[sourceEmbeddingPhase] *= base; // and shift the others up + sourcePastVal[sourceEmbeddingPhase] *= this.alphabetSize; // and shift the others up // then update the phase destEmbeddingPhase = (destEmbeddingPhase + 1) % destEmbeddingDelay; sourceEmbeddingPhase = (sourceEmbeddingPhase + 1) % sourceEmbeddingDelay; diff --git a/java/source/infodynamics/measures/discrete/UnivariateMeasureDiscrete.java b/java/source/infodynamics/measures/discrete/UnivariateMeasureDiscrete.java new file mode 100644 index 00000000..6023f94a --- /dev/null +++ b/java/source/infodynamics/measures/discrete/UnivariateMeasureDiscrete.java @@ -0,0 +1,150 @@ +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2012, Joseph T. Lizier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package infodynamics.measures.discrete; + +/** + * Interface for calculators of information-theoretic measures + * for single variables (e.g. entropy, active information storage). + * The interface defines common operations such as + * adding observations and calculating + * local and average values, etc. + * + *

Usage is as per {@link InfoMeasureCalculatorDiscrete}, with + * many methods for supplying observations and making + * calculations defined here.

+ * + *

It would ideally be an abstract class to be inherited from, but + * it's more important for some of our calculators to have inheritance from + * ContextOfPastCalculator, and since java doesn't allow multiple + * inheritance, one of them has to miss out. + * To get around this, we combine the two in + * {@link UnivariateMeasureDiscreteInContextOfPastCalculator}. + *

+ * + * @author Joseph Lizier (email, + * www) + */ +public interface UnivariateMeasureDiscrete { + + /** + * Property name for definite alphabet size. Attempting + * to use observations beyond this property will throw + * errors. + */ + public static final String ALPHABET_SIZE = "ALPHABET_SIZE"; + + /** + * Property name for if the alphabet size is known. + */ + public static final String KNOWN_INTEGER_RANGE = "KNOWN_INTEGER_RANGE"; + + /** + * Initialise the calculator with same or unknown alphabet size + */ + public void initialise(); + + /** + * Sets the samples from which to compute the PDF for the entropy. + * Should only be called once, the last call contains the + * observations that are used (they are not accumulated). + * + * @param observations array of (univariate) samples + * @throws Exception + */ + public void setObservations(Object[] observations) throws Exception; + + /** + * Signal that we will add in the samples for computing the PDF + * from several disjoint time-series or trials via calls to + * "addObservations" rather than "setObservations" type methods + * (defined by the child interfaces and classes). + */ + public void startAddObservations(); + + /** + * Signal that the observations are now all added, PDFs can now be constructed. + * + * @throws Exception when the estimator has no observations. + */ + public void finaliseAddObservations() throws Exception; + + /** + * Add observations in to our estimates of the pdfs. + * Univariate. + * + * @param states series of samples + */ + public void addObservations(int[] states); + + + /** + * Compute the average value of the measure + * from the previously-supplied samples. + * + * Must set average, min and max + * + * @return the estimate of the measure + */ + public double computeAverageLocalOfObservations(); + + /** + * Computes local information theoretic measure for the given + * states, using pdfs built up from observations previously + * sent in via the addObservations method. + * + * Must set average, min and max + * + * @param states time series of samples + * @return time-series of local values (indexed as per states) + */ + public double[] computeLocalFromPreviousObservations(int states[]); + + /** + * Standalone routine to + * compute the average information theoretic measure across a time-series + * of states. + * Return the average. + * + * @param states time series of samples + * @return average of the information-theoretic measure. + * @throws Exception if states parameter is empty. + */ + public double computeAverageLocal(int states[]) throws Exception; + + /** + * Add observations in to our estimates of the pdfs. + * This call suitable only for homogeneous agents, as all + * agents will contribute to the PDFs. + * + * @param states multivariate time series + * (1st index is time, 2nd index is variable number) + */ + @Deprecated + public void addObservations(int states[][]) throws Exception; + + /** + * Initialise the calculator with (potentially) a new alphabet size + * + * Deprecated, use initialise() and setProperty("ALPHABET_SIZE", "n") instead. + * @deprecated + * @param alphabetSize + */ + @Deprecated + public void initialise(int alphabetSize); +} diff --git a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java b/java/source/infodynamics/measures/discrete/UnivariateMeasureDiscreteInContextOfPastCalculator.java old mode 100755 new mode 100644 similarity index 61% rename from java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java rename to java/source/infodynamics/measures/discrete/UnivariateMeasureDiscreteInContextOfPastCalculator.java index d9af5939..253af5d3 --- a/java/source/infodynamics/measures/discrete/SingleAgentMeasureDiscreteInContextOfPastCalculator.java +++ b/java/source/infodynamics/measures/discrete/UnivariateMeasureDiscreteInContextOfPastCalculator.java @@ -1,150 +1,190 @@ -/* - * Java Information Dynamics Toolkit (JIDT) - * Copyright (C) 2012, Joseph T. Lizier - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package infodynamics.measures.discrete; - -/** - * A base class for calculators computing measures for - * a single variable which - * require knowledge of the embedded past state of a univariate - * discrete (ie int[]) variable. - * - *

This combines functionality for single agents from - * {@link SingleAgentMeasureDiscrete} with functionality - * required in the context of the past provided by - * {@link ContextOfPastMeasureCalculatorDiscrete}.

- * - *

Usage is as defined in {@link InfoMeasureCalculatorDiscrete}, with - * extra methods for supplying observations and making - * calculations defined in {@link SingleAgentMeasureDiscrete}

. - * - *

Users should not need to deal with this class directly; - * it is simply used to gather common functionality for several - * child classes. - *

- * - * TODO Make the Active info storage and entropy calculators inherit from this - * - * @author Joseph Lizier (email, - * www) - */ -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 - * - * @param base number of quantisation levels for each variable. - * E.g. binary variables are in base-2. - * @param history embedding length - */ - 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(); - addObservations(states); - return computeLocalFromPreviousObservations(states); - } - - @Override - public final double[][] computeLocal(int[][] states) { - initialise(); - addObservations(states); - return computeLocalFromPreviousObservations(states); - } - - @Override - public final double[][][] computeLocal(int[][][] states) { - initialise(); - addObservations(states); - return computeLocalFromPreviousObservations(states); - } - - @Override - public final double computeAverageLocal(int[] states) { - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double computeAverageLocal(int[][] states) { - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double computeAverageLocal(int[][][] states) { - initialise(); - addObservations(states); - return computeAverageLocalOfObservations(); - } - - @Override - public final double[] computeLocal(int[][] states, int col) { - initialise(); - addObservations(states, col); - return computeLocalFromPreviousObservations(states, col); - } - - @Override - public final double[] computeLocal(int[][][] states, int index1, int index2) { - initialise(); - addObservations(states, index1, index2); - return computeLocalFromPreviousObservations(states, index1, index2); - } - - @Override - public final double computeAverageLocal(int[][] states, int col) { - initialise(); - addObservations(states, col); - return computeAverageLocalOfObservations(); - } - - @Override - public final double computeAverageLocal(int[][][] states, int index1, int index2) { - initialise(); - addObservations(states, index1, index2); - return computeAverageLocalOfObservations(); - } -} +/* + * Java Information Dynamics Toolkit (JIDT) + * Copyright (C) 2012, Joseph T. Lizier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package infodynamics.measures.discrete; + +/** + * A base class for calculators computing measures for + * a single variable which + * require knowledge of the embedded past state of a univariate + * discrete (ie int[]) variable. + * + *

This combines functionality for single agents from + * {@link UnivariateMeasureDiscrete} with functionality + * required in the context of the past provided by + * {@link ContextOfPastMeasureCalculatorDiscrete}.

+ * + *

Usage is as defined in {@link InfoMeasureCalculatorDiscrete}, with + * extra methods for supplying observations and making + * calculations defined in {@link UnivariateMeasureDiscrete}

. + * + *

Users should not need to deal with this class directly; + * it is simply used to gather common functionality for several + * child classes. + *

+ * + * TODO Make the Active info storage and entropy calculators inherit from this + * + * @author Joseph Lizier (email, + * www) + */ +public abstract class UnivariateMeasureDiscreteInContextOfPastCalculator extends + ContextOfPastMeasureCalculatorDiscrete implements UnivariateMeasureDiscrete { + + /** + * Construct the calculator with default base of 2 and history 1 + */ + public UnivariateMeasureDiscreteInContextOfPastCalculator() { + super(2, 1); + } + + /** + * Construct the calculator + * + * @param base number of quantisation levels for each variable. + * E.g. binary variables are in base-2. + * @param history embedding length + */ + public UnivariateMeasureDiscreteInContextOfPastCalculator(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 UnivariateMeasureDiscreteInContextOfPastCalculator(int base, int history, boolean dontCreateObsStorage) { + super(base, history, dontCreateObsStorage); + } + + + public final double[] computeLocal(int[] states) { + initialise(); + addObservations(states); + return computeLocalFromPreviousObservations(states); + } + + @Override + public final double computeAverageLocal(int[] states) { + initialise(); + addObservations(states); + return computeAverageLocalOfObservations(); + } + + public final double[][] computeLocal(int[][] states) { + initialise(); + try { + addObservations(states); + } catch (Exception e) { + throw new RuntimeException(e); + } + return computeLocalFromPreviousObservations(states); + } + + public final double[][][] computeLocal(int[][][] states) { + initialise(); + addObservations(states); + return computeLocalFromPreviousObservations(states); + } + + public final double computeAverageLocal(int[][] states) { + initialise(); + try { + addObservations(states); + } catch (Exception e) { + throw new RuntimeException(e); + } + return computeAverageLocalOfObservations(); + } + + public final double computeAverageLocal(int[][][] states) { + initialise(); + addObservations(states); + return computeAverageLocalOfObservations(); + } + + public final double[] computeLocal(int[][] states, int col) { + initialise(); + addObservations(states, col); + return computeLocalFromPreviousObservations(states, col); + } + + public final double[] computeLocal(int[][][] states, int index1, int index2) { + initialise(); + addObservations(states, index1, index2); + return computeLocalFromPreviousObservations(states, index1, index2); + } + + public final double computeAverageLocal(int[][] states, int col) { + initialise(); + addObservations(states, col); + return computeAverageLocalOfObservations(); + } + + public final double computeAverageLocal(int[][][] states, int index1, int index2) { + initialise(); + addObservations(states, index1, index2); + return computeAverageLocalOfObservations(); + } + + // these are only here to get this thing to compile, they were in + // the interface, and maybe I could or should have done this in the classes + // that don't need them at all, but whatever, I think these should be + // removed or only added to the classes that need them. + + private double[][] computeLocalFromPreviousObservations(int[][] states) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'computeLocalFromPreviousObservations'"); + } + + private void addObservations(int[][][] states) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addObservations'"); + } + + private double[][][] computeLocalFromPreviousObservations(int[][][] states) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'computeLocalFromPreviousObservations'"); + } + + private double[] computeLocalFromPreviousObservations(int[][] states, int col) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'computeLocalFromPreviousObservations'"); + } + + private double[] computeLocalFromPreviousObservations(int[][][] states, int index1, int index2) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'computeLocalFromPreviousObservations'"); + } + + private void addObservations(int[][] states, int col) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addObservations'"); + } + + private void addObservations(int[][][] states, int index1, int index2) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addObservations'"); + } +} diff --git a/java/unittests/infodynamics/measures/discrete/EntropyTester.java b/java/unittests/infodynamics/measures/discrete/EntropyTester.java new file mode 100644 index 00000000..7767d83f --- /dev/null +++ b/java/unittests/infodynamics/measures/discrete/EntropyTester.java @@ -0,0 +1,196 @@ +package infodynamics.measures.discrete; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +import infodynamics.measures.discrete.InfoMeasureCalculatorDiscrete.State; +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +public class EntropyTester extends TestCase { + + private EntropyCalculatorDiscrete calc; + private static double ERR_ALLOWANCE = 0.02; + + public void setup() { + calc = new EntropyCalculatorDiscrete(); + } + + @Test + public void testHashTableUseBasic() throws Exception { + setup(); + + // 1. check the state machine is in the correct state + assertEquals(State.SETTING_PROPERTIES, calc.currentState); + + calc.initialise(); + String[] obs = new String[] {"a", "b", "b"}; + calc.addObservations(obs); + + // 2. check that the integer range is infact unknown + // 3. check the state machine + assertEquals(false, calc.knownIntegerRange); + assertEquals(State.ADDING_OBSERVATIONS, calc.currentState); + + // 4. check the hash table and it's values + assertEquals((Integer) 1, calc.hashedStateCount.get("a")); + assertEquals((Integer) 2, calc.hashedStateCount.get("b")); + + double ent = calc.computeAverageLocalOfObservations(); + + // 5. check result of entropy calculations - allowing small error margin + assertTrue(Math.abs(ent-0.918)/0.918 <= ERR_ALLOWANCE); + assertEquals(State.COMPUTING, calc.currentState); + } + + @Test + public void testArrayUseBasic() throws Exception { + setup(); + calc.setProperty("ALPHABET_SIZE", "2"); + + assertEquals(State.SETTING_PROPERTIES, calc.currentState); + assertEquals(true, calc.knownIntegerRange); + + int[] obs = new int[] {0,0,1,1}; + calc.addObservations(obs); + + assertEquals(State.ADDING_OBSERVATIONS, calc.currentState); + + double ent = calc.computeAverageLocalOfObservations(); + assertEquals(1.0, ent); + assertEquals(State.COMPUTING, calc.currentState); + } + + @Test + public void testArrayUseOverflowAlphaSize() throws Exception { + setup(); + calc.setProperty("ALPHABET_SIZE", "2"); + + int[] obs = new int[] { 0, 1, 2 }; + assertThrows(RuntimeException.class, () -> { + calc.addObservations(obs); + }); + } + + @Test + public void testArrayUseOverflowAlphaSize2() throws Exception { + setup(); + calc.setProperty("ALPHABET_SIZE", "2"); + + Integer[] obs = new Integer[] { 0, 1, 2 }; + assertThrows(RuntimeException.class, () -> { + calc.addObservations(obs); + }); + } + + @Test + public void testStringIntegerInterpretation() throws Exception { + setup(); + calc.setProperty("ALPHABET_SIZE", "2"); + + String[] obs = new String[] { "0", "1", "1" }; + calc.addObservations(obs); + + double ent = calc.computeAverageLocalOfObservations(); + + // ensure we did use the array implementation + assertEquals(calc.knownIntegerRange, true); + assertNotNull(calc.stateCount); + assertTrue(calc.hashedStateCount.isEmpty()); + + // ensure it gets correct output - allowing small error margin + assertTrue(Math.abs(ent-0.918)/0.918 <= ERR_ALLOWANCE); + } + + @Test + public void testUninterpretableInput() throws Exception { + setup(); + calc.setProperty("ALPHABET_SIZE", "2"); + + String[] obs = new String[] { "0", "1", "a" }; + + // we specified an alphabet size, but cannot interpret "a" as an integer + assertThrows(RuntimeException.class, () ->{ + calc.addObservations(obs); + }); + } + + @Test + public void testNegativeNums() throws Exception { + setup(); + + calc.setProperty("ALPHABET_SIZE", "4"); + int[] obs = new int[] { 0, 1, -1, 2 }; + + // Negative numbers are invalid observations. + assertThrows(RuntimeException.class, () -> { + calc.addObservations(obs); + }); + } + + @Test + public void testExceedMaxAlphaSize() throws Exception { + setup(); + + // TODO -- This test is under the assumption of the temporary max alphabet size of 100 + // when this number is solidified, this test needs to change (it will fail when it does change.) + int[] obs = new int[] { 99, 100, 101 }; + calc.addObservations(obs); + + assertFalse(calc.hashedStateCount.isEmpty()); + assertNull(calc.stateCount); + } + + @Test + public void testMultiDimensionalIntObservations() throws Exception { + setup(); + + calc.setProperty("NUM_DIMENSIONS", "3"); + + int[][] obs = new int[][] { + {1, 2, 3}, + {4, 5, 6}, + {1, 2, 3} + }; + + calc.addObservations(obs); + + // TODO -- compute combined values + + + // assertEquals((Integer) 2, calc.hashedStateCount.get(test1)); + // assertEquals((Integer) 1, calc.hashedStateCount.get(test2)); + } + + @Test + public void testMultiDimensionalObjObservations() throws Exception { + setup(); + + calc.setProperty("NUM_DIMENSIONS", "3"); + + Object[][] obs = new Object[][] { + {"A", "B", "C"}, + {"D", "E", "F"}, + {"A", "B", "C"} + }; + + calc.addObservations(obs); + + List test1 = Arrays.asList(obs[0]); + List test2 = Arrays.asList(obs[1]); + + assertEquals((Integer) 2, calc.hashedStateCount.get(test1)); + assertEquals((Integer) 1, calc.hashedStateCount.get(test2)); + } + + // TODO -- implement this test. + @Test + public void testZeroDimensions() throws Exception { + setup(); + + calc.setProperty("NUM_DIMENSIONS", "0"); + } +}