Skip to content

Commit 9fbc8e3

Browse files
authored
Seed samples for lightning.qubit/kokkos (#1164)
**Context:** There's still quite a few frontend tests stochastically failing because the `seed` option in `qjit` only controls the measurements, but not the samples. We add seeding to the samples. **Description of the Change:** When `qjit(seed=...)` receives a (unsigned 32 bit int) seed value from the user, the seed gets propagated through mlir and [eventually becomes a field of the `Catalyst::Runtime::Simulator::LightningSimulator` class, alongside the seeded `std::mt19937` rng instance ](https://github.com/PennyLaneAI/catalyst/blob/a580bada575793b780d5366aa77dff6157cd4f93/runtime/lib/backend/lightning/lightning_dynamic/LightningSimulator.hpp#L54). This was done in #936. In #936 , [the device's rng instance is used during measurements ](https://github.com/PennyLaneAI/catalyst/blob/a580bada575793b780d5366aa77dff6157cd4f93/runtime/lib/backend/lightning/lightning_dynamic/LightningSimulator.cpp#L451), but not during samples. This is because samples are performed from the `Pennylane::LightningQubit::Measures::Measurements` class through the `generate_samples` methods, which is controlled by the lightning repo. To seed samples, we use the device rng instance to generate a deterministic seed to pass it onto the state vector's `generate_samples` methods. This is the only change in catalyst. In lightning, the `generate_samples` method now can take in a seeding number. The catalyst devices pass in a seed into the lightning `generate_samples`; this seed is created deterministically from the aforementioned already seeded catalyst context rng instance. This makes the generated samples deterministc. The above is published on the lightning repo as the branch "seed_sample_lightning": PennyLaneAI/pennylane-lightning#927 PennyLaneAI/pennylane-lightning@6f3e0d5 **Benefits:** Fewer (hopefully no) stochatically failing frontend tests. **Related GitHub Issues:** #999 [sc-72878]
1 parent 4e58b7a commit 9fbc8e3

13 files changed

+69
-15
lines changed

.dep-versions

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ pennylane=0.39.0.dev26
1515
# 'runtime/Makefile' and at all GitHub workflows, using the exact
1616
# commit hash corresponding to the merged PR that implements the
1717
# desired feature.
18-
lightning=0.38.0
18+
lightning=0.39.0-dev38

.github/workflows/build-wheel-linux-x86_64.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ jobs:
342342
-DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \
343343
-Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \
344344
-DENABLE_LAPACK=OFF \
345-
-DLIGHTNING_GIT_TAG=latest_release \
345+
-DLIGHTNING_GIT_TAG=6f3e0d5d371ff9823a3177dd2c66052668883d42 \
346346
-DENABLE_WARNINGS=OFF \
347347
-DENABLE_OPENQASM=ON \
348348
-DENABLE_OPENMP=OFF \

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ jobs:
301301
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$GITHUB_WORKSPACE/runtime-build/lib \
302302
-DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \
303303
-Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \
304-
-DLIGHTNING_GIT_TAG=latest_release \
304+
-DLIGHTNING_GIT_TAG=6f3e0d5d371ff9823a3177dd2c66052668883d42\
305305
-DENABLE_LAPACK=OFF \
306306
-DENABLE_WARNINGS=OFF \
307307
-DENABLE_OPENQASM=ON \

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ jobs:
292292
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$GITHUB_WORKSPACE/runtime-build/lib \
293293
-DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \
294294
-Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \
295-
-DLIGHTNING_GIT_TAG=latest_release \
295+
-DLIGHTNING_GIT_TAG=6f3e0d5d371ff9823a3177dd2c66052668883d42 \
296296
-DENABLE_LAPACK=OFF \
297297
-DENABLE_WARNINGS=OFF \
298298
-DENABLE_OPENQASM=ON \

.github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ cmake -S runtime -B runtime-build -G Ninja \
4545
-DPYTHON_INCLUDE_DIR=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/include/python${PYTHON_VERSION} \
4646
-DPYTHON_LIBRARY=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/lib \
4747
-Dpybind11_DIR=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/lib/python${PYTHON_VERSION}/site-packages/pybind11/share/cmake/pybind11 \
48-
-DLIGHTNING_GIT_TAG=latest_release \
48+
-DLIGHTNING_GIT_TAG=6f3e0d5d371ff9823a3177dd2c66052668883d42 \
4949
-DENABLE_LAPACK=OFF \
5050
-DENABLE_WARNINGS=OFF \
5151
-DENABLE_OPENQASM=ON \

doc/releases/changelog-dev.md

+3
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@
202202
* Cached primitive lowerings is used instead of a custom cache structure.
203203
[(#1159)](https://github.com/PennyLaneAI/catalyst/pull/1159)
204204

205+
* Samples on lightning.qubit/kokkos can now be seeded with `qjit(seed=...)`.
206+
[(#1164)](https://github.com/PennyLaneAI/catalyst/pull/1164)
207+
205208
<h3>Breaking changes</h3>
206209

207210
* Remove `static_size` field from `AbstractQreg` class.

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.38.0
34-
pennylane-lightning==0.38.0
33+
pennylane-lightning-kokkos==0.39.0-dev38
34+
pennylane-lightning==0.39.0-dev38
3535
pennylane==0.39.0.dev26

frontend/test/pytest/test_measurement_transforms.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def basic_circuit(theta: float):
167167
assert "counts" in mlir
168168

169169
theta = 1.9
170-
expval_res, var_res, counts_res, probs_res = qml.qjit(transformed_circuit)(theta)
170+
expval_res, var_res, counts_res, probs_res = qml.qjit(transformed_circuit, seed=37)(theta)
171171

172172
expval_expected = np.sin(theta) * np.sin(theta / 2)
173173
var_expected = 1 - np.sin(2 * theta) ** 2
@@ -221,7 +221,7 @@ def basic_circuit(theta: float):
221221

222222
theta = 1.9
223223

224-
expval_res, var_res, sample_res, probs_res = qml.qjit(transformed_circuit)(theta)
224+
expval_res, var_res, sample_res, probs_res = qml.qjit(transformed_circuit, seed=37)(theta)
225225

226226
expval_expected = np.sin(theta) * np.sin(theta / 2)
227227
var_expected = 1 - np.sin(2 * theta) ** 2
@@ -389,7 +389,7 @@ def circuit(theta: float):
389389

390390
theta = 2.5
391391
counts_expected = circuit(theta)
392-
res = qml.qjit(measurements_from_counts(circuit, dev.wires))(theta)
392+
res = qml.qjit(measurements_from_counts(circuit, dev.wires), seed=37)(theta)
393393

394394
# counts comparison by converting catalyst format to PL style eigvals dict
395395
basis_states, counts = res
@@ -441,7 +441,7 @@ def circuit(theta: float):
441441
return measurement()
442442

443443
theta = 2.5
444-
res = qml.qjit(measurements_from_samples(circuit, dev.wires))(theta)
444+
res = qml.qjit(measurements_from_samples(circuit, dev.wires), seed=37)(theta)
445445

446446
if len(measurement().wires) == 1:
447447
samples_expected = qml.qjit(circuit)(theta)

frontend/test/pytest/test_seeded_qjit.py

+48
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,53 @@ def cfun0():
9999
assert np.allclose(results0, results2)
100100

101101

102+
@pytest.mark.parametrize(
103+
"seed",
104+
[
105+
42,
106+
37,
107+
1337,
108+
2**32 - 1,
109+
0,
110+
],
111+
)
112+
@pytest.mark.parametrize("shots", [10])
113+
def test_seeded_sample(seed, shots, backend):
114+
"""Test that different calls to qjits with the same seed produce the same sample results"""
115+
116+
if backend not in ["lightning.qubit", "lightning.kokkos"]:
117+
pytest.skip("Sample seeding is only supported on lightning.qubit and lightning.kokkos")
118+
119+
dev = qml.device(backend, wires=2, shots=shots)
120+
121+
@qjit(seed=seed)
122+
def workflow():
123+
@qml.qnode(dev)
124+
def circuit():
125+
qml.Hadamard(wires=[0])
126+
qml.RX(12.34, wires=[1])
127+
return qml.sample()
128+
129+
return circuit(), circuit(), circuit(), circuit()
130+
131+
@qjit(seed=seed)
132+
def workflow1():
133+
@qml.qnode(dev)
134+
def circuit():
135+
qml.Hadamard(wires=[0])
136+
qml.RX(12.34, wires=[1])
137+
return qml.sample()
138+
139+
return circuit(), circuit(), circuit(), circuit()
140+
141+
# Calls to qjits with the same seed should return the same samples
142+
for _ in range(5):
143+
results0 = workflow()
144+
results1 = workflow()
145+
results2 = workflow1()
146+
assert np.allclose(results0, results1)
147+
assert np.allclose(results0, results2)
148+
149+
102150
if __name__ == "__main__":
103151
pytest.main(["-x", __file__])

runtime/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ENABLE_LIGHTNING?=ON
1414
ENABLE_LIGHTNING_KOKKOS?=ON
1515
ENABLE_OPENQASM?=ON
1616
ENABLE_ASAN?=OFF
17-
LIGHTNING_GIT_TAG_VALUE?=latest_release
17+
LIGHTNING_GIT_TAG_VALUE?=6f3e0d5d371ff9823a3177dd2c66052668883d42
1818
ENABLE_LAPACK?=OFF
1919

2020
BUILD_TARGETS := rt_capi rtd_dummy

runtime/lib/backend/lightning/lightning_dynamic/LightningSimulator.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ std::vector<size_t> LightningSimulator::GenerateSamples(size_t shots)
314314
// the number of qubits.
315315
//
316316
// Return Value Optimization (RVO)
317+
if (this->gen) {
318+
return m.generate_samples(shots, (*(this->gen))());
319+
}
317320
return m.generate_samples(shots);
318321
}
319322

runtime/tests/Test_SVDynamicCPU_Allocation.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include "LinearAlgebra.hpp"
2525
#include "StateVectorLQubitDynamic.hpp"
2626
#include "Util.hpp"
27-
#include "cpu_kernels/GateImplementationsPI.hpp"
27+
#include "cpu_kernels/GateImplementationsLM.hpp"
2828
#include <StateVectorLQubit.hpp>
2929

3030
#include "TestHelpers.hpp"

runtime/tests/Test_SVDynamicCPU_Core.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include "LinearAlgebra.hpp"
2525
#include "StateVectorLQubitDynamic.hpp"
2626
#include "Util.hpp"
27-
#include "cpu_kernels/GateImplementationsPI.hpp"
27+
#include "cpu_kernels/GateImplementationsLM.hpp"
2828
#include <StateVectorLQubit.hpp>
2929

3030
#include "TestHelpers.hpp"
@@ -129,7 +129,7 @@ TEMPLATE_TEST_CASE("StateVectorLQubitDynamic::applyMatrix with a pointer",
129129

130130
const auto m = Pennylane::Util::randomUnitary<PrecisionT>(re, num_wires);
131131
sv1.applyMatrix(m, wires);
132-
Gates::GateImplementationsPI::applyMultiQubitOp<PrecisionT>(sv2.getData(), num_qubits,
132+
Gates::GateImplementationsLM::applyMultiQubitOp<PrecisionT>(sv2.getData(), num_qubits,
133133
m.data(), wires, false);
134134
CHECK(sv1.getDataVector() == approx(sv2.getDataVector()).margin(PrecisionT{1e-5}));
135135
}

0 commit comments

Comments
 (0)