From a86af7e3a6e07a3c01bc0f33bb6ac3774e67f4e7 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 17:24:33 -0400 Subject: [PATCH 1/8] Fix observable output size validation --- pennylane_qrack/qrack_device.cpp | 9 +++++---- requirements.txt | 2 +- setup.py | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index 6d281d8..52e6613 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -761,7 +761,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter // that could be instead implied by the size of "samples." - RT_FAIL_IF(samples.size() != shots, "Invalid size for the pre-allocated samples"); + RT_FAIL_IF(samples.size() != shots * qsim->GetQubitCount(), "Invalid size for the pre-allocated samples"); std::vector qPowers(qsim->GetQubitCount()); for (bitLenInt i = 0U; i < qPowers.size(); ++i) { @@ -781,7 +781,8 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter // that could be instead implied by the size of "samples." - RT_FAIL_IF(samples.size() != shots, "Invalid size for the pre-allocated samples"); + std::cout << shots << std::endl; + RT_FAIL_IF(samples.size() != shots * wires.size(), "Invalid size for the pre-allocated samples"); auto &&dev_wires = getDeviceWires(wires); std::vector qPowers(dev_wires.size()); @@ -804,7 +805,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter // that could be instead implied by the size of "eigvals"/"counts". const size_t numQubits = qsim->GetQubitCount(); - const size_t numElements = (size_t)qsim->GetMaxQPower(); + const size_t numElements = 1U << numQubits; RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements, "Invalid size for the pre-allocated counts"); @@ -835,7 +836,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter // that could be instead implied by the size of "eigvals"/"counts". const size_t numQubits = wires.size(); - const size_t numElements = (size_t)Qrack::pow2(numQubits); + const size_t numElements = 1U << numQubits; RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements, "Invalid size for the pre-allocated counts"); diff --git a/requirements.txt b/requirements.txt index 2f5b670..8df2da4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ pennylane>=0.32 pennylane-catalyst>=0.6 pyqrack>=0.13.0 numpy~=1.16 -scikit-build +scikit-build>=0.1.0 diff --git a/setup.py b/setup.py index 574d10c..1bb8d5d 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ requirements = [ "pennylane>=0.32", + "pennylane-catalyst>=0.6", "pyqrack>=0.13.0", "numpy~=1.16", "scikit-build>=0.1.0", From 914e5733e8bd8402f3e7bb5787200b0392ae9ee6 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 17:45:06 -0400 Subject: [PATCH 2/8] Fix Sample/Counts --- pennylane_qrack/qrack_device.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index 52e6613..b55fad4 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -773,7 +773,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { for (size_t shot = 0U; shot < shots; ++shot) { bitCapInt sample = q_samples[shot]; for (size_t wire = 0U; wire < qPowers.size(); ++wire) { - *(samplesIter++) = bi_to_double((sample >> wire) & 1U); + *(samplesIter++) = bi_compare_0(sample & (1U << wire)) ? 1.0 : 0.0; } } } @@ -795,7 +795,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { for (size_t shot = 0U; shot < shots; ++shot) { bitCapInt sample = q_samples[shot]; for (size_t wire = 0U; wire < qPowers.size(); ++wire) { - *(samplesIter++) = bi_to_double((sample >> wire) & 1U); + *(samplesIter++) = bi_compare_0(sample & (1U << wire)) ? 1.0 : 0.0; } } } @@ -824,7 +824,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { std::bitset<1U << QBCAPPOW> basisState; size_t idx = numQubits; for (size_t wire = 0; wire < numQubits; wire++) { - basisState[--idx] = bi_compare_0((sample >> wire) & 1U); + basisState[--idx] = bi_compare_0(sample & (1U << wire)); } ++counts(static_cast(basisState.to_ulong())); } From 90c3048f1e22762dcff7408521a5e7303d554553 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 18:52:22 -0400 Subject: [PATCH 3/8] _SampleBody()/_CountsBody() --- pennylane_qrack/qrack_device.cpp | 73 ++++++++++++++++---------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index b55fad4..1972f32 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -757,6 +757,21 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { std::copy(_p.get(), _p.get() + p.size(), p.begin()); #endif } + void _SampleBody(const size_t numQubits, const std::map& q_samples, DataView &samples) + { + auto samplesIter = samples.begin(); + auto q_samplesIter = q_samples.begin(); + for (size_t shot = 0U; shot < shots; ++shot) { + bitCapInt sample = q_samplesIter->first; + int shots = q_samplesIter->second; + ++q_samplesIter; + for (; shots > 0; --shots) { + for (size_t wire = 0U; wire < numQubits; ++wire) { + *(samplesIter++) = bi_compare_0((sample >> wire) & 1U) ? 1.0 : 0.0; + } + } + } + } void Sample(DataView &samples, size_t shots) override { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter @@ -765,17 +780,10 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { std::vector qPowers(qsim->GetQubitCount()); for (bitLenInt i = 0U; i < qPowers.size(); ++i) { - qPowers[i] = Qrack::pow2(qPowers.size() - (i + 1U)); - } - auto q_samples = qsim->MultiShotMeasureMask(qPowers, shots); - - auto samplesIter = samples.begin(); - for (size_t shot = 0U; shot < shots; ++shot) { - bitCapInt sample = q_samples[shot]; - for (size_t wire = 0U; wire < qPowers.size(); ++wire) { - *(samplesIter++) = bi_compare_0(sample & (1U << wire)) ? 1.0 : 0.0; - } + qPowers[i] = Qrack::pow2(i); } + const std::map q_samples = qsim->MultiShotMeasureMask(qPowers, shots); + _SampleBody(qPowers.size(), q_samples, samples); } void PartialSample(DataView &samples, const std::vector &wires, size_t shots) override { @@ -787,15 +795,22 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { auto &&dev_wires = getDeviceWires(wires); std::vector qPowers(dev_wires.size()); for (size_t i = 0U; i < qPowers.size(); ++i) { - qPowers[i] = Qrack::pow2(dev_wires[qPowers.size() - (i + 1U)]); + qPowers[i] = Qrack::pow2(dev_wires[i]); } - auto q_samples = qsim->MultiShotMeasureMask(qPowers, shots); - - auto samplesIter = samples.begin(); - for (size_t shot = 0U; shot < shots; ++shot) { - bitCapInt sample = q_samples[shot]; - for (size_t wire = 0U; wire < qPowers.size(); ++wire) { - *(samplesIter++) = bi_compare_0(sample & (1U << wire)) ? 1.0 : 0.0; + const std::map q_samples = qsim->MultiShotMeasureMask(qPowers, shots); + _SampleBody(qPowers.size(), q_samples, samples); + } + void _CountsBody(const size_t numQubits, const std::map& q_samples, DataView &counts) + { + for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) { + bitCapInt sample = q_samplesIter->first; + int shots = q_samplesIter->second; + for (; shots > 0; --shots) { + std::bitset<1U << QBCAPPOW> basisState; + for (size_t wire = 0; wire < numQubits; wire++) { + basisState[wire] = bi_compare_0((sample >> wire) & 1U); + } + ++counts(static_cast(basisState.to_ulong())); } } } @@ -812,22 +827,14 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { std::vector qPowers(numQubits); for (bitLenInt i = 0U; i < qPowers.size(); ++i) { - qPowers[i] = Qrack::pow2(qPowers.size() - (i + 1U)); + qPowers[i] = Qrack::pow2(i); } auto q_samples = qsim->MultiShotMeasureMask(qPowers, shots); std::iota(eigvals.begin(), eigvals.end(), 0); std::fill(counts.begin(), counts.end(), 0); - for (size_t shot = 0; shot < shots; ++shot) { - bitCapInt sample = q_samples[shot]; - std::bitset<1U << QBCAPPOW> basisState; - size_t idx = numQubits; - for (size_t wire = 0; wire < numQubits; wire++) { - basisState[--idx] = bi_compare_0(sample & (1U << wire)); - } - ++counts(static_cast(basisState.to_ulong())); - } + _CountsBody(numQubits, q_samples, counts); } void PartialCounts(DataView &eigvals, DataView &counts, @@ -851,15 +858,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { std::iota(eigvals.begin(), eigvals.end(), 0); std::fill(counts.begin(), counts.end(), 0); - for (size_t shot = 0; shot < shots; ++shot) { - bitCapInt sample = q_samples[shot]; - std::bitset<1U << QBCAPPOW> basisState; - size_t idx = numQubits; - for (size_t wire = 0; wire < numQubits; wire++) { - basisState[--idx] = bi_compare_0((sample >> wire) & 1U); - } - ++counts(static_cast(basisState.to_ulong())); - } + _CountsBody(numQubits, q_samples, counts); } void Gradient(std::vector> &, const std::vector &) override {} From b123a9d416344d182b43943ff3b48299486d9c67 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 18:54:16 -0400 Subject: [PATCH 4/8] Remove debug logging --- pennylane_qrack/qrack_device.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index 1972f32..5a17579 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -789,7 +789,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { { // TODO: We could suggest, for upstream, that "shots" is a redundant parameter // that could be instead implied by the size of "samples." - std::cout << shots << std::endl; RT_FAIL_IF(samples.size() != shots * wires.size(), "Invalid size for the pre-allocated samples"); auto &&dev_wires = getDeviceWires(wires); From 295dd34709596c4cba20f682ad901d7b297b6e17 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 19:00:54 -0400 Subject: [PATCH 5/8] Bump version (v0.6.7) --- pennylane_qrack/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_qrack/_version.py b/pennylane_qrack/_version.py index 3406b93..1f4eb3c 100644 --- a/pennylane_qrack/_version.py +++ b/pennylane_qrack/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.6.6" +__version__ = "0.6.7" From fee8a54ce35ce1fdb276d8a9a441f7e018030768 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 19:13:32 -0400 Subject: [PATCH 6/8] Reduce looping redundancy --- pennylane_qrack/qrack_device.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index 5a17579..03eb8e0 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -804,11 +804,11 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) { bitCapInt sample = q_samplesIter->first; int shots = q_samplesIter->second; + std::bitset<1U << QBCAPPOW> basisState; + for (size_t wire = 0; wire < numQubits; wire++) { + basisState[wire] = bi_compare_0((sample >> wire) & 1U); + } for (; shots > 0; --shots) { - std::bitset<1U << QBCAPPOW> basisState; - for (size_t wire = 0; wire < numQubits; wire++) { - basisState[wire] = bi_compare_0((sample >> wire) & 1U); - } ++counts(static_cast(basisState.to_ulong())); } } From c02ddf68bf7dd5d92358ecbbc16d5fcdfe02c5b6 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 19:34:40 -0400 Subject: [PATCH 7/8] Debug Samples() --- pennylane_qrack/qrack_device.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index 03eb8e0..b6f1e86 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -761,10 +761,9 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { { auto samplesIter = samples.begin(); auto q_samplesIter = q_samples.begin(); - for (size_t shot = 0U; shot < shots; ++shot) { - bitCapInt sample = q_samplesIter->first; + for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) { + const bitCapInt sample = q_samplesIter->first; int shots = q_samplesIter->second; - ++q_samplesIter; for (; shots > 0; --shots) { for (size_t wire = 0U; wire < numQubits; ++wire) { *(samplesIter++) = bi_compare_0((sample >> wire) & 1U) ? 1.0 : 0.0; @@ -802,7 +801,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { void _CountsBody(const size_t numQubits, const std::map& q_samples, DataView &counts) { for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) { - bitCapInt sample = q_samplesIter->first; + const bitCapInt sample = q_samplesIter->first; int shots = q_samplesIter->second; std::bitset<1U << QBCAPPOW> basisState; for (size_t wire = 0; wire < numQubits; wire++) { From 0df46eaf25ce8b28f6ed8b8f8177d962133e18b6 Mon Sep 17 00:00:00 2001 From: WrathfulSpatula Date: Wed, 5 Jun 2024 20:42:50 -0400 Subject: [PATCH 8/8] Remove TODO --- pennylane_qrack/qrack_device.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pennylane_qrack/qrack_device.cpp b/pennylane_qrack/qrack_device.cpp index b6f1e86..18a6dd8 100644 --- a/pennylane_qrack/qrack_device.cpp +++ b/pennylane_qrack/qrack_device.cpp @@ -773,8 +773,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { } void Sample(DataView &samples, size_t shots) override { - // TODO: We could suggest, for upstream, that "shots" is a redundant parameter - // that could be instead implied by the size of "samples." RT_FAIL_IF(samples.size() != shots * qsim->GetQubitCount(), "Invalid size for the pre-allocated samples"); std::vector qPowers(qsim->GetQubitCount()); @@ -786,8 +784,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { } void PartialSample(DataView &samples, const std::vector &wires, size_t shots) override { - // TODO: We could suggest, for upstream, that "shots" is a redundant parameter - // that could be instead implied by the size of "samples." RT_FAIL_IF(samples.size() != shots * wires.size(), "Invalid size for the pre-allocated samples"); auto &&dev_wires = getDeviceWires(wires); @@ -815,8 +811,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { void Counts(DataView &eigvals, DataView &counts, size_t shots) override { - // TODO: We could suggest, for upstream, that "shots" is a redundant parameter - // that could be instead implied by the size of "eigvals"/"counts". const size_t numQubits = qsim->GetQubitCount(); const size_t numElements = 1U << numQubits; @@ -838,8 +832,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice { void PartialCounts(DataView &eigvals, DataView &counts, const std::vector &wires, size_t shots) override { - // TODO: We could suggest, for upstream, that "shots" is a redundant parameter - // that could be instead implied by the size of "eigvals"/"counts". const size_t numQubits = wires.size(); const size_t numElements = 1U << numQubits;