Skip to content

Commit

Permalink
Add some C++ pauli string utility methods
Browse files Browse the repository at this point in the history
- Add `stim::PauliString::safe_accumulate_pauli_term`
- Add `stim::PauliStringRef::has_no_pauli_terms`
- Add `stim::PauliStringRef::intersects`
  • Loading branch information
Strilanc committed Feb 14, 2024
1 parent b5686eb commit a1e23f6
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 21 deletions.
2 changes: 2 additions & 0 deletions src/stim/stabilizers/pauli_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
25 changes: 25 additions & 0 deletions src/stim/stabilizers/pauli_string.inl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,31 @@ void PauliString<W>::ensure_num_qubits(size_t min_num_qubits, double resize_pad_
num_qubits = min_num_qubits;
}

template <size_t W>
void PauliString<W>::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 <size_t W>
uint8_t PauliString<W>::py_get_item(int64_t index) const {
if (index < 0) {
Expand Down
134 changes: 113 additions & 21 deletions src/stim/stabilizers/pauli_string.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<W>::from_str("+").ref().weight(), 0);
ASSERT_EQ(PauliString<W>::from_str("+I").ref().weight(), 0);
ASSERT_EQ(PauliString<W>::from_str("+X").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+Y").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+Z").ref().weight(), 1);

ASSERT_EQ(PauliString<W>::from_str("+IX").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+XZ").ref().weight(), 2);
ASSERT_EQ(PauliString<W>::from_str("+YY").ref().weight(), 2);
ASSERT_EQ(PauliString<W>::from_str("+XI").ref().weight(), 1);

PauliString<W> 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<W> p(0);
bool imag = false;

p.safe_accumulate_pauli_term(GateTarget::x(5), &imag);
ASSERT_EQ(imag, 0);
ASSERT_EQ(p, PauliString<W>("_____X"));

p.safe_accumulate_pauli_term(GateTarget::x(5), &imag);
ASSERT_EQ(imag, 0);
ASSERT_EQ(p, PauliString<W>("______"));

p.safe_accumulate_pauli_term(GateTarget::x(5), &imag);
ASSERT_EQ(imag, 0);
ASSERT_EQ(p, PauliString<W>("_____X"));

p.safe_accumulate_pauli_term(GateTarget::z(5), &imag);
ASSERT_EQ(imag, 1);
ASSERT_EQ(p, PauliString<W>("_____Y"));

p.safe_accumulate_pauli_term(GateTarget::z(5), &imag);
ASSERT_EQ(imag, 0);
ASSERT_EQ(p, PauliString<W>("_____X"));

p.safe_accumulate_pauli_term(GateTarget::y(5), &imag);
ASSERT_EQ(imag, 1);
ASSERT_EQ(p, PauliString<W>("-_____Z"));

p.safe_accumulate_pauli_term(GateTarget::y(15), &imag);
ASSERT_EQ(imag, 1);
ASSERT_EQ(p, PauliString<W>("-_____Z_________Y"));

p.safe_accumulate_pauli_term(GateTarget::y(15, true), &imag);
ASSERT_EQ(imag, 1);
ASSERT_EQ(p, PauliString<W>("_____Z__________"));
})

TEST_EACH_WORD_SIZE_W(pauli_string, safe_accumulate_pauli_term_mul_table, {
PauliString<W> p(1);
bool imag = false;

p = PauliString<W>(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<W>("-I"));

p = PauliString<W>(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<W>("I"));

p = PauliString<W>(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<W>("I"));
p = PauliString<W>(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<W>("-Z"));
p = PauliString<W>(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<W>("Y"));

p = PauliString<W>(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<W>("Z"));
p = PauliString<W>(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<W>("I"));
p = PauliString<W>(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<W>("-X"));

p = PauliString<W>(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<W>("-Y"));
p = PauliString<W>(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<W>("X"));
p = PauliString<W>(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<W>("I"));
})
2 changes: 2 additions & 0 deletions src/stim/stabilizers/pauli_string_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ struct PauliStringRef {
PauliString<W> before(const CircuitInstruction &operation) const;

size_t weight() const;
bool has_no_pauli_terms() const;
bool intersects(PauliStringRef<W> other) const;

private:
void check_avoids_MPP(const CircuitInstruction &inst);
Expand Down
20 changes: 20 additions & 0 deletions src/stim/stabilizers/pauli_string_ref.inl
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,16 @@ void PauliStringRef<W>::scatter_into(PauliStringRef<W> out, SpanRef<const size_t
out.sign ^= sign;
}

template <size_t W>
bool PauliStringRef<W>::intersects(const PauliStringRef<W> 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 W>
size_t PauliStringRef<W>::weight() const {
size_t total = 0;
Expand All @@ -660,6 +670,16 @@ size_t PauliStringRef<W>::weight() const {
return total;
}

template <size_t W>
bool PauliStringRef<W>::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 <size_t W>
std::ostream &operator<<(std::ostream &out, const PauliStringRef<W> &ps) {
out << "+-"[ps.sign];
Expand Down
77 changes: 77 additions & 0 deletions src/stim/stabilizers/pauli_string_ref.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<W>("_").ref().intersects(PauliString<W>("_"))));
ASSERT_FALSE((PauliString<W>("_").ref().intersects(PauliString<W>("X"))));
ASSERT_FALSE((PauliString<W>("_").ref().intersects(PauliString<W>("Y"))));
ASSERT_FALSE((PauliString<W>("_").ref().intersects(PauliString<W>("Z"))));
ASSERT_FALSE((PauliString<W>("X").ref().intersects(PauliString<W>("_"))));
ASSERT_TRUE((PauliString<W>("X").ref().intersects(PauliString<W>("X"))));
ASSERT_TRUE((PauliString<W>("X").ref().intersects(PauliString<W>("Y"))));
ASSERT_TRUE((PauliString<W>("X").ref().intersects(PauliString<W>("Z"))));
ASSERT_FALSE((PauliString<W>("Y").ref().intersects(PauliString<W>("_"))));
ASSERT_TRUE((PauliString<W>("Y").ref().intersects(PauliString<W>("X"))));
ASSERT_TRUE((PauliString<W>("Y").ref().intersects(PauliString<W>("Y"))));
ASSERT_TRUE((PauliString<W>("Y").ref().intersects(PauliString<W>("Z"))));
ASSERT_FALSE((PauliString<W>("Z").ref().intersects(PauliString<W>("_"))));
ASSERT_TRUE((PauliString<W>("Z").ref().intersects(PauliString<W>("X"))));
ASSERT_TRUE((PauliString<W>("Z").ref().intersects(PauliString<W>("Y"))));
ASSERT_TRUE((PauliString<W>("Z").ref().intersects(PauliString<W>("Z"))));

ASSERT_TRUE((PauliString<W>("_Z").ref().intersects(PauliString<W>("ZZ"))));
ASSERT_TRUE((PauliString<W>("Z_").ref().intersects(PauliString<W>("ZZ"))));
ASSERT_TRUE((PauliString<W>("ZZ").ref().intersects(PauliString<W>("ZZ"))));
ASSERT_FALSE((PauliString<W>("ZZ").ref().intersects(PauliString<W>("__"))));
ASSERT_FALSE((PauliString<W>("__").ref().intersects(PauliString<W>("XZ"))));
ASSERT_FALSE((PauliString<W>("________________________________________________")
.ref()
.intersects(PauliString<W>("________________________________________________"))));
ASSERT_TRUE((PauliString<W>("_______________________________________X________")
.ref()
.intersects(PauliString<W>("_______________________________________X________"))));
})

TEST_EACH_WORD_SIZE_W(pauli_string, weight, {
ASSERT_EQ(PauliString<W>::from_str("+").ref().weight(), 0);
ASSERT_EQ(PauliString<W>::from_str("+I").ref().weight(), 0);
ASSERT_EQ(PauliString<W>::from_str("+X").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+Y").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+Z").ref().weight(), 1);

ASSERT_EQ(PauliString<W>::from_str("+IX").ref().weight(), 1);
ASSERT_EQ(PauliString<W>::from_str("+XZ").ref().weight(), 2);
ASSERT_EQ(PauliString<W>::from_str("+YY").ref().weight(), 2);
ASSERT_EQ(PauliString<W>::from_str("+XI").ref().weight(), 1);

PauliString<W> 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<W>::from_str("+").ref().has_no_pauli_terms()), true);
ASSERT_EQ((PauliString<W>::from_str("+I").ref().has_no_pauli_terms()), true);
ASSERT_EQ((PauliString<W>::from_str("+X").ref().has_no_pauli_terms()), false);
ASSERT_EQ((PauliString<W>::from_str("+Y").ref().has_no_pauli_terms()), false);
ASSERT_EQ((PauliString<W>::from_str("+Z").ref().has_no_pauli_terms()), false);

ASSERT_EQ((PauliString<W>::from_str("+II").ref().has_no_pauli_terms()), true);
ASSERT_EQ((PauliString<W>::from_str("+IX").ref().has_no_pauli_terms()), false);
ASSERT_EQ((PauliString<W>::from_str("+XZ").ref().has_no_pauli_terms()), false);
ASSERT_EQ((PauliString<W>::from_str("+YY").ref().has_no_pauli_terms()), false);
ASSERT_EQ((PauliString<W>::from_str("+XI").ref().has_no_pauli_terms()), false);

PauliString<W> 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());
})

0 comments on commit a1e23f6

Please sign in to comment.