Source code for spistats.desynchronization.law
"""This module defnies an interface to the probability law of the number of packet sent by a device befor desynchronization when using the sequential pseudonym scheme."""
import math
import numpy as np
[docs]
class Law:
"""Probability law of the number of packet sent by a device dbefore desynchronization.
"""
[docs]
def __init__(self, p, m):
"""
:param p: Probability to lose connection.
:type p: float in [0,1]
:param m: Number of pseudonyms maintained by the device.
:type m: int
"""
self.p = p
self.q = 1-p
self.a = (1-p)*p**m
self.m = m
self.eigen_computed = False
[docs]
def eigenvalues(self):
"""Computes the eigenvalues to solve the recursive sequence."""
p = self.p
q = self.q
a = self.a
m = self.m
pol_char = np.zeros(m+2).astype(float)
pol_char[0] = 1
pol_char[1] = -1
pol_char[-1] = a
roots = np.roots(pol_char)
roots = roots.reshape(m+1)
self.l = roots
self.eigen_computed = True
#Finding initial conditions
u = np.zeros(m+1).astype(float)
for n in range(m+1):
u[n] = 1-p**m-n*(1-p)*p**m
I = u[:m+1].reshape(-1,1)
L = np.zeros([m+1,m+1]).astype(np.complex_)
for i in range(m+1):
L[i,:] = roots**(i)
try:
self.c = np.matmul(np.linalg.inv(L),I).reshape(m+1)
except:
self.c = float("nan")
print("pas invers")
[docs]
def mass(self, N):
"""Mass function of the number of packet before desynchronization.
In other words return P(desynchronization of N packets sent).
:param N: Number of packets.
:type N: int
:return: P(desynchronization of N packets sent)
:rtype: float in [0,1]
"""
m = self.m
p = self.p
if N<m:
return 0
elif N==m:
return p**m
elif N<=2*m:
return (1-p)*p**m
else:
return np.real(np.sum(self.c*self.l**(N-2*m-1))*(1-p)*p**m)
[docs]
def cdf(self, N):
"""Cumulative distribution function of the number of packet before desynchronization.
In other words return P(send less than N packets beforedesynchronization).
:param N: number of packets.
:type N: int
:return: P(send less than N packets beforedesynchronization).
:rtype: float in [0,1]
"""
if not(self.eigen_computed):
self.eigenvalues()
p = self.p
q = self.q
a = self.a
m = self.m
l = self.l
c = self.c
"""P(D<=N)"""
if N<m:
return 0
elif N<=2*m:
return p**m+(N-m)*(1-p)*p**m
else:
un_sum = np.sum(c*(1-l**(N-2*m))/(1-l))
return np.real(p**m+m*(1-p)*p**m + (1-p)*p**m*un_sum)
[docs]
def cdf_multin(self, N):
"""N : nombre de messages"""
P = np.zeros_like(N).astype(float)
for ni,n in enumerate(N):
P[ni] = self.cdf(n)
return np.mean(P)
[docs]
def cdf_position(self):
"""Return the position of the cumulative distribution function.
:return: (start, end) such that the cumulative distribution function is greater than 0.01 after start and less than 0.99 after end.
:rtype: tuple of floats
"""
self.eigenvalues()
p = self.p
q = self.q
a = self.a
m = self.m
l = self.l
c = self.c
#cdf start point
s = 0.01
if self.cdf(m)<0.01:
start = m
else:
x0=2*m+1
x1 = x0
#for i in range(1,niter):
#while (np.abs(f(x0,p,m,l,c,s))>1):
while (np.abs(self.cdf(x0)-s)>1):
left = (s-p**m-m*q*p**m)/(q*p**m)
f = np.real(np.sum(c*(1-l**(x0-2*m))/(1-l))) - left
fp = (-1)*np.real(np.sum(c*(l**(x0-2*m)*np.log(l))/(1-l)))
x1 = x0 - f/fp
x0 = x1
start = x1
#cdf end point
s = 0.99
x0=2*m+1
x1 = x0
i = 0
while (np.abs(self.cdf(x1)-s)>0.005):
for i in range(1000):
left = (s-p**m-m*q*p**m)/(q*p**m)
f = np.real(np.sum(c*(1-l**(x0-2*m))/(1-l))) - left
fp = (-1)*np.real(np.sum(c*(l**(x0-2*m)*np.log(l))/(1-l)))
x1 = x0 - f/fp
x0 = x1
i += 1
if math.isnan(x1):
x0 = 2*m+1
x1 = x0
s = s - 0.1*s
elif (np.abs(self.cdf(x1)-s)>0.005):
s = s - 0.1*s
end = x1
return start, end
[docs]
def cdf_inv(self, s):
"""Inverse cumulative distribution function.
:param s: Probability that the number of packet sent before desynchronization is at most N
:type s: float in [0,1]
:return: Return N such that P(send less than N packets beforedesynchronization) = N.
:rtype: int
"""
m = self.m
p = self.p
q = 1-p
c = self.c
l = self.l
x0=2*m+1
x1 = x0
i = 0
while (np.abs(self.cdf(x1)-s)>0.005):
left = (s-p**m-m*q*p**m)/(q*p**m)
f = np.real(np.sum(c*(1-l**(x0-2*m))/(1-l))) - left
fp = (-1)*np.real(np.sum(c*(l**(x0-2*m)*np.log(l))/(1-l)))
x1 = x0 - f/fp
x0 = x1
#i += 1
return x1
[docs]
def mean(self):
"""Expectation of the number of packet sent before desynchronization computed using.
:return: E(D) where D is the number of packet sent before desynchronization.
:rtype: int
"""
m = self.m
p = self.p
q = 1-p
E = 0
E += m*p**m + q*p**m*(3*m**2+m)/2
E += q*p**m*(np.sum(self.c*self.l/(1-self.l)**2)+(2*m+1)*np.sum(self.c/(1-self.l)))
return np.real(E)