def test_measure_kickback(): s = stim.TableauSimulator() assert s.measure_kickback(0) == (False, None) assert s.measure_kickback(0) == (False, None) assert s.current_measurement_record() == [False, False] s.h(0) v = s.measure_kickback(0) assert isinstance(v[0], bool) assert v[1] == stim.PauliString("X") assert s.measure_kickback(0) == (v[0], None) assert s.current_measurement_record() == [False, False, v[0], v[0]] s = stim.TableauSimulator() s.h(0) s.cnot(0, 1) v = s.measure_kickback(0) assert isinstance(v[0], bool) assert v[1] == stim.PauliString("XX") assert s.measure_kickback(0) == (v[0], None) s = stim.TableauSimulator() s.h(0) s.cnot(0, 1) v = s.measure_kickback(1) assert isinstance(v[0], bool) assert v[1] == stim.PauliString("XX") assert s.measure_kickback(0) == (v[0], None)
def test_gates_present(name: str): t = stim.Tableau.from_named_gate(name) n = len(t) s1 = stim.TableauSimulator() s2 = stim.TableauSimulator() for k in range(n): s1.h(k) s2.h(k) s1.cnot(k, k + n) s2.cnot(k, k + n) getattr(s1, name)(*range(n)) s2.do(stim.Circuit(f"{name} " + " ".join(str(e) for e in range(n)))) assert s1.current_inverse_tableau() == s2.current_inverse_tableau()
def test_access_tableau(): s = stim.TableauSimulator() assert s.current_inverse_tableau() == stim.Tableau(0) s.h(0) assert s.current_inverse_tableau() == stim.Tableau.from_named_gate("H") s.h(0) assert s.current_inverse_tableau() == stim.Tableau(1) s.h(1) s.h(1) assert s.current_inverse_tableau() == stim.Tableau(2) s.h(2) assert s.current_inverse_tableau( ) == stim.Tableau.from_conjugated_generators( xs=[ stim.PauliString("X__"), stim.PauliString("_X_"), stim.PauliString("__Z"), ], zs=[ stim.PauliString("Z__"), stim.PauliString("_Z_"), stim.PauliString("__X"), ], )
def test_measure_kickback_random_branches(): s = stim.TableauSimulator() s.set_inverse_tableau(stim.Tableau.random(8)) r = s.peek_bloch(4) if r[0] == 3: # +-Z? assert s.measure_kickback(4) == (r.sign == -1, None) return post_false = None post_true = None for _ in range(100): if post_false is not None and post_true is not None: break copy = s.copy() if copy.measure(4): post_true = copy else: post_false = copy assert post_false is not None and post_true is not None result, kick = s.measure_kickback(4) assert isinstance(kick, stim.PauliString) and len(kick) == 8 if result: s.do(kick) assert s.canonical_stabilizers() == post_false.canonical_stabilizers() s.do(kick) assert s.canonical_stabilizers() == post_true.canonical_stabilizers()
def test_paulis(): s = stim.TableauSimulator() s.h(*range(0, 22, 2)) s.cnot(*range(22)) s.do(stim.PauliString("ZZZ_YYY_XXX")) s.z(0, 1, 2) s.y(4, 5, 6) s.x(8, 9, 10) s.cnot(*range(22)) s.h(*range(0, 22, 2)) assert s.measure_many(*range(22)) == [False] * 22 s = stim.TableauSimulator() s.do(stim.PauliString("Z" * 500)) assert s.measure_many(*range(500)) == [False] * 500 s.do(stim.PauliString("X" * 500)) assert s.measure_many(*range(500)) == [True] * 500
def test_peek_bloch(): s = stim.TableauSimulator() assert s.peek_bloch(0) == stim.PauliString("+Z") s.x(0) assert s.peek_bloch(0) == stim.PauliString("-Z") s.h(0) assert s.peek_bloch(0) == stim.PauliString("-X") s.sqrt_x(1) assert s.peek_bloch(1) == stim.PauliString("-Y") s.cz(0, 1) assert s.peek_bloch(0) == stim.PauliString("+I") assert s.peek_bloch(1) == stim.PauliString("+I")
def test_classical_control_cnot(): s = stim.TableauSimulator() with pytest.raises(IndexError, match="beginning of time"): s.cnot(stim.target_rec(-1), 0) assert not s.measure(1) s.cnot(stim.target_rec(-1), 0) assert not s.measure(0) s.x(1) assert s.measure(1) s.cnot(stim.target_rec(-1), 0) assert s.measure(0)
def test_basic(): s = stim.TableauSimulator() assert s.measure(0) is False assert s.measure(0) is False s.x(0) assert s.measure(0) is True assert s.measure(0) is True s.reset(0) assert s.measure(0) is False s.h(0) s.h(0) s.sqrt_x(1) s.sqrt_x(1) assert s.measure_many(0, 1) == [False, True]
def test_set_num_qubits(): s = stim.TableauSimulator() s.h(0) s.cnot(0, 1) s.cnot(0, 2) s.cnot(0, 3) t = s.current_inverse_tableau() s.set_num_qubits(8) s.set_num_qubits(4) assert s.current_inverse_tableau() == t assert s.peek_bloch(0) == stim.PauliString("_") s.set_num_qubits(8) s.set_num_qubits(4) s.cnot(0, 4) s.set_num_qubits(4) assert s.peek_bloch(0) in [stim.PauliString("+Z"), stim.PauliString("-Z")]
def test_post_select_using_measure_kickback(): s = stim.TableauSimulator() def pseudo_post_select(qubit, desired_result): m, kick = s.measure_kickback(qubit) if m != desired_result: if kick is None: raise ValueError( "Deterministic measurement differed from desired result.") s.do(kick) s.h(0) s.cnot(0, 1) s.cnot(0, 2) pseudo_post_select(qubit=2, desired_result=True) assert s.measure_many(0, 1, 2) == [True, True, True]
def test_canonical_stabilizers(): s = stim.TableauSimulator() s.h(0) s.h(1) s.h(2) s.cz(0, 1) s.cz(1, 2) assert s.canonical_stabilizers() == [ stim.PauliString("+X_X"), stim.PauliString("+ZXZ"), stim.PauliString("+_ZX"), ] s.s(1) assert s.canonical_stabilizers() == [ stim.PauliString("+X_X"), stim.PauliString("-ZXY"), stim.PauliString("+_ZX"), ]
def test_tableau_simulator_error_mechanisms(gate: cirq.Gate): # Technically this be a test of the `stim` package itself, but it's so convenient to compare to cirq. # Create test circuit that uses superdense coding to quantify arbitrary Pauli error mixtures. n = gate.num_qubits() qs = cirq.LineQubit.range(n) circuit = cirq.Circuit( cirq.H.on_each(qs), [cirq.CNOT(q, q + n) for q in qs], gate(*qs), [cirq.CNOT(q, q + n) for q in qs], cirq.H.on_each(qs), ) expected_rates = cirq.final_density_matrix(circuit).diagonal().real # Convert test circuit to Stim and sample from it. stim_circuit, _ = cirq_circuit_to_stim_data(circuit + cirq.measure( *sorted(circuit.all_qubits())[::-1])) sample_count = 10000 samples = [] for _ in range(sample_count): sim = stim.TableauSimulator() sim.do(stim_circuit) s = 0 for k, v in enumerate(sim.current_measurement_record()): s |= v << k samples.append(s) unique, counts = np.unique(samples, return_counts=True) # Compare sample rates to expected rates. for value, count in zip(unique, counts): expected_rate = expected_rates[value] actual_rate = count / sample_count allowed_variation = 5 * (expected_rate * (1 - expected_rate) / sample_count)**0.5 if not 0 <= expected_rate - allowed_variation <= 1: raise ValueError( "Not enough samples to bound results away from extremes.") assert abs(expected_rate - actual_rate) < allowed_variation, ( f"Sample rate {actual_rate} is over 5 standard deviations away from {expected_rate}.\n" f"Gate: {gate}\n" f"Test circuit:\n{circuit}\n" f"Converted circuit:\n{stim_circuit}\n")
def test_copy(): s = stim.TableauSimulator() s.h(0) s2 = s.copy() assert s.current_inverse_tableau() == s2.current_inverse_tableau() assert s is not s2
def test_do(): s = stim.TableauSimulator() s.do(stim.Circuit(""" S 0 """)) assert s.current_inverse_tableau() == stim.Tableau.from_named_gate("S_DAG")