-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Add optional readout error correction to Quantum Volume #2522
Conversation
v: k for k, v in mapping.items() | ||
} | ||
|
||
for final_qubit, original_qubit in mapping.items(): |
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 this work?
data = trial_result.data
bad_measurements = set()
for q, pf in parity_mapping.items():
p = inverse_mapping[pf]
mismatches = np.nonzero(data[str(q)] != data[str(p)])
bad_measurements.update(mismatches)
Or more involved:
data = trial_result.data
kept = np.ones(data.shape[0], dtype=np.bool)
for q, pf in parity_mapping.items():
p = inverse_mapping[pf]
kept &= data[str(q)] == data[str(p)]
return data.where(kept)
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.
Yes, something similar to that does. Thanks, much cleaner now!
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.
Looks good!
- prefer odd-parity encoding (see comment). You can do in follow-up pr
- some other optional style things
# Add measure gates to the end of (a copy of) the circuit. Ensure that those | ||
# gates measure those in the given mapping, preserving this order. | ||
qubits = circuit.all_qubits() | ||
key = None | ||
if mapping: | ||
# Add any qubits that were not explicitly mapped, so they aren't lost in |
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.
you could do key = lambda q: mapping.get(q, q)
if you don't actually need these in mapping
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.
Nice, done.
final_parity_qubit = inverse_mapping[parity_qubit] | ||
parity_meas = trial_result.measurements[str(final_parity_qubit)] | ||
# Check each bit's parity qubit results to see if they are correct. | ||
for idx, qubit_val in enumerate(qubit_meas): |
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 bet this could be faster with vectorized numpy operations
correct_strings = np.ones(dtype=bool)
for dataq, parityq in whatever:
parity_check = (measurements[dataq] == measurements[parityq])
correct_strings = np.logical_and(correct_strings, parity_check)
bitstrings = bitstrings[correct_strings]
(roughly)
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.
Agreed that this should be improved - @Strilanc has a similar idea that uses the Pandas DataFrame directly, so I went with that.
@@ -338,6 +437,12 @@ def calculate_quantum_volume( | |||
routing_attempts: The number of times to route each model circuit onto | |||
the device. Each attempt will be graded using an ideal simulator | |||
and the best one will be used. | |||
add_readout_error_correction: If true, add some parity bits that willx |
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.
willx -> will
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.
Done.
add_readout_error_correction: If true, add some parity bits that willx | ||
later be used to detect readout error. WARNING: This makes the | ||
simulator run extremely slowly for any width/depth of 4 or more, | ||
probably because it doubles the circuit size. In reality, the |
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.
You can probably strike "probably" :) This is indeed the reason
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.
Haha good to know, thanks.
@@ -264,6 +360,8 @@ def execute_circuits( | |||
compiler: An optional function to compiler the model circuit's | |||
gates down to the target devices gate set and the optimize it. | |||
repetitions: The number of bitstrings to sample per circuit. | |||
add_readout_error_correction: If true, add some parity bits that willx |
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.
willx -> will
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.
Done.
# Sort just to make it deterministic. | ||
for idx, qubit in enumerate(sorted(compiled_circuit.all_qubits())): | ||
# For each qubit, create a new qubit that will serve as its parity | ||
# check. This parity bit is initialized to 0 and then CNOTed with |
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.
readout error is asymmetric: p(0|0) > p(1|1). You can account for that by encoding into the single-excitation subspace; i.e. use an inverse-controlled not: X CNOT X
to encode 0 -> 01 and 1 -> 10 so you (should) always readout one 1
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.
Neat, I did not know that - Fixed. Out of curiosity, why is readout error asymmetric? Is that just for our device, or for all devices?
Also, probably a dumb question, but could I instead do X(ancilla) CNOT(physical, ancilla)
instead of X(physical) CNOT(physical, ancilla) X(physical)
?
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.
Readout takes longer than gates (~1000ns vs 10) so the qubit can undergo T1 decay during readout (see cirq.contrib.noise_models). I think it's fairly common in superconducting architectures like ours.
You could do the suggested alternate, but please make sure that the X(ancilla) is done as late as possible. If cirq is left to its own devices, the X will happen at the beginning, the qubit will be hanging out in |1> and could more readily undergo T1 decay
This adds a parity qubit for each regular qubit in the compiled circuit. Each result where these qubits don't match up will be thrown out.