-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Add SparseObservable.compose
#13766
Add SparseObservable.compose
#13766
Conversation
Pull Request Test Coverage Report for Build 13631003893Details
💛 - Coveralls |
9479cc6
to
4dc04bf
Compare
This adds matrix multiplication between `SparseObservable`s (or one `SparseObservable` and anything that can be coerced to an observable), using the same signature as similar methods in `quantum_info`. The current implementation attempts to be relatively memory efficient, though there are specialisations that may be implemented if we wanted to be faster: - if all the contained terms are Paulis, so each pairwise multiplication produces either zero or one alphabet letters, we currently have a redundant copy-in and copy-out of the Cartesian-product generator that effectively doubles the overhead. We could check whether each term contains only Paulis once, and then avoid that overhead in those cases. - alternatively, the Cartesian-product iterator could be taught to use the buffers of the output `SparseObservable` directly as its output space, rather than requiring copy-out of its buffers, which would remove the overhead from all calls, not just the single-Pauli ones, but requires a bit more bookkeeping, since the references would keep need to be updated. - the case of `qargs` being not `None` and `front=True` currently involves an extra copy step to simplify the logic. If this is an important case, we could add a right-matmul specialisation of `compose_map`. This commit currently does not contain tests, because we currently don't have suitable methods to test the validity of the output without coupling to the particular choice of factorisation of the matrix multiplication. We need either a way to convert to a Pauli-only representation (with the exponential explosion that entails) or to a matrix; either of these can be uniquely canonicalised.
Before Rust 1.83, `const` functions can't borrow `static`s, even immutably and without interior mutability.o There's no need for `matmul` to be `const`; it's always inlined anyway, and `matmul_generate` can be used in `const` contexts.
4dc04bf
to
449ced2
Compare
Ok, I've written all the tests as being against the |
One or more of the following people are relevant to this code:
|
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.
This looks very nice, and given that it also worked in the Jordan-Wigner demo and the tests seems to cover a lot of edge cases I'm very happy with this PR. I just have a few question below, otherwise we can merge 🙂
/// If `self` and `other` have different numbers of qubits. | ||
pub fn compose(&self, other: &SparseObservable) -> SparseObservable { | ||
if other.num_qubits != self.num_qubits { | ||
panic!( |
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.
Theres the ArithmeticError::MismatchedQubits
which we could return 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.
I panicked in the Rust-space methods by analogy to how the other mathematical functions work, and how ndarray
works, etc - it's a Rust programmer logic error to pass bad inputs to compose
, and it's a simple invariant to uphold.
If you want, we can have try_compose
and try_compose_map
for Result
variants (there's a couple more errors we'd have to add), and compose
, compose_map
for panicking ones.
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.
Ah, yeah, that also makes sense. This comment mainly comes from the fact that we raise errors in the Python function, but we can add try_
variants also later 👍🏻
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.
Is there some norm/guidelines in Rust as to what goes into mod.rs
and what goes in separate files? To me its a bit confusing having a lot of logic in mod.rs
as I'd expect this to live in some sparse_observable.rs
file or something -- but if this is common practice I'm also fine
/// alphabet. | ||
#[pyfunction] | ||
#[pyo3(name = "label")] | ||
fn bit_term_label(py: Python, slf: BitTerm) -> &Bound<PyString> { |
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.
What's the difference of this to BitTerm.py_label
?
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.
it's in the comment 1 line before this diff haha:
This doesn't use
py_label
so we can useintern!
.
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.
Ahh, this reads as "we would like to use intern!
hence we don't use py_label
"
Co-authored-by: Julien Gacon <jul@zurich.ibm.com>
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.
LGTM, thank you!
Summary
This adds matrix multiplication between
SparseObservable
s (or oneSparseObservable
and anything that can be coerced to an observable), using the same signature as similar methods inquantum_info
.The current implementation attempts to be relatively memory efficient, though there are specialisations that may be implemented if we wanted to be faster:
if all the contained terms are Paulis, so each pairwise multiplication produces either zero or one alphabet letters, we currently have a redundant copy-in and copy-out of the Cartesian-product generator that effectively doubles the overhead. We could check whether each term contains only Paulis once, and then avoid that overhead in those cases.
alternatively, the Cartesian-product iterator could be taught to use the buffers of the output
SparseObservable
directly as its output space, rather than requiring copy-out of its buffers, which would remove the overhead from all calls, not just the single-Pauli ones, but requires a bit more bookkeeping, since the references would keep need to be updated.the case of
qargs
being notNone
andfront=True
currently involves an extra copy step to simplify the logic. If this is an important case, we could add a right-matmul specialisation ofcompose_map
.Details and comments
This commit currently does not contain tests, because we currently don't have suitable methods to test the validity of the output without coupling to the particular choice of factorisation of the matrix multiplication. We need either a way to convert to a Pauli-only representation (with the exponential explosion that entails) or to a matrix; either of these can be uniquely canonicalised.
WIP until it has tests.Close #13390