diff --git a/src/stim/stabilizers/pauli_string.h b/src/stim/stabilizers/pauli_string.h index b27009f09..8ba6435c9 100644 --- a/src/stim/stabilizers/pauli_string.h +++ b/src/stim/stabilizers/pauli_string.h @@ -131,6 +131,8 @@ struct PauliString { /// many times the number of requested qubits. Use this to /// avoid quadratic overheads from constant slight expansions. void ensure_num_qubits(size_t min_num_qubits, double resize_pad_factor); + + void safe_accumulate_pauli_term(GateTarget t, bool *imag); }; /// Writes a string describing the given Pauli string to an output stream. diff --git a/src/stim/stabilizers/pauli_string.inl b/src/stim/stabilizers/pauli_string.inl index 112bc28f3..a07b184f0 100644 --- a/src/stim/stabilizers/pauli_string.inl +++ b/src/stim/stabilizers/pauli_string.inl @@ -165,6 +165,31 @@ void PauliString::ensure_num_qubits(size_t min_num_qubits, double resize_pad_ num_qubits = min_num_qubits; } +template +void PauliString::safe_accumulate_pauli_term(GateTarget t, bool *imag) { + auto q = t.qubit_value(); + ensure_num_qubits(q + 1, 1.25); + bool x2 = (bool)(t.data & TARGET_PAULI_X_BIT); + bool z2 = (bool)(t.data & TARGET_PAULI_Z_BIT); + if (!(x2 | z2)) { + throw std::invalid_argument("Not a pauli target: " + t.str()); + } + + bit_ref x1 = xs[q]; + bit_ref z1 = zs[q]; + bool old_x1 = x1; + bool old_z1 = z1; + x1 ^= x2; + z1 ^= z2; + + // At each bit position: accumulate anti-commutation (+i or -i) counts. + bool x1z2 = x1 & z2; + bool anti_commutes = (x2 & z1) ^ x1z2; + sign ^= (*imag ^ old_x1 ^ old_z1 ^ x1z2) & anti_commutes; + sign ^= (bool)(t.data & TARGET_INVERTED_BIT); + *imag ^= anti_commutes; +} + template uint8_t PauliString::py_get_item(int64_t index) const { if (index < 0) { diff --git a/src/stim/stabilizers/pauli_string.test.cc b/src/stim/stabilizers/pauli_string.test.cc index b58bbe151..3467a387d 100644 --- a/src/stim/stabilizers/pauli_string.test.cc +++ b/src/stim/stabilizers/pauli_string.test.cc @@ -633,25 +633,117 @@ TEST_EACH_WORD_SIZE_W(pauli_string, before_tableau, { std::invalid_argument); }) -TEST_EACH_WORD_SIZE_W(pauli_string, weight, { - ASSERT_EQ(PauliString::from_str("+").ref().weight(), 0); - ASSERT_EQ(PauliString::from_str("+I").ref().weight(), 0); - ASSERT_EQ(PauliString::from_str("+X").ref().weight(), 1); - ASSERT_EQ(PauliString::from_str("+Y").ref().weight(), 1); - ASSERT_EQ(PauliString::from_str("+Z").ref().weight(), 1); - - ASSERT_EQ(PauliString::from_str("+IX").ref().weight(), 1); - ASSERT_EQ(PauliString::from_str("+XZ").ref().weight(), 2); - ASSERT_EQ(PauliString::from_str("+YY").ref().weight(), 2); - ASSERT_EQ(PauliString::from_str("+XI").ref().weight(), 1); - - PauliString p(1000); - ASSERT_EQ(p.ref().weight(), 0); - for (size_t k = 0; k < 1000; k++) { - p.xs[k] = k % 3 == 1; - p.zs[k] = k % 5 == 1; - } - ASSERT_EQ(p.ref().weight(), 333 + 199 - 66); - p.sign = true; - ASSERT_EQ(p.ref().weight(), 333 + 199 - 66); +TEST_EACH_WORD_SIZE_W(pauli_string, safe_accumulate_pauli_term, { + PauliString p(0); + bool imag = false; + + p.safe_accumulate_pauli_term(GateTarget::x(5), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("_____X")); + + p.safe_accumulate_pauli_term(GateTarget::x(5), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("______")); + + p.safe_accumulate_pauli_term(GateTarget::x(5), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("_____X")); + + p.safe_accumulate_pauli_term(GateTarget::z(5), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("_____Y")); + + p.safe_accumulate_pauli_term(GateTarget::z(5), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("_____X")); + + p.safe_accumulate_pauli_term(GateTarget::y(5), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-_____Z")); + + p.safe_accumulate_pauli_term(GateTarget::y(15), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-_____Z_________Y")); + + p.safe_accumulate_pauli_term(GateTarget::y(15, true), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("_____Z__________")); +}) + +TEST_EACH_WORD_SIZE_W(pauli_string, safe_accumulate_pauli_term_mul_table, { + PauliString p(1); + bool imag = false; + + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-I")); + + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("I")); + + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("I")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-Z")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("Y")); + + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("Z")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("I")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-X")); + + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::x(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("-Y")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::y(0), &imag); + ASSERT_EQ(imag, 1); + ASSERT_EQ(p, PauliString("X")); + p = PauliString(1); + imag = false; + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + p.safe_accumulate_pauli_term(GateTarget::z(0), &imag); + ASSERT_EQ(imag, 0); + ASSERT_EQ(p, PauliString("I")); }) diff --git a/src/stim/stabilizers/pauli_string_ref.h b/src/stim/stabilizers/pauli_string_ref.h index dc1d7ecfd..3519e7312 100644 --- a/src/stim/stabilizers/pauli_string_ref.h +++ b/src/stim/stabilizers/pauli_string_ref.h @@ -139,6 +139,8 @@ struct PauliStringRef { PauliString before(const CircuitInstruction &operation) const; size_t weight() const; + bool has_no_pauli_terms() const; + bool intersects(PauliStringRef other) const; private: void check_avoids_MPP(const CircuitInstruction &inst); diff --git a/src/stim/stabilizers/pauli_string_ref.inl b/src/stim/stabilizers/pauli_string_ref.inl index c91794f57..6c1b743d9 100644 --- a/src/stim/stabilizers/pauli_string_ref.inl +++ b/src/stim/stabilizers/pauli_string_ref.inl @@ -651,6 +651,16 @@ void PauliStringRef::scatter_into(PauliStringRef out, SpanRef +bool PauliStringRef::intersects(const PauliStringRef other) const { + size_t n = std::min(xs.num_u64_padded(), other.xs.num_u64_padded()); + uint64_t v = 0; + for (size_t k = 0; k < n; k++) { + v |= (xs.u64[k] | zs.u64[k]) & (other.xs.u64[k] | other.zs.u64[k]); + } + return v != 0; +} + template size_t PauliStringRef::weight() const { size_t total = 0; @@ -660,6 +670,16 @@ size_t PauliStringRef::weight() const { return total; } +template +bool PauliStringRef::has_no_pauli_terms() const { + size_t total = 0; + size_t n = xs.num_u64_padded(); + for (size_t k = 0; k < n; k++) { + total |= xs.u64[k] | zs.u64[k]; + } + return total == 0; +} + template std::ostream &operator<<(std::ostream &out, const PauliStringRef &ps) { out << "+-"[ps.sign]; diff --git a/src/stim/stabilizers/pauli_string_ref.test.cc b/src/stim/stabilizers/pauli_string_ref.test.cc index e730c82f8..35fdd091d 100644 --- a/src/stim/stabilizers/pauli_string_ref.test.cc +++ b/src/stim/stabilizers/pauli_string_ref.test.cc @@ -76,3 +76,80 @@ TEST_EACH_WORD_SIZE_W(pauli_string, do_instruction_agrees_with_tableau_sim, { } } }) + +TEST_EACH_WORD_SIZE_W(pauli_string, intersects, { + ASSERT_FALSE((PauliString("_").ref().intersects(PauliString("_")))); + ASSERT_FALSE((PauliString("_").ref().intersects(PauliString("X")))); + ASSERT_FALSE((PauliString("_").ref().intersects(PauliString("Y")))); + ASSERT_FALSE((PauliString("_").ref().intersects(PauliString("Z")))); + ASSERT_FALSE((PauliString("X").ref().intersects(PauliString("_")))); + ASSERT_TRUE((PauliString("X").ref().intersects(PauliString("X")))); + ASSERT_TRUE((PauliString("X").ref().intersects(PauliString("Y")))); + ASSERT_TRUE((PauliString("X").ref().intersects(PauliString("Z")))); + ASSERT_FALSE((PauliString("Y").ref().intersects(PauliString("_")))); + ASSERT_TRUE((PauliString("Y").ref().intersects(PauliString("X")))); + ASSERT_TRUE((PauliString("Y").ref().intersects(PauliString("Y")))); + ASSERT_TRUE((PauliString("Y").ref().intersects(PauliString("Z")))); + ASSERT_FALSE((PauliString("Z").ref().intersects(PauliString("_")))); + ASSERT_TRUE((PauliString("Z").ref().intersects(PauliString("X")))); + ASSERT_TRUE((PauliString("Z").ref().intersects(PauliString("Y")))); + ASSERT_TRUE((PauliString("Z").ref().intersects(PauliString("Z")))); + + ASSERT_TRUE((PauliString("_Z").ref().intersects(PauliString("ZZ")))); + ASSERT_TRUE((PauliString("Z_").ref().intersects(PauliString("ZZ")))); + ASSERT_TRUE((PauliString("ZZ").ref().intersects(PauliString("ZZ")))); + ASSERT_FALSE((PauliString("ZZ").ref().intersects(PauliString("__")))); + ASSERT_FALSE((PauliString("__").ref().intersects(PauliString("XZ")))); + ASSERT_FALSE((PauliString("________________________________________________") + .ref() + .intersects(PauliString("________________________________________________")))); + ASSERT_TRUE((PauliString("_______________________________________X________") + .ref() + .intersects(PauliString("_______________________________________X________")))); +}) + +TEST_EACH_WORD_SIZE_W(pauli_string, weight, { + ASSERT_EQ(PauliString::from_str("+").ref().weight(), 0); + ASSERT_EQ(PauliString::from_str("+I").ref().weight(), 0); + ASSERT_EQ(PauliString::from_str("+X").ref().weight(), 1); + ASSERT_EQ(PauliString::from_str("+Y").ref().weight(), 1); + ASSERT_EQ(PauliString::from_str("+Z").ref().weight(), 1); + + ASSERT_EQ(PauliString::from_str("+IX").ref().weight(), 1); + ASSERT_EQ(PauliString::from_str("+XZ").ref().weight(), 2); + ASSERT_EQ(PauliString::from_str("+YY").ref().weight(), 2); + ASSERT_EQ(PauliString::from_str("+XI").ref().weight(), 1); + + PauliString p(1000); + ASSERT_EQ(p.ref().weight(), 0); + for (size_t k = 0; k < 1000; k++) { + p.xs[k] = k % 3 == 1; + p.zs[k] = k % 5 == 1; + } + ASSERT_EQ(p.ref().weight(), 333 + 199 - 66); + p.sign = true; + ASSERT_EQ(p.ref().weight(), 333 + 199 - 66); +}) + +TEST_EACH_WORD_SIZE_W(pauli_string, has_no_pauli_terms, { + ASSERT_EQ((PauliString::from_str("+").ref().has_no_pauli_terms()), true); + ASSERT_EQ((PauliString::from_str("+I").ref().has_no_pauli_terms()), true); + ASSERT_EQ((PauliString::from_str("+X").ref().has_no_pauli_terms()), false); + ASSERT_EQ((PauliString::from_str("+Y").ref().has_no_pauli_terms()), false); + ASSERT_EQ((PauliString::from_str("+Z").ref().has_no_pauli_terms()), false); + + ASSERT_EQ((PauliString::from_str("+II").ref().has_no_pauli_terms()), true); + ASSERT_EQ((PauliString::from_str("+IX").ref().has_no_pauli_terms()), false); + ASSERT_EQ((PauliString::from_str("+XZ").ref().has_no_pauli_terms()), false); + ASSERT_EQ((PauliString::from_str("+YY").ref().has_no_pauli_terms()), false); + ASSERT_EQ((PauliString::from_str("+XI").ref().has_no_pauli_terms()), false); + + PauliString p(1000); + ASSERT_TRUE(p.ref().has_no_pauli_terms()); + p.xs[700] = true; + ASSERT_FALSE(p.ref().has_no_pauli_terms()); + p.zs[700] = true; + ASSERT_FALSE(p.ref().has_no_pauli_terms()); + p.xs[700] = false; + ASSERT_FALSE(p.ref().has_no_pauli_terms()); +})