from geqo.core.quantum_circuit import Sequence
from geqo.core.quantum_operation import QuantumOperation
[docs]
class QuantumControl(QuantumOperation):
"""This class allows to turn a quantum operation into an operation with additional quantum controls. The
argument ```onoff``` is a list of 0's and 1's corresponding to qubits that control the operations
when in the state 0 and 1, respectively. The number of qubits of the resulting unitary is the number of qubits of
the original gate plus the number of control qubits. The control qubits are ordered before the qubits of the
original gate.
For instance, ```QuantumControl([0,1],BasicGate("X", 2))``` defines a controlled version of
the gate ```BasicGate("X", 2)``` on two qubits. The resulting gate operates on four qubits and the gate is
executed when the first and second qubit are in the basis states 0 and 1, respectively.
"""
def __init__(self, onoff, qop):
"""
The constructor of this class takes a list of bit settings and an operator.
Parameters
----------
onoff : list( 0 | 1 )
A list of 0 and 1 values. A ```0``` corresponds to a negative control of a qubit (the operation is performed if the
basis state is 0) and ```1``` means a positive control qubit.
qop : geqo.core.quantum_operation.QuantumOperation
A quantum operation.
Returns
-------
QuantumControl : geqo.operations.controls.QuantumControl
An object of this class that corresponds to the controlled version of the provided gate.
"""
self.onoff = onoff
self.qop = qop
def __repr__(self):
"""
Returns
-------
string_representation : String
Representation of the object as character string.
"""
return "QuantumControl(" + str(self.onoff) + ", " + str(self.qop) + ")"
def __eq__(self, other):
"""
Comparator with other objects.
Parameters
----------
other : An object, which should be compared to this object.
Returns
-------
True : If the provided object is of the same type and if has the same list of qubit settings and the same operation.
False : else
"""
if not isinstance(other, QuantumControl):
return False
else:
return self.onoff == other.onoff and self.qop == other.qop
[docs]
def getInverse(self, mustInvert=False):
"""
Return an object of the same class, which corresponds to the inverse of the gate. The qubit setting is the same, but the operation is inverted, if possible. If no inverse exists, then an exception is raised.
Returns
-------
QuantumControl : geqo.operations.controls.QuantumControl
A new object of the class, which corresponds to the inverse gate. If no inverse exists, then an exception is raised.
"""
return QuantumControl(self.onoff, self.qop.getInverse())
[docs]
def getEquivalentSequence(self):
"""Return an object of the class ```Sequence```, which corresponds to the controlled version of the gate.
This function replaces a ```Sequence``` with control qubits into a ```Sequence``` of gates with the added control qubits.
Returns
-------
sequence : geqo.core.Sequence
An object of the class ```Sequence``` with controlled operations and with the appropriate bits and qubits. Note that the number
of qubits is extended by new qubits, which get new names, which are unique in the ```Sequence``` object.
"""
originalSequence = self.qop.getEquivalentSequence()
originalSequenceClassicalBits = originalSequence.bits
originalSequenceQuantumBits = originalSequence.qubits
# we need to prepend len(onoff) many new qubits; we have to find
# unused names for it. We try out 0,.... until we find new ones.
# The identifiers can be integers or character strings and we
# have to add a new identifier of the same type.
newQubits = []
counter = 0
while len(newQubits) < len(self.onoff):
if (
str(counter) not in newQubits
and counter not in newQubits
and str(counter) not in originalSequenceQuantumBits
and counter not in originalSequenceQuantumBits
):
if len(originalSequenceQuantumBits) > 0 and isinstance(
originalSequenceQuantumBits[0], int
):
newQubits.append(counter) # qubits have integers as identifier
else:
newQubits.append(str(counter)) # default case is string identifier
counter = counter + 1
newSequence = []
for s in originalSequence.gatesAndTargets:
if len(s) == 2:
gate = s[0]
targets = s[1]
newGate = QuantumControl(self.onoff, gate)
newSequence.append((newGate, newQubits + targets))
elif len(s) == 3:
gate = s[0]
ctargets = s[1]
s[2]
newGate = QuantumControl(self.onoff, gate)
newSequence.append((newGate, ctargets, newQubits + targets))
return Sequence(
originalSequenceClassicalBits,
newQubits + originalSequenceQuantumBits,
newSequence,
)
[docs]
def hasDecomposition(self):
"""
Returns
-------
hasDecomposition : Bool
True or False depending on whether the provided operation has a decomposition or not.
"""
return self.qop.hasDecomposition()
[docs]
def getNumberQubits(self):
"""
Return the number of qubits that are used by this operation. Note that this is the sum of the qubits of the provided operation and the number of control qubits.
Returns
-------
numberQubits : int
The number of qubits, which are used by the controlled operation.
"""
return self.qop.getNumberQubits() + len(self.onoff)
[docs]
def getNumberClassicalBits(self):
"""
Return the number of classical bits that are used by this operation. This is the same number as of the provided operation.
Returns
-------
numberBits : int
The number of classical bits, which are used by the controlled operation.
"""
return self.qop.getNumberClassicalBits()
[docs]
def isUnitary(self):
"""
Returns
-------
isUnitary : Bool
Return True or False depending on whether the provided operation is unitary or not.
"""
return self.qop.isUnitary()
[docs]
class ClassicalControl(QuantumOperation):
"""This class allows to turn a quantum operation into an operation with additional classical controls. The
quantum operation is specified by the argument ```qop```. The
argument ```onoff``` is a list of 0's and 1's corresponding to bits that control the operations
when set to 0 and 1, respectively. This operator acts on classical bits and qubits. The number of
classical bits is the length of the argument ```onofff``` and the number of qubits is the same
as for the quantum operation ```qop```.
For instance, ```ClassicalControl([0,1], BasicGate("X", 2))``` defines a controlled version of
the gate ```BasicGate("X", 2)``` on two qubits. The resulting gate operates on two classical bits and two qubits and
the gate is executed when the two bits are set to 0 and 1, respectively.
"""
def __init__(self, onoff, qop):
"""
The constructor of this class takes a list of bit settings and an operator.
Parameters
----------
onoff : list( 0 | 1 )
A list of 0 and 1 values. A ```0``` corresponds to a negative control of a classical bit (the operation is performed if the
bit is 0) and ```1``` means a positive classical control bit.
qop : geqo.core.quantum_operation.QuantumOperation
A quantum operation.
Returns
-------
ClassicalControl : geqo.operations.controls.ClassicalControl
An object of this class that corresponds to the controlled version of the provided gate.
"""
self.onoff = onoff
self.qop = qop
def __repr__(self):
"""
Returns
-------
string_representation : String
Representation of the object as character string.
"""
return "ClassicalControl(" + str(self.onoff) + ", " + str(self.qop) + ")"
def __eq__(self, other):
"""
Comparator with other objects.
Parameters
----------
other : An object, which should be compared to this object.
Returns
-------
True : If the provided object is of the same type and if has the same list of bit settings and the same operation.
False : else
"""
if not isinstance(other, ClassicalControl):
return False
else:
return self.onoff == other.onoff and self.qop == other.qop
[docs]
def getInverse(self):
"""
Return an object of the same class, which corresponds to the inverse of the gate. The bit setting is the same, but the operation is inverted, if possible. If no inverse exists, then an exception is raised.
Returns
-------
ClassicalControl : geqo.operations.controls.ClassicalControl
A new object of the class, which corresponds to the inverse gate. If no inverse exists, then an exception is raised.
"""
return ClassicalControl(self.onoff, self.qop.getInverse())
[docs]
def getEquivalentSequence(self):
"""Return an object of the class ```Sequence```, which corresponds to the controlled version of the gate.
This function replaces a ```Sequence``` with control bits into a ```Sequence``` of gates with the added control bits.
Returns
-------
sequence : geqo.core.Sequence
An object of the class ```Sequence``` with controlled operations and with the appropriate bits and qubits. Note that the number
of bits is extended by new bits, which get new names, which are unique in the ```Sequence``` object.
"""
originalSequence = self.qop.getEquivalentSequence()
originalSequenceClassicalBits = originalSequence.bits
originalSequenceQuantumBits = originalSequence.qubits
newBits = []
counter = 0
while len(newBits) < len(self.onoff):
if (
str(counter) not in newBits
and counter not in newBits
and str(counter) not in originalSequenceClassicalBits
and counter not in originalSequenceClassicalBits
):
if len(originalSequenceClassicalBits) > 0 and isinstance(
originalSequenceClassicalBits[0], int
):
newBits.append(counter) # qubits have integers as identifier
else:
newBits.append(str(counter)) # default case is string identifier
counter = counter + 1
newSequence = []
for s in originalSequence.gatesAndTargets:
if len(s) == 2:
gate = s[0]
targets = s[1]
newGate = ClassicalControl(self.onoff, gate)
newSequence.append((newGate, newBits, targets))
elif len(s) == 3:
gate = s[0]
ctargets = s[1]
s[2]
newGate = ClassicalControl(self.onoff, gate)
newSequence.append((newGate, newBits + ctargets, targets))
return Sequence(
newBits + originalSequenceClassicalBits,
originalSequenceQuantumBits,
newSequence,
)
[docs]
def getNumberQubits(self):
"""
Return the number of qubits that are used by this operation. This is the same number as of the provided operation.
Returns
-------
numberQubits : int
The number of qubits, which are used by the controlled operation.
"""
return self.qop.getNumberQubits()
[docs]
def getNumberClassicalBits(self):
"""
Return the number of classical bits that are used by this operation. This is the sum of the classical bits of the provided operation and the control bits.
Returns
-------
numberBits : int
The number of classical bits, which are used by the controlled operation.
"""
return self.qop.getNumberClassicalBits() + len(self.onoff)
[docs]
def hasDecomposition(self):
"""
Returns
-------
hasDecomposition : Bool
True or False depending on whether the provided operation has a decomposition or not.
"""
return self.qop.hasDecomposition()
[docs]
def isUnitary(self):
"""
Returns
-------
isUnitary : Bool
Return True or False depending on whether the provided operation is unitary or not.
"""
return self.qop.isUnitary()