diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningObsManager.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningObsManager.hpp index 181d2b5a4..6a92a220e 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningObsManager.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningObsManager.hpp @@ -45,7 +45,8 @@ template class LightningObsManager { private: using VectorStateT = StateVectorLQubitDynamic; using ComplexT = typename VectorStateT::ComplexT; - using ObservablePairType = std::pair>, ObsType>; + using ObservablePairType = + std::pair>, ObsType>; std::vector observables_{}; public: @@ -68,8 +69,8 @@ template class LightningObsManager { * @param obsKeys The vector of observable keys * @return bool */ - [[nodiscard]] auto isValidObservables(const std::vector &obsKeys) const -> bool - { + [[nodiscard]] auto + isValidObservables(const std::vector &obsKeys) const -> bool { return std::all_of(obsKeys.begin(), obsKeys.end(), [this](auto i) { return (i >= 0 && static_cast(i) < observables_.size()); }); @@ -81,8 +82,8 @@ template class LightningObsManager { * @param key The observable key * @return std::shared_ptr> */ - [[nodiscard]] auto getObservable(ObsIdType key) -> std::shared_ptr> - { + [[nodiscard]] auto getObservable(ObsIdType key) + -> std::shared_ptr> { RT_FAIL_IF(!isValidObservables({key}), "Invalid observable key"); return std::get<0>(observables_[key]); } @@ -92,7 +93,9 @@ template class LightningObsManager { * * @return size_t */ - [[nodiscard]] auto numObservables() const -> size_t { return observables_.size(); } + [[nodiscard]] auto numObservables() const -> size_t { + return observables_.size(); + } /** * @brief Create and cache a new NamedObs instance. @@ -101,14 +104,16 @@ template class LightningObsManager { * @param wires The vector of wires the observable acts on * @return ObsIdType */ - [[nodiscard]] auto createNamedObs(ObsId obsId, const std::vector &wires) -> ObsIdType - { - auto &&obs_str = - std::string(Lightning::lookup_obs( + [[nodiscard]] auto createNamedObs(ObsId obsId, + const std::vector &wires) + -> ObsIdType { + auto &&obs_str = std::string( + Lightning::lookup_obs( Lightning::simulator_observable_support, obsId)); observables_.push_back(std::make_pair( - std::make_shared>(obs_str, wires), ObsType::Basic)); + std::make_shared>(obs_str, wires), + ObsType::Basic)); return static_cast(observables_.size() - 1); } @@ -120,11 +125,12 @@ template class LightningObsManager { * @return ObsIdType */ [[nodiscard]] auto createHermitianObs(const std::vector &matrix, - const std::vector &wires) -> ObsIdType - { - observables_.push_back(std::make_pair( - std::make_shared>(HermitianObs{matrix, wires}), - ObsType::Basic)); + const std::vector &wires) + -> ObsIdType { + observables_.push_back( + std::make_pair(std::make_shared>( + HermitianObs{matrix, wires}), + ObsType::Basic)); return static_cast(observables_.size() - 1); } @@ -135,8 +141,8 @@ template class LightningObsManager { * @param obsKeys The vector of observable keys * @return ObsIdType */ - [[nodiscard]] auto createTensorProdObs(const std::vector &obsKeys) -> ObsIdType - { + [[nodiscard]] auto + createTensorProdObs(const std::vector &obsKeys) -> ObsIdType { const auto key_size = obsKeys.size(); const auto obs_size = observables_.size(); @@ -144,14 +150,15 @@ template class LightningObsManager { obs_vec.reserve(key_size); for (const auto &key : obsKeys) { - RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, "Invalid observable key"); + RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, + "Invalid observable key"); auto &&[obs, type] = observables_[key]; obs_vec.push_back(obs); } - observables_.push_back( - std::make_pair(TensorProdObs::create(obs_vec), ObsType::TensorProd)); + observables_.push_back(std::make_pair( + TensorProdObs::create(obs_vec), ObsType::TensorProd)); return static_cast(obs_size); } @@ -163,30 +170,33 @@ template class LightningObsManager { * @param obsKeys The vector of observable keys * @return ObsIdType */ - [[nodiscard]] auto createHamiltonianObs(const std::vector &coeffs, - const std::vector &obsKeys) -> ObsIdType - { + [[nodiscard]] auto + createHamiltonianObs(const std::vector &coeffs, + const std::vector &obsKeys) -> ObsIdType { const auto key_size = obsKeys.size(); const auto obs_size = observables_.size(); - RT_FAIL_IF(key_size != coeffs.size(), - "Incompatible list of observables and coefficients; " - "Number of observables and number of coefficients must be equal"); + RT_FAIL_IF( + key_size != coeffs.size(), + "Incompatible list of observables and coefficients; " + "Number of observables and number of coefficients must be equal"); std::vector>> obs_vec; obs_vec.reserve(key_size); for (auto key : obsKeys) { - RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, "Invalid observable key"); + RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, + "Invalid observable key"); auto &&[obs, type] = observables_[key]; obs_vec.push_back(obs); } observables_.push_back(std::make_pair( - std::make_shared>( - Pennylane::LightningQubit::Observables::Hamiltonian( - coeffs, std::move(obs_vec))), + std::make_shared>( + Pennylane::LightningQubit::Observables::Hamiltonian< + VectorStateT>(coeffs, std::move(obs_vec))), ObsType::Hamiltonian)); return static_cast(obs_size); diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.cpp index 5f9e59e91..cec08e162 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.cpp @@ -21,14 +21,13 @@ namespace Catalyst::Runtime::Simulator { -auto LightningSimulator::AllocateQubit() -> QubitIdType -{ +auto LightningSimulator::AllocateQubit() -> QubitIdType { size_t sv_id = this->device_sv->allocateWire(); return this->qubit_manager.Allocate(sv_id); } -auto LightningSimulator::AllocateQubits(size_t num_qubits) -> std::vector -{ +auto LightningSimulator::AllocateQubits(size_t num_qubits) + -> std::vector { if (num_qubits == 0U) { return {}; } @@ -40,55 +39,60 @@ auto LightningSimulator::AllocateQubits(size_t num_qubits) -> std::vector result(num_qubits); - std::generate_n(result.begin(), num_qubits, [this]() { return AllocateQubit(); }); + std::generate_n(result.begin(), num_qubits, + [this]() { return AllocateQubit(); }); return result; } -void LightningSimulator::ReleaseAllQubits() -{ +void LightningSimulator::ReleaseAllQubits() { this->device_sv->clearData(); this->qubit_manager.ReleaseAll(); } -void LightningSimulator::ReleaseQubit(QubitIdType q) -{ +void LightningSimulator::ReleaseQubit(QubitIdType q) { if (this->qubit_manager.isValidQubitId(q)) { this->device_sv->releaseWire(this->qubit_manager.getDeviceId(q)); } this->qubit_manager.Release(q); } -auto LightningSimulator::GetNumQubits() const -> size_t { return this->device_sv->getNumQubits(); } +auto LightningSimulator::GetNumQubits() const -> size_t { + return this->device_sv->getNumQubits(); +} -void LightningSimulator::StartTapeRecording() -{ +void LightningSimulator::StartTapeRecording() { RT_FAIL_IF(this->tape_recording, "Cannot re-activate the cache manager"); this->tape_recording = true; this->cache_manager.Reset(); } -void LightningSimulator::StopTapeRecording() -{ - RT_FAIL_IF(!this->tape_recording, "Cannot stop an already stopped cache manager"); +void LightningSimulator::StopTapeRecording() { + RT_FAIL_IF(!this->tape_recording, + "Cannot stop an already stopped cache manager"); this->tape_recording = false; } auto LightningSimulator::CacheManagerInfo() - -> std::tuple, std::vector> -{ - return {this->cache_manager.getNumOperations(), this->cache_manager.getNumObservables(), - this->cache_manager.getNumParams(), this->cache_manager.getOperationsNames(), + -> std::tuple, + std::vector> { + return {this->cache_manager.getNumOperations(), + this->cache_manager.getNumObservables(), + this->cache_manager.getNumParams(), + this->cache_manager.getOperationsNames(), this->cache_manager.getObservablesKeys()}; } -void LightningSimulator::SetDeviceShots(size_t shots) { this->device_shots = shots; } +void LightningSimulator::SetDeviceShots(size_t shots) { + this->device_shots = shots; +} -auto LightningSimulator::GetDeviceShots() const -> size_t { return this->device_shots; } +auto LightningSimulator::GetDeviceShots() const -> size_t { + return this->device_shots; +} void LightningSimulator::SetDevicePRNG(std::mt19937 *gen) { this->gen = gen; } -void LightningSimulator::PrintState() -{ +void LightningSimulator::PrintState() { using std::cout; using std::endl; @@ -105,38 +109,36 @@ void LightningSimulator::PrintState() } void LightningSimulator::SetState(DataView, 1> &state, - std::vector &wires) -{ + std::vector &wires) { std::vector> data_vector(state.begin(), state.end()); this->device_sv->setStateVector(data_vector, getDeviceWires(wires)); } -void LightningSimulator::SetBasisState(DataView &n, std::vector &wires) -{ +void LightningSimulator::SetBasisState(DataView &n, + std::vector &wires) { std::vector data_vector(n.begin(), n.end()); this->device_sv->setBasisState(data_vector, getDeviceWires(wires)); } -auto LightningSimulator::Zero() const -> Result -{ +auto LightningSimulator::Zero() const -> Result { return const_cast(&GLOBAL_RESULT_FALSE_CONST); } -auto LightningSimulator::One() const -> Result -{ +auto LightningSimulator::One() const -> Result { return const_cast(&GLOBAL_RESULT_TRUE_CONST); } -void LightningSimulator::NamedOperation(const std::string &name, const std::vector ¶ms, - const std::vector &wires, bool inverse, - const std::vector &controlled_wires, - const std::vector &controlled_values) -{ +void LightningSimulator::NamedOperation( + const std::string &name, const std::vector ¶ms, + const std::vector &wires, bool inverse, + const std::vector &controlled_wires, + const std::vector &controlled_values) { // Check the validity of number of qubits and parameters RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), "Controlled wires/values size mismatch"); RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); - RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); + RT_FAIL_IF(!isValidQubits(controlled_wires), + "Given controlled wires do not refer to qubits"); // Convert wires to device wires auto &&dev_wires = getDeviceWires(wires); @@ -145,28 +147,30 @@ void LightningSimulator::NamedOperation(const std::string &name, const std::vect // Update the state-vector if (controlled_wires.empty()) { this->device_sv->applyOperation(name, dev_wires, inverse, params); - } - else { - this->device_sv->applyOperation(name, dev_controlled_wires, controlled_values, dev_wires, - inverse, params); + } else { + this->device_sv->applyOperation(name, dev_controlled_wires, + controlled_values, dev_wires, inverse, + params); } // Update tape caching if required if (this->tape_recording) { - this->cache_manager.addOperation(name, params, dev_wires, inverse, {}, dev_controlled_wires, + this->cache_manager.addOperation(name, params, dev_wires, inverse, {}, + dev_controlled_wires, controlled_values); } } -void LightningSimulator::MatrixOperation(const std::vector> &matrix, - const std::vector &wires, bool inverse, - const std::vector &controlled_wires, - const std::vector &controlled_values) -{ +void LightningSimulator::MatrixOperation( + const std::vector> &matrix, + const std::vector &wires, bool inverse, + const std::vector &controlled_wires, + const std::vector &controlled_values) { RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), "Controlled wires/values size mismatch"); RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); - RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); + RT_FAIL_IF(!isValidQubits(controlled_wires), + "Given controlled wires do not refer to qubits"); // Convert wires to device wires // with checking validity of wires @@ -176,22 +180,23 @@ void LightningSimulator::MatrixOperation(const std::vector> // Update the state-vector if (controlled_wires.empty()) { this->device_sv->applyMatrix(matrix.data(), dev_wires, inverse); - } - else { - this->device_sv->applyControlledMatrix(matrix.data(), dev_controlled_wires, - controlled_values, dev_wires, inverse); + } else { + this->device_sv->applyControlledMatrix( + matrix.data(), dev_controlled_wires, controlled_values, dev_wires, + inverse); } // Update tape caching if required if (this->tape_recording) { - this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse, matrix, - dev_controlled_wires, controlled_values); + this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse, + matrix, dev_controlled_wires, + controlled_values); } } -auto LightningSimulator::Observable(ObsId id, const std::vector> &matrix, - const std::vector &wires) -> ObsIdType -{ +auto LightningSimulator::Observable( + ObsId id, const std::vector> &matrix, + const std::vector &wires) -> ObsIdType { RT_FAIL_IF(wires.size() > this->GetNumQubits(), "Invalid number of wires"); RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires"); @@ -204,19 +209,18 @@ auto LightningSimulator::Observable(ObsId id, const std::vectorobs_manager.createNamedObs(id, dev_wires); } -auto LightningSimulator::TensorObservable(const std::vector &obs) -> ObsIdType -{ +auto LightningSimulator::TensorObservable(const std::vector &obs) + -> ObsIdType { return this->obs_manager.createTensorProdObs(obs); } -auto LightningSimulator::HamiltonianObservable(const std::vector &coeffs, - const std::vector &obs) -> ObsIdType -{ +auto LightningSimulator::HamiltonianObservable( + const std::vector &coeffs, const std::vector &obs) + -> ObsIdType { return this->obs_manager.createHamiltonianObs(coeffs, obs); } -auto LightningSimulator::Expval(ObsIdType obsKey) -> double -{ +auto LightningSimulator::Expval(ObsIdType obsKey) -> double { RT_FAIL_IF(!this->obs_manager.isValidObservables({obsKey}), "Invalid key for cached observables"); auto &&obs = this->obs_manager.getObservable(obsKey); @@ -226,13 +230,14 @@ auto LightningSimulator::Expval(ObsIdType obsKey) -> double this->cache_manager.addObservable(obsKey, MeasurementsT::Expval); } - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; - return (device_shots != 0U) ? m.expval(*obs, device_shots, {}) : m.expval(*obs); + return (device_shots != 0U) ? m.expval(*obs, device_shots, {}) + : m.expval(*obs); } -auto LightningSimulator::Var(ObsIdType obsKey) -> double -{ +auto LightningSimulator::Var(ObsIdType obsKey) -> double { RT_FAIL_IF(!this->obs_manager.isValidObservables({obsKey}), "Invalid key for cached observables"); auto &&obs = this->obs_manager.getObservable(obsKey); @@ -242,32 +247,33 @@ auto LightningSimulator::Var(ObsIdType obsKey) -> double this->cache_manager.addObservable(obsKey, MeasurementsT::Var); } - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; return (device_shots != 0U) ? m.var(*obs, device_shots) : m.var(*obs); } -void LightningSimulator::State(DataView, 1> &state) -{ +void LightningSimulator::State(DataView, 1> &state) { auto &&dv_state = this->device_sv->getDataVector(); - RT_FAIL_IF(state.size() != dv_state.size(), "Invalid size for the pre-allocated state vector"); + RT_FAIL_IF(state.size() != dv_state.size(), + "Invalid size for the pre-allocated state vector"); std::move(dv_state.begin(), dv_state.end(), state.begin()); } -void LightningSimulator::Probs(DataView &probs) -{ - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; +void LightningSimulator::Probs(DataView &probs) { + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; auto &&dv_probs = (device_shots != 0U) ? m.probs(device_shots) : m.probs(); - RT_FAIL_IF(probs.size() != dv_probs.size(), "Invalid size for the pre-allocated probabilities"); + RT_FAIL_IF(probs.size() != dv_probs.size(), + "Invalid size for the pre-allocated probabilities"); std::move(dv_probs.begin(), dv_probs.end(), probs.begin()); } void LightningSimulator::PartialProbs(DataView &probs, - const std::vector &wires) -{ + const std::vector &wires) { const size_t numWires = wires.size(); const size_t numQubits = this->GetNumQubits(); @@ -275,8 +281,10 @@ void LightningSimulator::PartialProbs(DataView &probs, RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires to measure"); auto dev_wires = getDeviceWires(wires); - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; - auto &&dv_probs = (device_shots != 0U) ? m.probs(dev_wires, device_shots) : m.probs(dev_wires); + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; + auto &&dv_probs = (device_shots != 0U) ? m.probs(dev_wires, device_shots) + : m.probs(dev_wires); RT_FAIL_IF(probs.size() != dv_probs.size(), "Invalid size for the pre-allocated partial-probabilities"); @@ -284,10 +292,11 @@ void LightningSimulator::PartialProbs(DataView &probs, std::move(dv_probs.begin(), dv_probs.end(), probs.begin()); } -std::vector LightningSimulator::GenerateSamplesMetropolis(size_t shots) -{ +std::vector +LightningSimulator::GenerateSamplesMetropolis(size_t shots) { // generate_samples_metropolis is a member function of the Measures class. - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; // PL-Lightning generates samples using the alias method. // Reference: https://en.wikipedia.org/wiki/Alias_method @@ -296,16 +305,17 @@ std::vector LightningSimulator::GenerateSamplesMetropolis(size_t shots) // the number of qubits. // // Return Value Optimization (RVO) - return m.generate_samples_metropolis(this->kernel_name, this->num_burnin, shots); + return m.generate_samples_metropolis(this->kernel_name, this->num_burnin, + shots); } -std::vector LightningSimulator::GenerateSamples(size_t shots) -{ +std::vector LightningSimulator::GenerateSamples(size_t shots) { if (this->mcmc) { return this->GenerateSamplesMetropolis(shots); } // generate_samples is a member function of the Measures class. - Pennylane::LightningQubit::Measures::Measurements m{*(this->device_sv)}; + Pennylane::LightningQubit::Measures::Measurements m{ + *(this->device_sv)}; // PL-Lightning generates samples using the alias method. // Reference: https://en.wikipedia.org/wiki/Alias_method @@ -320,11 +330,11 @@ std::vector LightningSimulator::GenerateSamples(size_t shots) return m.generate_samples(shots); } -void LightningSimulator::Sample(DataView &samples, size_t shots) -{ +void LightningSimulator::Sample(DataView &samples, size_t shots) { auto li_samples = this->GenerateSamples(shots); - RT_FAIL_IF(samples.size() != li_samples.size(), "Invalid size for the pre-allocated samples"); + RT_FAIL_IF(samples.size() != li_samples.size(), + "Invalid size for the pre-allocated samples"); const size_t numQubits = this->GetNumQubits(); @@ -335,14 +345,15 @@ void LightningSimulator::Sample(DataView &samples, size_t shots) auto samplesIter = samples.begin(); for (size_t shot = 0; shot < shots; shot++) { for (size_t wire = 0; wire < numQubits; wire++) { - *(samplesIter++) = static_cast(li_samples[shot * numQubits + wire]); + *(samplesIter++) = + static_cast(li_samples[shot * numQubits + wire]); } } } void LightningSimulator::PartialSample(DataView &samples, - const std::vector &wires, size_t shots) -{ + const std::vector &wires, + size_t shots) { const size_t numWires = wires.size(); const size_t numQubits = this->GetNumQubits(); @@ -363,14 +374,14 @@ void LightningSimulator::PartialSample(DataView &samples, auto samplesIter = samples.begin(); for (size_t shot = 0; shot < shots; shot++) { for (auto wire : dev_wires) { - *(samplesIter++) = static_cast(li_samples[shot * numQubits + wire]); + *(samplesIter++) = + static_cast(li_samples[shot * numQubits + wire]); } } } -void LightningSimulator::Counts(DataView &eigvals, DataView &counts, - size_t shots) -{ +void LightningSimulator::Counts(DataView &eigvals, + DataView &counts, size_t shots) { const size_t numQubits = this->GetNumQubits(); const size_t numElements = 1U << numQubits; @@ -400,9 +411,10 @@ void LightningSimulator::Counts(DataView &eigvals, DataView &eigvals, DataView &counts, - const std::vector &wires, size_t shots) -{ +void LightningSimulator::PartialCounts(DataView &eigvals, + DataView &counts, + const std::vector &wires, + size_t shots) { const size_t numWires = wires.size(); const size_t numQubits = this->GetNumQubits(); const size_t numElements = 1U << numWires; @@ -438,8 +450,8 @@ void LightningSimulator::PartialCounts(DataView &eigvals, DataView postselect) -> Result -{ +auto LightningSimulator::Measure(QubitIdType wire, + std::optional postselect) -> Result { // get a measurement std::vector wires = {reinterpret_cast(wire)}; @@ -459,27 +471,29 @@ auto LightningSimulator::Measure(QubitIdType wire, std::optional postse // Gradient void LightningSimulator::Gradient(std::vector> &gradients, - const std::vector &trainParams) -{ + const std::vector &trainParams) { const bool tp_empty = trainParams.empty(); const size_t num_observables = this->cache_manager.getNumObservables(); const size_t num_params = this->cache_manager.getNumParams(); const size_t num_train_params = tp_empty ? num_params : trainParams.size(); - const size_t jac_size = num_train_params * this->cache_manager.getNumObservables(); + const size_t jac_size = + num_train_params * this->cache_manager.getNumObservables(); if (jac_size == 0U) { return; } - RT_FAIL_IF(gradients.size() != num_observables, "Invalid number of pre-allocated gradients"); + RT_FAIL_IF(gradients.size() != num_observables, + "Invalid number of pre-allocated gradients"); auto &&obs_callees = this->cache_manager.getObservablesCallees(); bool is_valid_measurements = std::all_of(obs_callees.begin(), obs_callees.end(), [](const auto &m) { return m == MeasurementsT::Expval; }); - RT_FAIL_IF(!is_valid_measurements, - "Unsupported measurements to compute gradient; " - "Adjoint differentiation method only supports expectation return type"); + RT_FAIL_IF( + !is_valid_measurements, + "Unsupported measurements to compute gradient; " + "Adjoint differentiation method only supports expectation return type"); auto &&state = this->device_sv->getDataVector(); @@ -489,16 +503,20 @@ void LightningSimulator::Gradient(std::vector> &gradients, auto &&ops_wires = this->cache_manager.getOperationsWires(); auto &&ops_inverses = this->cache_manager.getOperationsInverses(); auto &&ops_matrices = this->cache_manager.getOperationsMatrices(); - auto &&ops_controlled_wires = this->cache_manager.getOperationsControlledWires(); - auto &&ops_controlled_values = this->cache_manager.getOperationsControlledValues(); + auto &&ops_controlled_wires = + this->cache_manager.getOperationsControlledWires(); + auto &&ops_controlled_values = + this->cache_manager.getOperationsControlledValues(); const auto &&ops = Pennylane::Algorithms::OpsData( - ops_names, ops_params, ops_wires, ops_inverses, ops_matrices, ops_controlled_wires, - ops_controlled_values); + ops_names, ops_params, ops_wires, ops_inverses, ops_matrices, + ops_controlled_wires, ops_controlled_values); // create the vector of observables auto &&obs_keys = this->cache_manager.getObservablesKeys(); - std::vector>> obs_vec; + std::vector< + std::shared_ptr>> + obs_vec; obs_vec.reserve(obs_keys.size()); for (auto idx : obs_keys) { obs_vec.emplace_back(this->obs_manager.getObservable(idx)); @@ -514,7 +532,8 @@ void LightningSimulator::Gradient(std::vector> &gradients, // construct the Jacobian data Pennylane::Algorithms::JacobianData tape{ - num_params, state.size(), state.data(), obs_vec, ops, tp_empty ? all_params : trainParams}; + num_params, state.size(), state.data(), + obs_vec, ops, tp_empty ? all_params : trainParams}; Pennylane::LightningQubit::Algorithms::AdjointJacobian adj; std::vector jacobian(jac_size, 0); @@ -523,20 +542,23 @@ void LightningSimulator::Gradient(std::vector> &gradients, /* apply_operations */ false); // convert jacobians to a list of lists for each observable - std::vector jacobian_t = - Pennylane::LightningQubit::Util::Transpose(jacobian, num_train_params, num_observables); + std::vector jacobian_t = Pennylane::LightningQubit::Util::Transpose( + jacobian, num_train_params, num_observables); std::vector cur_buffer(num_train_params); auto begin_loc_iter = jacobian_t.begin(); for (size_t obs_idx = 0; obs_idx < num_observables; obs_idx++) { RT_ASSERT(begin_loc_iter != jacobian_t.end()); RT_ASSERT(num_train_params <= gradients[obs_idx].size()); - std::move(begin_loc_iter, begin_loc_iter + num_train_params, cur_buffer.begin()); - std::move(cur_buffer.begin(), cur_buffer.end(), gradients[obs_idx].begin()); + std::move(begin_loc_iter, begin_loc_iter + num_train_params, + cur_buffer.begin()); + std::move(cur_buffer.begin(), cur_buffer.end(), + gradients[obs_idx].begin()); begin_loc_iter += num_train_params; } } } // namespace Catalyst::Runtime::Simulator -GENERATE_DEVICE_FACTORY(LightningSimulator, Catalyst::Runtime::Simulator::LightningSimulator); +GENERATE_DEVICE_FACTORY(LightningSimulator, + Catalyst::Runtime::Simulator::LightningSimulator); diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.hpp index 0236e88e6..ba239a93f 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/LightningSimulator.hpp @@ -36,13 +36,15 @@ namespace Catalyst::Runtime::Simulator { class LightningSimulator final : public Catalyst::Runtime::QuantumDevice { private: - using StateVectorT = Pennylane::LightningQubit::StateVectorLQubitDynamic; + using StateVectorT = + Pennylane::LightningQubit::StateVectorLQubitDynamic; // static constants for RESULT values static constexpr bool GLOBAL_RESULT_TRUE_CONST = true; static constexpr bool GLOBAL_RESULT_FALSE_CONST = false; - static constexpr size_t default_num_burnin{100}; // tidy: readability-magic-numbers + static constexpr size_t default_num_burnin{ + 100}; // tidy: readability-magic-numbers static constexpr std::string_view default_kernel_name{ "Local"}; // tidy: readability-magic-numbers @@ -60,49 +62,55 @@ class LightningSimulator final : public Catalyst::Runtime::QuantumDevice { std::unique_ptr device_sv = std::make_unique(0); LightningObsManager obs_manager{}; - inline auto isValidQubit(QubitIdType wire) -> bool - { + inline auto isValidQubit(QubitIdType wire) -> bool { return this->qubit_manager.isValidQubitId(wire); } - inline auto isValidQubits(const std::vector &wires) -> bool - { - return std::all_of(wires.begin(), wires.end(), - [this](QubitIdType w) { return this->isValidQubit(w); }); + inline auto isValidQubits(const std::vector &wires) -> bool { + return std::all_of(wires.begin(), wires.end(), [this](QubitIdType w) { + return this->isValidQubit(w); + }); } - inline auto isValidQubits(size_t numWires, const QubitIdType *wires) -> bool - { - return std::all_of(wires, wires + numWires, - [this](QubitIdType w) { return this->isValidQubit(w); }); + inline auto isValidQubits(size_t numWires, const QubitIdType *wires) + -> bool { + return std::all_of(wires, wires + numWires, [this](QubitIdType w) { + return this->isValidQubit(w); + }); } - inline auto getDeviceWires(const std::vector &wires) -> std::vector - { + inline auto getDeviceWires(const std::vector &wires) + -> std::vector { std::vector res; res.reserve(wires.size()); - std::transform(wires.begin(), wires.end(), std::back_inserter(res), - [this](auto w) { return this->qubit_manager.getDeviceId(w); }); + std::transform( + wires.begin(), wires.end(), std::back_inserter(res), + [this](auto w) { return this->qubit_manager.getDeviceId(w); }); return res; } public: - explicit LightningSimulator(const std::string &kwargs = "{}") // NOLINT(hicpp-member-init) + explicit LightningSimulator( + const std::string &kwargs = "{}") // NOLINT(hicpp-member-init) { auto &&args = Catalyst::Runtime::parse_kwargs(kwargs); - device_shots = args.contains("shots") ? static_cast(std::stoll(args["shots"])) : 0; + device_shots = args.contains("shots") + ? static_cast(std::stoll(args["shots"])) + : 0; mcmc = args.contains("mcmc") ? args["mcmc"] == "True" : false; num_burnin = args.contains("num_burnin") ? static_cast(std::stoll(args["num_burnin"])) : default_num_burnin; - kernel_name = args.contains("kernel_name") ? args["kernel_name"] : default_kernel_name; + kernel_name = args.contains("kernel_name") ? args["kernel_name"] + : default_kernel_name; } ~LightningSimulator() override = default; void SetDevicePRNG(std::mt19937 *gen) override; void SetState(DataView, 1> &state, std::vector &wires) override; - void SetBasisState(DataView &n, std::vector &wires) override; + void SetBasisState(DataView &n, + std::vector &wires) override; QUANTUM_DEVICE_DEL_DECLARATIONS(LightningSimulator); @@ -114,7 +122,8 @@ class LightningSimulator final : public Catalyst::Runtime::QuantumDevice { QUANTUM_DEVICE_QIS_DECLARATIONS; auto CacheManagerInfo() - -> std::tuple, std::vector>; + -> std::tuple, + std::vector>; auto GenerateSamplesMetropolis(size_t shots) -> std::vector; auto GenerateSamples(size_t shots) -> std::vector; }; diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/StateVectorLQubitDynamic.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/StateVectorLQubitDynamic.hpp index 1e888eebf..dc967c235 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/StateVectorLQubitDynamic.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/catalyst/StateVectorLQubitDynamic.hpp @@ -48,7 +48,8 @@ namespace Pennylane::LightningQubit { * */ template -class StateVectorLQubitDynamic : public StateVectorLQubit> { +class StateVectorLQubitDynamic + : public StateVectorLQubit> { public: using PrecisionT = fp_t; using ComplexT = std::complex; @@ -57,14 +58,16 @@ class StateVectorLQubitDynamic : public StateVectorLQubit; private: - using BaseType = StateVectorLQubit>; + using BaseType = + StateVectorLQubit>; std::vector> data_; - static constexpr PrecisionT epsilon_ = std::numeric_limits::epsilon() * 100; + static constexpr PrecisionT epsilon_ = + std::numeric_limits::epsilon() * 100; template - inline OIter _move_data_elements(IIter first, size_t distance, OIter second) - { + inline OIter _move_data_elements(IIter first, size_t distance, + OIter second) { *second++ = std::move(*first); for (size_t i = 1; i < distance; i++) { *second++ = std::move(*++first); @@ -73,8 +76,8 @@ class StateVectorLQubitDynamic : public StateVectorLQubit - inline OIter _shallow_move_data_elements(IIter first, size_t distance, OIter second) - { + inline OIter _shallow_move_data_elements(IIter first, size_t distance, + OIter second) { for (size_t i = 0; i < distance; i++) { *second++ = std::move(*first); *first = ZERO(); @@ -83,16 +86,19 @@ class StateVectorLQubitDynamic : public StateVectorLQubit &data, ComplexT scalar) - { - std::transform(data.begin(), data.end(), data.begin(), - [scalar](const ComplexT &elem) { return elem * scalar; }); + inline void + _scalar_mul_data(std::vector &data, + ComplexT scalar) { + std::transform( + data.begin(), data.end(), data.begin(), + [scalar](const ComplexT &elem) { return elem * scalar; }); } - inline void _normalize_data(std::vector &data) - { + inline void + _normalize_data(std::vector &data) { _scalar_mul_data(data, - ONE() / std::sqrt(squaredNorm(data.data(), data.size()))); + ONE() / + std::sqrt(squaredNorm(data.data(), data.size()))); } public: @@ -103,14 +109,13 @@ class StateVectorLQubitDynamic : public StateVectorLQubit(), getAllocator( // LCOV_EXCL_LINE - this->memory_model_)} - { + this->memory_model_)} { data_[0] = ONE(); } @@ -122,12 +127,12 @@ class StateVectorLQubitDynamic : public StateVectorLQubit - explicit StateVectorLQubitDynamic(const StateVectorLQubit &other) - : BaseType(other.getNumQubits(), other.threading(), other.memoryModel()), + explicit StateVectorLQubitDynamic( + const StateVectorLQubit &other) + : BaseType(other.getNumQubits(), other.threading(), + other.memoryModel()), data_{other.getData(), other.getData() + other.getLength(), - getAllocator(this->memory_model_)} - { - } + getAllocator(this->memory_model_)} {} /** * @brief Construct a statevector from data pointer @@ -141,8 +146,8 @@ class StateVectorLQubitDynamic : public StateVectorLQubit(this->memory_model_)} - { + data_{other_data, other_data + other_size, + getAllocator(this->memory_model_)} { PL_ABORT_IF_NOT(isPerfectPowerOf2(other_size), "The size of provided data must be a power of 2."); } @@ -157,18 +162,20 @@ class StateVectorLQubitDynamic : public StateVectorLQubit - explicit StateVectorLQubitDynamic(const std::vector &other, - Threading threading = Threading::SingleThread, - CPUMemoryModel memory_model = bestCPUMemoryModel()) - : StateVectorLQubitDynamic(other.data(), other.size(), threading, memory_model) - { - } + explicit StateVectorLQubitDynamic( + const std::vector &other, + Threading threading = Threading::SingleThread, + CPUMemoryModel memory_model = bestCPUMemoryModel()) + : StateVectorLQubitDynamic(other.data(), other.size(), threading, + memory_model) {} StateVectorLQubitDynamic(const StateVectorLQubitDynamic &rhs) = default; StateVectorLQubitDynamic(StateVectorLQubitDynamic &&) noexcept = default; - StateVectorLQubitDynamic &operator=(const StateVectorLQubitDynamic &) = default; - StateVectorLQubitDynamic &operator=(StateVectorLQubitDynamic &&) noexcept = default; + StateVectorLQubitDynamic & + operator=(const StateVectorLQubitDynamic &) = default; + StateVectorLQubitDynamic & + operator=(StateVectorLQubitDynamic &&) noexcept = default; ~StateVectorLQubitDynamic() = default; @@ -187,21 +194,23 @@ class StateVectorLQubitDynamic : public StateVectorLQubit const ComplexT * { return data_.data(); } + [[nodiscard]] auto getData() const -> const ComplexT * { + return data_.data(); + } /** * @brief Get underlying data vector. */ - [[nodiscard]] auto getDataVector() -> std::vector & - { + [[nodiscard]] auto getDataVector() + -> std::vector & { return data_; } /** * @brief Get underlying data vector. */ - [[nodiscard]] auto getDataVector() const -> const std::vector & - { + [[nodiscard]] auto getDataVector() const + -> const std::vector & { return data_; } @@ -211,8 +220,7 @@ class StateVectorLQubitDynamic : public StateVectorLQubit void updateData(const std::vector &new_data) - { + template + void updateData(const std::vector &new_data) { updateData(new_data.data(), new_data.size()); } - [[nodiscard]] AlignedAllocComplexT allocator() const { return data_.get_allocator(); } + [[nodiscard]] AlignedAllocComplexT allocator() const { + return data_.get_allocator(); + } - [[nodiscard]] auto isValidWire(size_t wire) -> bool { return wire < this->getNumQubits(); } + [[nodiscard]] auto isValidWire(size_t wire) -> bool { + return wire < this->getNumQubits(); + } /** * @brief Compute the purity of the system after releasing (a qubit) `wire`. @@ -242,9 +254,9 @@ class StateVectorLQubitDynamic : public StateVectorLQubit ComplexT - { - PL_ABORT_IF_NOT(isValidWire(wire), "Invalid wire: The wire must be in the range of wires"); + auto getSubsystemPurity(size_t wire) -> ComplexT { + PL_ABORT_IF_NOT(isValidWire(wire), + "Invalid wire: The wire must be in the range of wires"); const size_t sv_size = data_.size(); @@ -263,8 +275,9 @@ class StateVectorLQubitDynamic : public StateVectorLQubit bool - { + [[nodiscard]] auto checkSubsystemPurity(size_t wire, double eps = epsilon_) + -> bool { ComplexT purity = getSubsystemPurity(wire); return (std::abs(1.0 - purity.real()) < eps) && (purity.imag() < eps); } @@ -300,8 +314,7 @@ class StateVectorLQubitDynamic : public StateVectorLQubit size_t - { + auto allocateWire() -> size_t { const size_t next_idx = this->getNumQubits(); const size_t dsize = data_.size(); data_.resize(dsize << 1UL); @@ -322,11 +335,11 @@ class StateVectorLQubitDynamic : public StateVectorLQubit(distance), - [](ComplexT &e) { return e != ZERO(); }); + for (auto src = dst; src < data_.end(); + std::advance(src, 2 * distance)) { + is_first_half = std::any_of( + src, src + static_cast(distance), + [](ComplexT &e) { return e != ZERO(); }); if (is_first_half) { break; } @@ -347,7 +362,8 @@ class StateVectorLQubitDynamic : public StateVectorLQubitsetNumQubits(0);