package aima.core.probability.hmm.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import aima.core.probability.CategoricalDistribution;
import aima.core.probability.RandomVariable;
import aima.core.probability.domain.FiniteDomain;
import aima.core.probability.hmm.HiddenMarkovModel;
import aima.core.probability.proposition.AssignmentProposition;
import aima.core.probability.util.ProbabilityTable;
import aima.core.util.Util;
import aima.core.util.math.Matrix;
/**
* Default implementation of the HiddenMarkovModel interface.
*
* @author Ciaran O'Reilly
* @author Ravi Mohan
*/
public class HMM implements HiddenMarkovModel {
private RandomVariable stateVariable = null;
private FiniteDomain stateVariableDomain = null;
private Matrix transitionModel = null;
private Map<Object, Matrix> sensorModel = null;
private Matrix prior = null;
/**
* Instantiate a Hidden Markov Model.
*
* @param stateVariable
* the single discrete random variable used to describe the
* process states 1,...,S.
* @param transitionModel
* the transition model:<br>
* <b>P</b>(X<sub>t</sub> | X<sub>t-1</sub>)<br>
* is represented by an S * S matrix <b>T</b> where<br>
* <b>T</b><sub>ij</sub> = P(X<sub>t</sub> = j | X<sub>t-1</sub>
* = i).
* @param sensorModel
* the sensor model in matrix form:<br>
* P(e<sub>t</sub> | X<sub>t</sub> = i) for each state i. For
* mathematical convenience we place each of these values into an
* S * S diagonal matrix.
* @param prior
* the prior distribution represented as a column vector in
* Matrix form.
*/
public HMM(RandomVariable stateVariable, Matrix transitionModel,
Map<Object, Matrix> sensorModel, Matrix prior) {
if (!stateVariable.getDomain().isFinite()) {
throw new IllegalArgumentException(
"State Variable for HHM must be finite.");
}
this.stateVariable = stateVariable;
stateVariableDomain = (FiniteDomain) stateVariable.getDomain();
if (transitionModel.getRowDimension() != transitionModel
.getColumnDimension()) {
throw new IllegalArgumentException(
"Transition Model row and column dimensions must match.");
}
if (stateVariableDomain.size() != transitionModel.getRowDimension()) {
throw new IllegalArgumentException(
"Transition Model Matrix does not map correctly to the HMM's State Variable.");
}
this.transitionModel = transitionModel;
for (Matrix smVal : sensorModel.values()) {
if (smVal.getRowDimension() != smVal.getColumnDimension()) {
throw new IllegalArgumentException(
"Sensor Model row and column dimensions must match.");
}
if (stateVariableDomain.size() != smVal.getRowDimension()) {
throw new IllegalArgumentException(
"Sensor Model Matrix does not map correctly to the HMM's State Variable.");
}
}
this.sensorModel = sensorModel;
if (transitionModel.getRowDimension() != prior.getRowDimension()
&& prior.getColumnDimension() != 1) {
throw new IllegalArgumentException(
"Prior is not of the correct dimensions.");
}
this.prior = prior;
}
//
// START-HiddenMarkovModel
@Override
public RandomVariable getStateVariable() {
return stateVariable;
}
@Override
public Matrix getTransitionModel() {
return transitionModel;
}
@Override
public Map<Object, Matrix> getSensorModel() {
return sensorModel;
}
@Override
public Matrix getPrior() {
return prior;
}
@Override
public Matrix getEvidence(List<AssignmentProposition> evidence) {
if (evidence.size() != 1) {
throw new IllegalArgumentException(
"Only a single evidence observation value should be provided.");
}
Matrix e = sensorModel.get(evidence.get(0).getValue());
if (null == e) {
throw new IllegalArgumentException(
"Evidence does not map to sensor model.");
}
return e;
}
@Override
public Matrix createUnitMessage() {
double[] values = new double[stateVariableDomain.size()];
Arrays.fill(values, 1.0);
return new Matrix(values, values.length);
}
@Override
public Matrix convert(CategoricalDistribution fromCD) {
double[] values = fromCD.getValues();
return new Matrix(values, values.length);
}
@Override
public CategoricalDistribution convert(Matrix fromMessage) {
return new ProbabilityTable(fromMessage.getRowPackedCopy(),
stateVariable);
}
@Override
public List<CategoricalDistribution> convert(List<Matrix> matrixs) {
List<CategoricalDistribution> cds = new ArrayList<CategoricalDistribution>();
for (Matrix m : matrixs) {
cds.add(convert(m));
}
return cds;
}
@Override
public Matrix normalize(Matrix m) {
double[] values = m.getRowPackedCopy();
return new Matrix(Util.normalize(values), values.length);
}
// END-HiddenMarkovModel
//
}