Skip to content

Commit 548c857

Browse files
jakelishmanCryoris
andauthored
Add base representation of SparseObservable (#12671)
* Add base representation of `SparseObservable` This adds the base representation of `SparseObservable`, including the simple constructors from Python space and the ability to view the data buffers. This commit does not include the mathematical manipulations of the operators, nor some of the helper methods that will be used to manipulate the operators in the context of primitives execution. These will follow in subsequent patches. The design and implementation notes of `SparseObservable` are described in a Qiskit RFC that preceeded this patch series[^1], and it's best to consult that document for full details on the operator considerations. [^1]: https://github.com/Qiskit/RFCs/blob/7a74b08793475b7b0142d3a3f7142cabcfd33ab8/0021-sparse-observable.md * Rename `num_ops` to `num_terms` * Fix typos and 🇺🇸 Co-authored-by: Julien Gacon <jul@zurich.ibm.com> * Add additional documentation * Fix tests of `num_terms` * Add more documentation * Fix error-message typo Co-authored-by: Julien Gacon <jul@zurich.ibm.com> --------- Co-authored-by: Julien Gacon <jul@zurich.ibm.com>
1 parent 35c0391 commit 548c857

10 files changed

Lines changed: 2684 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/accelerate/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ qiskit-circuit.workspace = true
2626
thiserror.workspace = true
2727
ndarray_einsum_beta = "0.7"
2828
once_cell = "1.20.2"
29+
bytemuck.workspace = true
2930

3031
[dependencies.smallvec]
3132
workspace = true

crates/accelerate/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub mod remove_diagonal_gates_before_measure;
4040
pub mod results;
4141
pub mod sabre;
4242
pub mod sampled_exp_val;
43+
pub mod sparse_observable;
4344
pub mod sparse_pauli_op;
4445
pub mod split_2q_unitaries;
4546
pub mod star_prerouting;

crates/accelerate/src/sparse_observable.rs

Lines changed: 1709 additions & 0 deletions
Large diffs are not rendered by default.

crates/circuit/src/imports.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ pub static XX_DECOMPOSER: ImportOnceCell =
122122
ImportOnceCell::new("qiskit.synthesis.two_qubit.xx_decompose", "XXDecomposer");
123123
pub static XX_EMBODIMENTS: ImportOnceCell =
124124
ImportOnceCell::new("qiskit.synthesis.two_qubit.xx_decompose", "XXEmbodiments");
125+
pub static NUMPY_COPY_ONLY_IF_NEEDED: ImportOnceCell =
126+
ImportOnceCell::new("qiskit._numpy_compat", "COPY_ONLY_IF_NEEDED");
125127

126128
/// A mapping from the enum variant in crate::operations::StandardGate to the python
127129
/// module path and class name to import it. This is used to populate the conversion table

crates/pyext/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ fn _accelerate(m: &Bound<PyModule>) -> PyResult<()> {
5353
add_submodule(m, ::qiskit_accelerate::results::results, "results")?;
5454
add_submodule(m, ::qiskit_accelerate::sabre::sabre, "sabre")?;
5555
add_submodule(m, ::qiskit_accelerate::sampled_exp_val::sampled_exp_val, "sampled_exp_val")?;
56+
add_submodule(m, ::qiskit_accelerate::sparse_observable::sparse_observable, "sparse_observable")?;
5657
add_submodule(m, ::qiskit_accelerate::sparse_pauli_op::sparse_pauli_op, "sparse_pauli_op")?;
5758
add_submodule(m, ::qiskit_accelerate::split_2q_unitaries::split_2q_unitaries_mod, "split_2q_unitaries")?;
5859
add_submodule(m, ::qiskit_accelerate::star_prerouting::star_prerouting, "star_prerouting")?;

qiskit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
sys.modules["qiskit._accelerate.results"] = _accelerate.results
8080
sys.modules["qiskit._accelerate.sabre"] = _accelerate.sabre
8181
sys.modules["qiskit._accelerate.sampled_exp_val"] = _accelerate.sampled_exp_val
82+
sys.modules["qiskit._accelerate.sparse_observable"] = _accelerate.sparse_observable
8283
sys.modules["qiskit._accelerate.sparse_pauli_op"] = _accelerate.sparse_pauli_op
8384
sys.modules["qiskit._accelerate.star_prerouting"] = _accelerate.star_prerouting
8485
sys.modules["qiskit._accelerate.stochastic_swap"] = _accelerate.stochastic_swap

qiskit/quantum_info/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
Pauli
2929
Clifford
3030
ScalarOp
31+
SparseObservable
3132
SparsePauliOp
3233
CNOTDihedral
3334
PauliList
@@ -113,6 +114,9 @@
113114
"""
114115

115116
from __future__ import annotations
117+
118+
from qiskit._accelerate.sparse_observable import SparseObservable
119+
116120
from .analysis import hellinger_distance, hellinger_fidelity, Z2Symmetries
117121
from .operators import (
118122
Clifford,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
features_quantum_info:
3+
- |
4+
A new observable class has been added. :class:`.SparseObservable` represents observables as a
5+
sum of terms, similar to :class:`.SparsePauliOp`, but with two core differences:
6+
7+
1. Each complete term is stored as (effectively) a series of ``(qubit, bit_term)`` pairs,
8+
without storing qubits that undergo the identity for that term. This significantly improves
9+
the memory usage of observables such as the weighted sum of Paulis :math:`\sum_i c_i Z_i`.
10+
11+
2. The single-qubit term alphabet is overcomplete for the operator space; it can represent Pauli
12+
operators (like :class:`.SparsePauliOp`), but also projectors onto the eigenstates of the
13+
Pauli operators, like :math:`\lvert 0\rangle\langle 0\rangle`. Such projectors can be
14+
measured on hardware equally as efficiently as their corresponding Pauli operator, but
15+
:class:`.SparsePauliOp` would require an exponential number of terms to represent
16+
:math:`{\lvert0\rangle\langle0\rvert}^{\otimes n}` over :math:`n` qubits, while
17+
:class:`.SparseObservable` needs only a single term.
18+
19+
You can construct and manipulate :class:`.SparseObservable` using an interface familiar to users
20+
of :class:`.SparsePauliOp`::
21+
22+
from qiskit.quantum_info import SparseObservable
23+
24+
obs = SparseObservable.from_sparse_list([
25+
("XZY", (2, 1, 0), 1.5j),
26+
("+-", (100, 99), 0.5j),
27+
("01", (50, 49), 0.5),
28+
])
29+
30+
:class:`.SparseObservable` is not currently supported as an input format to the primitives
31+
(:mod:`qiskit.primitives`), but we expect to expand these interfaces to include them in the
32+
future.

0 commit comments

Comments
 (0)