Source code for mimiqcircuits.operations.parallel
#
# Copyright © 2022-2024 University of Strasbourg. All Rights Reserved.
# Copyright © 2023-2025 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.
#
"""Parallel gate operation."""
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 instruction:
└── ⨷ ³ X @ q[1], q[2], q[3]
<BLANKLINE>
"""
_name = "Parallel"
_num_qubits = None
_num_bits = 0
_num_repeats = None
_op = None
[docs]
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 gettypekey(self):
return (Parallel, self.num_repeats, self.op.gettypekey())
[docs]
def get_operation(self):
return self.op
# export operations
__all__ = ["Parallel"]