Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ PYTHON = python3
PIP = pip3
CQC_DIR = cqc

clean: _clear_pyc _clear_build

_clear_pyc:
@find . -name '*.pyc' -delete

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

_verified:
@echo "CQC-Python is verified!"

verify: clean python-deps lint _verified

_remove_build:
Expand All @@ -27,8 +32,6 @@ _clear_build: _remove_build _remove_dist _remove_egg_info
_build:
@${PYTHON} setup.py sdist bdist_wheel

clean: _clear_pyc _clear_build

build: _clear_build _build

.PHONY: clean lint python-deps verify build
2 changes: 1 addition & 1 deletion cqc/Protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(self, factory):
# higher layers or an OS
self.app_id = 0

# Define the backend to use. Is a setting in settings.ini
# Define the backend to use.
self.messageHandler = factory.backend

# Flag to determine whether we already received _all_ of the CQC header
Expand Down
1 change: 1 addition & 0 deletions cqc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = '3.0.0'
2 changes: 1 addition & 1 deletion cqc/cqcHeader.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def setVals(self, *args, **kwargs):

def _check_vals(self):
"""
Method to be called after settings values, checks if values can be packed and sets is_set to True.
Method to be called after setting values, checks if values can be packed and sets is_set to True.

:return: None
"""
Expand Down
1 change: 0 additions & 1 deletion cqc/entInfoHeader.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ def __init__(self, headerBytes=None):
self.unpack(headerBytes)
self.is_set = True


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,
goodness=0, DF=0):
"""
Expand Down
35 changes: 0 additions & 35 deletions cqc/hostConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,41 +47,6 @@ def cqc_node_id_from_addrinfo(addr):
return cqc_node_id(fam, ip)


class networkConfig(pb.Referenceable):
def __init__(self, filename):
"""
Initialize by reading in the configuration file.
"""
# Dictionary where we will keep host details, indexed by node name (e.g. Alice)
self.hostDict = {}

# Read config file
self.read_config(filename)

def read_config(self, filename):
"""
Reads the configuration file in which each line has the form: node name, hostname, port number.
For example:
Alice, localhost, 8888
"""
with open(filename) as confFile:
for line in confFile:
if not line.startswith("#"):
words = line.split(",")

# We will simply ignore lines which are not of the right form
if len(words) == 3:
newHost = host(words[0].strip(), words[1].strip(), words[2].strip())
self.hostDict[words[0]] = newHost

def print_details(self, name):
"""
Prints the details of the specified node with name.
"""
host = self.hostDict[name]
print("Host details of ", name, ": ", host.hostname, ":", host.port)


class host(pb.Referenceable):
def __init__(self, name, hostname, port):
"""
Expand Down
80 changes: 45 additions & 35 deletions cqc/pythonLib.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import socket
import warnings

from cqc.settings import get_cqc_file, get_app_file
from cqc.cqcHeader import (
Header,
CQCCmdHeader,
Expand Down Expand Up @@ -90,7 +89,16 @@
CQC_TP_EXPIRE,
)
from cqc.entInfoHeader import EntInfoHeader
from cqc.hostConfig import cqc_node_id_from_addrinfo, networkConfig
from cqc.hostConfig import cqc_node_id_from_addrinfo

try:
import simulaqron
from simulaqron.general.hostConfig import socketsConfig
from simulaqron.settings import simulaqron_settings
_simulaqron_version = simulaqron.__version__
_simulaqron_major = int(_simulaqron_version.split('.')[0])
except ModuleNotFoundError:
_simulaqron_major = -1


def shouldReturn(command):
Expand Down Expand Up @@ -171,16 +179,19 @@ def createXtraHeader(command, values):
class CQCConnection:
_appIDs = {}

def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=None, pend_messages=False,
retry_connection=True, conn_retry_time=0.1, log_level=None):
def __init__(self, name, socket_address=None, appID=None, pend_messages=False,
retry_connection=True, conn_retry_time=0.1, log_level=None, backend=None,
use_classical_communication=True, network_name=None):
"""
Initialize a connection to the cqc server.

Since version 3.0.0: If socket_address is None or use_classical_communication is True, the CQC connection
needs some way of finding the correct socket addresses. If backend is None or "simulaqron" the connection
will try to make use of the network config file setup in simulaqron. If simulaqron is not installed

- **Arguments**
:param name: Name of the host.
:param socket_address: tuple (str, int) of ip and port number.
:param cqcFile: Path to cqcFile. If None, 'Setting.CONF_CQC_FILE' is used, unless socket_address
:param appFile: Path to appFile. If None, 'Setting.CONF_APP_FILE' is used.
:param appID: Application ID. If set to None, defaults to a nonused ID.
:param pend_messages: True if you want to wait with sending messages to the back end.
Use flush() to send all pending messages in one go as a sequence to the server
Expand All @@ -190,6 +201,14 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
How many seconds to wait between each connection retry
:param log_level: int or None
The log-level, for example logging.DEBUG (default: logging.WARNING)
:param backend: None or str
If socket_address is None or use_classical_communication is True, If None or "simulaqron" is used
the cqc library tries to use the network config file setup in simulaqron if network_config_file is None.
If network_config_file is None and simulaqron is not installed a ValueError is raised.
:param use_classical_communication: bool
Whether to use the built-in classical communication or not.
:param network_name: None or str
Used if simulaqron is used to load socket addresses for the backend
"""
self._setup_logging(log_level)

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

if socket_address is None:
# This file defines the network of CQC servers interfacing to virtual quantum nodes
if cqcFile is None:
self.cqcFile = get_cqc_file()
else:
self.cqcFile = cqcFile

# Read configuration files for the cqc network
if os.path.exists(self.cqcFile):
self._cqcNet = networkConfig(self.cqcFile)
if socket_address is None or use_classical_communication:
if backend is None or backend == "simulaqron":
if _simulaqron_major < 3:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is the best way to go about this. But this should probably be in simulaqron itself as requirement. V2 simulaqron should have cqc <3.0.0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LeonWubben Why should it be in SimulaQron? Why should simlaqron v2 have cqc<3.0.0, simulaqron v2 works fine with cqc 3 it's the pythonlibrary of cqc 3 which does not work with simulaqron 2.

raise ValueError("If (socket_address is None or use_classical_communication is True)"
"and (backend is None or 'simulaqron'\n"
"you need simulaqron>=3.0.0 installed.")
else:
network_config_file = simulaqron_settings.network_config_file
self._cqcNet = socketsConfig(network_config_file, network_name=network_name, config_type="cqc")
if use_classical_communication:
self._appNet = socketsConfig(network_config_file, network_name=network_name, config_type="app")
else:
self._appNet = None
else:
raise ValueError("There was no path specified for the cqc file containing addresses"
"and port numbers to the cqc nodes in the backend.")
raise ValueError("Unknown backend")

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

# Get IP and port number
addr = myHost.addr
else:
if socket_address is not None:
try:
hostname, port = socket_address
if not isinstance(hostname, str):
Expand Down Expand Up @@ -287,20 +308,7 @@ def __init__(self, name, socket_address=None, cqcFile=None, appFile=None, appID=
self._s.close()
raise err

# This file defines the application network
if appFile is None:
self.appFile = get_app_file()
else:
self.appFile = appFile

# Read configuration files for the application network
if os.path.exists(self.appFile):
self._appNet = networkConfig(self.appFile)
else:
logging.warning("Since there is no appFile was found the built-in classical commmunication cannot be used.")
self._appNet = None

# List of pending messages waiting to be send to the back-end
# List of pending messages waiting to be send to the back-end
self.pend_messages = pend_messages
self.pending_messages = []

Expand Down Expand Up @@ -362,7 +370,8 @@ def startClassicalServer(self):
"""
if self._appNet is None:
raise ValueError(
"Since there is no appFile was found the built-in classical commmunication cannot be used."
"Since use_classical_communication was set to False upon init, the built-in classical communication"
"cannot be used."
)

if not self._classicalServer:
Expand Down Expand Up @@ -412,7 +421,8 @@ def openClassicalChannel(self, name):
"""
if self._appNet is None:
raise ValueError(
"Since there is no appFile was found the built-in classical commmunication cannot be used."
"Since use_classical_communication was set to False upon init, the built-in classical communication"
"cannot be used."
)
if name not in self._classicalConn:
logging.debug("App {}: Opening classical channel to {}".format(self.name, name))
Expand Down
128 changes: 128 additions & 0 deletions cqc/pythonLib_protocols/measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#
# Copyright (c) 2017, Stephanie Wehner and Axel Dahlberg
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
# must display the following acknowledgement:
# This product includes software developed by Stephanie Wehner, QuTech.
# 4. Neither the name of the QuTech organization nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from cqc.pythonLib import qubit


def parity_meas(qubits, bases, node, negative=False):
"""
Performs a parity measurement on the provided qubits in the Pauli bases specified by 'bases'.
'bases' should be a string with letters in "IXYZ" and have the same length as the number of qubits provided.
If 'negative' is true the measurement outcome is flipped.
If more than one letter of 'bases' is not identity, then an ancilla qubit will be used, which is created using the
provided 'node'.

:param qubits: List of qubits to be measured.
:type qubits: list of :obj: `cqc.pythonLib.qubit`
:param bases: String specifying the Pauli-bases of the measurement. Example bases="IXY" for three qubits.
:type bases: str
:param node: The node storing the qubits. Used for creating an ancilla qubit.
:type node: :obj: `cqc.pythonLib.CQCConnection`
:param negative: If the measurement outcome should be flipped or not.
:type negative: bool
:return: The measurement outcome 0 or 1, where 0 correspond to the +1 eigenvalue of the measurement operator.
"""

if not (len(qubits) == len(bases)):
raise ValueError("Number of bases needs to be the number of qubits.")
if not all([(B in "IXYZ") for B in bases]):
raise ValueError("All elements of bases need to be in 'IXYZ'.")

num_qubits = len(qubits)

flip_basis = ["I"] * num_qubits
non_identity_bases = []

# Check if we need to flip the bases of the qubits
for i in range(len(bases)):
B = bases[i]
if B == "X":
flip_basis[i] = "H"
non_identity_bases.append(i)
elif B == "Y":
flip_basis[i] = "K"
non_identity_bases.append(i)
elif B == "Z":
non_identity_bases.append(i)
else:
pass

if len(non_identity_bases) == 0:
# Trivial measurement
m = 0

elif len(non_identity_bases) == 1:
# Single_qubit measurement
q_index = non_identity_bases[0]
q = qubits[q_index]

# Flip to correct basis
if flip_basis[q_index] == "H":
q.H()
if flip_basis[q_index] == "K":
q.K()

m = q.measure(inplace=True)

# Flip the qubit back
if flip_basis[q_index] == "H":
q.H()
if flip_basis[q_index] == "K":
q.K()

else:
# Parity measurement, ancilla needed

# Initialize ancilla qubit
anc = qubit(node)

# Flip to correct basis
for i in range(len(bases)):
if flip_basis[i] == "H":
qubits[i].H()
if flip_basis[i] == "K":
qubits[i].K()

# Transfer parity information to ancilla qubit
for i in non_identity_bases:
qubits[i].cnot(anc)

# Measure ancilla qubit
m = anc.measure()

# Flip to correct basis
for i in range(len(bases)):
if flip_basis[i] == "H":
qubits[i].H()
if flip_basis[i] == "K":
qubits[i].K()
if negative:
return (m + 1) % 2
else:
return m
3 changes: 0 additions & 3 deletions cqc/settings.ini

This file was deleted.

Loading