# Stim: How are the signs in tableau calculated and how to force them to +

I'd like to understand how the signs in the tableau are calculated and if there's a way to force them to be +. Here's an example that shows what I see : (this is an encoder for $$[[8,3,3]]$$ code)

import stim

def circuit_to_tableau(circuit: stim.Circuit) -> stim.Tableau:
s=stim.TableauSimulator()
s.do_circuit(circuit)
return s.current_inverse_tableau() ** -1

circuit=stim.Circuit('''
CX 5 4
CX 6 5
CX 7 6
H 0
CX 0 4
CX 0 5
CX 0 6
CZ 0 6
H 1
CZ 1 0
CX 1 4
CX 1 5
CZ 1 5
CX 1 7
H 2
CZ 2 0
CX 2 4
CZ 2 5
CX 2 6
CX 2 7
CZ 2 7
H 3
CZ 3 0
CZ 3 1
CX 3 5
CX 3 6
CZ 3 6
CX 3 7
CZ 3 7
MPP Y0*Z2*Z3*X4*X5*Y6
MPP Z0*Y1*Z3*X4*Y5*X7
MPP Z0*Y2*X4*Z5*X6*Y7
MPP Z0*Z1*X3*X5*Y6*Y7
MPP Z0*Z1*Z2*Z3*Z4*Z5*Z6*Z7
''')
sampler=circuit.compile_sampler()
print(sampler.sample(shots=8))
tableau=circuit_to_tableau(circuit)
print(repr(tableau))


running it gives this result :

[[False False False  True False]
[False False False  True False]
[False False False  True False]
[False False False  True False]
[False False False  True False]
[False False False  True False]
[False False False  True False]
[False False False  True False]]
stim.Tableau.from_conjugated_generators(
xs=[
stim.PauliString("+Z_______"),
stim.PauliString("+_Z______"),
stim.PauliString("+__Z_____"),
stim.PauliString("+___Z____"),
stim.PauliString("+____X___"),
stim.PauliString("+_ZZ_XX__"),
stim.PauliString("+ZZZZ_XX_"),
stim.PauliString("+Z_Z___XX"),
],
zs=[
stim.PauliString("+Y_ZZXXY_"),
stim.PauliString("+ZY_ZXY_X"),
stim.PauliString("+Z_Y_XZXY"),
stim.PauliString("-ZZ_X_XYY"),
stim.PauliString("+ZZZZZZZZ"),
stim.PauliString("+___Z_ZZZ"),
stim.PauliString("+ZZ____ZZ"),
stim.PauliString("+_ZZZ___Z"),
],
)


Why did the fourth z stabilizer pick up a minus sign? these are all stabilizers, destabilizers, and logical for the code and everything works the same with + for all of them.

(PS. Let me know if there's a better place to post Stim specific questions...I have several about syntax ... and I don't want to clutter things here)

Here's another approach to get the encoding circuit using the routines described in a previous post how to go from matrix to tableau to circuit in qiskit or stim

 matrix=[
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0],
[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0],
[0,0,0,0,1,0,1,0,1,0,0,1,0,0,0,0],
[0,0,0,0,1,0,0,1,0,0,1,1,0,0,0,0],
[1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0],
[0,1,0,0,1,1,0,1,1,1,0,1,0,1,0,0],
[0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1],
[0,0,0,1,0,1,1,1,1,1,0,0,0,0,1,1],
[0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0,1,1,0,1,0,1,0,0],
[0,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0],
[0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1]]
tableau = bit_matrix_to_tableau(matrix)
print(repr(tableau))
circuit1 = tableau_to_circuit_simple(tableau)
circuit2=stim.Circuit('''
MPP Y0*Z2*Z3*X4*X5*Y6
MPP Z0*Y1*Z3*X4*Y5*X7
MPP Z0*Y2*X4*Z5*X6*Y7
MPP Z0*Z1*X3*X5*Y6*Y7
MPP Z0*Z1*Z2*Z3*Z4*Z5*Z6*Z7
''')
circuit=circuit1+circuit2
sampler=circuit.compile_sampler()
print(sampler.sample(shots=8))
tableau=circuit_to_tableau(circuit)
print(repr(tableau))


This gives a + for all stabilizers when the tableau is generated from a matrix and even when you go matrix -> tableau -> circuit -> tableau

stim.Tableau.from_conjugated_generators(
xs=[
stim.PauliString("+Z_______"),
stim.PauliString("+_Z______"),
stim.PauliString("+__Z_____"),
stim.PauliString("+___Z____"),
stim.PauliString("+____X___"),
stim.PauliString("+_ZZ_XX__"),
stim.PauliString("+Z__ZX_X_"),
stim.PauliString("+__ZZX__X"),
],
zs=[
stim.PauliString("+Y_ZZXXY_"),
stim.PauliString("+ZY_ZXY_X"),
stim.PauliString("+Z_Y_XZXY"),
stim.PauliString("+ZZ_X_XYY"),
stim.PauliString("+ZZZZZZZZ"),
stim.PauliString("+ZZ_Z_Z__"),
stim.PauliString("+Z_ZZ__Z_"),
stim.PauliString("+_ZZZ___Z"),
],
)
[[False False False False False]
[False False False False False]
[False False False False False]
[False False False False False]
[False False False False False]
[False False False False False]
[False False False False False]
[False False False False False]]
stim.Tableau.from_conjugated_generators(
xs=[
stim.PauliString("+Z_______"),
stim.PauliString("+_Z______"),
stim.PauliString("+__Z_____"),
stim.PauliString("+___Z____"),
stim.PauliString("+____X___"),
stim.PauliString("+_ZZ_XX__"),
stim.PauliString("+Z__ZX_X_"),
stim.PauliString("+__ZZX__X"),
],
zs=[
stim.PauliString("+Y_ZZXXY_"),
stim.PauliString("+ZY_ZXY_X"),
stim.PauliString("+Z_Y_XZXY"),
stim.PauliString("+ZZ_X_XYY"),
stim.PauliString("+ZZZZZZZZ"),
stim.PauliString("+ZZ_Z_Z__"),
stim.PauliString("+Z_ZZ__Z_"),
stim.PauliString("+_ZZZ___Z"),
],
)

• The circuit_to_tableau method you used is only valid if you stick to unitary operations. Your circuit contains measurements, so it is not describable using a tableau. Tableaus only represent Cliffords, not measurements or other dissipative operations. If you happen to be measuring stabilizers so the measurements are deterministic then it's fine... but beware. Jul 19, 2022 at 22:21
• This is the right place to post stim questions. Jul 19, 2022 at 22:31
• Good! I'll continue to post here. I actually wanted to separate the circuit from the measurement but I didn't know the syntax of how to "concatenate" the H/CX/CZ part and the MPP statements. How would you combine circuit1=stim.Circuit(''' .... ''') with another circuit2=stim.Circuit(''' MPP ... ''')? Jul 19, 2022 at 23:07
• circuit1 + circuit2 concatenates their instructions into a new circuit. Jul 19, 2022 at 23:08
• so simple! I never would have guessed Jul 20, 2022 at 1:16

How are the signs in tableau calculated

The signs are computed along with the Pauli products, as a consequence of conjugating by the applied Clifford operations.

The system starts with the stabilizers $$Z_0$$, $$Z_1$$, ..., $$Z_{n-1}$$ and destabilizers $$X_0, X_1, ... X_{n-1}$$. Whenever you apply a Clifford operation $$C$$, each stabilizer and destabilizer is run through the function $$f(P) = C \cdot P \cdot C^\dagger$$. Sometimes this computation adds minus signs. For example, when you conjugate $$Y$$ by $$S$$ you compute $$S Y S^\dagger$$ which is... $$-X$$ instead of $$+X$$. That kind of thing where all the minus signs come from.

(In actuality Stim does something slightly different because it is tracking the inverse tableau, which has speed advantages, but it's ultimately isomorphic.)

how to force them to +

One of the great things about having the stabilizers and the destabilizers, is that they come in anticommuting pairs. To flip the sign of the 3rd stabilizer (and no others), append to the circuit the Paulis making up the 3rd destabilizer. To flip the sign of the 2nd destabilizer (and no others), append to the circuit the Paulis making up the 2nd stabilizer.

The above will work fine, but what will work even better over the long term is... doing nothing. My recommendation is to not worry about the signs. Just let them be free; let them be whatever they want to be. Design everything downstream around the signs being the same or different vs the noiseless case rather than being exactly -1 or +1. This saves a huge amount of hassle trying to trace and fix minus signs; effort which ultimately achieves no real benefit.

• I'd like just to forget about the sign but I can see how it can affect things. The False/True table I print corresponds to measuring the stabilizers of the code. It should have been all False, but the fourth stabilizer picked up a minus sign and its column is all True. Measuring the stabilizers gives the syndrome; if there are errors I'd like to know the syndrome exactly without worrying; the signs can really complicate things. That's why it would be good to be able to force them to +... Jul 19, 2022 at 23:12
• @unknown Once you start doing more complex things, like applying multi-qubit logical gates, forcing the signs to always be + becomes much more of a hassle than just tracking which sign is expected. In simple cases like this one it's tempting to keep things clean and always-+, but in more complex cases it becomes toil. Jul 19, 2022 at 23:15
• Let's take the example from your answer to quantumcomputing.stackexchange.com/questions/25640/… you need the exact value of the syndrome to do the correction. If one of the stabilizer has a - sign the correction logic would change, right? (BTW is there a way to export the quirk circuit to stim?) Jul 19, 2022 at 23:21
• I edited the question to add another approach; this one doesn't pick up any - signs and would be nicer to work with for the decoding stage Jul 20, 2022 at 1:18
• @unknown It's a complete accident that those encoding circuits produce stabilizers with positive sign. I didn't rely on that at all when I was figuring out the corrections. Jul 20, 2022 at 14:10

QuantumClifford.jl is an alternative that is convenient for working with the whole tableau algebra (not optimizing specifically for Pauli frames for which stim is extremely good). It supports Pauli frames, similarly to stim, but it provides a lot of other more "algebraic" functionality too.

In particular, the MixedDestabilizer datastructure, provides exactly what you are asking for: tableau that tracks stabilizer constraints, destabilizers, and rank with the logical operators. It is useful if you are working with circuits that contain non-unitary operations like measurements.