Program Listing for File TransitionKernels.hpp

Return to documentation for file (pennylane_lightning/core/src/simulators/lightning_qubit/measurements/TransitionKernels.hpp)

// Copyright 2018-2023 Xanadu Quantum Technologies Inc.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <algorithm>
#include <complex>
#include <cstdio>
#include <random>
#include <stack>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "Util.hpp" // exp2

namespace Pennylane::LightningQubit::Measures {
enum class TransitionKernelType { Local, NonZeroRandom };

template <typename fp_t> class TransitionKernel {
  protected:
    TransitionKernel() = default;
    TransitionKernel(const TransitionKernel &) = default;
    TransitionKernel(TransitionKernel &&) noexcept = default;
    TransitionKernel &operator=(const TransitionKernel &) = default;
    TransitionKernel &operator=(TransitionKernel &&) noexcept = default;

  public:
    //  outputs the next state and the qratio
    virtual std::pair<size_t, fp_t> operator()(size_t) = 0;
    virtual ~TransitionKernel() = default;
};

template <typename fp_t>
class LocalTransitionKernel : public TransitionKernel<fp_t> {
  private:
    size_t num_qubits_;
    std::random_device rd_;
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> distrib_num_qubits_;
    std::uniform_int_distribution<size_t> distrib_binary_;

  public:
    explicit LocalTransitionKernel(size_t num_qubits)
        : num_qubits_(num_qubits), gen_(std::mt19937(rd_())),
          distrib_num_qubits_(
              std::uniform_int_distribution<size_t>(0, num_qubits - 1)),
          distrib_binary_(std::uniform_int_distribution<size_t>(0, 1)) {}

    std::pair<size_t, fp_t> operator()(size_t init_idx) final {
        size_t qubit_site = distrib_num_qubits_(gen_);
        size_t qubit_value = distrib_binary_(gen_);
        size_t current_bit = (static_cast<unsigned>(init_idx) >>
                              static_cast<unsigned>(qubit_site)) &
                             1U;

        if (qubit_value == current_bit) {
            return std::pair<size_t, fp_t>(init_idx, 1);
        }
        if (current_bit == 0) {
            return std::pair<size_t, fp_t>(init_idx + std::pow(2, qubit_site),
                                           1);
        }
        return std::pair<size_t, fp_t>(init_idx - std::pow(2, qubit_site), 1);
    }
};

template <typename fp_t>
class NonZeroRandomTransitionKernel : public TransitionKernel<fp_t> {
  private:
    std::random_device rd_;
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> distrib_;
    size_t sv_length_;
    std::vector<size_t> non_zeros_;

  public:
    NonZeroRandomTransitionKernel(const std::complex<fp_t> *sv,
                                  size_t sv_length, fp_t min_error) {
        auto data = sv;
        sv_length_ = sv_length;
        // find nonzero candidates
        for (size_t i = 0; i < sv_length_; i++) {
            if (std::abs(data[i]) > min_error) {
                non_zeros_.push_back(i);
            }
        }
        gen_ = std::mt19937(rd_());
        distrib_ =
            std::uniform_int_distribution<size_t>(0, non_zeros_.size() - 1);
    }
    std::pair<size_t, fp_t> operator()([[maybe_unused]] size_t init_idx) final {
        auto trans_idx = distrib_(gen_);
        return std::pair<size_t, fp_t>(non_zeros_[trans_idx], 1);
    }
};

template <typename fp_t>
std::unique_ptr<TransitionKernel<fp_t>>
kernel_factory(const TransitionKernelType kernel_type,
               const std::complex<fp_t> *sv, size_t num_qubits) {
    auto sv_length = Pennylane::Util::exp2(num_qubits);
    if (kernel_type == TransitionKernelType::Local) {
        return std::unique_ptr<TransitionKernel<fp_t>>(
            new NonZeroRandomTransitionKernel<fp_t>(
                sv, sv_length, std::numeric_limits<fp_t>::epsilon()));
    }
    return std::unique_ptr<TransitionKernel<fp_t>>(
        new LocalTransitionKernel<fp_t>(num_qubits));
}
} // namespace Pennylane::LightningQubit::Measures