I would like to calculate the logical error probabilities of surface codes under depolarizing noise using Stim. In doing so, if all data qubits are initialized to |0>, it is not possible to account for the effects of Z errors, while if they are all initialized to |+>, it is not possible to account for the effects of X errors. Therefore, I am considering conducting simulations in two different ways, initializing all data qubits to |0> in one case and |+> in the other, to detect X, Z (and Y) errors. However, the problem here is that these two simulations need to be performed under the same noise conditions. It may be possible to achieve my goal by sampling noise in one case, extracting that noise, constructing a circuit in Stim that generates the same errors in the circuit, and then simulating the other case, but this method seems very inefficient. Is there a better way?
What you're specifically asking for, running two different circuits with the "same" noise, isn't supported by stim.
Functionality that's close is passing
stim.CompiledDetectorSampler.sample, or passing
return_errors=True then later
stim.CompiledDemSampler.sample. But neither of these is designed to work across circuits.
Do not try to use those methods across circuits and expect it to be the "same" noise, even if the circuits use the same noise channels in the same order and even if it looks like it's working. It will be incredibly brittle. There are dozens of reasons it wouldn't work, or would suddenly and silently stop working. You will mess it up somewhere. And then you'll have to retract a paper. (Okay, maybe not that extreme, but I'm serious don't do it.)
It may be possible to achieve my goal by sampling noise in one case, extracting that noise, constructing a circuit in Stim that generates the same errors in the circuit, and then simulating the other case, but this method seems very inefficient. Is there a better way?
There are a variety of things you can do.
Just don't force the noise to be the same. Use process tomography to convert the measured X and Z fidelities (and maybe also Y fidelity) into an overall fidelity. This option is appealing because you are holding yourself to the same limitations that hardware experiments are under, so your analysis code won't stop working as soon as it has to deal with the real world.
Check if there's a single qubit state that approximates simultaneous sensitivity. In the CSS and XZZX surface codes, logical errors are biased away from being logical Y errors. Therefore, in the CSS and XZZX surface codes, doing a Y basis memory experiment is a pretty good approximation of being simultaneously vulnerable to all errors. See data in "Inplace Access to the Surface Code Y Basis".
Use logical entanglement. Instead of working with single qubit states, work with two qubit states. Perform a logical Bell basis preparation, preserve both qubits against noise, then perform a logical Bell basis measurement. This creates a situation where X logical errors, Z logical errors, and Y logical errors must all be simultaneously protected against during the "preserve against noise" phase.
Use a magically noiseless time boundaries and entanglement with a magically noiseless ancilla qubit. This method can't be performed in hardware experiments. Instead of doing a normal preparation, do a noiseless preparation (and later measurement) that simply perfectly measures all stabilizers and observables. This allows you to prepare arbitrary observables; in particular you can prepare $|0_L0_a\rangle + |1_L1_a\rangle$ where $L$ is the logical qubit and $a$ is an ancilla qubit. This state has stabilizers $+X_LX_a$ and $+Z_LZ_a$. If $a$ is noiseless then the only way for these stabilizers to break is noise on $L$. And since you have both $X$ and $Z$ sensitivity on $L$, you're vulnerable to all logical errors. So you're checking everything simultaneously. Well, except for the logical preparation and logical measurement since those are being done magically.