1
$\begingroup$

I have a circuit that encodes the $[[5,1,3]]$ code, adds noise on one of the qubits and then measures the syndrome on 4 ancilla qubits :

 import stim

circuit_encode=stim.Circuit('''
H 0
CX 0 4
H 1
CZ 1 0
CX 1 4
CZ 1 4
H 2
CZ 2 1
CX 2 4
CZ 2 4
H 3
CZ 3 0
CZ 3 1
CX 3 4
''')

circuit_channel=stim.Circuit('''
 X_ERROR(0.1) 3
''')

circuit_syndrome=stim.Circuit('''
RZ 5 6 7 8
H 5 6 7 8
CX 5 0
CZ 5 2
CZ 5 3
CX 5 4
CZ 6 0
CX 6 1
CZ 6 1
CZ 6 2
CX 6 4
CZ 6 4
CZ 7 1
CX 7 2
CZ 7 2
CZ 7 3
CX 7 4
CZ 7 4
CZ 8 0
CZ 8 1
CX 8 3
CX 8 4
H 5 6 7 8
''')

circuit_measure=stim.Circuit('''
MPP Z5
DETECTOR rec[-1]
MPP Z6
DETECTOR rec[-1]
MPP Z7
DETECTOR rec[-1]
MPP Z8
DETECTOR rec[-1]
''')

circuit=circuit_encode+circuit_channel+circuit_syndrome+circuit_measure

circuit.detector_error_model()

sampler=circuit.compile_sampler()
print(sampler.sample(shots=20))

circuit_channel should apply an X error on qubit 3 10% of the time The results I get are :

[[False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [ True  True False False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [False  True  True False]
 [ True  True False False]
 [False  True  True False]
 [ True  True False False]]

I would expect all False to show up 90% of the time. This is when no error occurs; so the syndrome should be all 0. It does look like there are two patterns "FTTF" ~ 90% of the time and "TTFF" ~ 10% so there's probably some adjustment to make the "no error" case return with FFFF measurement; I don't know how that's done.

$\endgroup$
7
  • $\begingroup$ Try adding detector declarations saying each measurement should be deterministic. Then run circuit.detector_error_model(). If the detectors aren't deterministic, you will get an error saying which detector is not deterministic and which reset it's anticommuting with. $\endgroup$ Aug 8, 2022 at 22:39
  • $\begingroup$ I added DETECTOR rec[-1] after each MPP command and then circuit.detector_error_model(); no errors are generated and the output is still not deterministic. I never understood the logic of detectors/samplers/... in stim in more than attempt at it; seems counter intuitive to me if you don't mind me saying. $\endgroup$
    – unknown
    Aug 8, 2022 at 22:58
  • $\begingroup$ When you say "with the correction", what do you mean? There's no corrective operations in the circuit, and there's also no noise in the circuit that would need to be corrected. How do you know your corrections are correct? $\endgroup$ Aug 8, 2022 at 22:59
  • $\begingroup$ there's no correction yet. My next step is to add a pauli string that corresponds to the syndrome; but the syndrome has to be deterministic for that to work $\endgroup$
    – unknown
    Aug 8, 2022 at 23:03
  • 1
    $\begingroup$ I edited the question so all the parts are in one self contained script. The results almost look ok if there's a way to make the measurement return "False False False False" when there's no error. $\endgroup$
    – unknown
    Aug 9, 2022 at 0:53

1 Answer 1

2
$\begingroup$

Basically what you are trying to solve is: what Paulis do I insert to flip the sign of one stabilizer, but not the others or the logical qubit.

The easiest way to solve this problem is to not consider it a problem in the first place. It doesn't matter whether the "correct" value for a stabilizer is -1 or +1, all that matters is you notice when it's not that value. So just make your circuit however you want, write down what the stabilizers ended up being, and compare to that reference. If it's supposed to be True, and you see False, that's a detection event. If it's supposed to be False, and you see True, that's a detection event. Easy. This is an extremely valuable time saving skill to have, because when performing operations in more complex codes it becomes a real time waster to always ensure the stabilizers and the observables are not being flipped by the circuit (as opposed to just tracking when they are flipped).

But let's assume we're being obsessive about this and try to fix them.

The second easiest way to solve this problem is to just wing it. Try inserting Paulis, keep track of what flips, and find combinations that do the set of flips you need. This works great on 5 qubit circuits and absolutely terribly one 100 qubit circuits. For those you need to be more systematic, try each individual Pauli, make a table of flips, and then do Gaussian elimination on the table to solve for the effect you want.

The abstract way to solve this problem is to realize that what you are trying to do is, given a set of stabilizer generators, find the corresponding destabilizers. The destabilizer of a stabilizer generator is a Pauli product that flips one stabilizer generator, and no other stabilizer generators. Once you have the destabilizers you can, for each stabilizer you want to flip, append the corresponding destabilizer to the circuit.

In the development version of stim there is a method that happens to do this, indirectly: stim.TableauSimulator.set_state_from_stabilizers. Because this method guarantees it produces a tableau state that has exactly the given stabilizers as the generators, it has to solve for the corresponding destabilizers:

import stim
simulator = stim.TableauSimulator()
simulator.set_state_from_stabilizers([
    stim.PauliString("+X_ZZX"),
    stim.PauliString("+ZYZ_Y"),
    stim.PauliString("+_ZYZY"),
    stim.PauliString("+ZZ_XX"),
    stim.PauliString("+ZZZZZ"),
])
tableau = simulator.current_inverse_tableau()**-1
for k in range(5):
    print("stabilizer:", 
          tableau.z_output(k),
          "has associated destabilizer:",
          tableau.x_output(k))
stabilizer: +X_ZZX has associated destabilizer: +Z____
stabilizer: +ZYZ_Y has associated destabilizer: +Z_ZZZ
stabilizer: +_ZYZY has associated destabilizer: +__Z__
stabilizer: +ZZ_XX has associated destabilizer: +___Z_
stabilizer: +ZZZZZ has associated destabilizer: -ZX__Z

Note that this also gives a straightforward way to get a correct, if inefficient, encoding circuit starting from the desired stabilizers + desired observable:

import stim
simulator = stim.TableauSimulator()
simulator.set_state_from_stabilizers([
    stim.PauliString("+X_ZZX"),
    stim.PauliString("+ZYZ_Y"),
    stim.PauliString("+_ZYZY"),
    stim.PauliString("+ZZ_XX"),
    stim.PauliString("+ZZZZZ"),
])
tableau = simulator.current_inverse_tableau()**-1
print(tableau.to_circuit(method="elimination"))

Outputs:

H 0
S 1 4
CX 0 1 0 3 0 4 4 1 1 4 4 1
S 1 4
CX 1 2 1 3 1 4
H 4
CX 4 1
H 2
CX 2 3 2 4
H 3
S 3 4
CX 3 4
S 4
H 1 4
S 1 1 4 4
H 1 4
S 0 0 1 1 2 2 3 3 4 4
$\endgroup$
6
  • $\begingroup$ Thanks for the answer. For what I'm doing, neither option will do the job. If I change something in the encoder then the "no error" pattern can change; that means the decoder now has to change. The small code here is a toy example, in reality I will be working with larger codes and more advanced decoders; these decoders can possibly solve the error correction "algebraically" similar to what's done in classical decoding. The relation of syndrome to error has to be exact and predictable; otherwise I'd have to insert a different adjustment for syndrome sign bits every time I change anything. $\endgroup$
    – unknown
    Aug 9, 2022 at 15:34
  • $\begingroup$ @unknown This is exactly why stim defines the concept of a DETECTOR, which handles the hassle of comparing the actual results to expected results for you. This is why you want to define detectors and sample detection events instead of directly sampling measurements. $\endgroup$ Aug 9, 2022 at 17:37
  • $\begingroup$ I still don't understand the DETECTOR concept but I'll be happy to see how that would work. What would you change in the circuit above? How would you detect the $X$ error on qubits 3 and correct it...using DETECTOR's $\endgroup$
    – unknown
    Aug 9, 2022 at 17:42
  • $\begingroup$ @unknown You need to use an X_ERROR(1) instead of an X, because the X is being treated as intended behavior that defines the expected value of measurements instead of as noise. But other than that you should be able to just declare each individual measurement as a detector and use the detector outputs as if they were measurements, since you have prepared a state where these measurements are unsigned stabilizers of the state. $\endgroup$ Aug 9, 2022 at 17:51
  • $\begingroup$ I'm already using "X_ERROR(0.1) 3" to introduce errors $\endgroup$
    – unknown
    Aug 9, 2022 at 18:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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