package de.ugoe.cs.eventbench.models; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Random; import de.ugoe.cs.eventbench.data.Event; import de.ugoe.cs.util.StringTools; import de.ugoe.cs.util.console.Console; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.graph.SparseMultigraph; import edu.uci.ics.jung.graph.util.EdgeType; import Jama.Matrix; /** *

* Implements first-order Markov models. The implementation is based on * {@link HighOrderMarkovModel} and restricts the Markov order to 1. In * comparison to {@link HighOrderMarkovModel}, more calculations are possible * with first-order models, e.g., the calculation of the entropy ( * {@link #calcEntropy()}). *

* * @author Steffen Herbold * @version 1.0 */ public class FirstOrderMarkovModel extends HighOrderMarkovModel implements IDotCompatible { /** *

* Id for object serialization. *

*/ private static final long serialVersionUID = 1L; /** *

* Maximum number of iterations when calculating the stationary distribution * as the limit of multiplying the transmission matrix with itself. *

*/ final static int MAX_STATDIST_ITERATIONS = 1000; /** *

* Constructor. Creates a new FirstOrderMarkovModel. *

* * @param r * random number generator used by probabilistic methods of the * class */ public FirstOrderMarkovModel(Random r) { super(1, r); } /** *

* Generates the transmission matrix of the Markov model. *

* * @return transmission matrix */ private Matrix getTransmissionMatrix() { List> knownSymbols = new ArrayList>( trie.getKnownSymbols()); int numStates = knownSymbols.size(); Matrix transmissionMatrix = new Matrix(numStates, numStates); for (int i = 0; i < numStates; i++) { Event currentSymbol = knownSymbols.get(i); List> context = new ArrayList>(); context.add(currentSymbol); for (int j = 0; j < numStates; j++) { Event follower = knownSymbols.get(j); double prob = getProbability(context, follower); transmissionMatrix.set(i, j, prob); } } return transmissionMatrix; } /** *

* Calculates the entropy of the model. To make it possible that the model * is stationary, a transition from {@link Event#ENDEVENT} to * {@link Event#STARTEVENT} is added. *

* * @return entropy of the model or NaN if it could not be calculated */ public double calcEntropy() { Matrix transmissionMatrix = getTransmissionMatrix(); List> knownSymbols = new ArrayList>( trie.getKnownSymbols()); int numStates = knownSymbols.size(); int startStateIndex = knownSymbols.indexOf(Event.STARTEVENT); int endStateIndex = knownSymbols.indexOf(Event.ENDEVENT); if (startStateIndex == -1) { Console.printerrln("Error calculating entropy. Initial state of markov chain not found."); return Double.NaN; } if (endStateIndex == -1) { Console.printerrln("Error calculating entropy. End state of markov chain not found."); return Double.NaN; } transmissionMatrix.set(endStateIndex, startStateIndex, 1); // Calculate stationary distribution by raising the power of the // transmission matrix. // The rank of the matrix should fall to 1 and each two should be the // vector of the stationory distribution. int iter = 0; int rank = transmissionMatrix.rank(); Matrix stationaryMatrix = (Matrix) transmissionMatrix.clone(); while (iter < MAX_STATDIST_ITERATIONS && rank > 1) { stationaryMatrix = stationaryMatrix.times(stationaryMatrix); rank = stationaryMatrix.rank(); iter++; } if (rank != 1) { Console.traceln("rank: " + rank); Console.printerrln("Unable to calculate stationary distribution."); return Double.NaN; } double entropy = 0.0; for (int i = 0; i < numStates; i++) { for (int j = 0; j < numStates; j++) { if (transmissionMatrix.get(i, j) != 0) { double tmp = stationaryMatrix.get(i, 0); tmp *= transmissionMatrix.get(i, j); tmp *= Math.log(transmissionMatrix.get(i, j)) / Math.log(2); entropy -= tmp; } } } return entropy; } /** *

* The dot represenation of {@link FirstOrderMarkovModel}s is its graph * representation with the states as nodes and directed edges weighted with * transition probabilities. *

* * @see de.ugoe.cs.eventbench.models.IDotCompatible#getDotRepresentation() */ @Override public String getDotRepresentation() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("digraph model {" + StringTools.ENDLINE); List> knownSymbols = new ArrayList>( trie.getKnownSymbols()); for (Event symbol : knownSymbols) { final String thisSaneId = symbol.getShortId().replace("\"", "\\\"") .replaceAll("[\r\n]", ""); stringBuilder.append(" " + symbol.hashCode() + " [label=\"" + thisSaneId + "\"];" + StringTools.ENDLINE); List> context = new ArrayList>(); context.add(symbol); Collection> followers = trie.getFollowingSymbols(context); for (Event follower : followers) { stringBuilder.append(" " + symbol.hashCode() + " -> " + follower.hashCode() + " "); stringBuilder.append("[label=\"" + getProbability(context, follower) + "\"];" + StringTools.ENDLINE); } } stringBuilder.append('}' + StringTools.ENDLINE); return stringBuilder.toString(); } /** *

* Returns a {@link Graph} represenation of the model with the states as * nodes and directed edges weighted with transition probabilities. *

* * @return {@link Graph} of the model */ public Graph getGraph() { Graph graph = new SparseMultigraph(); List> knownSymbols = new ArrayList>( trie.getKnownSymbols()); for (Event symbol : knownSymbols) { String from = symbol.getShortId(); List> context = new ArrayList>(); context.add(symbol); Collection> followers = trie.getFollowingSymbols(context); for (Event follower : followers) { String to = follower.getShortId(); MarkovEdge prob = new MarkovEdge(getProbability(context, follower)); graph.addEdge(prob, from, to, EdgeType.DIRECTED); } } return graph; } /** * Inner class used for the {@link Graph} representation of the model. * * @author Steffen Herbold * @version 1.0 */ static public class MarkovEdge { /** *

* Weight of the edge, i.e., its transition probability. *

*/ double weight; /** *

* Constructor. Creates a new MarkovEdge. *

* * @param weight * weight of the edge, i.e., its transition probability */ MarkovEdge(double weight) { this.weight = weight; } /** *

* The weight of the edge as {@link String}. *

*/ public String toString() { return "" + weight; } } }