Source code for mimiqcircuits.operations.parallel
#
# Copyright © 2022-2024 University of Strasbourg. All Rights Reserved.
# Copyright © 2032-2024 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import numpy as np
from functools import reduce
from mimiqcircuits.operations.gates.gate import Gate
import symengine as se
import sympy as sp
import mimiqcircuits.lazy as lz
import mimiqcircuits as mc
def to_superscript(number):
superscript_map = str.maketrans("0123456789", "⁰¹²³⁴⁵⁶⁷⁸⁹")
return str(number).translate(superscript_map)
[docs]
class Parallel(Gate):
"""Parallel operation
This is a composite operation that applies multiple gates in parallel to the circuit at once.
Examples:
>>> from mimiqcircuits import *
>>> c= Circuit()
>>> c.push(Parallel(3,GateX()),1,2,3)
4-qubit circuit with 1 instructions:
└── ⨷ ³ X @ q[1], q[2], q[3]
<BLANKLINE>
"""
_name = "Parallel"
_num_qubits = None
_num_bits = 0
_num_repeats = None
_op = None
def __init__(self, num_repeats, op: Gate):
if not isinstance(op, (Gate)):
raise TypeError(f"{op.__class__.__name__} cannot be Paralleled.")
if self.num_bits != 0:
raise ValueError("Parallel operations cannot act on classical bits.")
if num_repeats < 2:
raise ValueError(
f"Parallel operations must have at least two repeats. If one, just use {op}."
)
super().__init__()
self._num_qubits = op.num_qubits * num_repeats
self._num_repeats = num_repeats
self._op = op
self._qregsizes = [1] * self._num_qubits
self._num_qregs = self._num_qubits
def _matrix(self):
op_matrix = self.op.matrix()
return reduce(np.kron, [op_matrix] * (self._num_repeats))
@property
def num_repeats(self):
return self._num_repeats
@num_repeats.setter
def num_repeats(self, value):
raise ValueError("Cannot set num_repeats. Read only parameter.")
@property
def op(self):
return self._op
@op.setter
def op(self, op):
raise ValueError("Cannot set op. Read only parameter.")
[docs]
def iswrapper(self):
return True
def _getparams(self):
return self.op.getparams()
[docs]
def inverse(self):
return Parallel(self.num_repeats, self.op.inverse())
[docs]
def power(self, *args):
if len(args) == 0:
return lz.power(self)
elif len(args) == 1:
pwr = args[0]
return mc.Power(self.op, pwr).parallel(self.num_repeats)
else:
raise ValueError("Invalid number of arguments.")
[docs]
def control(self, *args):
if len(args) == 0:
return lz.control(self)
elif len(args) == 1:
num_controls = args[0]
return mc.Control(num_controls, self)
else:
raise ValueError("Invalid number of arguments.")
[docs]
def parallel(self, *args):
if len(args) == 0:
return lz.parallel(self)
elif len(args) == 1:
num_repeats = args[0]
return Parallel(self.num_repeats * num_repeats, self.op)
else:
raise ValueError("Invalid number of arguments.")
def _decompose(self, circ, qubits, bits, zvars):
nq = self.op.num_qubits
for i in range(self.num_repeats):
q = [qubits[j] for j in range(i * nq, (i + 1) * nq)]
circ.push(self.op, *q)
return circ
def __str__(self):
tail = f"⨷ {to_superscript(self.num_repeats)}"
return f"{tail} {self.op}"
[docs]
def evaluate(self, d):
repeat = self.num_repeats
return self.op.evaluate(d).parallel(repeat)
[docs]
def get_operation(self):
return self.op
# export operations
__all__ = ["Parallel"]