Skip to content

Commit

Permalink
[SDS-617] Fix measurement statements in ProjectQ (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
QFer authored Jun 28, 2022
1 parent f4987cd commit 61b0821
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ matrix:
- name: python 3.7
python: 3.7
if: branch = dev
dist: focal
dist: bionic
- name: python 3.8
python: 3.8
if: branch = dev
Expand Down
21 changes: 5 additions & 16 deletions src/quantuminspire/projectq/backend_qx.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,8 @@ def _store(self, cmd: Command) -> None:
self._qasm += f"\nbarrier q[{qb_str}]"
return

# when we find a gate after measurements we don't have fsp
# add any delayed measurement statements
# When fsp is enabled and we have skipped measurements when we find a gate after these measurements,
# switch to non-fsp and add any delayed measurement statements.
if self._full_state_projection and len(self._measured_ids) != 0:
self._switch_fsp_to_nonfsp()

Expand Down Expand Up @@ -638,11 +638,6 @@ def _run(self) -> None:
if self._qasm == "":
return

# Finally: add measurement commands for all qubits if no measurements are given.
# Only for simulation backends, measurements after all gate operations will perform properly in FSP.
if not self._measured_ids and self._is_simulation_backend:
self.__add_measure_all_qubits()

self._finalize_qasm()
self._execute_cqasm()
self._filter_result_by_measured_qubits()
Expand Down Expand Up @@ -743,13 +738,15 @@ def __init__(self, qubit_id: int) -> None:
def _get_measured_qubit_iterator(self, measurement_block_index: int) -> Iterator[int]:
"""
Get an iterator for the measured qubits. The iterator returns the indexes of the qubits that are measured.
With fsp, the simulator will return all qubits as measured, but when there were measurements in the algorithm
we want to get these measured qubits only.
:param measurement_block_index: measurement block index for multi measurement results.
:return:
Iterator which iterated the indexes of the measured qubits
"""
if self._full_state_projection:
if self._full_state_projection and len(self._measured_ids) != 0:
bit_iterator = map(lambda bit: self._physical_to_simulated(self._logical_to_physical(bit)),
self._measured_ids)
else:
Expand Down Expand Up @@ -788,11 +785,3 @@ def receive(self, command_list: List[Command]) -> None:
self._run()
self._flushed = True
self._reset()

def __add_measure_all_qubits(self) -> None:
""" Adds measurements at the end of the quantum algorithm for all allocated qubits. """
qubits_reference = self.main_engine.active_qubits.copy()
qubits_counts = len(qubits_reference)
for _ in range(qubits_counts):
q = qubits_reference.pop()
Measure | q
31 changes: 12 additions & 19 deletions src/tests/quantuminspire/projectq/test_backend_qx.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import coreapi
from collections import OrderedDict
from functools import reduce
from unittest import mock, TestCase
from unittest import TestCase
from unittest.mock import MagicMock, patch

from projectq.meta import LogicalQubitIDTag
Expand Down Expand Up @@ -596,6 +596,7 @@ def test_receive(self, function_mock):
command = MagicMock(gate=NOT, qubits=[[MagicMock(id=0)]], control_qubits=[MagicMock(id=1)])
command_list = [command_alloc0, command_alloc1, command, MagicMock(gate=FlushGate())]
self.qi_backend.main_engine = MagicMock()
self.qi_backend.main_engine.mapper.current_mapping = {0: 0, 1: 1} # bits 0 and 1 are logical bits 0 and 1
with patch('sys.stdout', new_callable=io.StringIO):
self.qi_backend.receive(command_list)
self.assertEqual(self.qi_backend.qasm, "")
Expand All @@ -611,6 +612,7 @@ def test_reuse_after_flush_raises_runtime_error(self, function_mock):
api = MockApiClient()
backend = QIBackend(quantum_inspire_api=api)
backend.main_engine = MagicMock()
backend.main_engine.mapper.current_mapping = {0: 0, 1: 1} # bits 0 and 1 are logical bits 0 and 1
with patch('sys.stdout', new_callable=io.StringIO):
self.assertRaisesRegex(RuntimeError, "Same instance of QIBackend used for circuit after Flush.",
backend.receive, command_list)
Expand All @@ -624,6 +626,7 @@ def test_receive_multiple_flush(self, function_mock):
command_list = [command_alloc0, command_alloc1, command, MagicMock(gate=FlushGate()),
MagicMock(gate=FlushGate())]
self.qi_backend.main_engine = MagicMock()
self.qi_backend.main_engine.mapper.current_mapping = {0: 0, 1: 1} # bits 0 and 1 are logical bits 0 and 1
with patch('sys.stdout', new_callable=io.StringIO):
self.qi_backend.receive(command_list)
self.assertEqual(self.qi_backend.qasm, "")
Expand Down Expand Up @@ -918,32 +921,22 @@ def test_run_has_correct_output(self):
self.assertTrue(std_output.startswith('version 1.0\n# cQASM generated by Quantum Inspire'))
self.assertTrue('qubits 0' in std_output)

def test_run_raises_error_no_result(self):
with patch('sys.stdout', new_callable=io.StringIO):
self.qi_backend.qasm = "_"
self.qi_backend.measured_ids = [0, 1]
self.qi_backend.allocation_map = [(0, 0), (1, 1)]
self.qi_backend.main_engine = MagicMock()
self.qi_backend.main_engine.mapper.current_mapping = {0: 0, 1: 1}
result_mock = MagicMock()
result_mock.get.return_value = [OrderedDict()]
self.api.execute_qasm.return_value = result_mock
self.assertRaisesRegex(ProjectQBackendError, 'Result from backend contains no histogram data!',
self.qi_backend.run)
self.api.execute_qasm.assert_called_once()

@patch('quantuminspire.projectq.backend_qx.Measure')
def test_run_no_measurements(self, measure_mock):
def _run_raises_error_no_result(self, return_val):
with patch('sys.stdout', new_callable=io.StringIO):
self.qi_backend.qasm = "_"
self.qi_backend.measured_ids = []
self.qi_backend.allocation_map = [(0, 0), (1, 1)]
self.qi_backend.main_engine = MagicMock()
self.qi_backend.main_engine.active_qubits = [0, 1]
self.qi_backend.main_engine.mapper.current_mapping = {0: 0, 1: 1}
result_mock = MagicMock()
result_mock.get.return_value = [{}]
result_mock.get.return_value = return_val
self.api.execute_qasm.return_value = result_mock
self.assertRaisesRegex(ProjectQBackendError, 'Result from backend contains no histogram data!',
self.qi_backend.run)
self.api.execute_qasm.assert_called_once()

def test_run_raises_error_no_result_as_ordered_dict(self):
self._run_raises_error_no_result([OrderedDict()])

def test_run_raises_error_no_result_as_empty_dict(self):
self._run_raises_error_no_result([{}])

0 comments on commit 61b0821

Please sign in to comment.