-
Notifications
You must be signed in to change notification settings - Fork 2
/
protocol_units.py
287 lines (249 loc) · 8.48 KB
/
protocol_units.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
import numba as nb
import numpy as np
__all__ = [
"memory_cut_off", "fidelity_cut_off",
"get_one", "get_swap_wout",
"get_dist_prob_suc", "get_dist_prob_fail", "get_dist_prob_wout"
]
"""
This module contain the defined success probability and
resulting werner parameter of each protocol unit.
(see Table 1 in the article)
"""
########################################################################
"""
Success probability p and
the resulting Werner parameter of swap and distillation.
Parameters
----------
t1, t2: int
The waiting time of the two input links.
w1, w2: float
The Werner parameter of the two input links.
decay_factor: float
exp(- |t1 - t2| / t_coh)
Returns
-------
waiting_time: int
The time used for preparing this pair of input links with cut-off.
This time is different for a failing or successful attempt
result: bool
The result of the cut-off
"""
@nb.jit(nopython=True, error_model="numpy")
def get_one(t1, t2, w1, w2, decay_factor):
"""
Get a trivial one
"""
return 1.
@nb.jit(nopython=True, error_model="numpy")
def get_swap_wout(t1, t2, w1, w2, decay_factor):
"""
Get w_swap
"""
return w1 * w2 * decay_factor
@nb.jit(nopython=True, error_model="numpy")
def get_dist_prob_wout(t1, t2, w1, w2, decay_factor):
"""
Get p_dist * w_dist
"""
if t1 < t2:
w1 *= decay_factor
else:
w2 *= decay_factor
return 1./6. * (w1 + w2 + 4 * w1 * w2)
@nb.jit(nopython=True, error_model="numpy")
def get_dist_prob_fail(t1, t2, w1, w2, decay_factor):
"""
Get 1 - p_dist
"""
return 1. - get_dist_prob_suc(t1, t2, w1, w2, decay_factor)
@nb.jit(nopython=True, error_model="numpy")
def get_dist_prob_suc(t1, t2, w1, w2, decay_factor):
"""
Get p_dist
"""
return 0.5 + 0.5 * w1 * w2 * decay_factor
########################################################################
"""
Cut-off functions
Parameters
----------
t1, t2: int
The waiting time of the two input links.
w1, w2: float
The Werner parameter of the two input links.
mt_cut: int
The memory time cut-off.
w_cut: float
The Werner parameter cut-off. (0 < w_cut < 1)
Set a cut-off on the input links's Werner parameter
t_coh: int or float
The memory coherence time.
Returns
-------
waiting_time: int
The time used for preparing this pair of input links with cut-off.
This time is different for a failing or successful attempt
result: bool
The result of the cut-off
"""
@nb.jit(nopython=True, error_model="numpy")
def memory_cut_off(
t1, t2, w1=1.0, w2=1.0,
mt_cut=np.iinfo(np.int).max, w_cut=1.e-8, rt_cut=np.iinfo(np.int).max, t_coh=np.iinfo(np.int).max):
"""
Memory storage cut-off. The two input links suvives only if
|t1-t2|<=mt_cut
"""
if abs(t1 - t2) > mt_cut:
# constant shift mt_cut is added in the iterative convolution
return min(t1, t2), False
else:
return max(t1, t2), True
@nb.jit(nopython=True, error_model="numpy")
def fidelity_cut_off(
t1, t2, w1, w2,
mt_cut=np.iinfo(np.int).max, w_cut=1.e-8, rt_cut=np.iinfo(np.int).max, t_coh=np.iinfo(np.int).max):
"""
Fidelity-dependent cut-off, The two input links suvives only if
w1 <= w_cut and w2 <= w_cut including decoherence.
"""
if t1 == t2:
if w1 < w_cut or w2 < w_cut:
return t1, False
return t1, True
if t1 > t2: # make sure t1 < t2
t1, t2 = t2, t1
w1, w2 = w2, w1
# first link has low quality
if w1 < w_cut:
return t1, False # waiting_time = min(t1, t2)
waiting = np.int(np.floor(t_coh * np.log(w1/w_cut)))
# first link waits too long
if t1 + waiting < t2:
return t1 + waiting, False # min(t1, t2) < waiting_time < max(t1, t2)
# second link has low quality
elif w2 < w_cut:
return t2, False # waiting_time = max(t1, t2)
# both links are good
else:
return t2, True # waiting_time = max(t1, t2)
@nb.jit(nopython=True, error_model="numpy")
def run_time_cut_off(
t1, t2, w1, w2,
mt_cut=np.iinfo(np.int).max, w_cut=1.e-8, rt_cut=np.iinfo(np.int).max, t_coh=np.iinfo(np.int).max):
if t1 > rt_cut or t2 > rt_cut:
return rt_cut, False
else:
return max(t1, t2), True
@nb.jit(nopython=True, error_model="numpy")
def time_cut_off(
t1, t2, w1, w2,
mt_cut=np.iinfo(np.int).max, w_cut=1.e-8, rt_cut=np.iinfo(np.int).max, t_coh=np.iinfo(np.int).max):
waiting_time1, result1 = memory_cut_off(
t1, t2, w1, w2, mt_cut=mt_cut, w_cut=w_cut, rt_cut=rt_cut, t_coh=t_coh)
waiting_time2, result2 = run_time_cut_off(
t1, t2, w1, w2, mt_cut=mt_cut, w_cut=w_cut, rt_cut=rt_cut, t_coh=t_coh)
result1 += mt_cut
result2 += rt_cut
if result1 and result2:
return max(waiting_time1, waiting_time2), True
else:
# the waiting time of failing cutoff is always
# smaller than max(t1, t2), so we just need a min here.
return min(waiting_time1, waiting_time2), False
########################################################################
def join_links_compatible(
pmf1, pmf2, w_func1, w_func2,
cutoff=np.iinfo(np.int32).max, ycut=True,
cut_type="memory_time", evaluate_func=get_one, t_coh=np.inf):
"""
Calculate P_s and P_f.
Calculate sum_(t=tA+tB) Pr(TA=tA)*Pr(TB=tB)*f(tA, tB)
where f is the value function to
be evaluated for the joint distribution.
Note
----
For swap the success probability p is
considered in the iterative convolution.
For the memory time cut-off,
the constant shift is added in the iterative convolution.
Parameters
----------
pmf1, pmf2: array-like
The waiting time distribution of the two input links, Pr(T=t).
w_func1, w_func2: array-like
The Werner parameter function, W(t).
cutoff: int or float
The cut-off threshold.
ycut: bool
Success ful cut-off or failed cut-off.
cutoff_type: str
Type of cut-off.
`memory_time`, `run_time` or `fidelity`.
evaluate_func: str
The function to be evaluated the returns a float number.
It can be
``get_one`` for trival cases\n
``get_swap_wout`` for wout\n
``get_dist_prob_suc`` for pdist\n
``get_dist_prob_fail`` for 1-pdist\n
``get_dist_prob_wout`` for pdist * wdist
t_coh: int or float
The coherence time of the memory.
Returns
-------
result: array-like 1-D
The resulting array of joining the two links.
"""
mt_cut=np.iinfo(np.int32).max
w_cut=0.0
rt_cut=np.iinfo(np.int32).max
if cut_type == "memory_time":
cutoff_func = memory_cut_off
mt_cut = cutoff
elif cut_type == "fidelity":
cutoff_func = fidelity_cut_off
w_cut = cutoff
elif cut_type == "run_time":
cutoff_func = run_time_cut_off
rt_cut = cutoff
else:
raise NotImplementedError("Unknow cut-off type")
if evaluate_func == "1":
evaluate_func = get_one
elif evaluate_func == "w1w2":
evaluate_func = get_swap_wout
elif evaluate_func == "0.5+0.5w1w2":
evaluate_func = get_dist_prob_suc
elif evaluate_func == "0.5-0.5w1w2":
evaluate_func = get_dist_prob_fail
elif evaluate_func == "w1+w2+4w1w2":
evaluate_func = get_dist_prob_wout
elif isinstance(evaluate_func, str):
raise ValueError(evaluate_func)
result = join_links_helper(
pmf1, pmf2, w_func1, w_func2, cutoff_func=cutoff_func, evaluate_func=evaluate_func, ycut=ycut, t_coh=t_coh,
mt_cut=mt_cut, w_cut=w_cut, rt_cut=rt_cut)
return result
@nb.jit(nopython=True, error_model="numpy")
def join_links_helper(
pmf1, pmf2, w_func1, w_func2,
cutoff_func=memory_cut_off, evaluate_func=get_one, ycut=True, t_coh=np.inf, mt_cut=np.iinfo(np.int32).max, w_cut=0.0, rt_cut=np.iinfo(np.int32).max):
size = len(pmf1)
result = np.zeros(size, dtype=np.float64)
decay_factors = np.exp(- np.arange(size) / t_coh)
for t1 in range(1, size):
for t2 in range(1, size):
waiting_time, selection_pass = cutoff_func(
t1, t2, w_func1[t1], w_func2[t2],
mt_cut, w_cut, rt_cut, t_coh)
if not ycut:
selection_pass = not selection_pass
if selection_pass:
result[waiting_time] += pmf1[t1] * pmf2[t2] * \
evaluate_func(
t1, t2, w_func1[t1], w_func2[t2],
decay_factors[np.abs(t1-t2)])
return result