#
# 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.
import logging
import warnings
from cqc.cqcHeader import (
CQC_CMD_NEW,
CQC_CMD_I,
CQC_CMD_X,
CQC_CMD_Y,
CQC_CMD_Z,
CQC_CMD_T,
CQC_CMD_H,
CQC_CMD_K,
CQC_CMD_ROT_X,
CQC_CMD_ROT_Y,
CQC_CMD_ROT_Z,
CQC_CMD_CNOT,
CQC_CMD_CPHASE,
CQC_CMD_MEASURE,
CQC_CMD_MEASURE_INPLACE,
CQC_CMD_RESET,
CQC_CMD_RELEASE,
)
from .util import (
CQCGeneralError,
CQCUnsuppError,
QubitNotActiveError,
)
[docs]class qubit:
"""
A qubit.
"""
[docs] def __init__(self, cqc, notify=True, block=True, createNew=True, q_id=None, entInfo=None):
"""
Initializes the qubit. The cqc connection must be given.
If notify, the return message is received before the method finishes.
createNew is set to False when we receive a qubit.
- **Arguments**
:cqc: The CQCconnection used
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
:createNew: If NEW-message should be sent, used internally
:q_id: Qubit id, used internally if createNew
:entInfo: Entanglement information, if qubit is part of EPR-pair
"""
# Cqc connection
self._cqc = cqc
self.notify = cqc.notify
# Check if the cqc connection was openened using a 'with' statement
# If not, raise a deprecation warning
if not self._cqc._opened_with_with:
logging.info(
"You should open CQCConnection in a context, i.e. using 'with CQCConnection(...) as cqc:'. "
"Then qubits will be automatically released by the end of the program, independently of what happens. "
"For more information, see https://softwarequtech.github.io/SimulaQron/html/PythonLib.html"
)
# Whether the qubit is active. Will be set in the first run
self._active = None
if createNew:
# print info
logging.debug("App {} tells CQC: 'Create qubit'".format(self._cqc.name))
# Create new qubit at the cqc server
# TODO how to handle pending headers
self._cqc.commit_command(0, CQC_CMD_NEW, notify=notify, block=block)
# Get qubit id
try:
self._qID = self._cqc.new_qubitID()
except AttributeError:
raise CQCGeneralError("Didn't receive the qubit id")
# Activate qubit
self._set_active(True)
if notify and self.notify:
message = self._cqc.readMessage()
self._cqc.print_CQC_msg(message)
else:
self._qID = q_id
self._set_active(False) # Why?
# Entanglement information
self._set_entanglement_info(entInfo)
def __str__(self):
if self.active:
return "Qubit at the node {}".format(self._cqc.name)
else:
return "Not active qubit"
@property
def entanglement_info(self):
return self._entInfo
def _set_entanglement_info(self, ent_info):
self._entInfo = ent_info
# Lookup remote entangled node
self._remote_entNode = None
if self._entInfo:
ip = self._entInfo.node_B
port = self._entInfo.port_B
try:
for node in self._cqc._cqcNet.hostDict.values():
if (node.ip == ip) and (node.port == port):
self._remote_entNode = node.name
break
except AttributeError:
self._remote_entNode = None
@property
def active(self):
return self._active
@property
def remote_entangled_node(self):
return self._remote_entNode
def get_entInfo(self):
warnings.warn("get_entInfo is deprecated, use the property entanglement_info instead",
DeprecationWarning)
return self._entInfo
def print_entInfo(self):
warnings.warn("print_entInfo is deprecated",
DeprecationWarning)
if self.entanglement_info:
print(self.entanglement_info)
else:
print("No entanglement information")
def set_entInfo(self, entInfo):
warnings.warn("set_entInfo is deprecated, use _set_entanglement_info instead",
DeprecationWarning)
self._set_entanglement_info(entInfo)
def is_entangled(self):
if self._entInfo:
return True
return False
def get_remote_entNode(self):
warnings.warn("get_remote_entNode is deprecated, use the propery remote_entangled_node instead",
DeprecationWarning)
return self.remote_entangled_node
[docs] def check_active(self):
"""
Checks if the qubit is active
"""
if not self.active:
raise QubitNotActiveError("Qubit is not active")
def _set_active(self, be_active):
# Check if not already new state
if self._active == be_active:
return
if be_active:
self._cqc.active_qubits.append(self)
else:
if self in self._cqc.active_qubits:
self._cqc.active_qubits.remove(self)
self._active = be_active
def _single_qubit_gate(self, command, notify, block):
"""
Performs a single qubit gate specified by the command, called in I(), X(), Y() etc
:param command: the identifier of the command, as specified in cqcHeader.py
:param notify: Do we wish to be notified when done
:param block: Do we want the qubit to be blocked
"""
# check if qubit is active
self.check_active()
notify = notify and self.notify
self._cqc.put_command(
qID=self._qID,
command=command,
notify=notify,
block=block,
)
[docs] def I(self, notify=True, block=True):
"""
Performs an identity gate on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_I, notify, block)
[docs] def X(self, notify=True, block=True):
"""
Performs a X on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_X, notify, block)
[docs] def Y(self, notify=True, block=True):
"""
Performs a Y on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_Y, notify, block)
[docs] def Z(self, notify=True, block=True):
"""
Performs a Z on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_Z, notify, block)
[docs] def T(self, notify=True, block=True):
"""
Performs a T gate on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_T, notify, block)
[docs] def H(self, notify=True, block=True):
"""
Performs a Hadamard on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_H, notify, block)
[docs] def K(self, notify=True, block=True):
"""
Performs a K gate on the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_qubit_gate(CQC_CMD_K, notify, block)
def _single_gate_rotation(self, command, step, notify, block):
"""
Perform a rotation on a qubit
:param command: the rotation qubit command as specified in cqcHeader.py
:param step: Determines the rotation angle in steps of 2*pi/256
:param notify: Do we wish to be notified when done
:param block: Do we want the qubit to be blocked
:return:
"""
# check if qubit is active
self.check_active()
notify = notify and self.notify
self._cqc.put_command(
qID=self._qID,
command=command,
step=step,
notify=notify,
block=block,
)
[docs] def rot_X(self, step, notify=True, block=True):
"""
Applies rotation around the x-axis with the angle of step*2*pi/256 radians.
If notify, the return message is received before the method finishes.
- **Arguments**
:step: Determines the rotation angle in steps of 2*pi/256
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_gate_rotation(CQC_CMD_ROT_X, step, notify, block)
[docs] def rot_Y(self, step, notify=True, block=True):
"""
Applies rotation around the y-axis with the angle of step*2*pi/256 radians.
If notify, the return message is received before the method finishes.
- **Arguments**
:step: Determines the rotation angle in steps of 2*pi/256
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_gate_rotation(CQC_CMD_ROT_Y, step, notify, block)
[docs] def rot_Z(self, step, notify=True, block=True):
"""
Applies rotation around the z-axis with the angle of step*2*pi/256 radians.
If notify, the return message is received before the method finishes.
- **Arguments**
:step: Determines the rotation angle in steps of 2*pi/256
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._single_gate_rotation(CQC_CMD_ROT_Z, step, notify, block)
def _two_qubit_gate(self, command, target, notify, block):
"""
Perform a two qubit gate on the qubit
:param command: the two qubit gate command as specified in cqcHeader.py
:param target: The target qubit
:param notify: Do we wish to be notified when done
:param block: Do we want the qubit to be blocked
"""
# check if qubit is active
self.check_active()
target.check_active()
if self._cqc != target._cqc:
raise CQCUnsuppError("Multi qubit operations can only operate on qubits in the same process")
if self == target:
raise CQCUnsuppError("Cannot perform multi qubit operation where control and target are the same")
notify = notify and self.notify
self._cqc.put_command(
qID=self._qID,
command=command,
notify=notify,
block=block,
xtra_qID=target._qID,
)
[docs] def cnot(self, target, notify=True, block=True):
"""
Applies a cnot onto target.
Target should be a qubit-object with the same cqc connection.
If notify, the return message is received before the method finishes.
- **Arguments**
:target: The target qubit
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._two_qubit_gate(CQC_CMD_CNOT, target, notify, block)
[docs] def cphase(self, target, notify=True, block=True):
"""
Applies a cphase onto target.
Target should be a qubit-object with the same cqc connection.
If notify, the return message is received before the method finishes.
- **Arguments**
:target: The target qubit
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
self._two_qubit_gate(CQC_CMD_CPHASE, target, notify, block)
[docs] def measure(self, inplace=False, block=True):
"""
Measures the qubit in the standard basis and returns the measurement outcome.
If now MEASOUT message is received, None is returned.
If inplace=False, the measurement is destructive and the qubit is removed from memory.
If inplace=True, the qubit is left in the post-measurement state.
- **Arguments**
:inplace: If false, measure destructively.
:block: Do we want the qubit to be blocked
"""
# check if qubit is active
self.check_active()
if inplace:
command = CQC_CMD_MEASURE_INPLACE
else:
command = CQC_CMD_MEASURE
# Set qubit to non active so the user can receive helpful errors during compile time
# if this qubit is used after this measurement
self._set_active(False)
self._cqc.put_command(
qID=self._qID,
command=command,
notify=False,
block=block,
)
if self._cqc.pend_messages:
return None
else:
return self._cqc.return_meas_outcome()
[docs] def reset(self, notify=True, block=True):
"""
Resets the qubit.
If notify, the return message is received before the method finishes.
- **Arguments**
:nofify: Do we wish to be notified when done.
:block: Do we want the qubit to be blocked
"""
# check if qubit is active
self.check_active()
notify = notify and self.notify
self._cqc.put_command(
qID=self._qID,
command=CQC_CMD_RESET,
notify=notify,
block=block,
)
[docs] def release(self, notify=True, block=True):
"""
Release the current qubit
:param notify: Do we wish to be notified when done
:param block: Do we want the qubit to be blocked
:return:
"""
notify = notify and self.notify
self._set_active(False)
self._cqc.put_command(
qID=self._qID,
command=CQC_CMD_RELEASE,
notify=notify,
block=block,
)
[docs] def getTime(self, block=True):
"""
Returns the time information of the qubit.
If no INF_TIME message is received, None is returned.
- **Arguments**
:block: Do we want the qubit to be blocked
"""
# check if qubit is active
self.check_active()
# print info
logging.debug("App {} tells CQC: 'Return time-info of qubit with ID {}'".format(self._cqc.name, self._qID))
self._cqc.sendGetTime(self._qID, notify=0, block=int(block))
# Return time-stamp
message = self._cqc.readMessage()
try:
otherHdr = message[1]
return otherHdr.datetime
except AttributeError:
return None