Skip to content

Commit

Permalink
Merge pull request #1070 from qiboteam/circuit_output_bugfix
Browse files Browse the repository at this point in the history
Circuit Execution Output bugfix for the qibojit and qibolab backends
  • Loading branch information
scarrazza committed Nov 2, 2023
2 parents 8940245 + bc1baa3 commit 86bec1e
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 57 deletions.
4 changes: 3 additions & 1 deletion src/qibo/backends/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,10 +521,12 @@ def execute_circuit_repeated(self, circuit, nshots, initial_state=None):
results.append(sample)
if not circuit.density_matrix:
samples.append("".join([str(s) for s in sample]))
for gate in circuit.measurements:
gate.result.reset()

if circuit.density_matrix: # this implies also it has_collapse
assert circuit.has_collapse
final_state = np.asarray(final_states).mean(0)
final_state = np.mean(self.to_numpy(final_states), 0)
if circuit.measurements:
qubits = [q for m in circuit.measurements for q in m.target_qubits]
final_result = CircuitResult(
Expand Down
19 changes: 2 additions & 17 deletions src/qibo/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def state(self, numpy=False):
The state in the computational basis.
"""
if numpy:
return np.asarray(self._state)
return self.backend.to_numpy(self._state)
else:
return self._state

Expand Down Expand Up @@ -147,15 +147,6 @@ class MeasurementOutcomes:
def __init__(
self, measurements, backend, probabilities=None, samples=None, nshots=1000
):
if probabilities is None and samples is None:
raise RuntimeError(
"You have to provide either the `probabilities` or the `samples` to build a `MeasurementOutcomes` object."
)
if probabilities is not None and samples is not None:
raise RuntimeError(
"Both the `probabilities` and the `samples` were provided to build the `MeasurementOutcomes` object. Don't know which one to use."
)

self.backend = backend
self.measurements = measurements
self.nshots = nshots
Expand All @@ -166,9 +157,6 @@ def __init__(
self._frequencies = None
self._repeated_execution_frequencies = None

for gate in self.measurements:
gate.result.reset()

def frequencies(self, binary=True, registers=False):
"""Returns the frequencies of measured samples.
Expand Down Expand Up @@ -238,10 +226,7 @@ def frequencies(self, binary=True, registers=False):
return self._frequencies

def has_samples(self):
if self._samples is not None:
return True
else: # pragma: no cover
return False
return self.measurements[0].result.has_samples() or self._samples is not None

def samples(self, binary=True, registers=False):
"""Returns raw measurement samples.
Expand Down
36 changes: 22 additions & 14 deletions tests/test_models_circuit_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ def create_circuit(theta=0.1234):

# Run eager circuit
c1 = create_circuit()
r1 = backend.execute_circuit(c1)._state
r1 = backend.execute_circuit(c1).state()

# Run compiled circuit
c2 = create_circuit()
c2.compile(backend)
r2 = c2()._state
np.testing.assert_allclose(r1, r2)
r2 = c2().state()
np.testing.assert_allclose(backend.to_numpy(r1), backend.to_numpy(r2))


def test_compiling_twice_exception(backend):
Expand All @@ -59,14 +59,22 @@ def test_memory_error(backend, accelerators):


def test_repeated_execute(backend, accelerators):
c = Circuit(4, accelerators, density_matrix=True)
thetas = np.random.random(4)
c.add((gates.RY(i, t) for i, t in enumerate(thetas)))
target_state = backend.execute_circuit(c).state()
target_state = target_state
c.has_collapse = True
final_state = backend.execute_circuit(c, nshots=20)._state
backend.assert_allclose(final_state, target_state)
if accelerators is not None:
with pytest.raises(NotImplementedError):
c = Circuit(4, accelerators, density_matrix=True)
else:
c = Circuit(4, accelerators, density_matrix=True)
thetas = np.random.random(4)
c.add((gates.RY(i, t) for i, t in enumerate(thetas)))
target_state = backend.execute_circuit(c).state()
target_state = target_state
c.has_collapse = True
if accelerators is not None:
with pytest.raises(NotImplementedError):
final_state = backend.execute_circuit(c, nshots=20)
else:
final_state = backend.execute_circuit(c, nshots=20).state()
backend.assert_allclose(final_state, target_state)


def test_final_state_property(backend):
Expand All @@ -92,7 +100,7 @@ def test_density_matrix_circuit(backend):
c.add(gates.H(1))
c.add(gates.CNOT(0, 1))
c.add(gates.H(2))
final_rho = backend.execute_circuit(c, np.copy(initial_rho))._state
final_rho = backend.execute_circuit(c, np.copy(initial_rho)).state()

h = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
cnot = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
Expand Down Expand Up @@ -122,8 +130,8 @@ def test_circuit_as_initial_state(backend, density_matrix):

actual_circuit = c1 + c

output = backend.execute_circuit(c, c1)._state
target = backend.execute_circuit(actual_circuit)._state
output = backend.execute_circuit(c, c1).state()
target = backend.execute_circuit(actual_circuit).state()

backend.assert_allclose(target, output)

Expand Down
3 changes: 2 additions & 1 deletion tests/test_models_circuit_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,5 @@ def test_repeated_execute_probs_and_freqs(backend, nqubits):
else:
test_frequencies = Counter({"11": 618, "10": 169, "01": 185, "00": 52})

assert result.frequencies() == test_frequencies
for key in dict(test_frequencies).keys():
backend.assert_allclose(result.frequencies()[key], test_frequencies[key])
26 changes: 2 additions & 24 deletions tests/test_result.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from os import remove

import numpy as np
import pytest

Expand All @@ -16,26 +18,6 @@ def test_circuit_result_error(backend):
)


def test_measurementoutcomes_errors(backend):
c = models.Circuit(1)
c.add(gates.M(0))
samples = [np.array([1, 0]) for _ in range(5)]
with pytest.raises(Exception) as exc_info:
MeasurementOutcomes(c.measurements, backend)
assert (
str(exc_info.value)
== "You have to provide either the `probabilities` or the `samples` to build a `MeasurementOutcomes` object."
)
with pytest.raises(Exception) as exc_info:
MeasurementOutcomes(
c.measurements, backend, probabilities=np.array([1, 0]), samples=samples
)
assert (
str(exc_info.value)
== "Both the `probabilities` and the `samples` were provided to build the `MeasurementOutcomes` object. Don't know which one to use."
)


def test_measurement_gate_dump_load(backend):
c = models.Circuit(2)
c.add(gates.M(1, 0, basis=[gates.Z, gates.X]))
Expand All @@ -47,8 +29,6 @@ def test_measurement_gate_dump_load(backend):

@pytest.mark.parametrize("agnostic_load", [False, True])
def test_measurementoutcomes_dump_load(backend, agnostic_load):
from os import remove

c = models.Circuit(2)
c.add(gates.M(1, 0, basis=[gates.Z, gates.X]))
# just to trigger repeated execution and test MeasurementOutcomes
Expand All @@ -68,8 +48,6 @@ def test_measurementoutcomes_dump_load(backend, agnostic_load):

@pytest.mark.parametrize("agnostic_load", [False, True])
def test_circuitresult_dump_load(backend, agnostic_load):
from os import remove

c = models.Circuit(2, density_matrix=True)
c.add(gates.M(1, 0, basis=[gates.Z, gates.X]))
# trigger repeated execution to build the CircuitResult object
Expand Down

0 comments on commit 86bec1e

Please sign in to comment.