Skip to content

Commit d43581d

Browse files
author
Axel Dahlberg
authored
Merge pull request #5 from SoftwareQuTech/Develop
Develop
2 parents f736d25 + 43a06b9 commit d43581d

File tree

11 files changed

+196
-165
lines changed

11 files changed

+196
-165
lines changed

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ PYTHON = python3
22
PIP = pip3
33
CQC_DIR = cqc
44

5+
clean: _clear_pyc _clear_build
6+
57
_clear_pyc:
68
@find . -name '*.pyc' -delete
79

@@ -11,6 +13,9 @@ lint:
1113
python-deps:
1214
@cat requirements.txt | xargs -n 1 -L 1 $(PIP) install
1315

16+
_verified:
17+
@echo "CQC-Python is verified!"
18+
1419
verify: clean python-deps lint _verified
1520

1621
_remove_build:
@@ -27,8 +32,6 @@ _clear_build: _remove_build _remove_dist _remove_egg_info
2732
_build:
2833
@${PYTHON} setup.py sdist bdist_wheel
2934

30-
clean: _clear_pyc _clear_build
31-
3235
build: _clear_build _build
3336

3437
.PHONY: clean lint python-deps verify build

cqc/Protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def __init__(self, factory):
6060
# higher layers or an OS
6161
self.app_id = 0
6262

63-
# Define the backend to use. Is a setting in settings.ini
63+
# Define the backend to use.
6464
self.messageHandler = factory.backend
6565

6666
# Flag to determine whether we already received _all_ of the CQC header

cqc/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = '3.0.0'

cqc/cqcHeader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def setVals(self, *args, **kwargs):
135135

136136
def _check_vals(self):
137137
"""
138-
Method to be called after settings values, checks if values can be packed and sets is_set to True.
138+
Method to be called after setting values, checks if values can be packed and sets is_set to True.
139139
140140
:return: None
141141
"""

cqc/entInfoHeader.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def __init__(self, headerBytes=None):
7676
self.unpack(headerBytes)
7777
self.is_set = True
7878

79-
8079
def _setVals(self, node_A=0, port_A=0, app_id_A=0, node_B=0, port_B=0, app_id_B=0, id_AB=0, timestamp=0, ToG=0,
8180
goodness=0, DF=0):
8281
"""

cqc/hostConfig.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,6 @@ def cqc_node_id_from_addrinfo(addr):
4747
return cqc_node_id(fam, ip)
4848

4949

50-
class networkConfig(pb.Referenceable):
51-
def __init__(self, filename):
52-
"""
53-
Initialize by reading in the configuration file.
54-
"""
55-
# Dictionary where we will keep host details, indexed by node name (e.g. Alice)
56-
self.hostDict = {}
57-
58-
# Read config file
59-
self.read_config(filename)
60-
61-
def read_config(self, filename):
62-
"""
63-
Reads the configuration file in which each line has the form: node name, hostname, port number.
64-
For example:
65-
Alice, localhost, 8888
66-
"""
67-
with open(filename) as confFile:
68-
for line in confFile:
69-
if not line.startswith("#"):
70-
words = line.split(",")
71-
72-
# We will simply ignore lines which are not of the right form
73-
if len(words) == 3:
74-
newHost = host(words[0].strip(), words[1].strip(), words[2].strip())
75-
self.hostDict[words[0]] = newHost
76-
77-
def print_details(self, name):
78-
"""
79-
Prints the details of the specified node with name.
80-
"""
81-
host = self.hostDict[name]
82-
print("Host details of ", name, ": ", host.hostname, ":", host.port)
83-
84-
8550
class host(pb.Referenceable):
8651
def __init__(self, name, hostname, port):
8752
"""

cqc/pythonLib.py

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import socket
3636
import warnings
3737

38-
from cqc.settings import get_cqc_file, get_app_file
3938
from cqc.cqcHeader import (
4039
Header,
4140
CQCCmdHeader,
@@ -90,7 +89,16 @@
9089
CQC_TP_EXPIRE,
9190
)
9291
from cqc.entInfoHeader import EntInfoHeader
93-
from cqc.hostConfig import cqc_node_id_from_addrinfo, networkConfig
92+
from cqc.hostConfig import cqc_node_id_from_addrinfo
93+
94+
try:
95+
import simulaqron
96+
from simulaqron.general.hostConfig import socketsConfig
97+
from simulaqron.settings import simulaqron_settings
98+
_simulaqron_version = simulaqron.__version__
99+
_simulaqron_major = int(_simulaqron_version.split('.')[0])
100+
except ModuleNotFoundError:
101+
_simulaqron_major = -1
94102

95103

96104
def shouldReturn(command):
@@ -171,16 +179,19 @@ def createXtraHeader(command, values):
171179
class CQCConnection:
172180
_appIDs = {}
173181

174-
def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=None, pend_messages=False,
175-
retry_connection=True, conn_retry_time=0.1, log_level=None):
182+
def __init__(self, name, socket_address=None, appID=None, pend_messages=False,
183+
retry_connection=True, conn_retry_time=0.1, log_level=None, backend=None,
184+
use_classical_communication=True, network_name=None):
176185
"""
177186
Initialize a connection to the cqc server.
178187
188+
Since version 3.0.0: If socket_address is None or use_classical_communication is True, the CQC connection
189+
needs some way of finding the correct socket addresses. If backend is None or "simulaqron" the connection
190+
will try to make use of the network config file setup in simulaqron. If simulaqron is not installed
191+
179192
- **Arguments**
180193
:param name: Name of the host.
181194
:param socket_address: tuple (str, int) of ip and port number.
182-
:param cqcFile: Path to cqcFile. If None, 'Setting.CONF_CQC_FILE' is used, unless socket_address
183-
:param appFile: Path to appFile. If None, 'Setting.CONF_APP_FILE' is used.
184195
:param appID: Application ID. If set to None, defaults to a nonused ID.
185196
:param pend_messages: True if you want to wait with sending messages to the back end.
186197
Use flush() to send all pending messages in one go as a sequence to the server
@@ -190,6 +201,14 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
190201
How many seconds to wait between each connection retry
191202
:param log_level: int or None
192203
The log-level, for example logging.DEBUG (default: logging.WARNING)
204+
:param backend: None or str
205+
If socket_address is None or use_classical_communication is True, If None or "simulaqron" is used
206+
the cqc library tries to use the network config file setup in simulaqron if network_config_file is None.
207+
If network_config_file is None and simulaqron is not installed a ValueError is raised.
208+
:param use_classical_communication: bool
209+
Whether to use the built-in classical communication or not.
210+
:param network_name: None or str
211+
Used if simulaqron is used to load socket addresses for the backend
193212
"""
194213
self._setup_logging(log_level)
195214

@@ -233,19 +252,21 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
233252
# Classical connections in the application network
234253
self._classicalConn = {}
235254

236-
if socket_address is None:
237-
# This file defines the network of CQC servers interfacing to virtual quantum nodes
238-
if cqcFile is None:
239-
self.cqcFile = get_cqc_file()
240-
else:
241-
self.cqcFile = cqcFile
242-
243-
# Read configuration files for the cqc network
244-
if os.path.exists(self.cqcFile):
245-
self._cqcNet = networkConfig(self.cqcFile)
255+
if socket_address is None or use_classical_communication:
256+
if backend is None or backend == "simulaqron":
257+
if _simulaqron_major < 3:
258+
raise ValueError("If (socket_address is None or use_classical_communication is True)"
259+
"and (backend is None or 'simulaqron'\n"
260+
"you need simulaqron>=3.0.0 installed.")
261+
else:
262+
network_config_file = simulaqron_settings.network_config_file
263+
self._cqcNet = socketsConfig(network_config_file, network_name=network_name, config_type="cqc")
264+
if use_classical_communication:
265+
self._appNet = socketsConfig(network_config_file, network_name=network_name, config_type="app")
266+
else:
267+
self._appNet = None
246268
else:
247-
raise ValueError("There was no path specified for the cqc file containing addresses"
248-
"and port numbers to the cqc nodes in the backend.")
269+
raise ValueError("Unknown backend")
249270

250271
# Host data
251272
if self.name in self._cqcNet.hostDict:
@@ -255,7 +276,7 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
255276

256277
# Get IP and port number
257278
addr = myHost.addr
258-
else:
279+
if socket_address is not None:
259280
try:
260281
hostname, port = socket_address
261282
if not isinstance(hostname, str):
@@ -287,20 +308,7 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
287308
self._s.close()
288309
raise err
289310

290-
# This file defines the application network
291-
if appFile is None:
292-
self.appFile = get_app_file()
293-
else:
294-
self.appFile = appFile
295-
296-
# Read configuration files for the application network
297-
if os.path.exists(self.appFile):
298-
self._appNet = networkConfig(self.appFile)
299-
else:
300-
logging.warning("Since there is no appFile was found the built-in classical commmunication cannot be used.")
301-
self._appNet = None
302-
303-
# List of pending messages waiting to be send to the back-end
311+
# List of pending messages waiting to be send to the back-end
304312
self.pend_messages = pend_messages
305313
self.pending_messages = []
306314

@@ -362,7 +370,8 @@ def startClassicalServer(self):
362370
"""
363371
if self._appNet is None:
364372
raise ValueError(
365-
"Since there is no appFile was found the built-in classical commmunication cannot be used."
373+
"Since use_classical_communication was set to False upon init, the built-in classical communication"
374+
"cannot be used."
366375
)
367376

368377
if not self._classicalServer:
@@ -412,7 +421,8 @@ def openClassicalChannel(self, name):
412421
"""
413422
if self._appNet is None:
414423
raise ValueError(
415-
"Since there is no appFile was found the built-in classical commmunication cannot be used."
424+
"Since use_classical_communication was set to False upon init, the built-in classical communication"
425+
"cannot be used."
416426
)
417427
if name not in self._classicalConn:
418428
logging.debug("App {}: Opening classical channel to {}".format(self.name, name))
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#
2+
# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
3+
# All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without
6+
# modification, are permitted provided that the following conditions are met:
7+
# 1. Redistributions of source code must retain the above copyright
8+
# notice, this list of conditions and the following disclaimer.
9+
# 2. Redistributions in binary form must reproduce the above copyright
10+
# notice, this list of conditions and the following disclaimer in the
11+
# documentation and/or other materials provided with the distribution.
12+
# 3. All advertising materials mentioning features or use of this software
13+
# must display the following acknowledgement:
14+
# This product includes software developed by Stephanie Wehner, QuTech.
15+
# 4. Neither the name of the QuTech organization nor the
16+
# names of its contributors may be used to endorse or promote products
17+
# derived from this software without specific prior written permission.
18+
#
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
20+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
23+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
30+
from cqc.pythonLib import qubit
31+
32+
33+
def parity_meas(qubits, bases, node, negative=False):
34+
"""
35+
Performs a parity measurement on the provided qubits in the Pauli bases specified by 'bases'.
36+
'bases' should be a string with letters in "IXYZ" and have the same length as the number of qubits provided.
37+
If 'negative' is true the measurement outcome is flipped.
38+
If more than one letter of 'bases' is not identity, then an ancilla qubit will be used, which is created using the
39+
provided 'node'.
40+
41+
:param qubits: List of qubits to be measured.
42+
:type qubits: list of :obj: `cqc.pythonLib.qubit`
43+
:param bases: String specifying the Pauli-bases of the measurement. Example bases="IXY" for three qubits.
44+
:type bases: str
45+
:param node: The node storing the qubits. Used for creating an ancilla qubit.
46+
:type node: :obj: `cqc.pythonLib.CQCConnection`
47+
:param negative: If the measurement outcome should be flipped or not.
48+
:type negative: bool
49+
:return: The measurement outcome 0 or 1, where 0 correspond to the +1 eigenvalue of the measurement operator.
50+
"""
51+
52+
if not (len(qubits) == len(bases)):
53+
raise ValueError("Number of bases needs to be the number of qubits.")
54+
if not all([(B in "IXYZ") for B in bases]):
55+
raise ValueError("All elements of bases need to be in 'IXYZ'.")
56+
57+
num_qubits = len(qubits)
58+
59+
flip_basis = ["I"] * num_qubits
60+
non_identity_bases = []
61+
62+
# Check if we need to flip the bases of the qubits
63+
for i in range(len(bases)):
64+
B = bases[i]
65+
if B == "X":
66+
flip_basis[i] = "H"
67+
non_identity_bases.append(i)
68+
elif B == "Y":
69+
flip_basis[i] = "K"
70+
non_identity_bases.append(i)
71+
elif B == "Z":
72+
non_identity_bases.append(i)
73+
else:
74+
pass
75+
76+
if len(non_identity_bases) == 0:
77+
# Trivial measurement
78+
m = 0
79+
80+
elif len(non_identity_bases) == 1:
81+
# Single_qubit measurement
82+
q_index = non_identity_bases[0]
83+
q = qubits[q_index]
84+
85+
# Flip to correct basis
86+
if flip_basis[q_index] == "H":
87+
q.H()
88+
if flip_basis[q_index] == "K":
89+
q.K()
90+
91+
m = q.measure(inplace=True)
92+
93+
# Flip the qubit back
94+
if flip_basis[q_index] == "H":
95+
q.H()
96+
if flip_basis[q_index] == "K":
97+
q.K()
98+
99+
else:
100+
# Parity measurement, ancilla needed
101+
102+
# Initialize ancilla qubit
103+
anc = qubit(node)
104+
105+
# Flip to correct basis
106+
for i in range(len(bases)):
107+
if flip_basis[i] == "H":
108+
qubits[i].H()
109+
if flip_basis[i] == "K":
110+
qubits[i].K()
111+
112+
# Transfer parity information to ancilla qubit
113+
for i in non_identity_bases:
114+
qubits[i].cnot(anc)
115+
116+
# Measure ancilla qubit
117+
m = anc.measure()
118+
119+
# Flip to correct basis
120+
for i in range(len(bases)):
121+
if flip_basis[i] == "H":
122+
qubits[i].H()
123+
if flip_basis[i] == "K":
124+
qubits[i].K()
125+
if negative:
126+
return (m + 1) % 2
127+
else:
128+
return m

cqc/settings.ini

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)