Aller au contenu

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 PP 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é.

Commençons par la durée de fonctionnement du serveur avant la panne, notée DD. En fiabilité, on la modélise très souvent par une loi exponentielle de paramètre λ>0\lambda > 0, notée E(λ)\mathcal{E}(\lambda) :

f(t)=λ e−λt,P(D>t)=e−λt,E[D]=1λ(t≥0)f(t) = \lambda\, e^{-\lambda t}, \qquad P(D > t) = e^{-\lambda t}, \qquad E[D] = \frac{1}{\lambda} \qquad (t \geq 0)
  • ff est la densité de DD ;
  • P(D>t)P(D > t) est la probabilité que le serveur fonctionne encore à l’instant tt (on l’appelle la fonction de survie) ;
  • 1/λ1/\lambda est la durée moyenne de fonctionnement, et λ\lambda s’interprète comme un taux : un nombre moyen de pannes par unité de temps de fonctionnement.

Pour notre serveur, prenons λ=0,02\lambda = 0{,}02 panne par heure, soit une durée moyenne de fonctionnement de 50 heures. La probabilité qu’il tienne plus de 100 heures vaut e−0,02×100=e−2≈0,135e^{-0{,}02 \times 100} = e^{-2} \approx 0{,}135.

050100150200250t (heures)0,00,20,40,60,81,0probabilité de fonctionner encoredurée moyenne 1/λ = 50 hP(D>50)=e≈0,37−1P(D>100)=e≈0,135−2P(D>t)=e−0,02t
La survie du serveur décroît exponentiellement. À la durée moyenne de 50 h, il ne reste que 37 % de serveurs en marche : la majorité tombe en panne avant la moyenne. À 100 h, il en reste 13,5 %.

La propriété essentielle de cette loi est d’être sans mémoire :

P(D>t+u∣D>u)=P(D>t)P(D > t + u \mid D > u) = P(D > t)

Sachant que le serveur fonctionne depuis uu heures, la probabilité qu’il tienne encore tt 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 e−1≈0,37e^{-1} \approx 0{,}37. 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.

050100150200250t (heures)0,00,20,40,60,81,01,2probabilité de survieu = 80 h50 h50 h de plusmêmeprobabilité:e≈0,37−1serveur neuf : P(D > t)sachant qu’il tourne depuis 80 h : P(D > t | D > 80)
Sachant que le serveur a tenu 80 h, sa courbe de survie repart de 1 avec exactement la même forme : tenir 50 h de plus a la même probabilité, 0,37, que pour un serveur neuf.

Deux propriétés de calcul serviront tout au long de la partie. Si D1∼E(λ1)D_1 \sim \mathcal E(\lambda_1) et D2∼E(λ2)D_2 \sim \mathcal E(\lambda_2) sont indépendantes, alors :

  • le premier des deux événements survient après un temps min⁡(D1,D2)∼E(λ1+λ2)\min(D_1, D_2) \sim \mathcal E(\lambda_1 + \lambda_2) : les taux s’additionnent ;
  • c’est le premier qui gagne la course avec probabilité λ1/(λ1+λ2)\lambda_1 / (\lambda_1 + \lambda_2).

Une chaîne de Markov à temps continu est un processus (Xt)t≥0(X_t)_{t \geq 0} à valeurs dans un espace discret EE qui vérifie la propriété de Markov à tous les instants : pour tous instants t0<t1<⋯<tnt_0 < t_1 < \dots < t_n,

P(Xtn=in∣Xtn−1=in−1,…,Xt0=i0)=P(Xtn=in∣Xtn−1=in−1).P(X_{t_n} = i_n \mid X_{t_{n-1}} = i_{n-1}, \dots, X_{t_0} = i_0) = P(X_{t_n} = i_n \mid X_{t_{n-1}} = i_{n-1}).

On la suppose homogène : pij(t)=P(Xs+t=j∣Xs=i)p_{ij}(t) = P(X_{s+t} = j \mid X_s = i) ne dépend que de la durée tt, pas de ss. On range ces probabilités dans la matrice G(t)=(pij(t))G(t) = (p_{ij}(t)), qui joue le rôle de PnP^n. Tout ce que vous connaissez se transpose :

Temps discretTemps continu
p(n)=p(0) Pnp(n) = p(0)\,P^np(t)=p(0) G(t)p(t) = p(0)\,G(t)
Pn+m=Pn PmP^{n+m} = P^n\,P^mG(t+s)=G(t) G(s)G(t+s) = G(t)\,G(s) (Chapman-Kolmogorov)
P0=IP^0 = IG(0)=IG(0) = I

Mais il n’y a plus de « pas élémentaire » : comment décrire la chaîne par un seul objet, comme PP en temps discret ? En regardant ce qui se passe sur un intervalle de temps infiniment court.

Sur un petit intervalle de durée hh, un serveur en marche tombe en panne avec une probabilité à peu près proportionnelle à hh : p01(h)≈λhp_{01}(h) \approx \lambda h. C’est cette proportionnalité qu’on formalise. Pour i≠ji \neq j :

pij(h)=λij h+o(h),pii(h)=1+λii h+o(h)p_{ij}(h) = \lambda_{ij}\, h + o(h), \qquad p_{ii}(h) = 1 + \lambda_{ii}\, h + o(h)
  • λij≥0\lambda_{ij} \geq 0 est le taux de transition de ii vers jj ;
  • λii=−∑j≠iλij≤0\lambda_{ii} = -\sum_{j \neq i} \lambda_{ij} \leq 0, et −λii-\lambda_{ii} est le taux de sortie de l’état ii ;
  • o(h)o(h) désigne un terme négligeable devant hh quand h→0h \to 0.

La matrice A=(λij)A = (\lambda_{ij}) est le générateur infinitésimal de la chaîne. C’est la dérivée de GG en 0 :

A=G′(0)=lim⁡h→0+G(h)−Ih.A = G'(0) = \lim_{h \to 0^+} \frac{G(h) - I}{h}.

Comme chaque ligne de G(h)G(h) somme à 1 et chaque ligne de II aussi, chaque ligne de AA somme à 0. Pour notre serveur, avec l’état 0 = marche et 1 = panne, un taux de panne λ\lambda et un taux de réparation μ\mu :

A=(−λλμ−μ).A = \begin{pmatrix} -\lambda & \lambda \\ \mu & -\mu \end{pmatrix}.

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 ii, elle reste un temps de loi E(−λii)\mathcal E(-\lambda_{ii}), puis elle saute vers j≠ij \neq i avec probabilité λij/(−λii)\lambda_{ij} / (-\lambda_{ii}). C’est exactement une course d’horloges exponentielles, une par transition possible, dont la première qui sonne décide du saut.

marchepanneλ = 0,02μ = 0,50100200300400t (heures)pannemarcheséjours en marche : exponentiels, 50 h en moyennepannes : exponentielles, 2 h en moyenne
Le graphe porte des taux, pas des probabilités. Une trajectoire simulée alterne de longs séjours en marche (50 h en moyenne) et de brèves pannes (2 h en moyenne), chaque durée étant exponentielle.

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 G(t+s)=G(t) G(s)G(t + s) = G(t)\,G(s) par rapport à ss, puis par rapport à tt, et prenons la dérivée en 0. On obtient les équations de Kolmogorov :

G′(t)=A G(t)(eˊquation arrieˋre),G′(t)=G(t) A(eˊquation avant).G'(t) = A\, G(t) \quad \text{(équation arrière)}, \qquad G'(t) = G(t)\, A \quad \text{(équation avant)}.

En multipliant l’équation avant à gauche par p(0)p(0), on obtient l’équation d’évolution des probabilités d’état :

p′(t)=p(t) A.p'(t) = p(t)\, A.

Pour un espace d’états fini, ce système différentiel linéaire a pour solution G(t)=etAG(t) = e^{tA}, l’exponentielle de matrice. C’est l’analogue continu de PnP^n.

0,02,55,07,510,012,5t (heures)0,00,20,40,60,81,0p₀(t) = P(marche à t)marche à t = 0panne à t = 0Π₀ ≈ 0,96150,02,55,07,510,012,5t (heures)0,960,970,980,991,00t = 2 h : 0,975régime stationnaire 0,9615zoom, départ en marche
Quel que soit l'état de départ, p₀(t) rejoint la disponibilité 0,9615 en une dizaine d'heures, à la vitesse e^(−0,52 t). Le zoom montre la valeur transitoire 0,975 à t = 2 h, encore marquée par le départ en marche.

Pour la classification des états, rien ne change par rapport à la partie 1 : accessibilité, classes, irréductibilité et états absorbants (λij=0\lambda_{ij} = 0 pour tout j≠ij \neq i) 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.

À long terme, p(t)p(t) se stabilise et sa dérivée s’annule. Comme p′(t)=p(t) Ap'(t) = p(t)\,A, la limite Π\Pi doit vérifier ΠA=0\Pi A = 0.

Chaque équation de ΠA=0\Pi A = 0 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.

Πj∑k≠jλjk=∑i≠jΠi λij\Pi_j \sum_{k \neq j} \lambda_{jk} = \sum_{i \neq j} \Pi_i\, \lambda_{ij}

À gauche, la probabilité d’être en jj multipliée par le taux de sortie ; à droite, la somme des flux venant des autres états.

Le code suivant calcule G(t)=etAG(t) = e^{tA} avec SciPy, résout ΠA=0\Pi A = 0, et simule la chaîne par sa description « séjour exponentiel puis saut ».

import numpy as np
from 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 = 1
M = A.T.copy()
M[-1, :] = 1
pi = np.linalg.solve(M, [0, 1])
print(pi) # ≈ [0.9615 0.0385]
# Simulation : séjour exponentiel dans l'état courant, puis saut
rng = np.random.default_rng(0)
t, x, temps_marche, horizon = 0.0, 0, 0.0, 1_000_000
while 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'autre
print(temps_marche / horizon) # proche de 0.9615
  • La loi exponentielle E(λ)\mathcal E(\lambda) a pour moyenne 1/λ1/\lambda et vérifie P(D>t)=e−λtP(D > t) = e^{-\lambda t} ; 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 p(t)=p(0) G(t)p(t) = p(0)\,G(t) et G(t+s)=G(t) G(s)G(t+s) = G(t)\,G(s).
  • Le générateur A=G′(0)A = G'(0) contient les taux λij\lambda_{ij} ; ses lignes somment à 0. On séjourne en ii un temps E(−λii)\mathcal E(-\lambda_{ii}), puis on saute vers jj avec probabilité λij/(−λii)\lambda_{ij}/(-\lambda_{ii}).
  • Équations de Kolmogorov : G′(t)=A G(t)=G(t) AG'(t) = A\,G(t) = G(t)\,A, d’où p′(t)=p(t) Ap'(t) = p(t)\,A et G(t)=etAG(t) = e^{tA}.
  • Pas de période en temps continu. Si la chaîne est irréductible (et récurrente positive), Π\Pi est l’unique solution de ΠA=0\Pi A = 0, ∑Πj=1\sum \Pi_j = 1 : flux sortant = flux entrant.
  • Disponibilité d’un équipement : μ/(λ+μ)\mu / (\lambda + \mu) = MTTF / (MTTF + MTTR).

Exercice · Une ampoule sans mémoire

facile

La durée de vie d’une LED suit une loi exponentielle de moyenne 1 000 heures.

  1. Quelle est la probabilité qu’elle dure plus de 1 000 heures ?
  2. Elle fonctionne depuis 800 heures. Quelle est la probabilité qu’elle tienne encore 500 heures ?
Voir la solution

Le taux vaut λ=1/1000\lambda = 1/1000 par heure.

  1. P(D>1000)=e−1≈0,368P(D > 1000) = e^{-1} \approx 0{,}368. 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.
  2. Par l’absence de mémoire, P(D>1300∣D>800)=P(D>500)=e−0,5≈0,607P(D > 1300 \mid D > 800) = P(D > 500) = e^{-0{,}5} \approx 0{,}607. Les 800 heures déjà écoulées ne changent rien.

En code

import numpy as np
from scipy.stats import expon
duree = expon(scale=1000) # SciPy attend la moyenne 1/lambda
print(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 simulation
rng = np.random.default_rng(0)
d = rng.exponential(1000, 1_000_000)
print((d > 1000).mean(), (d[d > 800] > 1300).mean()) # 0.3677 0.6065

Exercice · Un serveur qui se dégrade

moyen

On 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.

  1. Écrivez le générateur AA et vérifiez ses lignes.
  2. Combien de temps un serveur reste-t-il dégradé en moyenne ? Avec quelle probabilité quitte-t-il cet état par une panne ?
  3. Calculez la loi stationnaire. Quelle proportion du temps le serveur est-il en panne ?
Voir la solution
  1. Dans l’ordre M, D, P :
A=(−0,10,100,3−0,50,20,50−0,5)A = \begin{pmatrix} -0{,}1 & 0{,}1 & 0 \\ 0{,}3 & -0{,}5 & 0{,}2 \\ 0{,}5 & 0 & -0{,}5 \end{pmatrix}

Chaque ligne somme bien à 0.

  1. Le taux de sortie de D vaut 0,3+0,2=0,50{,}3 + 0{,}2 = 0{,}5, donc le séjour moyen est de 1/0,5=21/0{,}5 = 2 heures. La sortie se fait par une panne avec probabilité 0,2/0,5=0,40{,}2/0{,}5 = 0{,}4.

  2. Balances globales : pour D, 0,5 ΠD=0,1 ΠM0{,}5\,\Pi_D = 0{,}1\,\Pi_M, donc ΠD=0,2 ΠM\Pi_D = 0{,}2\,\Pi_M ; pour P, 0,5 ΠP=0,2 ΠD0{,}5\,\Pi_P = 0{,}2\,\Pi_D, donc ΠP=0,08 ΠM\Pi_P = 0{,}08\,\Pi_M. On vérifie la balance de M : 0,1 ΠM=0,3×0,2 ΠM+0,5×0,08 ΠM=0,1 ΠM0{,}1\,\Pi_M = 0{,}3 \times 0{,}2\,\Pi_M + 0{,}5 \times 0{,}08\,\Pi_M = 0{,}1\,\Pi_M. La normalisation donne ΠM (1+0,2+0,08)=1\Pi_M\,(1 + 0{,}2 + 0{,}08) = 1, soit

Π=(0,78125 ; 0,15625 ; 0,0625).\Pi = (0{,}78125 \,;\, 0{,}15625 \,;\, 0{,}0625).

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 = 1
M = A.T.copy()
M[-1, :] = 1
pi = np.linalg.solve(M, [0, 0, 1])
print(pi) # [0.78125 0.15625 0.0625 ]

Exercice · Combien de temps pour oublier une panne ?

difficile

On reprend le serveur à deux états (λ=0,02\lambda = 0{,}02, μ=0,5\mu = 0{,}5 par heure). À l’instant 0, il vient de tomber en panne.

  1. Résolvez p0′(t)=−(λ+μ) p0(t)+μp_0'(t) = -(\lambda + \mu)\,p_0(t) + \mu avec p0(0)=0p_0(0) = 0.
  2. Quelle est la probabilité qu’il fonctionne 2 heures plus tard ?
  3. Au bout de combien de temps p0(t)p_0(t) est-elle à moins de 1 % de sa limite (en écart relatif) ?
Voir la solution
  1. La solution générale est p0(t)=μλ+μ+C e−(λ+μ)tp_0(t) = \dfrac{\mu}{\lambda + \mu} + C\,e^{-(\lambda + \mu)t}. La condition p0(0)=0p_0(0) = 0 impose C=−μλ+μC = -\dfrac{\mu}{\lambda + \mu}, donc
p0(t)=μλ+μ(1−e−(λ+μ)t).p_0(t) = \frac{\mu}{\lambda + \mu}\left(1 - e^{-(\lambda + \mu)t}\right).
  1. p0(2)=0,9615×(1−e−1,04)≈0,9615×0,6465≈0,622p_0(2) = 0{,}9615 \times (1 - e^{-1{,}04}) \approx 0{,}9615 \times 0{,}6465 \approx 0{,}622. 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.

  2. L’écart relatif à la limite vaut e−(λ+μ)te^{-(\lambda + \mu)t}. Il passe sous 1 % quand 0,52 t>ln⁡100≈4,6050{,}52\,t > \ln 100 \approx 4{,}605, soit t>8,86t > 8{,}86 heures environ. Au-delà, l’état initial n’a pratiquement plus d’influence.

En code

import numpy as np
from scipy.linalg import expm
lam, mu = 0.02, 0.5
A = np.array([[-lam, lam],
[mu, -mu]])
p0 = np.array([0.0, 1.0]) # le serveur vient de tomber en panne
print((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 heures