Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(LK-C-2) Add controlled named 2/4-qubit gate (e.g. NCIsingXX) support to Lightning Kokkos #953

Open
wants to merge 10 commits into
base: lk-control-gate-1Q
Choose a base branch
from

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,44 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param "
}
}

TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param "
"two-qubit with controls",
"[StateVectorKokkos_NonParam]", float, double) {
using StateVectorT = StateVectorKokkos<TestType>;

const TestType EP = 1e-4;
const std::size_t num_qubits = 4;
const bool inverse = GENERATE(true, false);
const std::size_t control = GENERATE(0, 1, 2, 3);
const std::size_t wire0 = GENERATE(0, 1, 2, 3);
const std::size_t wire1 = GENERATE(0, 1, 2, 3);

auto ini_st = createNonTrivialState<StateVectorT>(num_qubits);

SECTION("N-controlled SWAP") {
if (control != wire0 && control != wire1 && wire0 != wire1) {

StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()};
kokkos_sv0.applyOperation("CSWAP", {control, wire0, wire1},
inverse);
auto matrix = getSWAP<Kokkos::complex, TestType>();
kokkos_sv1.applyOperation("SWAP", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1},
inverse);
auto result_sv0 = kokkos_sv0.getDataVector();
auto result_sv1 = kokkos_sv1.getDataVector();
for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_sv0[j]) ==
Approx(real(result_sv1[j])).margin(EP));
CHECK(imag(result_sv0[j]) ==
Approx(imag(result_sv1[j])).margin(EP));
}
}
}
}

TEMPLATE_TEST_CASE("StateVectorKokkos::SetStateVector",
"[StateVectorKokkos_Nonparam]", float, double) {
using PrecisionT = TestType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,348 @@ TEMPLATE_TEST_CASE(
}
}

TEMPLATE_TEST_CASE(
"StateVectorKokkos::applyOperation param two-qubit with controls",
"[StateVectorKokkos_Operation]", float, double) {
using StateVectorT = StateVectorKokkos<TestType>;
using PrecisionT = StateVectorT::PrecisionT;
using ComplexT = Kokkos::complex<PrecisionT>;

const std::size_t num_qubits = 4;
const TestType EP = 1e-4;
auto ini_st = createNonTrivialState<StateVectorT>(num_qubits);

const bool inverse = GENERATE(false, true);
const std::size_t control = GENERATE(0, 1, 2, 3);
const std::size_t wire0 = GENERATE(0, 1, 2, 3);
const std::size_t wire1 = GENERATE(0, 1, 2, 3);
const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5);

auto getControlledGate = [](std::vector<ComplexT> matrix) {
std::vector<ComplexT> cmatrix(matrix.size() * 4);
for (std::size_t i = 0; i < 4; i++) {
cmatrix[i * 8 + i] = ComplexT{1.0};
}
for (std::size_t i = 0; i < 4; i++) {
for (std::size_t j = 0; j < 4; j++) {
cmatrix[(i + 4) * 8 + j + 4] = matrix[i * 4 + j];
}
}
return cmatrix;
};

SECTION("N-controlled IsingXX") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix = getIsingXX<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"IsingXX", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled IsingXY") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix = getIsingXY<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"IsingXY", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled IsingYY") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix = getIsingYY<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"IsingYY", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled IsingZZ") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix = getIsingZZ<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"IsingZZ", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled SingleExcitation") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getSingleExcitation<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"SingleExcitation", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled SingleExcitationMinus") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getSingleExcitationMinus<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"SingleExcitationMinus", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled SingleExcitationPlus") {
if (control != wire0 && control != wire1 && wire0 != wire1) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getSingleExcitationPlus<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);
kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1},
inverse);
kokkos_sv_op.applyOperation(
"SingleExcitationPlus", std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire0, wire1},
inverse, {param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}
}

TEMPLATE_TEST_CASE(
"StateVectorKokkos::applyOperation param four-qubit with controls",
"[StateVectorKokkos_Operation]", float, double) {
using StateVectorT = StateVectorKokkos<TestType>;
using PrecisionT = StateVectorT::PrecisionT;
using ComplexT = Kokkos::complex<PrecisionT>;

const std::size_t num_qubits = 5;
const TestType EP = 1e-4;
auto ini_st = createNonTrivialState<StateVectorT>(num_qubits);

const bool inverse = GENERATE(false, true);
const std::size_t control = GENERATE(0, 1, 2, 3, 4);
const std::size_t wire0 = GENERATE(0, 1, 2, 3, 4);
const std::size_t wire1 = GENERATE(0, 1, 2, 3, 4);
const std::size_t wire2 = GENERATE(0, 1, 2, 3, 4);
const std::size_t wire3 = GENERATE(0, 1, 2, 3, 4);
const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5);

auto getControlledGate = [](std::vector<ComplexT> matrix) {
std::vector<ComplexT> cmatrix(matrix.size() * 4);
for (std::size_t i = 0; i < 16; i++) {
cmatrix[i * 32 + i] = ComplexT{1.0};
}
for (std::size_t i = 0; i < 16; i++) {
for (std::size_t j = 0; j < 16; j++) {
cmatrix[(i + 16) * 32 + j + 16] = matrix[i * 16 + j];
}
}
return cmatrix;
};

SECTION("N-controlled DoubleExcitation") {
std::vector<std::size_t> wires = {control, wire0, wire1, wire2, wire3};
std::sort(wires.begin(), wires.end());
if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getDoubleExcitation<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);

kokkos_sv_mat.applyMatrix(
cmatrix, {control, wire0, wire1, wire2, wire3}, inverse);
kokkos_sv_op.applyOperation(
"DoubleExcitation", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1, wire2, wire3}, inverse,
{param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled DoubleExcitationPlus") {
std::vector<std::size_t> wires = {control, wire0, wire1, wire2, wire3};
std::sort(wires.begin(), wires.end());
if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getDoubleExcitationPlus<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);

kokkos_sv_mat.applyMatrix(
cmatrix, {control, wire0, wire1, wire2, wire3}, inverse);
kokkos_sv_op.applyOperation(
"DoubleExcitationPlus", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1, wire2, wire3}, inverse,
{param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}

SECTION("N-controlled DoubleExcitationMinus") {
std::vector<std::size_t> wires = {control, wire0, wire1, wire2, wire3};
std::sort(wires.begin(), wires.end());
if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) {
StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()};
StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()};

auto matrix =
getDoubleExcitationMinus<Kokkos::complex, PrecisionT>(param);
std::vector<ComplexT> cmatrix = getControlledGate(matrix);

kokkos_sv_mat.applyMatrix(
cmatrix, {control, wire0, wire1, wire2, wire3}, inverse);
kokkos_sv_op.applyOperation(
"DoubleExcitationMinus", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1, wire2, wire3}, inverse,
{param});

auto result_mat = kokkos_sv_mat.getDataVector();
auto result_op = kokkos_sv_op.getDataVector();

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK(real(result_op[j]) ==
Approx(real(result_mat[j])).margin(EP));
CHECK(imag(result_op[j]) ==
Approx(imag(result_mat[j])).margin(EP));
}
}
}
}

TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingXY",
"[StateVectorKokkosManaged_Param]", float, double) {
{
Expand Down
Loading
Loading