Set the precision and initialize butools (load all packages)
%precision %g
%run "~/github/butools/Python/BuToolsInit.py"
First the global butools.verbose flag is set to True to obtain more messages from the functions.
butools.verbose = True
RAPs (Rational Arrival Processes) are the generalizations of MAPs without the Markovian restrictions for the rate matrices. Some functions need a MAP, some others a RAP input, which is enforced if the global flag called butools.checkInput is set to True, with tolerance given by the global butools.checkPrecision variable.
The corresponding functions to check if a matrix pair is a MAP or a RAP are called CheckMAPRepresentation and CheckRAPRepresentation.
H0 = ml.matrix([[-1., 0, 0],[0, -2., 1.],[0, -1., -2.]])
H1 = ml.matrix([[1., 0, 0],[0, 1., 0],[1., 1., 1.]])
CheckMAPRepresentation(H0, H1)
CheckRAPRepresentation(H0, H1)
H0 = ml.matrix([[-2., 0, 0],[0, -1., 1.],[0, -1., -1.]])
H1 = ml.matrix([[1., 0, 1.],[0, 1., -1.],[1., 0, 1.]])
CheckRAPRepresentation(H0, H1)
BuTools also supports the multi-type, marked variant of these arrival processes, which are referred to as MMAPs and MRAPs. Instead of a matrix pair, they are characterized by a list of matrices. If the arrival process is able to generate K different types of arrivals, K+1 matrices are necessary to define the process. The list of these K+1 matrices is the input of the corresponding functions of BuTools.
H0 = ml.matrix([[-5., 0.28, 0.9, 1.],[1., -8., 0.9, 0.1],[0.9, 0.1, -4., 1.],[1., 2., 3., -9.]])
H1 = ml.matrix([[-0.08, 0.7, 0.1, 0.1],[0.1, 1., 1.8, 0.1],[0.1, 0.1, 0.1, 0.7],[0.7, 0.1, 0.1, 0.1]])
H2 = ml.matrix([[0.1, 0.1, 0.1, 1.7],[1.8, 0.1, 1., 0.1],[0.1, 0.1, 0.7, 0.1],[0.1, 1., 0.1, 0.8]])
CheckMMAPRepresentation([H0, H1, H2])
CheckMRAPRepresentation([H0, H1, H2])
Let us define a simple MAP given by matrices $D_0$ and $D_1$
D0 = ml.matrix ([[-5, 1, 0], [3, -3, 0], [1, 1, -5]])
D1 = ml.matrix ([[0, 0, 4], [0, 0, 0], [1, 1, 1]])
If the AT&T graphviz tool is installed, the ImageFromMAP and ImageFromMMAP functions are able to visualize the MAPs and MMAPs.
ImageFromMAP(D0, D1)
It is possible to obtain the phase type distributed marginal distribution.
alpha, A = MarginalDistributionFromMAP (D0, D1)
print("alpha = ", alpha)
print("A = ", A)
From this distribution it is easy to compute the marginal moments by the appropriate functions of the PH package. However, there is a convenience function MarginalMomentsFromMAP that does the same.
print(MarginalMomentsFromMAP (D0, D1, 3))
print(MomentsFromPH(alpha, A, 3))
Now we compute the autocorrelation function up to lag 10.
LagCorrelationsFromMAP (D0, D1, 10)
The marginal moments and the lag-1 joint moments are known to characterize the MAP uniquely:
m = MarginalMomentsFromMAP (D0, D1)
Nm = LagkJointMomentsFromMAP (D0, D1)
print("marginal moments = ", m)
print("lag-1 joint moments = ", Nm)
BuTools has several functions for the inverse characterization of MAPs and RAPs. For example, we can obtain a simple 2-state MAP based on 3 moments and the lag-1 autocorrelation. (Note however, that the moments and the autocorrelation must fall into a given interval, othervise MAP2FromMoments does not return a valid result)
D0,D1 = MAP2FromMoments([1,3,20], 0.2)
print("D0 = ", D0)
print("D1 = ", D1)
print("Checking moments and correlation:")
moms = MarginalMomentsFromMAP (D0, D1)
ro = LagCorrelationsFromMAP (D0, D1, 1)
print("Moments = ", moms)
print("Lag-1 autocorrelation = ", ro)
Based on $2N-1$ marginal moments and $N\times N$ lag-1 joint moments we can obtain an order-$N$ RAP as well. First we define a new MAP, and compute its marginal and joint moments...
D0 = ml.matrix ([[-6, 2, 1], [3, -4, 0], [2, 0, -5]])
D1 = ml.matrix ([[0, 1, 2], [0, 0, 1], [1, 1, 1]])
m = MarginalMomentsFromMAP (D0, D1)
Nm = LagkJointMomentsFromRAP (D0, D1)
print("marginal moments = ", m)
print("lag-1 joint moments = ", Nm)
... and solve the inverse characterization problem.
H0, H1 = RAPFromMoments (m, Nm)
print("H0 = ", H0)
print("H1 = ", H1)
We can check if the moments are really the same:
m = MarginalMomentsFromRAP (H0, H1)
Nm = LagkJointMomentsFromRAP (H0, H1)
print("marginal moments = ", m)
print("lag-1 joint moments = ", Nm)
It is easy to see that $(H_0,H_1)$ is a non-Markovian representation. However, it is not at all easy to check if it is a valid process (with non-negative joint densities), or not. Something like MonocyclicPHFromME for RAPs is not available yet.
Now we try to transform $(H_0,H_1)$ into a Markovian representation.
D0, D1 = MAPFromRAP (H0, H1)
print("D0=", D0)
print("D1=", D1)
Altough we managed to find a Markovian representation, keep in mind that it is not always possible.
If a MAP or a RAP is redundant, we have functions to obtain the minimal representation as well. The next $(D_0,D_1)$ matrices define a redundant MAP.
D0 = ml.matrix ([[-5, 1, 0], [3, -3, 0], [1, 1, -5]])
D1 = ml.matrix ([[0, 0, 4], [0, 0, 0], [1, 1, 1]])
Let us compute the minimal representation:
H0, H1 = MinimalRepFromRAP(D0, D1)
print("H0=", H0)
print("H1=", H1)
Thus it is an order-2 rational arrival process. We can check that $(D_0,D_1)$ and $(H_0,H_1)$ are equal indeed by comparing their moments.
m = MarginalMomentsFromMAP (D0, D1)
N = LagkJointMomentsFromMAP (D0, D1, 2)
print("marginal moments = ", m)
print("lag-1 joint moments = ", N)
m = MarginalMomentsFromRAP (H0, H1)
N = LagkJointMomentsFromRAP (H0, H1, 2)
print("marginal moments = ", m)
print("lag-1 joint moments = ", N)
...or, by finding an appropriate similarity transformation matrix (with the SimilarityMatrix function of the RepTrans package) that transforms $(D_0,D_1)$ to $(H_0,H_1)$.
T = SimilarityMatrix(H0,D0)
Check if matrix $T$ transforms $(D_0,D_1)$ to $(H_0,H_1)$ indeed:
la.norm (T*D0*T.I - H0), la.norm (T*D1*T.I - H1)
Most MAP/RAP related functions in butools work with marked processes as well. Let us try out the representation minimization method with the following MMAP.
D0 = ml.matrix ([[-6, 0, 0], [0, -5, 2], [0, 0, -3]])
D1 = ml.matrix ([[0, 1, 5], [1, 1, 0], [1, 0, 1]])
D2 = ml.matrix ([[0, 0, 0], [0, 1, 0], [0, 0, 1]])
H0, H1, H2 = MinimalRepFromMRAP ([D0, D1, D2])
print("H0=", H0)
print("H1=", H1)
print("H2=", H2)
Thus it is an order-2 representation again. To confirm it, we compare the marginal and the lag-1 joint moments.
m = MarginalMomentsFromMMAP ([D0, D1, D2], 5)
N = LagkJointMomentsFromMMAP ([D0, D1, D2], 2)
print("marginal moments = ", m)
print("lag-1 joint moments = ", N)
m = MarginalMomentsFromMRAP ([H0, H1, H2], 5)
N = LagkJointMomentsFromMRAP ([H0, H1, H2], 2)
print("marginal moments = ", m)
print("lag-1 joint moments = ", N)
The tool called RAPFromMomentsAndCorrelations does what its name suggests. The input is a set of moments and lag-k correlations, and the output is a RAP.
For testing purposes we feed it with the moments and correlations of an other RAP, $(H_0,H_1)$:
H0 = ml.matrix([[-6.2, 2., 0],[2., -9., 1.],[1., 0, -3.]])
H1 = ml.matrix([[2.2, 0, 2.],[0, 4., 2.],[0, 1., 1.]])
mom = MarginalMomentsFromRAP(H0, H1)
corr = LagCorrelationsFromRAP(H0, H1, 3)
G0, G1 = RAPFromMomentsAndCorrelations(mom, corr)
print("G0=",G0)
print("G1=",G1)
The next code shows that the procedure was successfull.
rmom = MarginalMomentsFromRAP(G0, G1)
rcorr = LagCorrelationsFromRAP(G0, G1, 3)
print("mom=",mom)
print("rmom=",rmom)
print("corr=",corr)
print("rcorr=",rcorr)
However, the we did not get back the original RAP! Let us find the similarity matrix that transforms $H_0$ to $G_0$:
T = SimilarityMatrix (H0, G0)
This matrix does the job: transforms $H_0$ to $G_0$:
T.I*H0*T - G0
...but the same similarity transformation does not transform $H_1$ to $G_1$!
T.I*H1*T - G1
The last inverse characterization too is MAPFromFewMomentsAndCorrelations. Contrary to all previous procedures, this one always gives a valid MAP representation based on 2 or 3 moments and the lag-1 autocorrelation.
D0, D1 = MAPFromFewMomentsAndCorrelations([1.2, 4.32, 20.], -0.4)
print("D0=",D0)
print("D1=",D1)
The result is sometimes large, but at least Markovian. Let us check the target parameters.
rmoms = MarginalMomentsFromMAP(D0, D1, 3)
rcorr1 = LagCorrelationsFromMAP(D0, D1, 1)
print("rmoms=",rmoms)
print("rcorr1=",rcorr1)
The RandomMAP and RandomMMAP procedures provide a random MAP and MMAP, respectively.The input parameters are the number of phases, the number of arrival types (in case of MMAP), the mean inter-arrival time and the number of zero entries.
D0, D1 = RandomMAP(4, 1.62, 10)
print("D0=",D0)
print("D1=",D1)
D0, D1, D2, D3 = RandomMMAP(4, 3, 1.62, 10)
print("D0=",D0)
print("D1=",D1)
print("D2=",D2)
print("D3=",D3)
The SamplesFromMAP / SamplesFromMMAP functions return a vector of random samples from the given arrival processes. These samples can be used in simulations, for instance.
D0,D1 = MAP2FromMoments([1,3,20], 0.2)
x = SamplesFromMAP(D0, D1, 100000)
print("mean=",np.mean(x))
In the MMAP case the result is two dimensional, the output is the list of sample-type pairs.
D = RandomMMAP(4, 3, 1.62, 10)
x = SamplesFromMMAP(D, 10000)
print(x[0:10])