Source code for mimiqcircuits.operations.parallel
#
# Copyright © 2022-2023 University of Strasbourg. 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
import mimiqcircuits as mc
import symengine as se
import sympy as sp
import mimiqcircuits.lazy as lz
[docs]
class Parallel(mc.Operation):
"""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:
└── Parallel(3, 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: mc.Operation):
if not isinstance(op, mc.Operation):
raise ValueError("op must be an Operation")
if isinstance(op, (mc.Barrier, mc.Reset, mc.Measure)):
raise TypeError(f"{op.__class__.__name__} cannot be Paralleled operation.")
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
[docs]
def matrix(self):
op_matrix = se.Matrix(sp.simplify(sp.Matrix(self.op.matrix()).evalf()))
return se.Matrix(reduce(np.kron, [op_matrix] * (self._num_repeats)).tolist())
@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):
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):
return f"Parallel({self.num_repeats}, {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"]