Passez au temps continu
Un serveur de calcul ne tombe pas en panne « à la fin de l’heure » : il tombe en panne à 14 h 37 min 12 s, puis il est réparé à 16 h 05. Découper le temps en pas réguliers, comme dans la partie 1, est commode mais artificiel. Et si l’on veut modéliser un système où les événements surviennent à n’importe quel instant ?
On passe alors aux chaînes de Markov à temps continu. Presque tout ce que vous savez reste vrai, à une différence près : la matrice de transition est remplacée par une matrice de taux, le générateur. Nous suivrons un serveur qui tombe en panne et qu’on répare, et nous calculerons sa disponibilité.
La loi exponentielle, l’horloge sans mémoire
Section intitulée « La loi exponentielle, l’horloge sans mémoire »Commençons par la durée de fonctionnement du serveur avant la panne, notée . En fiabilité, on la modélise très souvent par une loi exponentielle de paramètre , notée :
- est la densité de ;
- est la probabilité que le serveur fonctionne encore à l’instant (on l’appelle la fonction de survie) ;
- est la durée moyenne de fonctionnement, et s’interprète comme un taux : un nombre moyen de pannes par unité de temps de fonctionnement.
Pour notre serveur, prenons panne par heure, soit une durée moyenne de fonctionnement de 50 heures. La probabilité qu’il tienne plus de 100 heures vaut .
La propriété essentielle de cette loi est d’être sans mémoire :
Sachant que le serveur fonctionne depuis heures, la probabilité qu’il tienne encore heures est la même que s’il venait d’être mis en route. Après 80 heures sans panne, la probabilité de tenir 50 heures de plus vaut toujours . L’exponentielle est la seule loi continue qui ait cette propriété, et c’est précisément ce qui rend le processus markovien : l’état présent (« le serveur marche ») suffit, il n’y a pas besoin de savoir depuis quand.
Deux propriétés de calcul serviront tout au long de la partie. Si et sont indépendantes, alors :
- le premier des deux événements survient après un temps : les taux s’additionnent ;
- c’est le premier qui gagne la course avec probabilité .
Du temps discret au temps continu
Section intitulée « Du temps discret au temps continu »Une chaîne de Markov à temps continu est un processus à valeurs dans un espace discret qui vérifie la propriété de Markov à tous les instants : pour tous instants ,
On la suppose homogène : ne dépend que de la durée , pas de . On range ces probabilités dans la matrice , qui joue le rôle de . Tout ce que vous connaissez se transpose :
| Temps discret | Temps continu |
|---|---|
| (Chapman-Kolmogorov) | |
Mais il n’y a plus de « pas élémentaire » : comment décrire la chaîne par un seul objet, comme en temps discret ? En regardant ce qui se passe sur un intervalle de temps infiniment court.
Le générateur infinitésimal
Section intitulée « Le générateur infinitésimal »Sur un petit intervalle de durée , un serveur en marche tombe en panne avec une probabilité à peu près proportionnelle à : . C’est cette proportionnalité qu’on formalise. Pour :
- est le taux de transition de vers ;
- , et est le taux de sortie de l’état ;
- désigne un terme négligeable devant quand .
La matrice est le générateur infinitésimal de la chaîne. C’est la dérivée de en 0 :
Comme chaque ligne de somme à 1 et chaque ligne de aussi, chaque ligne de somme à 0. Pour notre serveur, avec l’état 0 = marche et 1 = panne, un taux de panne et un taux de réparation :
Le graphe de transition porte maintenant les taux sur les arcs, et n’a pas de boucles (rester sur place n’est pas une transition).
Mais que fait concrètement la chaîne ? Grâce aux propriétés de l’exponentielle, on peut la décrire comme une suite de séjours et de sauts : dans l’état , elle reste un temps de loi , puis elle saute vers avec probabilité . C’est exactement une course d’horloges exponentielles, une par transition possible, dont la première qui sonne décide du saut.
Les équations de Kolmogorov et le régime transitoire
Section intitulée « Les équations de Kolmogorov et le régime transitoire »Dérivons la relation de Chapman-Kolmogorov par rapport à , puis par rapport à , et prenons la dérivée en 0. On obtient les équations de Kolmogorov :
En multipliant l’équation avant à gauche par , on obtient l’équation d’évolution des probabilités d’état :
Pour un espace d’états fini, ce système différentiel linéaire a pour solution , l’exponentielle de matrice. C’est l’analogue continu de .
Pour la classification des états, rien ne change par rapport à la partie 1 : accessibilité, classes, irréductibilité et états absorbants ( pour tout ) se lisent sur le graphe des taux. Seule différence, la notion de période disparaît : en temps continu, on peut revenir dans un état après n’importe quelle durée.
Le régime stationnaire : ΠA = 0
Section intitulée « Le régime stationnaire : ΠA = 0 »À long terme, se stabilise et sa dérivée s’annule. Comme , la limite doit vérifier .
Chaque équation de a une lecture physique, appelée équation de balance globale : en régime stationnaire, le flux de probabilité qui sort d’un état égale le flux qui y entre.
À gauche, la probabilité d’être en multipliée par le taux de sortie ; à droite, la somme des flux venant des autres états.
Le code suivant calcule avec SciPy, résout , et simule la chaîne par sa description « séjour exponentiel puis saut ».
import numpy as npfrom scipy.linalg import expm
lam, mu = 0.02, 0.5 # taux de panne et de réparation (par heure)A = np.array([[-lam, lam], [mu, -mu]])assert np.allclose(A.sum(axis=1), 0) # les lignes d'un générateur somment à 0
G2 = expm(2 * A) # G(2) = exp(2A)print(G2[0, 0]) # P(marche à t=2 | marche à t=0) ≈ 0.975
# Régime stationnaire : Pi A = 0 et somme = 1M = A.T.copy()M[-1, :] = 1pi = np.linalg.solve(M, [0, 1])print(pi) # ≈ [0.9615 0.0385]
# Simulation : séjour exponentiel dans l'état courant, puis sautrng = np.random.default_rng(0)t, x, temps_marche, horizon = 0.0, 0, 0.0, 1_000_000while t < horizon: taux_sortie = -A[x, x] sejour = rng.exponential(1 / taux_sortie) # NumPy attend la moyenne 1/taux if x == 0: temps_marche += min(sejour, horizon - t) t += sejour x = 1 - x # deux états : on saute vers l'autreprint(temps_marche / horizon) # proche de 0.9615En résumé
Section intitulée « En résumé »- La loi exponentielle a pour moyenne et vérifie ; elle est sans mémoire, ce qui rend le processus markovien. Le minimum d’exponentielles indépendantes est exponentiel, de taux la somme des taux.
- Une chaîne à temps continu homogène vérifie et .
- Le générateur contient les taux ; ses lignes somment à 0. On séjourne en un temps , puis on saute vers avec probabilité .
- Équations de Kolmogorov : , d’où et .
- Pas de période en temps continu. Si la chaîne est irréductible (et récurrente positive), est l’unique solution de , : flux sortant = flux entrant.
- Disponibilité d’un équipement : = MTTF / (MTTF + MTTR).
Exercices
Section intitulée « Exercices »Exercice · Une ampoule sans mémoire
facileLa durée de vie d’une LED suit une loi exponentielle de moyenne 1 000 heures.
- Quelle est la probabilité qu’elle dure plus de 1 000 heures ?
- Elle fonctionne depuis 800 heures. Quelle est la probabilité qu’elle tienne encore 500 heures ?
Voir la solution
Le taux vaut par heure.
- . Remarquez que ce n’est pas 1/2 : la loi exponentielle n’est pas symétrique autour de sa moyenne, et la majorité des LED lâchent avant la durée moyenne.
- Par l’absence de mémoire, . Les 800 heures déjà écoulées ne changent rien.
En code
import numpy as npfrom scipy.stats import expon
duree = expon(scale=1000) # SciPy attend la moyenne 1/lambdaprint(duree.sf(1000)) # 0.3679 = P(D > 1000)print(duree.sf(1300) / duree.sf(800)) # 0.6065 = P(D > 1300 | D > 800)print(duree.sf(500)) # 0.6065 = P(D > 500) : sans mémoire
# Vérification par simulationrng = np.random.default_rng(0)d = rng.exponential(1000, 1_000_000)print((d > 1000).mean(), (d[d > 800] > 1300).mean()) # 0.3677 0.6065taux <- 1 / 1000 # R attend le taux lambdapexp(1000, taux, lower.tail = FALSE) # 0.3679 = P(D > 1000)pexp(1300, taux, lower.tail = FALSE) / pexp(800, taux, lower.tail = FALSE) # 0.6065 = P(D > 1300 | D > 800)pexp(500, taux, lower.tail = FALSE) # 0.6065 = P(D > 500) : sans mémoire
# Vérification par simulationset.seed(0)d <- rexp(1e6, taux)c(mean(d > 1000), mean(d[d > 800] > 1300)) # 0.3683 0.6071Exercice · Un serveur qui se dégrade
moyenOn raffine le modèle du serveur avec trois états : marche (M), dégradé (D), panne (P). Un serveur en marche se dégrade au taux 0,1 par heure. Un serveur dégradé tombe en panne au taux 0,2 par heure, ou est remis en état par une maintenance préventive au taux 0,3 par heure. Un serveur en panne est réparé (et revient en marche) au taux 0,5 par heure.
- Écrivez le générateur et vérifiez ses lignes.
- Combien de temps un serveur reste-t-il dégradé en moyenne ? Avec quelle probabilité quitte-t-il cet état par une panne ?
- Calculez la loi stationnaire. Quelle proportion du temps le serveur est-il en panne ?
Voir la solution
- Dans l’ordre M, D, P :
Chaque ligne somme bien à 0.
-
Le taux de sortie de D vaut , donc le séjour moyen est de heures. La sortie se fait par une panne avec probabilité .
-
Balances globales : pour D, , donc ; pour P, , donc . On vérifie la balance de M : . La normalisation donne , soit
Le serveur est en panne 6,25 % du temps.
En code
import numpy as np
# États M (marche), D (dégradé), P (panne)A = np.array([[-0.1, 0.1, 0.0], [0.3, -0.5, 0.2], [0.5, 0.0, -0.5]])print(A.sum(axis=1)) # [0. 0. 0.]
sortie_D = -A[1, 1]print(1 / sortie_D, A[1, 2] / sortie_D) # 2.0 heures en D, sortie par panne : 0.4
# Pi A = 0 et somme = 1M = A.T.copy()M[-1, :] = 1pi = np.linalg.solve(M, [0, 0, 1])print(pi) # [0.78125 0.15625 0.0625 ]# États M (marche), D (dégradé), P (panne)A <- matrix(c(-0.1, 0.1, 0, 0.3, -0.5, 0.2, 0.5, 0, -0.5), nrow = 3, byrow = TRUE)rowSums(A) # 0 0 0
sortie_D <- -A[2, 2]c(1 / sortie_D, A[2, 3] / sortie_D) # 2 heures en D, sortie par panne : 0.4
# Pi A = 0 et somme = 1M <- t(A)M[3, ] <- 1solve(M, c(0, 0, 1)) # 0.78125 0.15625 0.06250Exercice · Combien de temps pour oublier une panne ?
difficileOn reprend le serveur à deux états (, par heure). À l’instant 0, il vient de tomber en panne.
- Résolvez avec .
- Quelle est la probabilité qu’il fonctionne 2 heures plus tard ?
- Au bout de combien de temps est-elle à moins de 1 % de sa limite (en écart relatif) ?
Voir la solution
- La solution générale est . La condition impose , donc
-
. C’est bien moins que la disponibilité stationnaire : 2 heures après une panne, la réparation (de durée moyenne 2 heures) a une chance sur trois environ de ne pas être terminée.
-
L’écart relatif à la limite vaut . Il passe sous 1 % quand , soit heures environ. Au-delà, l’état initial n’a pratiquement plus d’influence.
En code
import numpy as npfrom scipy.linalg import expm
lam, mu = 0.02, 0.5A = np.array([[-lam, lam], [mu, -mu]])
p0 = np.array([0.0, 1.0]) # le serveur vient de tomber en panneprint((p0 @ expm(2 * A))[0]) # 0.6217 = P(marche à t = 2 h)print(mu / (lam + mu) * (1 - np.exp(-(lam + mu) * 2))) # 0.6217, formule fermée
t_oubli = np.log(100) / (lam + mu) # écart relatif e^{-(lam+mu)t} < 1 %print(t_oubli) # 8.86 heureslam <- 0.02; mu <- 0.5A <- matrix(c(-lam, lam, mu, -mu), nrow = 2, byrow = TRUE)
# exp(tA) par diagonalisation (A est diagonalisable)expm_diag <- function(M) { e <- eigen(M) Re(e$vectors %*% diag(exp(e$values)) %*% solve(e$vectors))}p0 <- c(0, 1) # le serveur vient de tomber en panne(p0 %*% expm_diag(2 * A))[1] # 0.6217 = P(marche à t = 2 h)mu / (lam + mu) * (1 - exp(-(lam + mu) * 2)) # 0.6217, formule fermée
log(100) / (lam + mu) # 8.86 heures pour un écart relatif < 1 %