jidt/java/source/infodynamics/measures/discrete/InfoMeasureCalculatorDiscre...

255 lines
6.8 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;
/**
* <p>Base class for our information-theoretic calculators
* on discrete (int[]) data,
* providing common functionality
* for user-level measure classes.</p>
*
* <p>
* Usage of the child classes extending this class is intended to follow this paradigm:
* </p>
* <ol>
* <li>Construct the calculator;</li>
* <li>Initialise the calculator using {@link #initialise()} or
* other initialise methods defined by child classes;</li>
* <li>Provide the observations/samples for the calculator
* to set up the PDFs, using one or more calls to
* sets of "addObservations" methods defined by child classes, then</li>
* <li>Compute the required quantities, being one or more of:
* <ul>
* <li>the average measure: {@link #computeAverageLocalOfObservations()};</li>
* <li>or other quantities as defined by child classes.</li>
* </ul>
* </li>
* <li>
* Return to step 2 to re-use the calculator on a new data set.
* </li>
* </ol>
*
* @author Joseph Lizier (<a href="joseph.lizier at gmail.com">email</a>,
* <a href="http://lizier.me/joseph/">www</a>)
*/
public abstract class InfoMeasureCalculatorDiscrete {
/**
* Last computed average of the measure
*/
protected double average = 0.0;
/**
* Last computed max local value of the measure
*/
protected double max = 0.0;
/**
* Last computed min local value of the measure
*/
protected double min = 0.0;
/**
* Last computed standard deviation of local values of the measure
*/
protected double std = 0.0;
/**
* Number of observations supplied for the PDFs
*/
protected int observations = 0;
/**
* 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
/**
* Cached value of ln(base)
*/
protected double log_base = 0;
/**
* Cached value of ln(2)
*/
protected double log_2 = Math.log(2.0);
/**
* Cache of whether the base is a power of 2
*/
protected boolean power_of_2_base = false;
/**
* Cached value of log_2(base)
*/
protected int log_2_base = 0;
/**
* Whether we're in debug mode
*/
protected boolean debug = false;
/**
* Construct an instance with default base of 2
*/
protected InfoMeasureCalculatorDiscrete() {
this(2);
}
/**
* Construct an instance
*
* @param base number of quantisation levels for each variable.
* E.g. binary variables are in base-2.
*/
protected InfoMeasureCalculatorDiscrete(int base) {
resetBase(base);
}
protected void resetBase(int base) {
this.base = base;
log_base = Math.log(base);
if (base < 2) {
throw new RuntimeException("Can't calculate info theoretic measures for base " + base);
}
// Check if we've got a power of 2
power_of_2_base = isPowerOf2(base);
if (power_of_2_base) {
log_2_base = (int) Math.round(Math.log(base) / Math.log(2));
}
}
/**
* Initialise the calculator for re-use with new observations.
* (Child classes should clear the existing PDFs)
* @throws Exception
*/
public void initialise() {
initialise(base);
}
/**
* Initialise the calculator for re-use with new observations,
* and a new base.
* (Child classes should clear the existing PDFs)
*/
public void initialise(int base){
resetBase(base);
average = 0.0;
max = 0.0;
min = 0.0;
std = 0.0;
observations = 0;
}
/**
* Return the measure last calculated in a call to
* {@link #computeAverageLocalOfObservations()}
* or related methods after the previous
* {@link #initialise()} call.
*
* @return the last computed measure value
*/
public final double getLastAverage() {
return average;
}
/**
* Return the last computed max local value of the measure.
* Not declaring this final so that separable calculator
* can throw an exception on it since it does not support it
*/
public double getLastMax() {
return max;
}
/**
* Return the last computed min local value of the measure.
* Not declaring this final so that separable calculator
* can throw an exception on it since it does not support it
*/
public double getLastMin() {
return min;
}
/**
* Return the last computed standard deviation of
* local values of the measure.
*/
public final double getLastStd() {
return std;
}
/**
* Get the number of samples to be used for the PDFs here
* which have been supplied by calls to
* "setObservations", "addObservations" etc.
*
* <p>Note that the number of samples may not be equal to the length of time-series
* supplied (e.g. for transfer entropy, where we need to accumulate
* a number of samples for the past history of the destination).
* </p>
*
* @return the number of samples to be used for the PDFs
*/
public final int getNumObservations() {
return observations;
}
/**
* Return whether a given integer is a power of 2
*
* @param num an integer
* @return whether the integer is a power of 2
*/
public final static boolean isPowerOf2(int num) {
int bits = 0;
int shiftedValue = num;
for (int b = 0; b < Integer.SIZE; b ++) {
if ((shiftedValue & 0x01) > 0) {
// LSB is a 1
bits++;
if (bits > 1) {
// Encountered more than 1 bit set to 1.
// Is not a power of 2
return false;
}
}
// Shift a new bit down into the LSB
shiftedValue = shiftedValue >> 1;
}
// Post: num has either 1 bit set to 1 or 0 bits set to 1
if (bits == 0) {
return false;
}
return true;
}
/**
* Compute the average value of the measure
* from the previously-supplied samples.
*
* @return the estimate of the measure
*/
public abstract double computeAverageLocalOfObservations();
/**
* Set or clear debug mode for extra debug printing to stdout
*
* @param debug new setting for debug mode (on/off)
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
}