""" ******************************************************************************** * Copyright (c) 2025 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 ******************************************************************************** """ import numpy as np from qrisp.circuit import QuantumCircuit, XGate from qrisp.simulator import QuantumState, advance_quantum_state, gen_res_dict class BufferedQuantumState: def __init__(self, simulator="qrisp"): if simulator == "qrisp": self.quantum_state = QuantumState(n=0) elif simulator == "stim": import stim self.quantum_state = stim.TableauSimulator() else: raise Exception("Don't know simulator {simulator}") self.buffer_qc = QuantumCircuit(0) self.deallocated_qubits = [] self.simulator = simulator self.qubit_to_index_dict = {} self.qubit_counter = 0 self.gate_counts = {} def add_qubit(self): if self.simulator == "qrisp": self.quantum_state.add_qubit() qb = self.buffer_qc.add_qubit() self.qubit_to_index_dict[qb] = self.qubit_counter self.qubit_counter += 1 return qb def append(self, op, qubits): self.buffer_qc.append(op, qubits) try: if op.name != "qb_alloc" and op.name != "qb_dealloc": self.gate_counts[op.name] += 1 except KeyError: self.gate_counts[op.name] = 1 def apply_buffer(self): if self.simulator == "qrisp": self.quantum_state = advance_quantum_state( self.buffer_qc.copy(), self.quantum_state, self.deallocated_qubits, self.qubit_to_index_dict, ) else: for instr in self.buffer_qc.data: qubit_indices = [self.qubit_to_index_dict[qb] for qb in instr.qubits] if instr.op.name == "x": self.quantum_state.x(*qubit_indices) elif instr.op.name == "y": self.quantum_state.y(*qubit_indices) elif instr.op.name == "z": self.quantum_state.z(*qubit_indices) elif instr.op.name == "h": self.quantum_state.h(*qubit_indices) elif instr.op.name == "cx": self.quantum_state.cx(*qubit_indices) elif instr.op.name == "cy": self.quantum_state.cy(*qubit_indices) elif instr.op.name == "cz": self.quantum_state.cz(*qubit_indices) elif instr.op.name == "s": self.quantum_state.s(*qubit_indices) elif instr.op.name == "s_dg": self.quantum_state.s_dag(*qubit_indices) elif not instr.op.name in ["qb_alloc", "qb_dealloc"]: raise Exception( f"Don't know how to simulate quantum gate {instr.op.name} with stim" ) for instr in self.buffer_qc.data: if instr.op.name == "qb_dealloc": self.buffer_qc.qubits.remove(instr.qubits[0]) del self.qubit_to_index_dict[instr.qubits[0]] self.buffer_qc = self.buffer_qc.clearcopy() def measure(self, qubit, track_measurement=True): if track_measurement: try: self.gate_counts["measure"] += 1 except KeyError: self.gate_counts["measure"] = 1 self.apply_buffer() if self.simulator == "qrisp": meas_res, self.quantum_state = self.quantum_state.measure( self.qubit_to_index_dict[qubit[0]], keep_res=True ) return meas_res elif self.simulator == "stim": return self.quantum_state.measure(self.qubit_to_index_dict[qubit[0]]) def reset(self, qubit): if qubit[0] not in self.qubit_to_index_dict: return meas_res = self.measure(qubit, track_measurement=False) if meas_res: self.buffer_qc.append(XGate(), qubit) def copy(self): res = BufferedQuantumState() res.buffer_qc = self.buffer_qc.copy() res.deallocated_qubits = list(self.deallocated_qubits) res.quantum_state = self.quantum_state.copy() res.qubit_to_index_dict = dict(self.qubit_to_index_dict) res.qubit_counter = self.qubit_counter return res def multi_measure(self, qubits, shots): try: self.gate_counts["measure"] += len(qubits) except KeyError: self.gate_counts["measure"] = len(qubits) self.apply_buffer() qubit_indices = [self.qubit_to_index_dict[qb] for qb in qubits] mes_ints, probs = self.quantum_state.multi_measure(qubit_indices) if shots is not None and shots != 0: samples = np.random.choice(len(mes_ints), int(shots), p=probs) samples = gen_res_dict(samples) res = {} for k, v in samples.items(): res[mes_ints[k]] = v return res else: res = {} for i in range(len(mes_ints)): res[mes_ints[i]] = probs[i] return res