Skip to content

Commit 0ad5dc1

Browse files
Add Hermitian Support for Finite-Shot Measurement Statistics (#451)
Add hermitian support for finite-shot measurement statistics [sc-53754] --------- Co-authored-by: Ali Asadi <[email protected]>
1 parent 0b107d7 commit 0ad5dc1

File tree

7 files changed

+56
-46
lines changed

7 files changed

+56
-46
lines changed

.dep-versions

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ pennylane=0.40.0-dev20
1212

1313
# For a custom LQ/LK version, update the package version here and at
1414
# 'doc/requirements.txt'
15-
lightning=0.40.0-dev24
15+
lightning=0.40.0-dev28

.github/workflows/build-wheel-macos-arm64.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,10 @@ jobs:
399399
- name: Checkout Catalyst repo
400400
uses: actions/checkout@v4
401401

402-
- name: Install Python 3.10
402+
- name: Install Python ${{ matrix.python_version }}
403403
uses: actions/setup-python@v5
404404
with:
405-
python-version: '3.10'
405+
python-version: ${{ matrix.python_version }}
406406

407407
- name: Download Wheel Artifact
408408
uses: actions/download-artifact@v4
@@ -426,6 +426,7 @@ jobs:
426426
python${{ matrix.python_version }} -m pip install PennyLane-Lightning-Kokkos
427427
python${{ matrix.python_version }} -m pip install 'amazon-braket-pennylane-plugin>1.27.1'
428428
429+
429430
- name: Install OQC client
430431
run: |
431432
python${{ matrix.python_version }} -m pip install oqc-qcaas-client

doc/releases/changelog-dev.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
<h3>Improvements 🛠</h3>
66

7+
* Lightning runtime shot-measurement support for Hermitian observables.
8+
[(#451)](https://github.com/PennyLaneAI/catalyst/pull/451)
9+
710
* Replace pybind11 with nanobind for C++/Python bindings in the frontend and in the runtime.
811
[(#1173)](https://github.com/PennyLaneAI/catalyst/pull/1173)
912
[(#1293)](https://github.com/PennyLaneAI/catalyst/pull/1293)
@@ -128,5 +131,6 @@ Erick Ochoa Lopez,
128131
Mehrdad Malekmohammadi,
129132
William Maxwell
130133
Romain Moyard,
134+
Shuli Shu,
131135
Raul Torres,
132136
Paul Haochen Wang.

doc/requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ lxml_html_clean
3030

3131
# Pre-install PL development wheels
3232
--extra-index-url https://test.pypi.org/simple/
33-
pennylane-lightning-kokkos==0.40.0-dev24
34-
pennylane-lightning==0.40.0-dev24
33+
pennylane-lightning-kokkos==0.40.0-dev28
34+
pennylane-lightning==0.40.0-dev28
3535
pennylane==0.40.0-dev20

frontend/catalyst/compiler.py

+20-17
Original file line numberDiff line numberDiff line change
@@ -121,24 +121,25 @@ def get_default_flags(options):
121121
path_within_package = "lib"
122122
file_extension = ".so" if platform.system() == "Linux" else ".dylib" # pragma: no branch
123123

124-
if platform.system() == "Darwin" and platform.machine() == "arm64": # pragma: nocover
125-
# use our own build of LAPACKe to interface with Accelerate
126-
lapack_lib_name = "lapacke.3"
127-
else:
128-
package_spec = importlib.util.find_spec(package_name)
129-
package_directory = path.dirname(package_spec.origin)
130-
lapack_lib_path = path.join(package_directory, path_within_package)
124+
package_spec = importlib.util.find_spec(package_name)
125+
package_directory = path.dirname(package_spec.origin)
126+
lapack_lib_path = path.join(package_directory, path_within_package)
127+
128+
search_pattern = path.join(lapack_lib_path, f"lib*{lib_name}*{file_extension}")
129+
search_result = glob.glob(search_pattern)
130+
if not search_result: # pragma: nocover
131+
raise CompileError(
132+
f'Unable to find OpenBLAS library at "{search_pattern}". '
133+
"Please ensure that scipy is installed and available via pip."
134+
)
131135

132-
search_pattern = path.join(lapack_lib_path, f"lib*{lib_name}*{file_extension}")
133-
search_result = glob.glob(search_pattern)
134-
if not search_result: # pragma: nocover
135-
raise CompileError(
136-
f'Unable to find OpenBLAS library at "{search_pattern}". '
137-
"Please ensure that scipy is installed and available via pip."
138-
)
136+
lib_path_flags += [f"-Wl,-rpath,{lapack_lib_path}", f"-L{lapack_lib_path}"]
137+
lapack_lib_name = path.basename(search_result[0])[3 : -len(file_extension)]
139138

140-
lib_path_flags += [f"-Wl,-rpath,{lapack_lib_path}", f"-L{lapack_lib_path}"]
141-
lapack_lib_name = path.basename(search_result[0])[3 : -len(file_extension)]
139+
lapack_libs = [lapack_lib_name]
140+
if platform.system() == "Darwin" and platform.machine() == "arm64": # pragma: nocover
141+
# use our own build of LAPACKe to interface with Accelerate
142+
lapack_libs += ["lapacke.3"]
142143

143144
system_flags = []
144145
if platform.system() == "Linux":
@@ -158,6 +159,8 @@ def get_default_flags(options):
158159
elif options.async_qnodes and platform.system() == "Darwin": # pragma: nocover
159160
system_flags += ["-lc++"]
160161

162+
lapack_libs = [f"-l{name}" for name in lapack_libs]
163+
161164
default_flags = [
162165
"-shared",
163166
"-rdynamic",
@@ -166,7 +169,7 @@ def get_default_flags(options):
166169
"-lrt_capi",
167170
"-lpthread",
168171
"-lmlir_c_runner_utils", # required for memref.copy
169-
f"-l{lapack_lib_name}", # required for custom_calls lib
172+
*lapack_libs,
170173
"-lcustom_calls",
171174
"-lmlir_async_runtime",
172175
]

frontend/test/pytest/test_measurements_shots_results.py

+20-18
Original file line numberDiff line numberDiff line change
@@ -124,25 +124,26 @@ def circuit():
124124
result = qjit(circuit)()
125125
assert np.allclose(result, expected, atol=tol_stochastic, rtol=tol_stochastic)
126126

127-
def test_hermitian(self, backend):
127+
def test_hermitian(self, backend, tol_stochastic):
128128
"""Test expval Hermitian observables with shots."""
129+
n_wires = 3
130+
n_shots = 10000
131+
dev = qml.device(backend, wires=n_wires, shots=n_shots)
129132

130-
@qjit
131-
@qml.qnode(qml.device(backend, wires=3, shots=10000))
133+
@qml.qnode(dev)
132134
def circuit(x, y):
133135
qml.RX(x, wires=0)
134136
qml.RX(y, wires=1)
135137
qml.CNOT(wires=[0, 1])
136138
A = np.array(
137139
[[complex(1.0, 0.0), complex(2.0, 0.0)], [complex(2.0, 0.0), complex(1.0, 0.0)]]
138140
)
139-
return qml.expval(qml.Hermitian(A, wires=2) + qml.PauliX(0) + qml.Hermitian(A, wires=1))
141+
return qml.expval(qml.Hermitian(A, wires=2))
140142

141-
with pytest.raises(
142-
RuntimeError,
143-
match="Hermitian observables with shot measurement are not supported",
144-
):
145-
circuit(np.pi / 4, np.pi / 4)
143+
expected = circuit(np.pi / 4, np.pi / 4)
144+
result = qjit(circuit)(np.pi / 4, np.pi / 4)
145+
146+
assert np.allclose(result, expected, atol=tol_stochastic, rtol=tol_stochastic)
146147

147148
def test_paulix_pauliy(self, backend, tol_stochastic):
148149
"""Test that a tensor product involving PauliX and PauliY works correctly"""
@@ -332,25 +333,26 @@ def circuit():
332333
result = qjit(circuit)()
333334
assert np.allclose(result, expected, atol=tol_stochastic, rtol=tol_stochastic)
334335

335-
def test_hermitian_shots(self, backend):
336+
def test_hermitian_shots(self, backend, tol_stochastic):
336337
"""Test var Hermitian observables with shots."""
338+
n_wires = 3
339+
n_shots = 10000
340+
dev = qml.device(backend, wires=n_wires, shots=n_shots)
337341

338-
@qjit
339-
@qml.qnode(qml.device(backend, wires=3, shots=10000))
342+
@qml.qnode(dev)
340343
def circuit(x, y):
341344
qml.RX(x, wires=0)
342345
qml.RX(y, wires=1)
343346
qml.CNOT(wires=[0, 1])
344347
A = np.array(
345348
[[complex(1.0, 0.0), complex(2.0, 0.0)], [complex(2.0, 0.0), complex(1.0, 0.0)]]
346349
)
347-
return qml.var(qml.Hermitian(A, wires=2) + qml.PauliX(0) + qml.Hermitian(A, wires=1))
350+
return qml.var(qml.Hermitian(A, wires=2))
348351

349-
with pytest.raises(
350-
RuntimeError,
351-
match="Hermitian observables with shot measurement are not supported",
352-
):
353-
circuit(np.pi / 4, np.pi / 4)
352+
expected = circuit(np.pi / 4, np.pi / 4)
353+
result = qjit(circuit)(np.pi / 4, np.pi / 4)
354+
355+
assert np.allclose(result, expected, atol=tol_stochastic, rtol=tol_stochastic)
354356

355357
def test_paulix_pauliy(self, backend, tol_stochastic):
356358
"""Test that a tensor product involving PauliX and PauliY works correctly"""

runtime/README.rst

+6-6
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,16 @@ The following table shows the available devices along with supported features:
6363
- ``Identity``, ``PauliX``, ``PauliY``, ``PauliZ``, ``Hadamard``, ``Hermitian``, ``Hamiltonian``, and Tensor Product of Observables
6464
- ``Identity``, ``PauliX``, ``PauliY``, ``PauliZ``, ``Hadamard``, ``Hermitian``, and Tensor Product of Observables
6565
* - Expectation Value
66-
- All observables; Finite-shots supported except for ``Hermitian``
67-
- All observables; Finite-shots supported except for ``Hermitian``
66+
- All observables; Finite-shots supported
67+
- All observables; Finite-shots supported
6868
- All observables; Finite-shots supported
6969
* - Variance
70-
- All observables; Finite-shots supported except for ``Hermitian``
71-
- All observables; Finite-shots supported except for ``Hermitian``
70+
- All observables; Finite-shots supported
71+
- All observables; Finite-shots supported
7272
- All observables; Finite-shots supported
7373
* - Probability
74-
- Only for the computational basis on the supplied qubits; Finite-shots supported except for ``Hermitian``
75-
- Only for the computational basis on the supplied qubits; Finite-shots supported except for ``Hermitian``
74+
- Only for the computational basis on the supplied qubits; Finite-shots supported
75+
- Only for the computational basis on the supplied qubits; Finite-shots supported
7676
- The computational basis on all active qubits; Finite-shots supported
7777
* - Sampling
7878
- Only for the computational basis on the supplied qubits

0 commit comments

Comments
 (0)