Program Listing for File TestHelpersWires.hpp

Return to documentation for file (pennylane_lightning/core/src/utils/TestHelpersWires.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 <cstdlib>
#include <numeric> // iota
#include <vector>

#include "BitUtil.hpp"
#include "Constant.hpp"
#include "ConstantUtil.hpp" // array_has_elem, lookup
#include "Error.hpp"
#include "GateOperation.hpp"
#include "Macros.hpp"

namespace Pennylane::Util {
inline auto createWires(Pennylane::Gates::GateOperation op,
                        std::size_t num_qubits) -> std::vector<std::size_t> {
    if (array_has_elem(Pennylane::Gates::Constant::multi_qubit_gates, op)) {
        std::vector<std::size_t> wires(num_qubits);
        std::iota(wires.begin(), wires.end(), 0);
        return wires;
    }
    switch (lookup(Pennylane::Gates::Constant::gate_wires, op)) {
    case 1:
        return {0};
    case 2:
        return {0, 1};
    case 3:
        return {0, 1, 2};
    case 4:
        return {0, 1, 2, 3};
    default:
        PL_ABORT("The number of wires for a given gate is unknown.");
    }
    return {};
}

inline auto createWires(Pennylane::Gates::ControlledGateOperation op,
                        std::size_t num_qubits) -> std::vector<std::size_t> {
    if (array_has_elem(Pennylane::Gates::Constant::controlled_multi_qubit_gates,
                       op)) {
        std::vector<std::size_t> wires(num_qubits - 2);
        std::iota(wires.begin(), wires.end(), 0);
        return wires;
    }
    switch (lookup(Pennylane::Gates::Constant::controlled_gate_wires, op)) {
    case 1:
        return {0};
    case 2:
        return {0, 1};
    case 3:
        return {0, 1, 2};
    case 4:
        return {0, 1, 2, 3};
    default:
        PL_ABORT("The number of wires for a given gate is unknown.");
    }
    return {};
}

template <class PrecisionT>
auto createParams(Pennylane::Gates::GateOperation op)
    -> std::vector<PrecisionT> {
    switch (lookup(Pennylane::Gates::Constant::gate_num_params, op)) {
    case 0:
        return {};
    case 1:
        return {static_cast<PrecisionT>(0.312)};
    case 3:
        return {static_cast<PrecisionT>(0.128), static_cast<PrecisionT>(-0.563),
                static_cast<PrecisionT>(1.414)};
    default:
        PL_ABORT("The number of parameters for a given gate is unknown.");
    }
    return {};
}

class WiresGenerator {
  public:
    [[nodiscard]] virtual auto all_perms() const
        -> const std::vector<std::vector<std::size_t>> & = 0;
};

class CombinationGenerator : public WiresGenerator {
  private:
    std::vector<std::size_t> v_;
    std::vector<std::vector<std::size_t>> all_perms_;

  public:
    void comb(size_t n, std::size_t r) {
        if (r == 0) {
            all_perms_.push_back(v_);
            return;
        }
        if (n < r) {
            return;
        }

        v_[r - 1] = n - 1;
        comb(n - 1, r - 1);

        comb(n - 1, r);
    }

    CombinationGenerator(size_t n, std::size_t r) {
        v_.resize(r);
        comb(n, r);
    }

    [[nodiscard]] auto all_perms() const
        -> const std::vector<std::vector<std::size_t>> & override {
        return all_perms_;
    }
};

class PermutationGenerator : public WiresGenerator {
  private:
    std::vector<std::vector<std::size_t>> all_perms_;
    std::vector<std::size_t> available_elems_;
    std::vector<std::size_t> v;

  public:
    void perm(size_t n, std::size_t r) {
        if (r == 0) {
            all_perms_.push_back(v);
            return;
        }
        for (size_t i = 0; i < n; i++) {
            v[r - 1] = available_elems_[i];
            std::swap(available_elems_[n - 1], available_elems_[i]);
            perm(n - 1, r - 1);
            std::swap(available_elems_[n - 1], available_elems_[i]);
        }
    }

    PermutationGenerator(size_t n, std::size_t r) {
        v.resize(r);

        available_elems_.resize(n);
        std::iota(available_elems_.begin(), available_elems_.end(), 0);
        perm(n, r);
    }

    [[nodiscard]] auto all_perms() const
        -> const std::vector<std::vector<std::size_t>> & override {
        return all_perms_;
    }
};

auto inline createAllWires(size_t n_qubits,
                           Pennylane::Gates::GateOperation gate_op, bool order)
    -> std::vector<std::vector<std::size_t>> {
    if (array_has_elem(Pennylane::Gates::Constant::multi_qubit_gates,
                       gate_op)) {
        // make all possible 2^N permutations
        std::vector<std::vector<std::size_t>> res;
        res.reserve((1U << n_qubits) - 1);
        for (size_t k = 1; k < (static_cast<std::size_t>(1U) << n_qubits);
             k++) {
            std::vector<std::size_t> wires;
            wires.reserve(std::popcount(k));

            for (size_t i = 0; i < n_qubits; i++) {
                if (((k >> i) & 1U) == 1U) {
                    wires.emplace_back(i);
                }
            }

            res.push_back(wires);
        }
        return res;
    } // else
    const std::size_t n_wires =
        lookup(Pennylane::Gates::Constant::gate_wires, gate_op);
    if (order) {
        return PermutationGenerator(n_qubits, n_wires).all_perms();
    } // else
    return CombinationGenerator(n_qubits, n_wires).all_perms();
}

} // namespace Pennylane::Util