Skip to content

Commit a6f4c99

Browse files
Add IsingXY (#303)
* Add IsingXY gate Co-authored-by: Dev version update bot <[email protected]>
1 parent e4bca20 commit a6f4c99

11 files changed

+245
-1
lines changed

pennylane_lightning/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
Version number (major.minor.patch[-label])
1717
"""
1818

19-
__version__ = "0.24.0-dev14"
19+
__version__ = "0.24.0-dev15"

pennylane_lightning/src/gates/Constant.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ namespace Pennylane::Gates::Constant {
6363
std::pair<GateOperation, std::string_view>{GateOperation::CZ, "CZ"},
6464
std::pair<GateOperation, std::string_view>{GateOperation::IsingXX,
6565
"IsingXX"},
66+
std::pair<GateOperation, std::string_view>{GateOperation::IsingXY,
67+
"IsingXY"},
6668
std::pair<GateOperation, std::string_view>{GateOperation::IsingYY,
6769
"IsingYY"},
6870
std::pair<GateOperation, std::string_view>{GateOperation::IsingZZ,
@@ -173,6 +175,7 @@ namespace Pennylane::Gates::Constant {
173175
std::pair<GateOperation, size_t>{GateOperation::CZ, 2},
174176
std::pair<GateOperation, size_t>{GateOperation::SWAP, 2},
175177
std::pair<GateOperation, size_t>{GateOperation::IsingXX, 2},
178+
std::pair<GateOperation, size_t>{GateOperation::IsingXY, 2},
176179
std::pair<GateOperation, size_t>{GateOperation::IsingYY, 2},
177180
std::pair<GateOperation, size_t>{GateOperation::IsingZZ, 2},
178181
std::pair<GateOperation, size_t>{GateOperation::ControlledPhaseShift, 2},
@@ -242,6 +245,7 @@ namespace Pennylane::Gates::Constant {
242245
std::pair<GateOperation, size_t>{GateOperation::CZ, 0},
243246
std::pair<GateOperation, size_t>{GateOperation::SWAP, 0},
244247
std::pair<GateOperation, size_t>{GateOperation::IsingXX, 1},
248+
std::pair<GateOperation, size_t>{GateOperation::IsingXY, 1},
245249
std::pair<GateOperation, size_t>{GateOperation::IsingYY, 1},
246250
std::pair<GateOperation, size_t>{GateOperation::IsingZZ, 1},
247251
std::pair<GateOperation, size_t>{GateOperation::ControlledPhaseShift, 1},

pennylane_lightning/src/gates/GateOperation.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum class GateOperation : uint32_t {
4545
CZ,
4646
SWAP,
4747
IsingXX,
48+
IsingXY,
4849
IsingYY,
4950
IsingZZ,
5051
ControlledPhaseShift,

pennylane_lightning/src/gates/OpToMemberFuncPtr.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ struct GateOpToMemberFuncPtr<PrecisionT, ParamT, GateImplementation,
145145
&GateImplementation::template applyIsingXX<PrecisionT, ParamT>;
146146
};
147147
template <class PrecisionT, class ParamT, class GateImplementation>
148+
struct GateOpToMemberFuncPtr<PrecisionT, ParamT, GateImplementation,
149+
GateOperation::IsingXY> {
150+
constexpr static auto value =
151+
&GateImplementation::template applyIsingXY<PrecisionT, ParamT>;
152+
};
153+
template <class PrecisionT, class ParamT, class GateImplementation>
148154
struct GateOpToMemberFuncPtr<PrecisionT, ParamT, GateImplementation,
149155
GateOperation::IsingYY> {
150156
constexpr static auto value =

pennylane_lightning/src/gates/cpu_kernels/GateImplementationsLM.hpp

+44
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class GateImplementationsLM : public PauliGenerator<GateImplementationsLM> {
9898
GateOperation::CRZ,
9999
GateOperation::CRot,
100100
GateOperation::IsingXX,
101+
GateOperation::IsingXY,
101102
GateOperation::IsingYY,
102103
GateOperation::IsingZZ,
103104
GateOperation::SingleExcitation,
@@ -762,6 +763,49 @@ class GateImplementationsLM : public PauliGenerator<GateImplementationsLM> {
762763
}
763764
}
764765

766+
template <class PrecisionT, class ParamT>
767+
static void
768+
applyIsingXY(std::complex<PrecisionT> *arr, const size_t num_qubits,
769+
const std::vector<size_t> &wires, bool inverse, ParamT angle) {
770+
using ComplexPrecisionT = std::complex<PrecisionT>;
771+
using std::imag;
772+
using std::real;
773+
PL_ASSERT(wires.size() == 2);
774+
775+
const size_t rev_wire0 = num_qubits - wires[1] - 1;
776+
const size_t rev_wire1 = num_qubits - wires[0] - 1; // Control qubit
777+
778+
const size_t rev_wire0_shift = static_cast<size_t>(1U) << rev_wire0;
779+
const size_t rev_wire1_shift = static_cast<size_t>(1U) << rev_wire1;
780+
781+
const auto [parity_high, parity_middle, parity_low] =
782+
revWireParity(rev_wire0, rev_wire1);
783+
784+
const PrecisionT cr = std::cos(angle / 2);
785+
const PrecisionT sj =
786+
inverse ? -std::sin(angle / 2) : std::sin(angle / 2);
787+
788+
for (size_t k = 0; k < Util::exp2(num_qubits - 2); k++) {
789+
const size_t i00 = ((k << 2U) & parity_high) |
790+
((k << 1U) & parity_middle) | (k & parity_low);
791+
const size_t i10 = i00 | rev_wire1_shift;
792+
const size_t i01 = i00 | rev_wire0_shift;
793+
const size_t i11 = i00 | rev_wire0_shift | rev_wire1_shift;
794+
795+
const ComplexPrecisionT v00 = arr[i00];
796+
const ComplexPrecisionT v01 = arr[i01];
797+
const ComplexPrecisionT v10 = arr[i10];
798+
const ComplexPrecisionT v11 = arr[i11];
799+
800+
arr[i00] = ComplexPrecisionT{real(v00), imag(v00)};
801+
arr[i01] = ComplexPrecisionT{cr * real(v01) - sj * imag(v10),
802+
cr * imag(v01) + sj * real(v10)};
803+
arr[i10] = ComplexPrecisionT{cr * real(v10) - sj * imag(v01),
804+
cr * imag(v10) + sj * real(v01)};
805+
arr[i11] = ComplexPrecisionT{real(v11), imag(v11)};
806+
}
807+
}
808+
765809
template <class PrecisionT, class ParamT>
766810
static void applyIsingYY(std::complex<PrecisionT> *arr, size_t num_qubits,
767811
const std::vector<size_t> &wires, bool inverse,

pennylane_lightning/src/gates/cpu_kernels/GateImplementationsPI.hpp

+30
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class GateImplementationsPI : public PauliGenerator<GateImplementationsPI> {
7474
GateOperation::CZ,
7575
GateOperation::SWAP,
7676
GateOperation::IsingXX,
77+
GateOperation::IsingXY,
7778
GateOperation::IsingYY,
7879
GateOperation::IsingZZ,
7980
GateOperation::CRX,
@@ -600,6 +601,35 @@ class GateImplementationsPI : public PauliGenerator<GateImplementationsPI> {
600601
}
601602
}
602603

604+
template <class PrecisionT, class ParamT = PrecisionT>
605+
static void applyIsingXY(std::complex<PrecisionT> *arr, size_t num_qubits,
606+
const std::vector<size_t> &wires, bool inverse,
607+
ParamT angle) {
608+
using ComplexPrecisionT = std::complex<PrecisionT>;
609+
PL_ASSERT(wires.size() == 2);
610+
const auto [indices, externalIndices] = GateIndices(wires, num_qubits);
611+
612+
const PrecisionT cr = std::cos(angle / 2);
613+
const PrecisionT sj =
614+
inverse ? -std::sin(angle / 2) : std::sin(angle / 2);
615+
616+
for (const size_t &externalIndex : externalIndices) {
617+
std::complex<PrecisionT> *shiftedState = arr + externalIndex;
618+
619+
const auto v0 = shiftedState[indices[0]];
620+
const auto v1 = shiftedState[indices[1]];
621+
const auto v2 = shiftedState[indices[2]];
622+
const auto v3 = shiftedState[indices[3]];
623+
624+
shiftedState[indices[0]] = ComplexPrecisionT{real(v0), imag(v0)};
625+
shiftedState[indices[1]] = ComplexPrecisionT{
626+
cr * real(v1) - sj * imag(v2), cr * imag(v1) + sj * real(v2)};
627+
shiftedState[indices[2]] = ComplexPrecisionT{
628+
cr * real(v2) - sj * imag(v1), cr * imag(v2) + sj * real(v1)};
629+
shiftedState[indices[3]] = ComplexPrecisionT{real(v3), imag(v3)};
630+
}
631+
}
632+
603633
template <class PrecisionT, class ParamT = PrecisionT>
604634
static void applyIsingYY(std::complex<PrecisionT> *arr, size_t num_qubits,
605635
const std::vector<size_t> &wires, bool inverse,

pennylane_lightning/src/simulator/KernelMap.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ int assignDefaultKernelsForGateOp() {
9292
instance.assignKernelForOp(GateOperation::IsingXX, all_threading,
9393
all_memory_model, all_qubit_numbers,
9494
Gates::KernelType::LM);
95+
instance.assignKernelForOp(GateOperation::IsingXY, all_threading,
96+
all_memory_model, all_qubit_numbers,
97+
Gates::KernelType::LM);
9598
instance.assignKernelForOp(GateOperation::IsingYY, all_threading,
9699
all_memory_model, all_qubit_numbers,
97100
Gates::KernelType::LM);

pennylane_lightning/src/tests/Test_GateImplementations_Param.cpp

+147
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,153 @@ void testApplyIsingXX() {
411411
}
412412
PENNYLANE_RUN_TEST(IsingXX);
413413

414+
template <typename PrecisionT, typename ParamT, class GateImplementation>
415+
void testApplyIsingXY() {
416+
using ComplexPrecisionT = std::complex<PrecisionT>;
417+
using std::cos;
418+
using std::sin;
419+
420+
DYNAMIC_SECTION(GateImplementation::name
421+
<< ", IsingXY0,1 |000> -> a|000> - "
422+
<< PrecisionToName<PrecisionT>::value) {
423+
const size_t num_qubits = 3;
424+
const auto ini_st = createZeroState<PrecisionT>(num_qubits);
425+
ParamT angle = 0.312;
426+
427+
const std::vector<ComplexPrecisionT> expected_results{
428+
ComplexPrecisionT{1.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
429+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
430+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
431+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
432+
};
433+
434+
auto st = ini_st;
435+
GateImplementation::applyIsingXY(st.data(), num_qubits, {0, 1}, false,
436+
angle);
437+
REQUIRE(st == approx(expected_results).margin(1e-7));
438+
}
439+
DYNAMIC_SECTION(GateImplementation::name
440+
<< ", IsingXY0,1 |100> -> a|100> + b|010> - "
441+
<< PrecisionToName<PrecisionT>::value) {
442+
const size_t num_qubits = 3;
443+
const auto ini_st = createProductState<PrecisionT>("100");
444+
ParamT angle = 0.312;
445+
446+
const std::vector<ComplexPrecisionT> expected_results{
447+
ComplexPrecisionT{0.0, 0.0},
448+
ComplexPrecisionT{0.0, 0.0},
449+
ComplexPrecisionT{0.0, sin(angle / 2)},
450+
ComplexPrecisionT{0.0, 0.0},
451+
ComplexPrecisionT{cos(angle / 2), 0.0},
452+
ComplexPrecisionT{0.0, 0.0},
453+
ComplexPrecisionT{0.0, 0.0},
454+
ComplexPrecisionT{0.0, 0.0},
455+
};
456+
457+
auto st = ini_st;
458+
GateImplementation::applyIsingXY(st.data(), num_qubits, {0, 1}, false,
459+
angle);
460+
REQUIRE(st == approx(expected_results).margin(1e-7));
461+
}
462+
463+
DYNAMIC_SECTION(GateImplementation::name
464+
<< ", IsingXY0,1 |010> -> a|010> + b|100> - "
465+
<< PrecisionToName<PrecisionT>::value) {
466+
const size_t num_qubits = 3;
467+
const auto ini_st = createProductState<PrecisionT>("010");
468+
ParamT angle = 0.312;
469+
470+
const std::vector<ComplexPrecisionT> expected_results{
471+
ComplexPrecisionT{0.0, 0.0},
472+
ComplexPrecisionT{0.0, 0.0},
473+
ComplexPrecisionT{cos(angle / 2), 0.0},
474+
ComplexPrecisionT{0.0, 0.0},
475+
ComplexPrecisionT{0.0, sin(angle / 2)},
476+
ComplexPrecisionT{0.0, 0.0},
477+
ComplexPrecisionT{0.0, 0.0},
478+
ComplexPrecisionT{0.0, 0.0},
479+
};
480+
481+
auto st = ini_st;
482+
GateImplementation::applyIsingXY(st.data(), num_qubits, {0, 1}, false,
483+
angle);
484+
REQUIRE(st == approx(expected_results).margin(1e-7));
485+
}
486+
487+
DYNAMIC_SECTION(GateImplementation::name
488+
<< ", IsingXY0,1 |110> -> a|110> - "
489+
<< PrecisionToName<PrecisionT>::value) {
490+
const size_t num_qubits = 3;
491+
const auto ini_st = createProductState<PrecisionT>("110");
492+
ParamT angle = 0.312;
493+
494+
const std::vector<ComplexPrecisionT> expected_results{
495+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
496+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
497+
ComplexPrecisionT{0.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
498+
ComplexPrecisionT{1.0, 0.0}, ComplexPrecisionT{0.0, 0.0},
499+
};
500+
501+
auto st = ini_st;
502+
GateImplementation::applyIsingXY(st.data(), num_qubits, {0, 1}, false,
503+
angle);
504+
REQUIRE(st == approx(expected_results).margin(1e-7));
505+
}
506+
507+
DYNAMIC_SECTION(GateImplementation::name
508+
<< ", IsingXY0,1 - "
509+
<< PrecisionToName<PrecisionT>::value) {
510+
const size_t num_qubits = 4;
511+
512+
std::vector<ComplexPrecisionT> ini_st{
513+
ComplexPrecisionT{0.267462841882, 0.010768564798},
514+
ComplexPrecisionT{0.228575129706, 0.010564590956},
515+
ComplexPrecisionT{0.099492749900, 0.260849823392},
516+
ComplexPrecisionT{0.093690204310, 0.189847108173},
517+
ComplexPrecisionT{0.033390732374, 0.203836830144},
518+
ComplexPrecisionT{0.226979395737, 0.081852150975},
519+
ComplexPrecisionT{0.031235505729, 0.176933497281},
520+
ComplexPrecisionT{0.294287602843, 0.145156781198},
521+
ComplexPrecisionT{0.152742706049, 0.111628061129},
522+
ComplexPrecisionT{0.012553863703, 0.120027860480},
523+
ComplexPrecisionT{0.237156555364, 0.154658769755},
524+
ComplexPrecisionT{0.117001120872, 0.228059505033},
525+
ComplexPrecisionT{0.041495873225, 0.065934827444},
526+
ComplexPrecisionT{0.089653239407, 0.221581340372},
527+
ComplexPrecisionT{0.217892322429, 0.291261296999},
528+
ComplexPrecisionT{0.292993251871, 0.186570798697},
529+
};
530+
531+
const std::vector<size_t> wires = {0, 1};
532+
const ParamT angle = 0.312;
533+
534+
std::vector<ComplexPrecisionT> expected{
535+
ComplexPrecisionT{0.267462849617, 0.010768564418},
536+
ComplexPrecisionT{0.228575125337, 0.010564590804},
537+
ComplexPrecisionT{0.099492751062, 0.260849833488},
538+
ComplexPrecisionT{0.093690201640, 0.189847111702},
539+
ComplexPrecisionT{0.015641822883, 0.225092900621},
540+
ComplexPrecisionT{0.205574608177, 0.082808663337},
541+
ComplexPrecisionT{0.006827173322, 0.211631480575},
542+
ComplexPrecisionT{0.255280800811, 0.161572331669},
543+
ComplexPrecisionT{0.119218164572, 0.115460377284},
544+
ComplexPrecisionT{-0.000315789761, 0.153835664378},
545+
ComplexPrecisionT{0.206786872079, 0.157633689097},
546+
ComplexPrecisionT{0.093027614553, 0.271012980118},
547+
ComplexPrecisionT{0.041495874524, 0.065934829414},
548+
ComplexPrecisionT{0.089653238654, 0.221581339836},
549+
ComplexPrecisionT{0.217892318964, 0.291261285543},
550+
ComplexPrecisionT{0.292993247509, 0.186570793390},
551+
};
552+
553+
auto st = ini_st;
554+
GateImplementation::applyIsingXY(st.data(), num_qubits, wires, false,
555+
angle);
556+
REQUIRE(st == approx(expected).margin(1e-5));
557+
}
558+
}
559+
PENNYLANE_RUN_TEST(IsingXY);
560+
414561
template <typename PrecisionT, typename ParamT, class GateImplementation>
415562
void testApplyIsingYY() {
416563
using ComplexPrecisionT = std::complex<PrecisionT>;

pennylane_lightning/src/tests/Test_OpToMemberFuncPtr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class DummyImplementation {
124124
PENNYLANE_TESTS_DEFINE_GATE_OP(SWAP, 0)
125125
PENNYLANE_TESTS_DEFINE_GATE_OP(ControlledPhaseShift, 1)
126126
PENNYLANE_TESTS_DEFINE_GATE_OP(IsingXX, 1)
127+
PENNYLANE_TESTS_DEFINE_GATE_OP(IsingXY, 1)
127128
PENNYLANE_TESTS_DEFINE_GATE_OP(IsingYY, 1)
128129
PENNYLANE_TESTS_DEFINE_GATE_OP(IsingZZ, 1)
129130
PENNYLANE_TESTS_DEFINE_GATE_OP(CRX, 1)

tests/test_apply.py

+7
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,13 @@ def test_apply_operation_preserve_pointer_single_wire_with_parameters(
360360
[-0.5j, 0.5, -0.5j, 0.5],
361361
[math.pi / 2],
362362
),
363+
(qml.IsingXY, [1, 0, 0, 0], [1, 0, 0, 0], [math.pi / 2]),
364+
(
365+
qml.IsingXY,
366+
[0, 1 / math.sqrt(2), 0, 1 / math.sqrt(2)],
367+
[0, 0.5, 0.5j, 1 / math.sqrt(2)],
368+
[math.pi / 2],
369+
),
363370
(qml.IsingYY, [1, 0, 0, 0], [1 / math.sqrt(2), 0, 0, 1j / math.sqrt(2)], [math.pi / 2]),
364371
(
365372
qml.IsingYY,

tests/test_gates.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def op(op_name):
4242
"CSWAP": qml.CSWAP(wires=[0, 1, 2]),
4343
"PauliRot": qml.PauliRot(0.123, "Y", wires=0),
4444
"IsingXX": qml.IsingXX(0.123, wires=[0, 1]),
45+
"IsingXY": qml.IsingXY(0.123, wires=[0, 1]),
4546
"IsingYY": qml.IsingYY(0.123, wires=[0, 1]),
4647
"IsingZZ": qml.IsingZZ(0.123, wires=[0, 1]),
4748
"Identity": qml.Identity(wires=0),

0 commit comments

Comments
 (0)