From 52740faf31752bd92432021ddba59cff7081f43f Mon Sep 17 00:00:00 2001 From: BrunoLiegiBastonLiegi Date: Wed, 27 Mar 2024 16:41:21 +0100 Subject: [PATCH 1/5] fix: fixed classical registers in to_qasm --- src/qibo/models/_openqasm.py | 1 + src/qibo/models/circuit.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/qibo/models/_openqasm.py b/src/qibo/models/_openqasm.py index 5b03470cd3..96ce10ce1d 100644 --- a/src/qibo/models/_openqasm.py +++ b/src/qibo/models/_openqasm.py @@ -178,6 +178,7 @@ def to_circuit( def _get_measurement(self, measurement): """Converts a :class:`openqasm3.ast.QuantumMeasurementStatement` statement into :class:`qibo.gates.measurements.M`.""" + qubit = self._get_qubit(measurement.measure.qubit) register = measurement.target.name.name if register not in self.c_registers: diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index db7e0ea880..bc2c2ed40c 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -1156,16 +1156,14 @@ def to_qasm(self): code += [f"qreg q[{self.nqubits}];"] # Set measurements - for measurement in self.measurements: - reg_name = measurement.register_name - reg_qubits = measurement.target_qubits - if not reg_name.islower(): + for register, qubits in self.measurement_tuples.items(): + if not register.islower(): raise_error( NameError, "OpenQASM does not support capital letters in " - + f"register names but {reg_name} was used", + + f"register names but {register} was used", ) - code.append(f"creg {reg_name}[{len(reg_qubits)}];") + code.append(f"creg {register}[{len(qubits)}];") # Add gates for gate in self.queue: @@ -1186,10 +1184,9 @@ def to_qasm(self): code.append(f"{name} {qubits};") # Add measurements - for measurement in self.measurements: - reg_name = measurement.register_name - for i, q in enumerate(measurement.target_qubits): - code.append(f"measure q[{q}] -> {reg_name}[{i}];") + for register, qubits in self.measurement_tuples.items(): + for i, q in enumerate(qubits): + code.append(f"measure q[{q}] -> {register}[{i}];") return "\n".join(code) From e4f35f0b0f04c127b79e37ccf19e14c5c5919dea Mon Sep 17 00:00:00 2001 From: BrunoLiegiBastonLiegi Date: Wed, 27 Mar 2024 17:09:10 +0100 Subject: [PATCH 2/5] fix: added test with separated measurements but same register --- src/qibo/models/_openqasm.py | 1 - tests/test_models_circuit_qasm.py | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/qibo/models/_openqasm.py b/src/qibo/models/_openqasm.py index 96ce10ce1d..5b03470cd3 100644 --- a/src/qibo/models/_openqasm.py +++ b/src/qibo/models/_openqasm.py @@ -178,7 +178,6 @@ def to_circuit( def _get_measurement(self, measurement): """Converts a :class:`openqasm3.ast.QuantumMeasurementStatement` statement into :class:`qibo.gates.measurements.M`.""" - qubit = self._get_qubit(measurement.measure.qubit) register = measurement.target.name.name if register not in self.c_registers: diff --git a/tests/test_models_circuit_qasm.py b/tests/test_models_circuit_qasm.py index 5170b3bd01..7eda4106bd 100644 --- a/tests/test_models_circuit_qasm.py +++ b/tests/test_models_circuit_qasm.py @@ -188,11 +188,19 @@ def test_crotations(): target = c.to_qasm() -def test_measurements(): +@pytest.mark.parametrize( + "measurements", + [ + [gates.M(0, 1)], + [gates.M(0, register_name="register0"), gates.M(1, register_name="register0")], + ], +) +def test_measurements(measurements): c = Circuit(2) c.add(gates.X(0)) c.add(gates.Y(1)) - c.add(gates.M(0, 1)) + for m in measurements: + c.add(m) target = f"""// Generated by QIBO {__version__} OPENQASM 2.0; include "qelib1.inc"; From bcaebac2afb37261d9bb29deaaaf186afe140a3a Mon Sep 17 00:00:00 2001 From: BrunoLiegiBastonLiegi Date: Tue, 2 Apr 2024 17:01:45 +0200 Subject: [PATCH 3/5] fix: removing support for register append --- src/qibo/models/_openqasm.py | 30 ++++++++++++++++++++++-------- src/qibo/models/circuit.py | 8 ++++++++ src/qibo/quantum_info/clifford.py | 4 ++-- src/qibo/result.py | 21 +++++++++++++++++---- tests/test_models_circuit_qasm.py | 1 - tests/test_result.py | 13 +++++++++++++ 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/qibo/models/_openqasm.py b/src/qibo/models/_openqasm.py index 5b03470cd3..0ebd1c7acb 100644 --- a/src/qibo/models/_openqasm.py +++ b/src/qibo/models/_openqasm.py @@ -170,9 +170,7 @@ def to_circuit( density_matrix, wire_names=self._construct_wire_names(), ) - for gate in gates: - circ.add(gate) - self._reorder_registers(circ.measurements) + circ.add(self._merge_measurements(gates)) return circ def _get_measurement(self, measurement): @@ -267,11 +265,27 @@ def _def_gate(self, definition): gates = [self._get_gate(gate) for gate in definition.body] self.defined_gates.update({name: CustomQASMGate(name, gates, qubits, args)}) - def _reorder_registers(self, measurements): - """Reorders the registers of the provided :class:`qibo.gates.measurements.M` - gates according to the classical registers order defined in the QASM program.""" - for meas in measurements: - meas.target_qubits = [self.c_registers[meas.register_name].pop(0)] + def _merge_measurements(self, gates): + """Merges separated measurements of a same register into a single one. + This is needed because qibo doesn't allow to separetely define two measurements in a same register: + + # not allowed + c.add(gates.M(0, register="m0")) + c.add(gates.M(1, register="m0")) + """ + updated_queue = [] + for gate in gates: + if isinstance(gate, qibo.gates.M): + if gate.register_name in self.c_registers: + updated_queue.append( + qibo.gates.M( + *self.c_registers.pop(gate.register_name), + register_name=gate.register_name, + ) + ) + else: + updated_queue.append(gate) + return updated_queue def _construct_wire_names(self): """Builds the wires names from the declared quantum registers.""" diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index bc2c2ed40c..b87af1bbbf 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -667,6 +667,13 @@ def add(self, gate): nreg = self.queue.nmeasurements - 1 gate.register_name = f"register{nreg}" else: + name = gate.register_name + for mgate in self.measurements: + if name == mgate.register_name: + raise_error( + KeyError, f"Register {name} already exists in circuit." + ) + """ registers = self.measurement_tuples for register, qubits in registers.items(): intersection = set(qubits).intersection(set(gate.target_qubits)) @@ -675,6 +682,7 @@ def add(self, gate): KeyError, f"Qubits {tuple(intersection)} already measured in register `{register}`.", ) + """ gate.result.circuit = self if gate.collapse: diff --git a/src/qibo/quantum_info/clifford.py b/src/qibo/quantum_info/clifford.py index 11d1b4e9ec..1acce5bcdb 100644 --- a/src/qibo/quantum_info/clifford.py +++ b/src/qibo/quantum_info/clifford.py @@ -273,8 +273,8 @@ def samples(self, binary: bool = True, registers: bool = False): if registers: return { - gate.register_name: gate.result.samples(binary) - for gate in self.measurements + register: gate.result.samples(binary) + for register, gate in self._measurement_registers } if binary: diff --git a/src/qibo/result.py b/src/qibo/result.py index 934f156e39..4936af18b5 100644 --- a/src/qibo/result.py +++ b/src/qibo/result.py @@ -177,6 +177,7 @@ def __init__( self.nshots = nshots self._measurement_gate = None + self._measurement_registers = None self._probs = probabilities self._samples = samples self._frequencies = None @@ -255,8 +256,8 @@ def frequencies(self, binary: bool = True, registers: bool = False): if registers: return { - gate.register_name: gate.result.frequencies(binary) - for gate in self.measurements + register: gate.result.frequencies(binary) + for register, gate in self._measurement_registers } if binary: @@ -362,8 +363,8 @@ def samples(self, binary: bool = True, registers: bool = False): if registers: return { - gate.register_name: gate.result.samples(binary) - for gate in self.measurements + register: gate.result.samples(binary) + for register, gate in self._measurement_registers } if binary: @@ -388,6 +389,18 @@ def measurement_gate(self): return self._measurement_gate + def measurement_registers(self): + if self._measurement_registers is None: + registers = {} + for gate in self.measurements: + if gate.register_name in registers: + registers[gate.register_name].add(gate) + else: + registers[gate.register_name] = gate + self._measurement_registers = registers + + return self._measurement_registers + def apply_bitflips(self, p0: float, p1: Optional[float] = None): """Apply bitflips to the measurements with probabilities `p0` and `p1` diff --git a/tests/test_models_circuit_qasm.py b/tests/test_models_circuit_qasm.py index 7eda4106bd..39c4e4c44f 100644 --- a/tests/test_models_circuit_qasm.py +++ b/tests/test_models_circuit_qasm.py @@ -192,7 +192,6 @@ def test_crotations(): "measurements", [ [gates.M(0, 1)], - [gates.M(0, register_name="register0"), gates.M(1, register_name="register0")], ], ) def test_measurements(measurements): diff --git a/tests/test_result.py b/tests/test_result.py index 53d274d896..0d2aa8b131 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -40,6 +40,19 @@ def test_measurementoutcomes_probabilities(backend, qubits): backend.assert_allclose(result.probabilities(qubits), probabilities, atol=1e-1) +def test_measurementoutcomes_measurement_registers(backend): + c = Circuit(3) + c.add(gates.H(0)) + c_copy = c.copy() + c.add(gates.M(0, register_name="m0")) + c.add(gates.M(2, register_name="m0")) + c_copy.add(gates.M(0, 2, register_name="m0")) + res = backend.execute_circuit(c) + res_copy = backend.execute_circuit(c_copy) + samples = res.samples(registers=True) + samples_copy = res_copy.samples(registers=True) + + def test_circuit_result_error(backend): c = models.Circuit(1) state = np.array([1, 0]) From 56b1168e9f6df773fd66c8309f8e04a38309433d Mon Sep 17 00:00:00 2001 From: BrunoLiegiBastonLiegi Date: Tue, 2 Apr 2024 17:12:31 +0200 Subject: [PATCH 4/5] fix: some cleaning --- src/qibo/models/circuit.py | 10 ---------- src/qibo/result.py | 21 ++++----------------- tests/test_result.py | 13 ------------- 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index b87af1bbbf..67ec3a670b 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -673,16 +673,6 @@ def add(self, gate): raise_error( KeyError, f"Register {name} already exists in circuit." ) - """ - registers = self.measurement_tuples - for register, qubits in registers.items(): - intersection = set(qubits).intersection(set(gate.target_qubits)) - if len(intersection) > 0: - raise_error( - KeyError, - f"Qubits {tuple(intersection)} already measured in register `{register}`.", - ) - """ gate.result.circuit = self if gate.collapse: diff --git a/src/qibo/result.py b/src/qibo/result.py index 4936af18b5..934f156e39 100644 --- a/src/qibo/result.py +++ b/src/qibo/result.py @@ -177,7 +177,6 @@ def __init__( self.nshots = nshots self._measurement_gate = None - self._measurement_registers = None self._probs = probabilities self._samples = samples self._frequencies = None @@ -256,8 +255,8 @@ def frequencies(self, binary: bool = True, registers: bool = False): if registers: return { - register: gate.result.frequencies(binary) - for register, gate in self._measurement_registers + gate.register_name: gate.result.frequencies(binary) + for gate in self.measurements } if binary: @@ -363,8 +362,8 @@ def samples(self, binary: bool = True, registers: bool = False): if registers: return { - register: gate.result.samples(binary) - for register, gate in self._measurement_registers + gate.register_name: gate.result.samples(binary) + for gate in self.measurements } if binary: @@ -389,18 +388,6 @@ def measurement_gate(self): return self._measurement_gate - def measurement_registers(self): - if self._measurement_registers is None: - registers = {} - for gate in self.measurements: - if gate.register_name in registers: - registers[gate.register_name].add(gate) - else: - registers[gate.register_name] = gate - self._measurement_registers = registers - - return self._measurement_registers - def apply_bitflips(self, p0: float, p1: Optional[float] = None): """Apply bitflips to the measurements with probabilities `p0` and `p1` diff --git a/tests/test_result.py b/tests/test_result.py index 0d2aa8b131..53d274d896 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -40,19 +40,6 @@ def test_measurementoutcomes_probabilities(backend, qubits): backend.assert_allclose(result.probabilities(qubits), probabilities, atol=1e-1) -def test_measurementoutcomes_measurement_registers(backend): - c = Circuit(3) - c.add(gates.H(0)) - c_copy = c.copy() - c.add(gates.M(0, register_name="m0")) - c.add(gates.M(2, register_name="m0")) - c_copy.add(gates.M(0, 2, register_name="m0")) - res = backend.execute_circuit(c) - res_copy = backend.execute_circuit(c_copy) - samples = res.samples(registers=True) - samples_copy = res_copy.samples(registers=True) - - def test_circuit_result_error(backend): c = models.Circuit(1) state = np.array([1, 0]) From 78c805673c430eb378f48f3e7d148ccc007ceb8f Mon Sep 17 00:00:00 2001 From: BrunoLiegiBastonLiegi Date: Tue, 2 Apr 2024 17:16:56 +0200 Subject: [PATCH 5/5] fix: further leftovers --- src/qibo/quantum_info/clifford.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/quantum_info/clifford.py b/src/qibo/quantum_info/clifford.py index 1acce5bcdb..11d1b4e9ec 100644 --- a/src/qibo/quantum_info/clifford.py +++ b/src/qibo/quantum_info/clifford.py @@ -273,8 +273,8 @@ def samples(self, binary: bool = True, registers: bool = False): if registers: return { - register: gate.result.samples(binary) - for register, gate in self._measurement_registers + gate.register_name: gate.result.samples(binary) + for gate in self.measurements } if binary: