Source code for qrisp.environments.gate_wrap_environment

"""
\********************************************************************************
* Copyright (c) 2023 the Qrisp authors
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
"""


from qrisp.circuit import QuantumCircuit, QubitAlloc, QubitDealloc, XGate
from qrisp.environments import QuantumEnvironment


[docs] class GateWrapEnvironment(QuantumEnvironment): """ This environment allows to hide complexity in the circuit visualisation. Operations appended inside this environment are bundled into a single :ref:`Instruction` object. The functionality of this :ref:`QuantumEnvironment` can also be used with the :meth:`gate_wrap <qrisp.gate_wrap>` decorator. After compiling, the wrapped instruction can be retrieved using the ``.instruction`` attribute. Parameters ---------- name : string, optional The name of the resulting gate. The default is None. Examples -------- We create some :ref:`QuantumVariable` and execute some gates inside a GateWrapEnvironment: :: from qrisp import QuantumVariable, GateWrapEnvironment, x, y, z qv = QuantumVariable(3) gwe = GateWrapEnvironment("example") with gwe: x(qv[0]) y(qv[1]) z(qv[2]) >>> print(qv.qs) :: QuantumCircuit: -------------- ┌──────────┐ qv.0: ┤0 ├ │ example │ qv.1: ┤1 ├ └──┬───┬───┘ qv.2: ───┤ Z ├──── └───┘ Live QuantumVariables: --------------------- QuantumVariable qv We can access the instruction, which has been appended using the ``.instruction`` attribute: >>> instruction = gwe.instruction >>> print(instruction.op.definition) :: ┌───┐ qb_41: ┤ X ├ ├───┤ qb_42: ┤ Y ├ └───┘ Using the :meth:`gate_wrap <qrisp.gate_wrap>` decorator we can quickly gate wrap functions: :: from qrisp import gate_wrap @gate_wrap def example_function(qv): x(qv[0]) y(qv[1]) z(qv[2]) example_function(qv) >>> print(qv.qs) :: QuantumCircuit: -------------- ┌──────────┐┌───────────────────┐ qv.0: ┤0 ├┤0 ├ │ example ││ │ qv.1: ┤1 ├┤1 example_function ├ └──┬───┬───┘│ │ qv.2: ───┤ Z ├────┤2 ├ └───┘ └───────────────────┘ Live QuantumVariables: --------------------- QuantumVariable qv """ def __init__(self, name=None): super().__init__() self.name = name self.manual_allocation_management = True def compile(self): temp_data_list = list(self.env_qs.data) self.env_qs.data = [] super().compile() compiled_qc = self.env_qs.clearcopy() compiled_qc.data = list(self.env_qs.data) self.env_qs.clear_data() self.env_qs.data.extend(temp_data_list) if len(compiled_qc.data) == 0: self.instruction = None return None qc = QuantumCircuit(len(self.env_qs.qubits), len(self.env_qs.clbits)) translation_dic = { self.env_qs.qubits[i]: qc.qubits[i] for i in range(len(qc.qubits)) } translation_dic.update( {self.env_qs.clbits[i]: qc.clbits[i] for i in range(len(qc.clbits))} ) qubit_set = set([]) dealloc_list = [] alloc_list = [] for instr in compiled_qc.data: qubit_set = qubit_set.union( set([translation_dic[qb] for qb in instr.qubits]) ) if instr.op.name == "qb_dealloc": instr.qubits[0].allocated = True # dealloc_list.append(instr) dealloc_list.append(instr.qubits[0]) continue if instr.op.name == "qb_alloc": alloc_list.append(instr.qubits[0]) try: dealloc_list.remove(instr.qubits[0]) except ValueError: pass continue qc.append( instr.op, [translation_dic[qb] for qb in instr.qubits], [translation_dic[cb] for cb in instr.clbits], ) idle_qubit_list = list(set(qc.qubits) - qubit_set) for j in range(len(idle_qubit_list)): for i in range(len(qc.qubits)): if qc.qubits[i].identifier == idle_qubit_list[j].identifier: qc.qubits.pop(i) break translation_dic_inv = { translation_dic[key]: key for key in translation_dic.keys() } gate = qc.to_gate(self.name) alloc_list = list(set(alloc_list)) for qb in alloc_list: self.env_qs.append(QubitAlloc(), [qb]) self.env_qs.append( gate, [translation_dic_inv[qb] for qb in qc.qubits], [translation_dic_inv[cb] for cb in qc.clbits], ) self.instruction = self.env_qs.data[-1] dealloc_list = list(set(dealloc_list)) for qb in dealloc_list: self.env_qs.append(QubitDealloc(), [qb]) qb.allocated = False