Source code for mimiqcircuits.operations.gates.custom

#
# 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 mimiqcircuits.operations.gates.gate as mcg
from numpy import ndarray
import symengine as se
import sympy as sp
import numpy as np


[docs] class GateCustom(mcg.Gate): """One or Two qubit Custom gates. Examples: >>> from mimiqcircuits import Circuit, GateCustom >>> import numpy as np >>> matrix = np.array([[1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 0, -1j],[0, 0, 1j, 0]]) >>> c = Circuit() >>> c.push(GateCustom(matrix), 0, 1) 2-qubit circuit with 1 instructions: └── Custom([1.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 0.0*I]...[0.0 + 0.0*I, 0.0 + 0.0*I, 0.0 + 1.0*I, 0.0 + 0.0*I]) @ q[0,1] <BLANKLINE> """ _name = "Custom" _num_qubits = None _qregsizes = None def __init__(self, matrix): super().__init__() if isinstance(matrix, ndarray): mat = se.Matrix(matrix.tolist()) elif isinstance(matrix, se.Matrix): mat = matrix else: raise TypeError( f"{type(matrix)} not supported in GateCustom, use symengine.Matrix or numpy.ndarray." ) if mat.rows != mat.cols: raise ValueError("Matrix is not square") tolerance = 1e-8 if any( isinstance(mat[i, j], (se.Symbol, str)) for i in range(mat.rows) for j in range(mat.cols) ): pass else: if not self.is_unitary(mat, tol=tolerance): raise ValueError("Matrix is not unitary") num_qubits = (mat.rows >> 2) + 1 if mat.rows != 2**num_qubits: raise ValueError("Wrong number of the rows for the matrix") self.matrix = mat self._num_qubits = num_qubits self._qregsizes = [ num_qubits, ] @property def _matrix(self): return self.matrix @property def num_qubits(self): return self._num_qubits
[docs] def inverse(self): return GateCustom(self.matrix.inv())
[docs] @staticmethod def is_unitary(matrix, tol=1e-8): conjugate_transpose = matrix.transpose().conjugate() product = matrix * conjugate_transpose identity_matrix = se.eye(matrix.rows) return all( abs(product[i, j] - identity_matrix[i, j]) < tol for i in range(matrix.rows) for j in range(matrix.cols) )
def __repr__(self): return self.pretty_print() def __str__(self): return f"{self._name}({se.Matrix(self.matrix).tolist()[0]}...{se.Matrix(self.matrix).tolist()[-1]})"
[docs] def pretty_print(self): if isinstance(self.matrix, se.Matrix): U = np.array(self.matrix.tolist(), dtype=object) else: U = np.array(self.matrix, dtype=object) result = f"{self._num_qubits}-qubit GateCustom:\n" rows, cols = U.shape for i in range(rows): if i < rows - 1: result += "├── " else: result += "└── " result += " ".join(map(str, U[i, :])) if i < rows - 1: result += "\n" return result
[docs] def evaluate(self, d): sympy_matrix = sp.Matrix(self.matrix) matrix = sympy_matrix.applyfunc( lambda entry: entry.subs(d) if not isinstance(entry, (float, int)) else entry ) evaluated_matrix = se.Matrix(matrix.tolist()) return GateCustom(evaluated_matrix)
__all__ = ["Custom"]