4
$\begingroup$

In a quantum error correcting code using flag qubits, it's common to have flag measurements that tell you it's necessary to do some extra measurements for safety. So, for example, I want to say:

if measurement_result_was_true:
    do_a_different_measurement

How do I do this in Stim? There doesn't seem to be an if.

(This was a question I received by email. Moving it here for posterity.)

$\endgroup$
3
$\begingroup$

This is definitely a place where you'll struggle to use Stim. It's possible but it's not nice. It could still be worth your time just because you don't have to eg. manually write code to turn a circuit into a matching graph, but it'll be more tedious than branchless code.

Part 1: Simulating

The easiest thing to do is to use stim.TableauSimulator. It gives you the ability to apply operations one by one driven by python code, which is more than flexible enough to do what you need.

Here is python code showing the general idea:

import stim

simulator = stim.TableauSimulator()

# Run first part of circuit.
simulator.do(stim.Circuit("""
    H 0
    CNOT 0 1
    M 0
"""))

# Do something depending on the measurement result.
latest_measurement_result = simulator.current_measurement_record()[-1]
if latest_measurement_result:
    simulator.do(stim.Circuit("""
        # ...
    """))

# Run the rest of the circuit.
simulator.do(stim.Circuit("""
    # ...
"""))

shot = simulator.current_measurement_record()

The reason Stim doesn't support branching in its circuit format is because branches break an algorithmic optimization where the tableau simulator is only used once for a reference sample, and then stim switches to using much faster error frame simulation. Consequently, you will find that getting thousands of samples by repeatedly using stim.TableauSimulator is easily 100x slower than using circuit.compile_sampler().sample(shots). Hopefully it's still fast enough for your needs.

If you have less than 10 branches, a possible alternative would be to run simulations for each possible case and then postselect out the inconsistent ones where the hardcoded branch disagreed with the measurement result deciding whether or not it was taken. The performance difference can be large enough to overcome the losses from the postselections.

Part 2: Correcting

I'm assuming you're using PyMatching.

For each shot, you need to build the circuit that was actually run. This circuit will give you access to a detector error model (circuit.detector_error_model(decompose_errors=True)) and the ability to convert the measurement results into detection events (circuit.compile_m2d_converter().convert(shot, append_observables=True)). Convert the measurements into detection events + observable frame changes, and then run pymatching on the detector data, configured with the circuit's detector error model, and check whether or not it predicted the observable frame data.

If you only have a few possible cases, you will get a large benefit from grouping shots that took the same paths together. If you have a lot of possible cases, so that each case is hit less than 3 times on average, grouping isn't worth it.

It's likely that error model extraction will be your main bottleneck here. Stim is pretty quick at this, it takes it a third of a second or so to analyze the error model of a distance 25 surface code, but a third of a second per sample is awful.

I intend for this use case to get better in the future, but haven't got any concrete designs I'm happy with yet.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.