The problem: Given an arbitrary matrix which represents the probability of transition from one state to another state in one step for a Markov chain, determine all closed sets of states and the transient state.
This small note describes an algorithm that I developed which solves this problem. The algorithm is recursive in nature.
A Matlab implementation is given below with an example run on 3 different matrices and how to call the matlab function.
This algorithm describes how to find all closed sets and the transient set (if any) given as input
the
An implementation of the algorithm is provided (in Matlab) with a number of examples.
As an example of an input, let us consider the following simple Markov chain state diagram and
its corresponding
The matrix
The output of the algorithm below will be the following
closed sets:
Once the closed sets and the transient set are known, generating the canonical form is a simple
matter of formatting the
Now the algorithm is described.
The basic idea of the algorithm is the following: For each state, we generate the path of states that can be travelled from that state. Next, we collect the states which has the same states on its path. Each set of states which has the same states on their path are one closed set. The remaining states, if any, would all have different set of states on their path. These states make up the set of transient states.
The algorithm will terminate since the length of the longest path that could possibly be travelled from any state is finite (which is the number of states in the chain).
Since there are
Here is a description of the algorithm
Input: A square
output: all sets which are closed and transient set (if it exists)
For each state
To illustrate how this algorithm works, we apply it to the example Markov chain shown above.
In step 1, we generate the LHS and the RHS lists from the input
The above is send to step 2. In step 2 we start by scanning the RHS list of each state listed in
the LHS set. For state
We start again by looking at the set of states of the RHS of state
We now compare RHS sets in (1) with those in (2). Since they are not the same, we repeat step 2 again, but use the above RHS now as an input to step 2.
Again, we start by scanning the RHS list of each state in the LHS. We repeat the
same thing as before, lets say we reached state
We see now that the RHS in (3) is the same as the RHS in
Now we collect all states from the LHS which has the same list in its RHS. We see that
state
The following is a test run of the implementation showing in each case the matrix
clear all close all nmaTestMarkov() *************************** 1.0000 0 0 0 0 0 0.2000 0.8000 0 0 0 0.7000 0.3000 0 0 0.1000 0 0.1000 0.4000 0.4000 0 0.1000 0.3000 0.2000 0.4000 found the following closed sets {1} {2,3} found the following transient set {4,5} *************************** 0.1000 0.3000 0 0 0.6000 0 0.2000 0.7000 0 0.1000 0 0.7000 0.3000 0 0 0.1000 0 0.1000 0.4000 0.4000 0 0.1000 0.3000 0.2000 0.4000 found the following closed sets {1,2,3,4,5} No transient set found *************************** 0.5000 0.5000 0 0 0 0 0 0 1.0000 0 0 0 0.3333 0 0 0.3333 0.3333 0 0 0 0 0.5000 0.5000 0 0 0 0 0 0 1.0000 0 0 0 0 1.0000 0 found the following closed sets {5,6} found the following transient set {1,2,3,4} *************************** 1.0000 0 0 0 0 0 0 1.0000 0 0 0 0 0 0 1.0000 0 0 0 0 0 0 0 0.5000 0.5000 0.2500 0.2500 0 0 0 0.5000 0.2500 0 0.2500 0 0.5000 0 found the following closed sets {1} {2} {3} found the following transient set {4,5,6} *************************** 1.0000 0 0 0 0 0 0 0 0 0 1.0000 0 0 0 0 0 0 0 0.2222 0.1111 0 0.0833 0.1111 0.1389 0.1389 0.1111 0.0833 0.1389 0.1667 0 0.7500 0 0 0 0 0 0.1111 0.1667 0 0 0.7222 0 0 0 0 0.1389 0.1667 0 0 0 0.6944 0 0 0 0.1389 0.1667 0 0 0 0 0.7222 0 0 0.1111 0.1667 0 0 0 0 0 0.7222 0 0.0833 0.1667 0 0 0 0 0 0 0.7500 found the following closed sets {1} {2} found the following transient set {3,4,5,6,7,8,9}
function nmaTestMarkov() p1=[1 0 0 0 0; 0 .2 .8 0 0; 0 .7 .3 0 0; .1 0 .1 .4 .4; 0 .1 .3 .2 .4]; testIt(p1); fprintf('\n\n'); p1=[ .1 .3 0 0 .6; 0 .2 .7 0 .1; 0 .7 .3 0 0; .1 0 .1 .4 .4; 0 .1 .3 .2 .4]; testIt(p1); fprintf('\n\n'); p1=[ .5 .5 0 0 0 0; 0 0 1 0 0 0; 1/3 0 0 1/3 1/3 0; 0 0 0 1/2 1/2 0; 0 0 0 0 0 1; 0 0 0 0 1 0]; testIt(p1); p1=[ 1 0 0 0 0 0; 0 1 0 0 0 0; 0 0 1 0 0 0; 0 0 0 0 1/2 1/2; 1/4 1/4 0 0 0 .5; 1/4 0 1/4 0 1/2 0]; %closed {1},{2},{3} testIt(p1); p1=[ 1 0 0 0 0 0 0 0 0; 0 1 0 0 0 0 0 0 0; 2/9 1/9 0 1/12 1/9 5/36 5/36 1/9 1/12; 5/36 1/6 0 3/4 0 0 0 0 0; 1/9 1/6 0 0 13/18 0 0 0 0; 5/36 1/6 0 0 0 25/36 0 0 0; 5/36 1/6 0 0 0 0 13/18 0 0; 1/9 1/6 0 0 0 0 0 13/18 0; 1/12 1/6 0 0 0 0 0 0 3/4]; %closed {1},{2},{3} testIt(p1); end %%%%%%%%%%%%%%%%%%%%%%%%%%%% % % %%%%%%%%%%%%%%%%%%%%%%%%%%%% function testIt(p) [nRow,nCol]=size(p); [closedSet,tranSet]=nmaChainMarkov(p); fprintf('***************************\n\n'); disp(p); if length(closedSet)>0 fprintf('found the following closed sets\n'); emit(closedSet); else fprintf('No closed sets found\n'); end if length(tranSet)>0 fprintf('found the following transient set\n'); emitV2(tranSet); else fprintf('No transient set found\n'); end end %%%%%%%%%%%%%% % % %%%%%%%%%%%%%% function emit(s) for i=1:length(s) c=cell2mat(s{i}(1)); fprintf('{'); for j=1:length(c) fprintf('%d',c(j)); if(j<length(c)) fprintf(','); else fprintf('}'); end end fprintf('\n'); end end %%%%%%%%%%%%%% % % %%%%%%%%%%%%%% function emitV2(s) fprintf('{'); for i=1:length(s) c=cell2mat(s{i}(1)); for j=1:length(c) fprintf('%d',c(j)); if i<length(s) fprintf(','); else if j<length(c) fprintf(','); end end end end fprintf('}\n'); end
function [cs,tr]=nmaChainMarkov(p) %function [cs,tr]=nmaChainMarkov(p) % %By Nasser Abbasi %March 8, 2008 % [nRow,nCol]=size(p); if nRow~=nCol error 'matrix must be square'; end lhs=1:nCol; rhs=cell(nRow,1)'; for i=1:nCol rhs{i}=find(p(i,:)~=0); end keepOn=1; while keepOn newRhs=updateRhs(lhs,rhs); if isequal(newRhs,rhs) keepOn=0; else rhs=newRhs; end end theClasses=findSets(lhs,newRhs); [cs,tr]=classifyClasses(theClasses); end %%%%%%%%%%%%%%%%%%%%%%%%%%%% % called after all classes are found % this function classify the class as closed or % transient %%%%%%%%%%%%%%%%%%%%%%%%%%%% function [cs,tr]=classifyClasses(c) n=length(c); cs=cell(0); tr=cell(0); csIndex=0; trIndex=0; for i=1:n if isequal( c{i}(1), c{i}(2) ) csIndex=csIndex+1; cs{csIndex}(1)=c{i}(1); cs{csIndex}(2)=c{i}(2); else trIndex=trIndex+1; tr{trIndex}(1)=c{i}(1); tr{trIndex}(2)=c{i}(2); end end end %%%%%%%%%%%%%%%%%%%%%%%%%%%% % updated the RHS lists, see paper. % %%%%%%%%%%%%%%%%%%%%%%%%%%%% function newRhs=updateRhs(lhs,rhs) for i=1:length(lhs) n=0; for j=1:length(rhs{i}) if rhs{i}(j)==lhs(i) n=n+1; tmp{i}(n)=lhs(i); else n=n+1; tmp{i}(n)=rhs{i}(j); z=rhs{i}(j); for k=1:length(rhs{z}) n=n+1; tmp{i}(n)=rhs{z}(k); end end end tmp{i}=unique(tmp{i}); end newRhs=tmp; end %%%%%%%%%%%%%%%%%%%%% % find all the states with the same rhs % %%%%%%%%%%%%%%%%%%%%%% function theClasses=findSets(lhs,rhs) n=length(lhs); skip=cell(0); nskip=0; skip{1}(1)=0; entryNumber=0; for i=1:n if isempty(find(skip{1}==i)) entryNumber=entryNumber+1; nskip=nskip+1; skip{1}(nskip)=i; tmp=cell(2,1); k=1; tmp{1}(k)=i; tmp{2}=rhs{i}; for j=1:n if j ~= i if isempty(find(skip{1}==j)) if isequal(tmp{2},rhs{j}) k=k+1; tmp{1}(k)=j; nskip=nskip+1; skip{1}(nskip)=j; end end end end if ~isempty(tmp) theClasses{entryNumber}=tmp; end end end end