Skip to content

Commit ac41e6a

Browse files
Merge pull request #50 from tqsd/two_qubit_control
Two qubit control
2 parents 271af94 + 8d27964 commit ac41e6a

File tree

11 files changed

+155
-20
lines changed

11 files changed

+155
-20
lines changed

dev_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ decorator==4.4.1
2121
defusedxml==0.6.0
2222
docutils==0.15.2
2323
entrypoints==0.3
24-
EQSN==0.0.7
24+
eqsn
2525
flake8==3.7.9
2626
future==0.18.2
2727
hyperlink==19.0.0

integration_tests/test_host.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import unittest
2-
from qunetsim.components.host import Host
2+
3+
from qunetsim.objects import Qubit
4+
from qunetsim.components import Host
35
from random import randint
46

57

@@ -15,6 +17,32 @@ def setUpClass(cls):
1517
def tearDownClass(cls):
1618
pass
1719

20+
def test_get_qubits_by_id(self):
21+
host = Host('A')
22+
q1 = Qubit(host)
23+
q2 = Qubit(host)
24+
q3 = Qubit(host)
25+
26+
host2 = Host('B')
27+
q4 = Qubit(host2)
28+
29+
host.add_epr('B', q1)
30+
host.add_data_qubit('C', q2)
31+
host.add_ghz_qubit('D', q3)
32+
33+
host2.add_data_qubit('A', q4)
34+
35+
# Test all types of qubits
36+
self.assertEqual(q1, host.get_qubit_by_id(q1.id))
37+
self.assertEqual(q2, host.get_qubit_by_id(q2.id))
38+
self.assertEqual(q3, host.get_qubit_by_id(q3.id))
39+
40+
# Test getting qubits from other hosts
41+
self.assertIsNone(host.get_qubit_by_id(q4.id))
42+
43+
# Test getting qubits that don't exist
44+
self.assertIsNone(host.get_qubit_by_id('fake'))
45+
1846
# @unittest.skip('')
1947
def test_connections(self):
2048
name = "A"

integration_tests/test_quantum_storage.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
import uuid
33

44
from qunetsim.objects import QuantumStorage
5+
from qunetsim.utils.constants import Constants
56

67

78
class FakeQubit(object):
9+
"""
10+
A qubit object that has fewer properties to test quantum storage.
11+
"""
812

913
def __init__(self, id=None):
1014
if id is not None:
@@ -146,3 +150,17 @@ def test_change_id_of_qubits(self):
146150
self.assertEqual(q1, None)
147151
q2 = storage.get_qubit_from_host('Bob', new_id2)
148152
self.assertNotEqual(q2, None)
153+
154+
def test_get_qubit_by_id(self):
155+
q1 = FakeQubit()
156+
q2 = FakeQubit()
157+
q3 = FakeQubit()
158+
159+
storage = QuantumStorage()
160+
storage.add_qubit_from_host(q1, from_host_id='S', purpose=Constants.DATA)
161+
storage.add_qubit_from_host(q2, from_host_id='T', purpose=Constants.EPR)
162+
storage.add_qubit_from_host(q3, from_host_id='V', purpose=Constants.GHZ)
163+
164+
self.assertEqual(q1, storage.get_qubit_by_id(q1.id))
165+
self.assertEqual(q2, storage.get_qubit_by_id(q2.id))
166+
self.assertEqual(q3, storage.get_qubit_by_id(q3.id))

qunetsim/backends/cqc_backend.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ def custom_gate(self, qubit, gate):
339339
qubit(Qubit): Qubit to which the gate is applied.
340340
gate(np.ndarray): 2x2 array of the gate.
341341
"""
342-
raise(EnvironmentError("Not implemented for this backend!"))
342+
raise (EnvironmentError("Not implemented for this backend!"))
343343

344344
def custom_controlled_gate(self, qubit, target, gate):
345345
"""
@@ -350,7 +350,7 @@ def custom_controlled_gate(self, qubit, target, gate):
350350
target(Qubit): Qubit on which the gate is applied.
351351
gate(nd.array): 2x2 array for the gate applied to target.
352352
"""
353-
raise(EnvironmentError("Not implemented for this backend!"))
353+
raise (EnvironmentError("Not implemented for this backend!"))
354354

355355
def custom_two_qubit_gate(self, qubit1, qubit2, gate):
356356
"""
@@ -361,7 +361,19 @@ def custom_two_qubit_gate(self, qubit1, qubit2, gate):
361361
qubit2(Qubit): Second qubit of the gate.
362362
gate(np.ndarray): 4x4 array for the gate applied.
363363
"""
364-
raise(EnvironmentError("Not implemented for this backend!"))
364+
raise (EnvironmentError("Not implemented for this backend!"))
365+
366+
def custom_controlled_two_qubit_gate(self, qubit, target_1, target_2, gate):
367+
"""
368+
Applies a custom gate to the target qubit, controlled by the qubit.
369+
370+
Args:
371+
qubit (Qubit): Qubit to control the gate.
372+
target_1 (Qubit): Qubit on which the gate is applied.
373+
target_2 (Qubit): Qubit on which the gate is applied.
374+
gate (nd.array): 4x4 array for the gate applied to target.
375+
"""
376+
raise (EnvironmentError("Not implemented for this backend!"))
365377

366378
def measure(self, qubit, non_destructive):
367379
"""

qunetsim/backends/eqsn_backend.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,18 @@ def custom_controlled_gate(self, qubit, target, gate):
373373
"""
374374
self.eqsn.custom_controlled_gate(target.qubit, qubit.qubit, gate)
375375

376+
def custom_controlled_two_qubit_gate(self, qubit, target_1, target_2, gate):
377+
"""
378+
Applies a custom gate to the target qubit, controlled by the qubit.
379+
380+
Args:
381+
qubit (Qubit): Qubit to control the gate.
382+
target_1 (Qubit): Qubit on which the gate is applied.
383+
target_2 (Qubit): Qubit on which the gate is applied.
384+
gate (nd.array): 4x4 array for the gate applied to target.
385+
"""
386+
self.eqsn.custom_two_qubit_control_gate(qubit.qubit, target_1.qubit, target_2.qubit, gate)
387+
376388
def custom_two_qubit_gate(self, qubit1, qubit2, gate):
377389
"""
378390
Applies a custom two qubit gate to qubit1 \\otimes qubit2.

qunetsim/backends/projectq_backend.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ def custom_gate(self, qubit, gate):
288288
qubit(Qubit): Qubit to which the gate is applied.
289289
gate(np.ndarray): 2x2 array of the gate.
290290
"""
291-
raise(EnvironmentError("Not implemented for this backend!"))
291+
raise (EnvironmentError("Not implemented for this backend!"))
292292

293293
def custom_controlled_gate(self, qubit, target, gate):
294294
"""
@@ -299,7 +299,7 @@ def custom_controlled_gate(self, qubit, target, gate):
299299
target(Qubit): Qubit on which the gate is applied.
300300
gate(nd.array): 2x2 array for the gate applied to target.
301301
"""
302-
raise(EnvironmentError("Not implemented for this backend!"))
302+
raise (EnvironmentError("Not implemented for this backend!"))
303303

304304
def custom_two_qubit_gate(self, qubit1, qubit2, gate):
305305
"""
@@ -310,7 +310,19 @@ def custom_two_qubit_gate(self, qubit1, qubit2, gate):
310310
qubit2(Qubit): Second qubit of the gate.
311311
gate(np.ndarray): 4x4 array for the gate applied.
312312
"""
313-
raise(EnvironmentError("Not implemented for this backend!"))
313+
raise (EnvironmentError("Not implemented for this backend!"))
314+
315+
def custom_controlled_two_qubit_gate(self, qubit, target_1, target_2, gate):
316+
"""
317+
Applies a custom gate to the target qubit, controlled by the qubit.
318+
319+
Args:
320+
qubit (Qubit): Qubit to control the gate.
321+
target_1 (Qubit): Qubit on which the gate is applied.
322+
target_2 (Qubit): Qubit on which the gate is applied.
323+
gate (nd.array): 4x4 array for the gate applied to target.
324+
"""
325+
raise (EnvironmentError("Not implemented for this backend!"))
314326

315327
def measure(self, qubit, non_destructive):
316328
"""

qunetsim/components/host.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,17 @@ def run_protocol(self, protocol, arguments=(), blocking=False):
13341334
else:
13351335
return DaemonThread(protocol, args=arguments)
13361336

1337+
def get_qubit_by_id(self, q_id):
1338+
"""
1339+
Return the qubit that has the id *q_id* from the quantum storage.
1340+
1341+
Args:
1342+
q_id (str): The ID of the qubit
1343+
Returns:
1344+
(Qubit): The qubit with the id *q_id* or None if it does not exist
1345+
"""
1346+
return self._qubit_storage.get_qubit_by_id(q_id)
1347+
13371348
def get_next_classical_message(self, receive_from_id, buffer, sequence_nr):
13381349
"""
13391350
*WILL BE DELETED*

qunetsim/objects/quantum_storage.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ def check_qubit_from_host_exists(self, from_host_id, purpose=None):
154154
self.lock.release_write()
155155
return False
156156

157+
def get_qubit_by_id(self, q_id):
158+
"""
159+
Return the qubit that has the id *q_id*
160+
161+
Args:
162+
q_id (str): The ID of the qubit
163+
Returns:
164+
(Qubit): The qubit with the id *q_id* or None if it does not exist
165+
"""
166+
if q_id in self._qubit_dict:
167+
return list(self._qubit_dict[q_id].values())[0]
168+
return None
169+
157170
def change_qubit_id(self, from_host_id, new_id, old_id=None):
158171
"""
159172
Changes the ID of a qubit. If the ID is not given, a random
@@ -242,10 +255,10 @@ def get_all_qubits_from_host(self, from_host_id, purpose=None):
242255

243256
def _check_all_requests(self):
244257
"""
245-
Checks if any of the pending requests is now fullfilled.
258+
Checks if any of the pending requests is now fulfilled.
246259
247260
Returns:
248-
If a request is fullfilled, the request is handeled and the function
261+
If a request is fulfilled, the request is handled and the function
249262
returns the qubit of this request.
250263
"""
251264
for req_id, args in self._pending_request_dict.items():
@@ -351,7 +364,9 @@ def _get_qubit_from_host(self, from_host_id, q_id, purpose):
351364
return None
352365

353366
def _pop_qubit_with_id_and_host_from_qubit_dict(self, q_id, from_host_id, purpose=None):
354-
def _pop_purpose_from_purpose_dict(q_id, from_host_id):
367+
def _pop_purpose_from_purpose_dict():
368+
nonlocal q_id, from_host_id
369+
355370
if q_id not in self._purpose_dict:
356371
return None
357372
pur = self._purpose_dict[q_id].pop(from_host_id, None)
@@ -361,7 +376,7 @@ def _pop_purpose_from_purpose_dict(q_id, from_host_id):
361376
return pur
362377
return None
363378

364-
purp = _pop_purpose_from_purpose_dict(q_id, from_host_id)
379+
purp = _pop_purpose_from_purpose_dict()
365380
if purp is not None:
366381
if purpose is None or purpose == purp:
367382
qubit = self._qubit_dict[q_id].pop(from_host_id, None)
@@ -376,15 +391,16 @@ def _pop_purpose_from_purpose_dict(q_id, from_host_id):
376391
return None
377392

378393
def _add_qubit_to_qubit_dict(self, qubit, purpose, from_host_id):
379-
def _add_purpose_to_purpose_dict(purpose, id, from_host_id):
380-
if id not in self._purpose_dict:
381-
self._purpose_dict[id] = {}
382-
self._purpose_dict[id][from_host_id] = purpose
394+
def _add_purpose_to_purpose_dict(q_id):
395+
nonlocal purpose, from_host_id
396+
if q_id not in self._purpose_dict:
397+
self._purpose_dict[q_id] = {}
398+
self._purpose_dict[q_id][from_host_id] = purpose
383399

384400
if qubit.id not in self._qubit_dict:
385401
self._qubit_dict[qubit.id] = {}
386402
self._qubit_dict[qubit.id][from_host_id] = qubit
387-
_add_purpose_to_purpose_dict(purpose, qubit.id, from_host_id)
403+
_add_purpose_to_purpose_dict(qubit.id)
388404

389405
def _add_new_host(self, host_id):
390406
if host_id not in self._host_dict:
@@ -396,6 +412,14 @@ def _add_new_host(self, host_id):
396412
def _check_qubit_in_system(self, qubit, from_host_id, purpose=None):
397413
"""
398414
True if qubit with same parameters already in the systems
415+
416+
Args:
417+
qubit (Qubit): The qubit in question
418+
from_host_id (str): The ID of the sending host
419+
purpose (int): Qubit's purpose
420+
421+
Returns:
422+
(bool): If the qubit is in the system.
399423
"""
400424
if qubit.id in self._qubit_dict and \
401425
from_host_id in self._qubit_dict[qubit.id]:

qunetsim/objects/qubit.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,21 +241,38 @@ def custom_controlled_gate(self, target, gate):
241241

242242
self._host.backend.custom_controlled_gate(self, target, gate)
243243

244+
def custom_two_qubit_control_gate(self, q1, q2, gate):
245+
"""
246+
Applies the gate *gate* to qubits q1 and q2 as a controlled gate.
247+
248+
Args:
249+
q1 (Qubit): First qubit
250+
q2 (Qubit): Second qubit
251+
gate (Numpy ndarray): The gate to apply
252+
"""
253+
if not isinstance(gate, np.ndarray):
254+
raise (InputError("Only Numpy matrices are allowed"))
255+
if not is_unitary(gate):
256+
raise (InputError("Custom gates must be unitary operations"))
257+
if gate.shape != (4, 4):
258+
raise (InputError("Custom controlled gates must be 4x4 matrices"))
259+
260+
self._host.backend.custom_controlled_two_qubit_gate(self, q1, q2, gate)
261+
244262
def custom_two_qubit_gate(self, other_qubit, gate):
245263
"""
246264
Applies a custom 2 qubit gate.
247265
248266
Args:
249267
other_qubit (Qubit): The second qubit.
250268
gate (Numpy ndarray): The gate
251-
252269
"""
253270
if not isinstance(gate, np.ndarray):
254271
raise (InputError("Only Numpy matrices are allowed"))
255272
if not is_unitary(gate):
256273
raise (InputError("Custom gates must be unitary operations"))
257274
if gate.shape != (4, 4):
258-
raise (InputError("Custom controlled gates must be 2x2 matrices"))
275+
raise (InputError("Custom controlled gates must be 4x4 matrices"))
259276

260277
self._host.backend.custom_two_qubit_gate(self, other_qubit, gate)
261278

qunetsim/utils/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Constants:
1717
# QUBIT TYPES
1818
EPR = 0
1919
DATA = 1
20+
GHZ = 2
2021

2122
# DATA KINDS
2223
SIGNAL = 'signal'

0 commit comments

Comments
 (0)