mirror of https://github.com/jlizier/jidt
627 lines
18 KiB
Java
Executable File
627 lines
18 KiB
Java
Executable File
/*
|
|
* Java Information Dynamics Toolkit (JIDT)
|
|
* Copyright (C) 2012, Joseph T. Lizier
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package infodynamics.measures.discrete;
|
|
|
|
import infodynamics.utils.MatrixUtils;
|
|
|
|
/**
|
|
* <p>Univariate entropy calculator</p>
|
|
*
|
|
* <p>Usage:
|
|
* <ol>
|
|
* <li>Continuous accumulation of observations. Call:
|
|
* <ol>
|
|
* <li>{@link #initialise()};</li>
|
|
* <li>then supply the observations using any of the addObservations()
|
|
* methods (several times over), e.g. {@link #addObservations(int[][])};</li>
|
|
* <li>then when all observations have been added, call any of the
|
|
* compute methods (several times over), e.g.:
|
|
* {@link #computeLocalFromPreviousObservations(int[][])}</li>
|
|
* </ol>
|
|
* </li>
|
|
* <li>Standalone mode. Call:
|
|
* <ol>
|
|
* <li>one of the standalone methods which supply observations
|
|
* and compute at once, e.g. {@link #computeAverageLocal(int[][])}.</li>
|
|
* </ol>
|
|
* </li>
|
|
* </ol></p>
|
|
*
|
|
*
|
|
* @author Joseph Lizier
|
|
*/
|
|
public class EntropyCalculator extends InfoMeasureCalculator
|
|
implements SingleAgentMeasure
|
|
{
|
|
|
|
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
|
|
* @param blocksize
|
|
*
|
|
* @return
|
|
*/
|
|
public static EntropyCalculator newInstance(int base, int blocksize) {
|
|
if (blocksize > 1) {
|
|
return BlockEntropyCalculator.newInstance(blocksize, base);
|
|
} else {
|
|
return EntropyCalculator.newInstance(base);
|
|
}
|
|
}
|
|
public static EntropyCalculator newInstance(int base) {
|
|
return new EntropyCalculator(base);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param base
|
|
*/
|
|
public EntropyCalculator(int base) {
|
|
|
|
super(base);
|
|
|
|
// Create storage for counts of observations
|
|
stateCount = new int[base];
|
|
}
|
|
|
|
/**
|
|
* Initialise calculator, preparing to take observation sets in
|
|
* Should be called prior to any of the addObservations() methods.
|
|
* You can reinitialise without needing to create a new object.
|
|
*/
|
|
public void initialise(){
|
|
super.initialise();
|
|
MatrixUtils.fill(stateCount, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Add observations in to our estimates of the pdfs.
|
|
*
|
|
* @param states 1st index is time
|
|
*/
|
|
public void addObservations(int states[]) {
|
|
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]]++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 1st index is time, 2nd index is agent number
|
|
*/
|
|
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]]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 1st index is time, 2nd and 3rd index give the 2D agent number
|
|
*/
|
|
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]]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 addObservations(int states[][])
|
|
* for computing active info for heterogeneous agents.
|
|
*
|
|
* @param states 1st index is time, 2nd index is agent number
|
|
* @param agentNumber
|
|
*/
|
|
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]]++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 addObservations(int states[][])
|
|
* for computing active info for heterogeneous agents.
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index give the 2D agent number
|
|
* @param agentIndex1
|
|
* @param agentIndex2
|
|
*/
|
|
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]]++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param stateVal
|
|
* @return count of observations of the given state
|
|
*/
|
|
public int getStateCount(int stateVal) {
|
|
return stateCount[stateVal];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param stateVal
|
|
* @return probability of the given state
|
|
*/
|
|
public double getStateProbability(int stateVal) {
|
|
return (double) stateCount[stateVal] / (double) observations;
|
|
}
|
|
|
|
/**
|
|
* Returns the average entropy from
|
|
* the observed values which have been passed in previously.
|
|
*
|
|
* @return the average entropy
|
|
*/
|
|
public double computeAverageLocalOfObservations() {
|
|
double ent = 0.0;
|
|
double entCont = 0.0;
|
|
|
|
max = 0;
|
|
min = 0;
|
|
for (int stateVal = 0; stateVal < base; 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;
|
|
}
|
|
} else {
|
|
entCont = 0.0;
|
|
}
|
|
ent += entCont;
|
|
}
|
|
|
|
average = ent;
|
|
return ent;
|
|
}
|
|
|
|
/**
|
|
* Computes local entropy for the given
|
|
* states, using pdfs built up from observations previously
|
|
* sent in via the addObservations method
|
|
*
|
|
* @param states index is time
|
|
* @return
|
|
*/
|
|
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];
|
|
}
|
|
}
|
|
average = average/(double) rows;
|
|
|
|
return localEntropy;
|
|
|
|
}
|
|
|
|
/**
|
|
* Computes local entropy 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
|
|
*
|
|
* @param states 1st index is time, 2nd index is agent number
|
|
* @return
|
|
*/
|
|
public double[][] computeLocalFromPreviousObservations(int states[][]){
|
|
int rows = states.length;
|
|
int columns = states[0].length;
|
|
|
|
double[][] localEntropy = new double[rows][columns];
|
|
average = 0;
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
average = average/(double) (columns * rows);
|
|
|
|
return localEntropy;
|
|
|
|
}
|
|
|
|
/**
|
|
* Computes local entropy 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
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index are agent number
|
|
* @return
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
average = average/(double) (agentRows * agentColumns * timeSteps);
|
|
|
|
return localEntropy;
|
|
|
|
}
|
|
|
|
/**
|
|
* Computes local entropy for the given
|
|
* states, using pdfs built up from observations previously
|
|
* sent in via the addObservations method
|
|
* This method is suitable for heterogeneous agents
|
|
*
|
|
* @param states
|
|
* @param agentNumber
|
|
* @return
|
|
*/
|
|
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;
|
|
min = 0;
|
|
for (int r = 0; r < rows; r++) {
|
|
double p_state = (double) stateCount[states[r][agentNumber]] / (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];
|
|
}
|
|
}
|
|
average = average/(double) (rows);
|
|
|
|
return localEntropy;
|
|
|
|
}
|
|
|
|
/**
|
|
* Computes local entropy for the given
|
|
* states, using pdfs built up from observations previously
|
|
* sent in via the addObservations method
|
|
* This method is suitable for heterogeneous agents
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index are agent number
|
|
* @param agentIndex1
|
|
* @param agentIndex2
|
|
* @return
|
|
*/
|
|
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];
|
|
}
|
|
}
|
|
average = average/(double) (timeSteps);
|
|
|
|
return localEntropy;
|
|
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute local information theoretic measure across a 1D temporal
|
|
* array of the states of homogeneous agents
|
|
* Return a 1D temporal array of local values.
|
|
* First history rows are zeros
|
|
*
|
|
* @param states - 1D array of states
|
|
* @return
|
|
*/
|
|
public final double[] computeLocal(int states[]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeLocalFromPreviousObservations(states);
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute 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
|
|
*
|
|
* @param states - 2D array of states
|
|
* @return
|
|
*/
|
|
public final double[][] computeLocal(int states[][]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeLocalFromPreviousObservations(states);
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute local information theoretic measure across a 3D spatiotemporal
|
|
* array of the states of 2D homogeneous agents
|
|
* Return a 3D spatiotemporal array of local values.
|
|
* First history rows are zeros
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index are agent number
|
|
* @return
|
|
*/
|
|
public final double[][][] computeLocal(int states[][][]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeLocalFromPreviousObservations(states);
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute average local information theoretic measure across a 1D temporal
|
|
* array of the states of homogeneous agents
|
|
* Return the average
|
|
*
|
|
* @param states - 1D array of states
|
|
* @return
|
|
*/
|
|
public final double computeAverageLocal(int states[]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeAverageLocalOfObservations();
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute average local 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
|
|
*
|
|
* @param states - 2D array of states
|
|
* @return
|
|
*/
|
|
public final double computeAverageLocal(int states[][]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeAverageLocalOfObservations();
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute average local information theoretic measure across a 3D spatiotemporal
|
|
* array of the states of 2D homogeneous agents
|
|
* Return the average
|
|
* This method to be called for homogeneous agents only
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index are agent number
|
|
* @return
|
|
*/
|
|
public final double computeAverageLocal(int states[][][]) {
|
|
|
|
initialise();
|
|
addObservations(states);
|
|
return computeAverageLocalOfObservations();
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute local information theoretic measure for one agent in a 2D spatiotemporal
|
|
* array of the states of agents
|
|
* Return a 2D spatiotemporal array of local values.
|
|
* First history rows are zeros
|
|
* This method should be used for heterogeneous agents
|
|
*
|
|
* @param states - 2D array of states
|
|
* @param col - column number of the agent in the states array
|
|
* @return
|
|
*/
|
|
public final double[] computeLocalAtAgent(int states[][], int col) {
|
|
|
|
initialise();
|
|
addObservations(states, col);
|
|
return computeLocalFromPreviousObservations(states, col);
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute local information theoretic measure for one agent in a 2D spatiotemporal
|
|
* array of the states of agents
|
|
* Return a 2D spatiotemporal array of local values.
|
|
* First history rows are zeros
|
|
* This method should be used for heterogeneous agents
|
|
*
|
|
* @param states 1st index is time, 2nd and 3rd index are agent number
|
|
* @param agentIndex1
|
|
* @param agentIndex2
|
|
* @return
|
|
*/
|
|
public final double[] computeLocalAtAgent(int states[][][], int agentIndex1, int agentIndex2) {
|
|
|
|
initialise();
|
|
addObservations(states, agentIndex1, agentIndex2);
|
|
return computeLocalFromPreviousObservations(states, agentIndex1, agentIndex2);
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute average local information theoretic measure
|
|
* for a single agent
|
|
* Returns the average
|
|
* This method suitable for heterogeneous agents
|
|
* @param states - 2D array of states
|
|
* @param col - column number of the agent in the states array
|
|
*
|
|
* @return
|
|
*/
|
|
public final double computeAverageLocalAtAgent(int states[][], int col) {
|
|
initialise();
|
|
addObservations(states, col);
|
|
return computeAverageLocalOfObservations();
|
|
}
|
|
|
|
/**
|
|
* Standalone routine to
|
|
* compute average local information theoretic measure
|
|
* for a single agent
|
|
* Returns the average
|
|
* This method suitable for heterogeneous agents
|
|
* @param states - 2D array of states
|
|
* @param agentIndex1
|
|
* @param agentIndex2
|
|
*
|
|
* @return
|
|
*/
|
|
public final double computeAverageLocalAtAgent(int states[][][], int agentIndex1, int agentIndex2) {
|
|
initialise();
|
|
addObservations(states, agentIndex1, agentIndex2);
|
|
return computeAverageLocalOfObservations();
|
|
}
|
|
}
|