@@ -22,6 +22,18 @@ def reliability_data(responses: np.ndarray) -> np.ndarray:
22
22
where :math:`\text{eval}(x)` is the response of the (noisy) PUF when evaluated on input :math:`x`, and the
23
23
probability is taken over the noise in the evaluation process.
24
24
25
+ The approximation is derived from an approximation of :math:`E_\text{eval} \left[ \text{eval}(x) \right]`,
26
+ which is given by the mean of ``responses``,
27
+
28
+ .. math:: \Pr_\text{eval} \left[ \text{eval}(x) = \text{eval}(x) \right] =
29
+ \frac{1}{2} E_\text{eval} \left[ \text{eval}(x) \right]^2 + \frac{1}{2}.
30
+
31
+ The formula can be obtained by observing
32
+ :math:`\Pr_\text{eval} \left[ \text{eval}(x) = \text{eval}(x) \right] =
33
+ \Pr\left[\text{eval}(x)=1\right]^2 + \Pr\left[\text{eval}(x)=-1\right]^2` and
34
+ :math:`\Pr_\text{eval}\left[\text{eval}(x)=1\right] = \frac{1}{2}E_\text{eval}\left[\text{eval}(x)\right]
35
+ + \frac{1}{2}`.
36
+
25
37
An array of shape :math:`(N,m)` is returned, estimating above probability for each challenge and each response bit.
26
38
To obtain a the reliability for each response bit, average along the first axis, to obtain the general reliability,
27
39
average over all axes.
@@ -30,9 +42,11 @@ def reliability_data(responses: np.ndarray) -> np.ndarray:
30
42
>>> from numpy import average, array
31
43
>>> responses = array([[[1, 1, -1]], [[1, 1, 1]], [[1, 1, 1]], [[1, 1, 1]]])
32
44
>>> average(reliability_data(responses), axis=0)
33
- array([0.83333333 ])
45
+ array([0.88888889 ])
34
46
"""
35
- return np .absolute (np .average (responses , axis = - 1 ))
47
+ m = np .average (responses , axis = - 1 ) # approximate E[eval(x)]
48
+ f1 = (m + 1 ) / 2 # P[eval(x) = 1] = 1/2 E[eval(x)] + 1/2
49
+ return f1 ** 2 + (1 - f1 )** 2 # P[eval(x) = eval(x)] = P[eval(x) = 1]**2 + P[eval(x) = -1]**2
36
50
37
51
38
52
def reliability (instance : Simulation , seed : int , N : int = 10000 , r : int = 17 ) -> np .ndarray :
@@ -50,12 +64,28 @@ def reliability(instance: Simulation, seed: int, N: int = 10000, r: int = 17) ->
50
64
reliability of this instance, average across all axes. Note that bad reliability on single response bits may be
51
65
problematic.
52
66
53
- >>> from pypuf.simulation import XORArbiterPUF
67
+ >>> from pypuf.simulation import ArbiterPUF, XORArbiterPUF
54
68
>>> from pypuf.metrics import reliability
55
69
>>> from numpy import average
56
70
>>> puf = XORArbiterPUF(n=128, k=2, seed=1, noisiness=.2)
57
71
>>> average(reliability(puf, seed=2), axis=0)
58
- array([0.77729412])
72
+ array([0.84887197])
73
+
74
+ An example of an extremely noisy PUF:
75
+
76
+ >>> average(reliability(XORArbiterPUF(n=32, k=12, seed=1, noisiness=1), seed=2), axis=0)
77
+ array([0.52879308])
78
+
79
+
80
+ An example of a very reliable PUF:
81
+
82
+ >>> average(reliability(ArbiterPUF(n=32, seed=1, noisiness=.01), seed=2), axis=0)
83
+ array([0.99576886])
84
+
85
+ An example of a perfectly reliable PUF:
86
+
87
+ >>> average(reliability(ArbiterPUF(n=64, seed=1, noisiness=0), seed=2), axis=0)
88
+ array([1.])
59
89
"""
60
90
inputs = random_inputs (n = instance .challenge_length , N = N , seed = seed )
61
91
return np .absolute (reliability_data (instance .r_eval (r , inputs )))
0 commit comments