Program Listing for File AdjointJacobianBase.hpp

Return to documentation for file (pennylane_lightning/core/src/algorithms/AdjointJacobianBase.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 <span>

#include "JacobianData.hpp"
#include "Observables.hpp"

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

  protected:
    AdjointJacobianBase() = default;
    AdjointJacobianBase(const AdjointJacobianBase &) = default;
    AdjointJacobianBase(AdjointJacobianBase &&) noexcept = default;
    AdjointJacobianBase &operator=(const AdjointJacobianBase &) = default;
    AdjointJacobianBase &operator=(AdjointJacobianBase &&) noexcept = default;

    template <class UpdatedStateVectorT>
    inline void applyOperations(UpdatedStateVectorT &state,
                                const OpsData<StateVectorT> &operations,
                                bool adj = false) {
        for (std::size_t op_idx = 0; op_idx < operations.getOpsName().size();
             op_idx++) {
            if (operations.getOpsControlledWires()[op_idx].empty()) {
                state.applyOperation(operations.getOpsName()[op_idx],
                                     operations.getOpsWires()[op_idx],
                                     operations.getOpsInverses()[op_idx] ^ adj,
                                     operations.getOpsParams()[op_idx],
                                     operations.getOpsMatrices()[op_idx]);
            } else {
                state.applyOperation(
                    operations.getOpsName()[op_idx],
                    operations.getOpsControlledWires()[op_idx],
                    operations.getOpsControlledValues()[op_idx],
                    operations.getOpsWires()[op_idx],
                    operations.getOpsInverses()[op_idx] ^ adj,
                    operations.getOpsParams()[op_idx],
                    operations.getOpsMatrices()[op_idx]);
            }
        }
    }

    template <class UpdatedStateVectorT>
    inline void applyOperationAdj(UpdatedStateVectorT &state,
                                  const OpsData<StateVectorT> &operations,
                                  std::size_t op_idx) {
        if (operations.getOpsControlledWires()[op_idx].empty()) {
            state.applyOperation(operations.getOpsName()[op_idx],
                                 operations.getOpsWires()[op_idx],
                                 !operations.getOpsInverses()[op_idx],
                                 operations.getOpsParams()[op_idx],
                                 operations.getOpsMatrices()[op_idx]);
        } else {
            state.applyOperation(operations.getOpsName()[op_idx],
                                 operations.getOpsControlledWires()[op_idx],
                                 operations.getOpsControlledValues()[op_idx],
                                 operations.getOpsWires()[op_idx],
                                 !operations.getOpsInverses()[op_idx],
                                 operations.getOpsParams()[op_idx],
                                 operations.getOpsMatrices()[op_idx]);
        }
    }

    inline void applyOperationsAdj(std::vector<StateVectorT> &states,
                                   const OpsData<StateVectorT> &operations,
                                   std::size_t op_idx) {
        for (auto &state : states) {
            applyOperationAdj(state, operations, op_idx);
        }
    }

    inline auto applyGenerator(StateVectorT &sv, const std::string &op_name,
                               const std::vector<std::size_t> &wires,
                               const bool adj) -> PrecisionT {
        return sv.applyGenerator(op_name, wires, adj);
    }

    inline auto applyGenerator(StateVectorT &sv, const std::string &op_name,
                               const std::vector<std::size_t> &controlled_wires,
                               const std::vector<bool> &controlled_values,
                               const std::vector<std::size_t> &wires,
                               const bool adj) -> PrecisionT {
        return sv.applyGenerator(op_name, controlled_wires, controlled_values,
                                 wires, adj);
    }

    inline void applyObservable(StateVectorT &state,
                                const Observable<StateVectorT> &observable) {
        observable.applyInPlace(state);
    }

    inline void applyObservables(
        std::vector<StateVectorT> &states, const StateVectorT &reference_state,
        const std::vector<std::shared_ptr<Observable<StateVectorT>>>
            &observables) {
        std::size_t num_observables = observables.size();
        for (std::size_t i = 0; i < num_observables; i++) {
            states[i].updateData(reference_state);
            applyObservable(states[i], *observables[i]);
        }
    }

    inline void adjointJacobian(std::span<PrecisionT> jac,
                                const JacobianData<StateVectorT> &jd,
                                const StateVectorT &ref_data = {0},
                                bool apply_operations = false) {
        return static_cast<Derived *>(this)->adjointJacobian(jac, jd, ref_data,
                                                             apply_operations);
    }

  public:
    ~AdjointJacobianBase() = default;
};
} // namespace Pennylane::Algorithms