-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support contiguous qubits for OpenQASM IR #237
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #237 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 48 48
Lines 3770 3798 +28
Branches 915 925 +10
=========================================
+ Hits 3770 3798 +28 ☔ View full report in Codecov by Sentry. |
measuredQubits=( | ||
measured_qubits if measured_qubits else self._get_all_qubits(simulation.qubit_count) | ||
), | ||
measuredQubits=(measured_qubits if measured_qubits else used_qubits), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the order have an effect? Does it align with classical bit indices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used only in the case that there's no requested measurement, and changes the default behavior to measure all used qubits rather than all declared qubits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the order have an effect? Does it align with classical bit indices?
This must be the sorted order of the used qubits, right?
@@ -301,7 +301,7 @@ def test_properties(): | |||
], | |||
"supportPhysicalQubits": False, | |||
"supportsPartialVerbatimBox": False, | |||
"requiresContiguousQubitIndices": True, | |||
"requiresContiguousQubitIndices": False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why the test properties don't use the actual properties?
@@ -795,3 +795,21 @@ def test_measure_targets(): | |||
assert 400 < np.sum(measurements, axis=0)[0] < 600 | |||
assert len(measurements[0]) == 1 | |||
assert result.measuredQubits == [0] | |||
|
|||
|
|||
def test_non_contiguous_qubits_with_shots0(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add tests showing returning measurements for multiple qubits? Does the ordering follow the user's order, or is it sorted by the qubit index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add tests with sampled results? I think that's the kind of result that _formatted_measurements
acts on, is that right?
@@ -438,8 +475,12 @@ def run_openqasm( | |||
""" | |||
circuit = self.parse_program(openqasm_ir).circuit | |||
qubit_count = circuit.num_qubits | |||
used_qubits = sorted(list(circuit.qubit_set)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer it when variables are defined closer to where they are used. That requires less jumping around the code to figure out what a variable means, and less "mental mapping" where I have to store a lot of info in my head while reading a function. Since this is only used once, you don't even have to define it at all, and can just pass it to the function that needs it.
If you do define it here, why not use it for line 481? used_qubits[-1]
would be faster than max(set)
.
If you keep the variable, could we call it sorted_used_qubits
? It's a bit verbose, but it was my first question when I saw this variable name elsewhere.
@@ -479,7 +520,9 @@ def run_openqasm( | |||
else: | |||
simulation.evolve(circuit.basis_rotation_instructions) | |||
|
|||
return self._create_results_obj(results, openqasm_ir, simulation, measured_qubits) | |||
return self._create_results_obj( | |||
results, openqasm_ir, simulation, used_qubits, measured_qubits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if you moved the measured_qubits if measured_qubits else used_qubits
logic here?
It looks to me at the moment that _create_results_obj
won't ever need both of them -- and the used_qubits
simply are the measured qubits when measured qubits is None. This has the extra benefit of reducing the number of variables passed to that function, making it a little easier to understand what the function does.
measured_qubits = circuit.measured_qubits | ||
|
||
if max(circuit.qubit_set) != len(circuit.qubit_set) - 1: | ||
circuit = self._map_to_contiguous_qubits(circuit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe _map_circuit_to_contiguous_qubits
? The name as is makes me expect indices as inputs. Note how similar, semantically, this name is to _contiguous_qubit_mapping
. They're synonyms.
Another nit: Can we move line 481 into this function? _formatted_measurements
also containerizes this check.
I prefer it because it makes it clear that every circuit will be mapped to operate on contiguous qubits. It just so happens that some circuits already trivially do this, but that fact might be distracting here -- a cursory look might make someone think we're only conditionally getting contiguous qubits
"""Map the qubits in operations and result types to contiguous qubits. | ||
|
||
Args: | ||
circuit (Circuit): Circuit which qubits are not contiguous. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
circuit (Circuit): Circuit which qubits are not contiguous. | |
circuit (Circuit): Circuit in which qubits may not be contiguous. |
Or simply, Circuit to map.
circuit (Circuit): Circuit which qubits are not contiguous. | ||
|
||
Returns: | ||
Circuit: Circuit qubits mapped to with contiguous qubits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circuit: Circuit qubits mapped to with contiguous qubits. | |
Circuit: Circuit with qubits mapped to contiguous qubits. |
return measurements | ||
|
||
def _map_to_contiguous_qubits(self, circuit: Circuit) -> Circuit: | ||
"""Map the qubits in operations and result types to contiguous qubits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to include here a Note:
describing what contiguous qubits are, explaining that simulators require them for computation, and we handle the user's requested mapping here (which is useful for running on QPUs, that might be TMI though, idk)
measurements = np.array(measurements)[:, measured_qubits].tolist() | ||
if max(measured_qubits) != len(measured_qubits) - 1: | ||
qubit_map = BaseLocalSimulator._contiguous_qubit_mapping(measured_qubits) | ||
mapped_measured_qubits = [qubit_map[q] for q in measured_qubits] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is mapped_measured_qubits ever different from the inverse of qubit_map
?
Description of changes:
Testing done:
tox
Merge Checklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.General
Tests
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.