-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pauli iter all #598
Pauli iter all #598
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good so far. A few ideas commented.
pybind11::arg("num_qubits"), | ||
pybind11::kw_only(), | ||
pybind11::arg("min_weight") = pybind11::none(), | ||
pybind11::arg("max_weight") = pybind11::none(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A useful argument here would be allowed_paulis: str
, so the user could restrict to X errors (allowed_paulis="X") or not-Y-errors (allowed_paulis="XZ").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to add this, and also the random selection of signs/phases.
doc/python_api_reference_vDev.md
Outdated
) -> None: | ||
"""Seed the iterator with a given qubit pattern. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not clear to me what this means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually going to remove this function, after closer thought it's not super helpful. The idea was to be able to "seed" the iterator at a specific bit pattern which may be difficult to reach if w was large, but this can be tested on the C++ side more easily, and not sure if it's actually useful on the python side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually scratch that, I needed it for testing random starting points for long strings with higher weight. I will change the name to something more descriptive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this to set_current_permutation.
Sorry, I forced pushed when it was still a draft to clean up the many doc related fixes. Should be ready for review now sans profiling. |
I'd be fine with those additions to |
Here's bit twiddle python code that generates all the requested pauli strings of a given weight, assuming length<=32.
|
A better one (in particular see
|
Ok, I can replace my ternary iteration with the bit twiddle above. |
In particular, I will add additional ops to simd_bits (left/right shift and an adder) and replace my ternary iteration with the bit twiddle. I may separate out the new operations in a separate PR. |
SGTM |
From #598 added +=, >>= and <<= to simd_bits. It wasn't obvious to me that these could use word level parallelism without using more memory? For example, the shifts could store the relevant carry masks and or these at the end but this would require a temporary of the same size as the simd_bits instance.
Rename some functions. WIP. Better tests. Update python test.
The windows failures seem to be due to a memory error pauli_it = list(
stim.PauliString.iter_all(
> num_qubits, min_weight=min_weight, max_weight=max_weight
)
)
E MemoryError The test_iter_all_random_permutation tests also seems to trigger test failures on windows periodically on win32 platforms. Not sure what's going on there. |
Caught this when trying to address #598.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the major tasks left are debugging the windows crash and adding pytest unit tests using the python side of the API.
// x ^= up | ||
result.xs ^= up; | ||
cur_k++; | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a while
loop if it always exits?
|
||
template <size_t W> | ||
bool PauliStringIterator<W>::pair_sat_increment() { | ||
// This will overflow for large cur_w. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overflow is bad or good here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad. I think I should assert on max_weight <= 40 on the pybind side? Or catch the wrapping and exit? Or just use the too large value since it would be pretty hard to iterate through 10^19 values.
template <size_t W> | ||
bool PauliStringIterator<W>::pair_sat_increment() { | ||
// This will overflow for large cur_w. | ||
size_t num_terms = static_cast<size_t>(pow(3, cur_w)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this actually returns the right answer for all relevant values? Doubles have 53 bits of precision; not enough for a 64 bit integer. It'd be safer to have a method like
uint64_t pow3(uint64_t p) {
assert(p < 41);
uint64_t r = 1;
if (p & 1) r *= 3
if (p & 2) r *= 9;
if (p & 4) r *= 81;
if (p & 8) r *= 6561;
if (p & 16) r *= 43046721;
if (p & 32) r *= 1853020188851841;
return r;
}
simd_bits<W> one(cur_perm.num_bits_padded()); | ||
one.u64[0] = uint64_t{1}; | ||
// inc += 1 | ||
inc += one; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a ++x
method to simd_bits
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it probably makes sense to add -- / -= too. For example, there are a few awkward parts where I'm doing (1 << sum_number_greather_than_64) - 1 in a clunky way.
I'm going to close this as I won't have time to come back to it for another month or so. |
Adds stim.PauliString.iter_all method.
Following the discussion in #397 the algorithm is a combination of finding the next lexicographically ordered permutation of w bits followed by iterating over all 3^w PauliStrings given this permutation of the qubit labels. For the first part I modified the bit twiddle algorithm to account for multiple words, for the second part I just iterate over 3^w integers and map this to a Pauli using the ternary representation of the integer. This was a bit trickier to get right than I expected, plenty of edge cases.
The modifications for the bit twiddle algorithm are quite specific and I was a bit torn between adding more general operators to simd_bits (like left / right shift + subtraction) which would be cleaner, and what I did, which is a bit clunky.
There are also a few optimizations that could be made which I haven't:
Draft for the moment until: