Skip to content

Commit 3772153

Browse files
author
Axel Dahlberg
committed
New FEU is now updated with tests
1 parent e137892 commit 3772153

File tree

2 files changed

+71
-137
lines changed

2 files changed

+71
-137
lines changed

qlinklayer/feu.py

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,6 @@ def select_bright_state(self, min_fidelity):
113113

114114
return None
115115

116-
def recalculate_estimate(self):
117-
"""
118-
Recalculates and updates the stored fidelity estimate
119-
:return: float
120-
The recalculated fidelity estimate
121-
"""
122-
self.estimated_fidelity = self._estimate_fidelity()
123-
return self.estimated_fidelity
124-
125116
def _estimate_fidelity(self, alphaA=None, alphaB=None):
126117
"""
127118
Calculates the fidelity by extracting parameters from the mhp components, calculating an estimated state of
@@ -139,7 +130,7 @@ def _compute_total_detection_probabilities(self):
139130
what is the probability that a detector clicks at the midpoint.
140131
:return: tuple (p_det_A, p_det_B)
141132
"""
142-
mhp_conn = self.mhp_service.conn
133+
mhp_conn = self.mhp_service.get_mhp_conn(self.node)
143134
total_det_effA = self._compute_total_detection_probability_of_node(mhp_conn.nodeA)
144135
total_det_effB = self._compute_total_detection_probability_of_node(mhp_conn.nodeB)
145136
return total_det_effA, total_det_effB
@@ -152,13 +143,14 @@ def _compute_total_detection_probability_of_node(self, node):
152143
"""
153144
p_zero_phonon = node.qmem.photon_emission_noise.p_zero_phonon
154145
collection_eff = node.qmem.photon_emission_noise.collection_eff
155-
if node == self.mhp_service.conn.nodeA:
156-
p_loss = self.mhp_service.get_fibre_transmissivities(node)[0]
146+
mhp_conn = self.mhp_service.get_mhp_conn(node)
147+
if node == mhp_conn.nodeA:
148+
p_no_loss = self.mhp_service.get_fibre_transmissivities(node)[0]
157149
else:
158-
p_loss = self.mhp_service.get_fibre_transmissivities(node)[1]
159-
detection_eff = self.mhp_service.conn.midpoint.detection_eff
150+
p_no_loss = self.mhp_service.get_fibre_transmissivities(node)[1]
151+
detection_eff = mhp_conn.midPoint.detection_eff
160152

161-
total_detection_eff = p_zero_phonon * collection_eff * (1 - p_loss) * detection_eff
153+
total_detection_eff = p_zero_phonon * collection_eff * p_no_loss * detection_eff
162154

163155
return total_detection_eff
164156

@@ -179,7 +171,8 @@ def _compute_conditional_detection_probabilities(self, alphaA=None, alphaB=None)
179171
alphaB = alphaA
180172

181173
p_det_A, p_det_B = self._compute_total_detection_probabilities()
182-
p_dc = self.mhp_service.conn.midpoint.pdark
174+
mhp_conn = self.mhp_service.get_mhp_conn(self.node)
175+
p_dc = mhp_conn.midPoint.pdark
183176
# Dark count probabilities for both detectors
184177
p_no_dc = (1 - p_dc)**2
185178
p_at_least_one_dc = 1 - p_no_dc
@@ -208,27 +201,6 @@ def _estimate_success_probability(self, alphaA=None, alphaB=None):
208201
"""
209202
return sum(self._compute_conditional_detection_probabilities(alphaA, alphaB))
210203

211-
def _extract_params(self):
212-
"""
213-
Extracts parameters for fidelity estimation from the provided hardware such as midpoint detectors, fibre
214-
properties, node state preparation, etc.
215-
:return: Parameters to use for fidelity estimation
216-
"""
217-
# Get the transmissivity info from the service
218-
etaA, etaB = self.mhp_service.get_fibre_transmissivities(self.node)
219-
220-
# Get bright state info from the service (states are prepared in sqrt(1-alpha)|0> + sqrt(alpha)|1> so the
221-
# probability of emission is simply alpha for both end nodes
222-
alphaA, alphaB = self.mhp_service.get_bright_state_populations(self.node)
223-
224-
# Get the probability of a dark count at the midpoint
225-
pdark = self.mhp_service.calculate_dark_count_probability(self.node)
226-
227-
# Get the dephasing photon parameter from the quantum memory device at our node
228-
dp_photon = (1 - getattr(self.node.qmem.photon_emission_noise, '_dp_photon', 0))
229-
230-
return etaA, etaB, alphaA, alphaB, pdark, dp_photon
231-
232204
def _calculate_estimated_state(self, alphaA=None, alphaB=None):
233205
"""
234206
Calculates an estimated state based on the provided parameters. See eq (8) in
@@ -237,11 +209,12 @@ def _calculate_estimated_state(self, alphaA=None, alphaB=None):
237209
:return: obj `~numpy.matrix`
238210
A density matrix of the estimated entangled state
239211
"""
240-
if self._estimate_success_probability(alphaA, alphaB) == 0:
241-
raise LinkLayerException("Cannot estimate state when success probability is zero")
242212
p_uu, p_ud, p_du, p_dd = self._compute_conditional_detection_probabilities(alphaA, alphaB)
213+
if (p_uu + p_ud + p_du + p_dd) == 0:
214+
raise LinkLayerException("Cannot estimate state when success probability is zero")
243215

244-
visibility = self.mhp_service.conn.midpoint.visibility
216+
mhp_conn = self.mhp_service.get_mhp_conn(self.node)
217+
visibility = mhp_conn.midPoint.visibility
245218

246219
ent_state = (p_ud * dm10 + # |10><10| part
247220
p_du * dm01 + # |01><01| part

tests/test_feu.py

Lines changed: 58 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@
1313

1414

1515
class TestSingleClickFidelityEstimationUnit(unittest.TestCase):
16-
def create_nodes(self, alice_device_positions, bob_device_positions):
17-
# Set up Alice
18-
aliceMemory = NVCommunicationDevice(name="AliceMem", num_positions=alice_device_positions)
19-
alice = QuantumNode(name="Alice", nodeID=1, memDevice=aliceMemory)
20-
21-
# Set up Bob
22-
bobMemory = NVCommunicationDevice(name="BobMem", num_positions=bob_device_positions)
23-
bob = QuantumNode(name="Bob", nodeID=2, memDevice=bobMemory)
24-
25-
return alice, bob
16+
def setUp(self):
17+
# Setup MHP network and MHP service
18+
network = setup_physical_network(ConfigPathStorage.NETWORK_NV_LAB_NOCAV_NOCONV)
19+
alice = network.get_node_by_id(0)
20+
bob = network.get_node_by_id(1)
21+
self.mhp_conn = network.get_connection(alice, bob, "mhp_conn")
22+
mhp_service = SimulatedNodeCentricMHPService("mhp_service", alice, bob, conn=self.mhp_conn)
23+
self.feuA = SingleClickFidelityEstimationUnit(alice, mhp_service)
24+
self.feuB = SingleClickFidelityEstimationUnit(alice, mhp_service)
2625

2726
def test_dm_fidelity(self):
2827
# Basic test cases using bell states
@@ -52,103 +51,65 @@ def test_dm_fidelity(self):
5251
self.assertEqual(round(dm_fidelity(dm11, dm11, squared=True), ndigits=8), 1)
5352

5453
def test_success_prob_est(self):
55-
# Setup MHP network and MHP service
56-
network = setup_physical_network(ConfigPathStorage.NETWORK_NV_LAB_NOCAV_NOCONV)
57-
alice = network.get_node_by_id(0)
58-
bob = network.get_node_by_id(1)
59-
mhp_conn = network.get_connection(alice, bob, "mhp_conn")
60-
mhp_service = SimulatedNodeCentricMHPService("mhp_service", alice, bob, conn=mhp_conn)
61-
feu = SingleClickFidelityEstimationUnit(alice, mhp_service)
6254

63-
print(feu._estimate_success_probability())
55+
with self.assertRaises(LinkLayerException):
56+
self.feuA._estimate_success_probability()
57+
58+
# Should evaluate to total detection probability
59+
p_succ = self.feuA._estimate_success_probability(1, 0)
60+
self.assertAlmostEqual(p_succ, 4e-4, places=4)
61+
62+
# Test zero alpha
63+
p_succ = self.feuA._estimate_success_probability(0)
64+
self.assertAlmostEqual(p_succ, 0, places=4)
65+
66+
# Test linearity in alpha
67+
for i in range(11):
68+
alpha = i/20
69+
p_succ = self.feuA._estimate_success_probability(alpha)
70+
self.assertAlmostEqual(p_succ, 2 * alpha * (4e-4), places=4)
6471

6572
def test_fidelity_estimation(self):
66-
# Check various parameters against the estimation
67-
etaA = 1 # Transmitivity of fibre from A to midpoint
68-
etaB = 1 # Transmitivity of fibre from B to midpoint
69-
alphaA = 0.5 # Bright state population at A
70-
alphaB = 0.5 # Bright state population at B
71-
pdark = 0 # Probability of a dark count at the midpoint
72-
dp_photon = 1 # Dephasing parameter of the entangled qubit
73-
ideal_state = kron(b01.H, b01) # (|01> + |10>)(<01| + <10|)
74-
75-
# Check a lossless scenario with bright state populations of 1/2
76-
estimated_state = SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA, etaB, alphaA, alphaB,
77-
pdark, dp_photon)
78-
79-
estimated_fidelity = dm_fidelity(estimated_state, ideal_state, squared=True)
80-
self.assertTrue(isclose(estimated_fidelity, 2 / 3))
81-
82-
# Check a lossless scenario with a dark count probability of 1/2
83-
pdark = 0.5
84-
estimated_state = SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA, etaB, alphaA, alphaB,
85-
pdark, dp_photon)
86-
87-
estimated_fidelity = dm_fidelity(ideal_state, estimated_state, squared=True)
88-
self.assertEqual(round(estimated_fidelity, ndigits=5), 0.5)
89-
90-
# Check that depolarizing the electron completely would estimate 0 fidelity with the desired state
91-
dp_photon = 0
92-
estimated_state = SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA, etaB, alphaA, alphaB,
93-
pdark, dp_photon)
94-
95-
estimated_fidelity = dm_fidelity(ideal_state, estimated_state, squared=True)
96-
self.assertEqual(estimated_fidelity, 0)
97-
98-
# Check that effectively setting chance of no clicks to zero yields a state with 0 fidelity to the ideal state
99-
alphaA = 1
100-
alphaB = 1
101-
estimated_state = SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA, etaB, alphaA, alphaB,
102-
pdark, dp_photon)
103-
104-
estimated_fidelity = dm_fidelity(ideal_state, estimated_state, squared=True)
105-
self.assertEqual(estimated_fidelity, 0)
106-
107-
# Test a few extreme cases
108-
# Transmitivity of the fibres from a and b are 0 (imagine someone cuts the fibre)
109-
with self.assertRaises(EasySquidException):
110-
SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA=0, etaB=0, alphaA=0, alphaB=0, pdark=0,
111-
dp_photon=0)
112-
113-
with self.assertRaises(EasySquidException):
114-
SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA=0, etaB=0, alphaA=1, alphaB=1, pdark=0,
115-
dp_photon=0)
116-
117-
# Probability of a dark count is 1, effectively never get single clicks
118-
with self.assertRaises(EasySquidException):
119-
SingleClickFidelityEstimationUnit._calculate_estimated_state(etaA=1, etaB=1, alphaA=1, alphaB=1, pdark=1,
120-
dp_photon=0)
73+
# Both electrons excited
74+
self.assertAlmostEqual(self.feuA._estimate_fidelity(1), 0, places=4)
75+
76+
# No excitation (all dark counts)
77+
self.assertAlmostEqual(self.feuA._estimate_fidelity(0), 0, places=4)
78+
79+
# Check linearity in alpha (for high enough alpha due to dark counts)
80+
for i in range(40, 100):
81+
alpha = i/200
82+
F = self.feuA._estimate_fidelity(alpha)
83+
self.assertAlmostEqual(F, 1 - alpha, places=1)
84+
85+
# Perfect entanglement when no dark counts
86+
self.mhp_conn.midPoint.pdark = 0
87+
self.assertAlmostEqual(self.feuA._estimate_fidelity(0.00001), 1, places=1)
88+
89+
with self.assertRaises(LinkLayerException):
90+
self.feuA._calculate_estimated_state(0)
12191

12292
def test_minimum_fidelities(self):
123-
nodeA, nodeB = self.create_nodes(1, 1)
124-
mhp_service = SimulatedNodeCentricMHPService("mhp_service", nodeA, nodeB)
125-
feuA = SingleClickFidelityEstimationUnit(nodeA, mhp_service)
126-
feuB = SingleClickFidelityEstimationUnit(nodeB, mhp_service)
127-
128-
self.assertTrue(isclose(feuA.estimated_fidelity, 0.9473684210526312))
129-
self.assertTrue(isclose(feuB.estimated_fidelity, 0.9473684210526312))
130-
self.assertEqual(feuA.achievable_fidelities, feuB.achievable_fidelities)
131-
self.assertEqual(feuA.achievable_fidelities[0][0], 0.1)
132-
self.assertTrue(isclose(feuA.achievable_fidelities[0][1], 0.9473684210526312))
133-
self.assertEqual(feuA.achievable_fidelities[1][0], 0.3)
134-
self.assertTrue(isclose(feuA.achievable_fidelities[1][1], 0.8235294278458571))
135-
136-
self.assertTrue(isclose(feuA.get_max_fidelity(), 0.9473684210526312))
137-
138-
protoA = mhp_service.get_node_proto(nodeA)
93+
94+
self.assertEqual(self.feuA.achievable_fidelities, self.feuB.achievable_fidelities)
95+
self.assertEqual(self.feuA.achievable_fidelities[0][0], 0.1)
96+
self.assertTrue(isclose(self.feuA.achievable_fidelities[0][1], 0.8614038170052276))
97+
self.assertEqual(self.feuA.achievable_fidelities[1][0], 0.3)
98+
self.assertTrue(isclose(self.feuA.achievable_fidelities[1][1], 0.6800331800062903))
99+
100+
self.assertTrue(isclose(self.feuA.get_max_fidelity(), 0.8614038170052276))
101+
102+
protoA = self.feuA.mhp_service.get_node_proto(self.feuA.node)
139103
protoA.set_allowed_bright_state_populations([0.1, 0.2])
140104

141105
with self.assertRaises(LinkLayerException):
142-
feuA._calculate_achievable_fidelities()
106+
self.feuA._calculate_achievable_fidelities()
143107

144108
def test_get_bright_state(self):
145-
nodeA, nodeB = self.create_nodes(1, 1)
146-
mhp_service = SimulatedNodeCentricMHPService("mhp_service", nodeA, nodeB)
147-
feuA = SingleClickFidelityEstimationUnit(nodeA, mhp_service)
148-
149-
self.assertEqual(feuA.select_bright_state(0.9), 0.1)
150-
self.assertEqual(feuA.select_bright_state(0.8), 0.3)
151-
self.assertEqual(feuA.select_bright_state(0.95), None)
109+
self.assertEqual(self.feuA.select_bright_state(0.9), None)
110+
self.assertEqual(self.feuA.select_bright_state(0.8), 0.1)
111+
self.assertEqual(self.feuA.select_bright_state(0.7), 0.1)
112+
self.assertEqual(self.feuA.select_bright_state(0.6), 0.3)
152113

153114

154115
if __name__ == '__main__':

0 commit comments

Comments
 (0)