Program Listing for File JacobianData.hpp

Return to documentation for file (pennylane_lightning/core/src/algorithms/JacobianData.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 <cstring>
#include <memory>
#include <stdexcept>
#include <typeinfo>
#include <utility>
#include <vector>

#include "Macros.hpp"
#include "Observables.hpp"
#include "Util.hpp"

// using namespace Pennylane;
namespace {
using Pennylane::Observables::Observable;
} // namespace

namespace Pennylane::Algorithms {
template <class StateVectorT> class OpsData {
  private:
    using PrecisionT = typename StateVectorT::PrecisionT;
    using ComplexT = typename StateVectorT::ComplexT;

    size_t num_par_ops_;
    size_t num_nonpar_ops_;
    const std::vector<std::string> ops_name_;
    const std::vector<std::vector<PrecisionT>> ops_params_;
    const std::vector<std::vector<size_t>> ops_wires_;
    const std::vector<bool> ops_inverses_;
    const std::vector<std::vector<ComplexT>> ops_matrices_;
    const std::vector<std::vector<size_t>> ops_controlled_wires_;
    const std::vector<std::vector<bool>> ops_controlled_values_;

  public:
    OpsData(std::vector<std::string> ops_name,
            const std::vector<std::vector<PrecisionT>> &ops_params,
            std::vector<std::vector<size_t>> ops_wires,
            std::vector<bool> ops_inverses,
            std::vector<std::vector<ComplexT>> ops_matrices,
            std::vector<std::vector<size_t>> ops_controlled_wires,
            std::vector<std::vector<bool>> ops_controlled_values)
        : num_par_ops_{0}, ops_name_{std::move(ops_name)},
          ops_params_{ops_params}, ops_wires_{std::move(ops_wires)},
          ops_inverses_{std::move(ops_inverses)},
          ops_matrices_{std::move(ops_matrices)},
          ops_controlled_wires_{std::move(ops_controlled_wires)},
          ops_controlled_values_{std::move(ops_controlled_values)} {
        for (const auto &p : ops_params) {
            num_par_ops_ += static_cast<size_t>(!p.empty());
        }
        num_nonpar_ops_ = ops_params.size() - num_par_ops_;
    };

    OpsData(std::vector<std::string> ops_name,
            const std::vector<std::vector<PrecisionT>> &ops_params,
            std::vector<std::vector<size_t>> ops_wires,
            std::vector<bool> ops_inverses,
            std::vector<std::vector<ComplexT>> ops_matrices)
        : num_par_ops_{0}, ops_name_{std::move(ops_name)},
          ops_params_{ops_params}, ops_wires_{std::move(ops_wires)},
          ops_inverses_{std::move(ops_inverses)},
          ops_matrices_{std::move(ops_matrices)},
          ops_controlled_wires_(ops_name.size()),
          ops_controlled_values_(ops_name.size()) {
        for (const auto &p : ops_params) {
            num_par_ops_ += static_cast<size_t>(!p.empty());
        }
        num_nonpar_ops_ = ops_params.size() - num_par_ops_;
    };

    OpsData(const std::vector<std::string> &ops_name,
            const std::vector<std::vector<PrecisionT>> &ops_params,
            std::vector<std::vector<size_t>> ops_wires,
            std::vector<bool> ops_inverses)
        : num_par_ops_{0}, ops_name_{ops_name}, ops_params_{ops_params},
          ops_wires_{std::move(ops_wires)},
          ops_inverses_{std::move(ops_inverses)},
          ops_matrices_(ops_name.size()),
          ops_controlled_wires_(ops_name.size()),
          ops_controlled_values_(ops_name.size()) {
        for (const auto &p : ops_params) {
            num_par_ops_ += static_cast<size_t>(!p.empty());
        }
        num_nonpar_ops_ = ops_params.size() - num_par_ops_;
    };

    [[nodiscard]] auto getSize() const -> size_t { return ops_name_.size(); }

    [[nodiscard]] auto getOpsName() const -> const std::vector<std::string> & {
        return ops_name_;
    }
    [[nodiscard]] auto getOpsParams() const
        -> const std::vector<std::vector<PrecisionT>> & {
        return ops_params_;
    }
    [[nodiscard]] auto getOpsWires() const
        -> const std::vector<std::vector<size_t>> & {
        return ops_wires_;
    }
    [[nodiscard]] auto getOpsControlledWires() const
        -> const std::vector<std::vector<size_t>> & {
        return ops_controlled_wires_;
    }
    [[nodiscard]] auto getOpsControlledValues() const
        -> const std::vector<std::vector<bool>> & {
        return ops_controlled_values_;
    }
    [[nodiscard]] auto getOpsInverses() const -> const std::vector<bool> & {
        return ops_inverses_;
    }
    [[nodiscard]] auto getOpsMatrices() const
        -> const std::vector<std::vector<ComplexT>> & {
        return ops_matrices_;
    }

    [[nodiscard]] inline auto hasParams(size_t index) const -> bool {
        return !ops_params_[index].empty();
    }

    [[nodiscard]] auto getNumParOps() const -> size_t { return num_par_ops_; }

    [[nodiscard]] auto getNumNonParOps() const -> size_t {
        return num_nonpar_ops_;
    }

    [[nodiscard]] auto getTotalNumParams() const -> size_t {
        return std::accumulate(
            ops_params_.begin(), ops_params_.end(), size_t{0U},
            [](size_t acc, auto &params) { return acc + params.size(); });
    }
};

template <class StateVectorT> class JacobianData {
  private:
    using CFP_t = typename StateVectorT::CFP_t;
    size_t num_parameters;
    size_t num_elements;
    const CFP_t *psi;
    const std::vector<std::shared_ptr<Observable<StateVectorT>>> observables;

    const OpsData<StateVectorT> operations;

    /* @var trainableParams      */
    const std::vector<size_t> trainableParams;

  public:
    JacobianData(const JacobianData &) = default;
    JacobianData(JacobianData &&) noexcept = default;
    JacobianData &operator=(const JacobianData &) = default;
    JacobianData &operator=(JacobianData &&) noexcept = default;
    virtual ~JacobianData() = default;

    JacobianData(size_t num_params, size_t num_elem, const CFP_t *sv_ptr,
                 std::vector<std::shared_ptr<Observable<StateVectorT>>> obs,
                 OpsData<StateVectorT> ops, std::vector<size_t> trainP)
        : num_parameters(num_params), num_elements(num_elem), psi(sv_ptr),
          observables(std::move(obs)), operations(std::move(ops)),
          trainableParams(std::move(trainP)) {
        /* When the Hamiltonian has parameters, trainable parameters include
         * these. We explicitly ignore them. */
    }

    [[nodiscard]] auto getNumParams() const -> size_t { return num_parameters; }

    [[nodiscard]] auto getSizeStateVec() const -> size_t {
        return num_elements;
    }

    [[nodiscard]] auto getPtrStateVec() const -> const CFP_t * { return psi; }

    [[nodiscard]] auto getObservables() const
        -> const std::vector<std::shared_ptr<Observable<StateVectorT>>> & {
        return observables;
    }

    [[nodiscard]] auto getNumObservables() const -> size_t {
        return observables.size();
    }

    [[nodiscard]] auto getOperations() const -> const OpsData<StateVectorT> & {
        return operations;
    }

    [[nodiscard]] auto getTrainableParams() const
        -> const std::vector<size_t> & {
        return trainableParams;
    }

    [[nodiscard]] auto hasTrainableParams() const -> bool {
        return !trainableParams.empty();
    }
};
} // namespace Pennylane::Algorithms