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"]