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

Add implementations for optimised gate kernels #85

Merged
merged 6 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 1 addition & 19 deletions pennylane_lightning/src/Apply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ void Pennylane::constructAndApplyOperation(
unique_ptr<AbstractGate> gate = constructGate(opLabel, opParams);
if (gate->numQubits != opWires.size())
throw std::invalid_argument(string("The gate of type ") + opLabel + " requires " + std::to_string(gate->numQubits) + " wires, but " + std::to_string(opWires.size()) + " were supplied");

const vector<CplxType>& matrix = gate->asMatrix();

vector<size_t> internalIndices = generateBitPatterns(opWires, qubits);

Expand All @@ -69,23 +67,7 @@ void Pennylane::constructAndApplyOperation(
vector<CplxType> inputVector(internalIndices.size());
for (const size_t& externalIndex : externalIndices) {
CplxType* shiftedStatePtr = state.arr + externalIndex;

// Gather
size_t pos = 0;
for (const size_t& internalIndex : internalIndices) {
inputVector[pos] = shiftedStatePtr[internalIndex];
pos++;
}

// Apply + scatter
for (size_t i = 0; i < internalIndices.size(); i++) {
size_t internalIndex = internalIndices[i];
shiftedStatePtr[internalIndex] = 0;
size_t baseIndex = i * internalIndices.size();
for (size_t j = 0; j < internalIndices.size(); j++) {
shiftedStatePtr[internalIndex] += matrix[baseIndex + j] * inputVector[j];
}
}
gate->applyKernel(shiftedStatePtr, internalIndices, inputVector);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

}
}

Expand Down
128 changes: 123 additions & 5 deletions pennylane_lightning/src/Gates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define _USE_MATH_DEFINES

#include <algorithm>
#include <cmath>
#include <functional>
#include <map>
Expand All @@ -24,6 +25,7 @@
using std::function;
using std::map;
using std::string;
using std::swap;
using std::unique_ptr;
using std::vector;

Expand All @@ -42,6 +44,28 @@ Pennylane::AbstractGate::AbstractGate(int numQubits)
, length(exp2(numQubits))
{}

void Pennylane::AbstractGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
const vector<CplxType>& matrix = asMatrix();
trbromley marked this conversation as resolved.
Show resolved Hide resolved
assert(indices.size() == length && v.size() == length);

// Gather
size_t pos = 0;
for (const size_t& index : indices) {
v[pos] = state[index];
pos++;
}

// Apply + scatter
for (size_t i = 0; i < indices.size(); i++) {
size_t index = indices[i];
state[index] = 0;
size_t baseIndex = i * indices.size();
for (size_t j = 0; j < indices.size(); j++) {
state[index] += matrix[baseIndex + j] * v[j];
}
}
}

// -------------------------------------------------------------------------------------------------------------

Pennylane::SingleQubitGate::SingleQubitGate()
Expand All @@ -61,6 +85,10 @@ const vector<CplxType> Pennylane::XGate::matrix{
0, 1,
1, 0 };

void Pennylane::XGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constness and making v unnamed if unused would be a suggestion in other cases too

Suggested change
void Pennylane::XGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
void Pennylane::XGate::applyKernel(CplxType* state, const vector<size_t>& indices, vector<CplxType>&) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with adding the const; I'm generally not a fan of removing variable names from method signatures, but I guess opinions may differ on this one--if nothing else, its making it clear that its overriding the same virtual method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! A reason to make v unnamed would be that while reading, having the name v was somewhat confusing, as one would expect that to be used within the function (as is in some of them).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True; although I think that's indication we should rework the API (i.e. we shouldn't be passing in parameters we don't need if we don't have to). As mentioned in the PR description, its probably beneficial, although it will unfortunately result in some measure of code duplication.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or if branching off would result in a performance degradation, how about incorporating the internal loop in applyKernel or creating another member function that implements the internal loop and depends on applyKernel? That way inputVector would only be created if used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be ideal. However, ultimately I think we want to inline applyKernel. And for that to happen I think we need to duplicate the outer loop for every subclass, because of the requirement to be able to statically infer which method is being called. Again, probably beneficial in the long run, but just a bit painful to look at.

swap(state[indices[0]], state[indices[1]]);
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::YGate::label = "PauliY";
Expand All @@ -74,6 +102,12 @@ const vector<CplxType> Pennylane::YGate::matrix{
0, -IMAG,
IMAG, 0 };

void Pennylane::YGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
v[0] = state[indices[0]];
state[indices[0]] = -IMAG * state[indices[1]];
state[indices[1]] = IMAG * v[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come we need to update v[0] here (and not for PauliX for example)? Seems like it's used as a temp for the swap, but it would also have effect afterwards, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v is really just scratch space. If we move the outer loop in, we can avoid allocating v altogether, except in the base implementation. The requirement to pass v is one reason why I'm not entirely happy with this interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it!

we can avoid allocating v altogether, except in the base implementation

This would be preferable as just creating v would result in an overhead, correct? Could we branch of at the beginning of the outer loop based on the type of operation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The allocation for v is really insignificant in the grand scheme of things, so I don't think branching is necessary. The first rule of optimisation is to not over-optimise, and I don't think this ever becomes a bottleneck, so its more a question of code clarity as to which API we adopt.

}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::ZGate::label = "PauliZ";
Expand All @@ -87,6 +121,10 @@ const std::vector<CplxType> Pennylane::ZGate::matrix{
1, 0,
0, -1 };

void Pennylane::ZGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[1]] *= -1;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::HadamardGate::label = "Hadamard";
Expand All @@ -100,6 +138,13 @@ const vector<CplxType> Pennylane::HadamardGate::matrix{
SQRT2INV, SQRT2INV,
SQRT2INV, -SQRT2INV };

void Pennylane::HadamardGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
v[0] = state[indices[0]];
v[1] = state[indices[1]];
state[indices[0]] = SQRT2INV * (v[0] + v[1]);
state[indices[1]] = SQRT2INV * (v[0] - v[1]);
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::SGate::label = "S";
Expand All @@ -113,6 +158,10 @@ const vector<CplxType> Pennylane::SGate::matrix{
1, 0,
0, IMAG };

void Pennylane::SGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[1]] *= IMAG;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::TGate::label = "T";
Expand All @@ -122,9 +171,15 @@ Pennylane::TGate Pennylane::TGate::create(const vector<double>& parameters) {
return Pennylane::TGate();
}

const CplxType Pennylane::TGate::shift = std::pow(M_E, CplxType(0, M_PI / 4));

const vector<CplxType> Pennylane::TGate::matrix{
1, 0,
0, std::pow(M_E, CplxType(0, M_PI / 4)) };
0, Pennylane::TGate::shift };

void Pennylane::TGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[1]] *= Pennylane::TGate::shift;
}

// -------------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -177,6 +232,11 @@ Pennylane::RotationZGate::RotationZGate(double rotationAngle)
0, second }
{}

void Pennylane::RotationZGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[0]] *= first;
state[indices[1]] *= second;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::PhaseShiftGate::label = "PhaseShift";
Expand All @@ -193,6 +253,10 @@ Pennylane::PhaseShiftGate::PhaseShiftGate(double rotationAngle)
0, shift }
{}

void Pennylane::PhaseShiftGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[1]] *= shift;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::GeneralRotationGate::label = "Rot";
Expand All @@ -205,9 +269,13 @@ Pennylane::GeneralRotationGate Pennylane::GeneralRotationGate::create(const vect
Pennylane::GeneralRotationGate::GeneralRotationGate(double phi, double theta, double omega)
: c(std::cos(theta / 2), 0)
, s(std::sin(theta / 2), 0)
, r1(c* std::pow(M_E, CplxType(0, (-phi - omega) / 2)))
, r2(-s * std::pow(M_E, CplxType(0, (phi - omega) / 2)))
, r3(s* std::pow(M_E, CplxType(0, (-phi + omega) / 2)))
, r4(c* std::pow(M_E, CplxType(0, (phi + omega) / 2)))
, matrix{
c * std::pow(M_E, CplxType(0, (-phi - omega) / 2)), -s * std::pow(M_E, CplxType(0, (phi - omega) / 2)),
s * std::pow(M_E, CplxType(0, (-phi + omega) / 2)), c * std::pow(M_E, CplxType(0, (phi + omega) / 2)) }
r1, r2,
r3, r4 }
Comment on lines +300 to +306
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

{}

// -------------------------------------------------------------------------------------------------------------
Expand All @@ -231,6 +299,10 @@ const std::vector<CplxType> Pennylane::CNOTGate::matrix{
0, 0, 0, 1,
0, 0, 1, 0 };

void Pennylane::CNOTGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
swap(state[indices[2]], state[indices[3]]);
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::SWAPGate::label = "SWAP";
Expand All @@ -246,6 +318,10 @@ const std::vector<CplxType> Pennylane::SWAPGate::matrix{
0, 1, 0, 0,
0, 0, 0, 1 };

void Pennylane::SWAPGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
swap(state[indices[1]], state[indices[2]]);
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CZGate::label = "CZ";
Expand All @@ -261,6 +337,10 @@ const std::vector<CplxType> Pennylane::CZGate::matrix{
0, 0, 1, 0,
0, 0, 0, -1 };

void Pennylane::CZGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[3]] *= -1;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CRotationXGate::label = "CRX";
Expand All @@ -280,6 +360,13 @@ Pennylane::CRotationXGate::CRotationXGate(double rotationAngle)
0, 0, js, c }
{}

void Pennylane::CRotationXGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
v[0] = state[indices[2]];
v[1] = state[indices[3]];
state[indices[2]] = c * v[0] + js * v[1];
state[indices[3]] = js * v[0] + c * v[1];
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CRotationYGate::label = "CRY";
Expand All @@ -299,6 +386,13 @@ Pennylane::CRotationYGate::CRotationYGate(double rotationAngle)
0, 0, s, c }
{}

void Pennylane::CRotationYGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
v[0] = state[indices[2]];
v[1] = state[indices[3]];
state[indices[2]] = c * v[0] - s * v[1];
state[indices[3]] = s * v[0] + c * v[1];
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CRotationZGate::label = "CRZ";
Expand All @@ -318,6 +412,11 @@ Pennylane::CRotationZGate::CRotationZGate(double rotationAngle)
0, 0, 0, second }
{}

void Pennylane::CRotationZGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
state[indices[2]] *= first;
state[indices[3]] *= second;
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CGeneralRotationGate::label = "CRot";
Expand All @@ -330,13 +429,24 @@ Pennylane::CGeneralRotationGate Pennylane::CGeneralRotationGate::create(const ve
Pennylane::CGeneralRotationGate::CGeneralRotationGate(double phi, double theta, double omega)
: c(std::cos(theta / 2), 0)
, s(std::sin(theta / 2), 0)
, r1(c* std::pow(M_E, CplxType(0, (-phi - omega) / 2)))
, r2(-s * std::pow(M_E, CplxType(0, (phi - omega) / 2)))
, r3(s* std::pow(M_E, CplxType(0, (-phi + omega) / 2)))
, r4(c* std::pow(M_E, CplxType(0, (phi + omega) / 2)))
, matrix{
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, c * std::pow(M_E, CplxType(0, (-phi - omega) / 2)), -s * std::pow(M_E, CplxType(0, (phi - omega) / 2)),
0, 0, s * std::pow(M_E, CplxType(0, (-phi + omega) / 2)), c * std::pow(M_E, CplxType(0, (phi + omega) / 2)) }
0, 0, r1, r2,
0, 0, r3, r4 }
{}

void Pennylane::CGeneralRotationGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
v[0] = state[indices[2]];
v[1] = state[indices[3]];
state[indices[2]] = r1 * v[0] + r2 * v[1];
state[indices[3]] = r3 * v[0] + r4 * v[1];
}

// -------------------------------------------------------------------------------------------------------------

Pennylane::ThreeQubitGate::ThreeQubitGate()
Expand All @@ -362,6 +472,10 @@ const std::vector<CplxType> Pennylane::ToffoliGate::matrix{
0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 1, 0 };

void Pennylane::ToffoliGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
swap(state[indices[6]], state[indices[7]]);
}

// -------------------------------------------------------------------------------------------------------------

const string Pennylane::CSWAPGate::label = "CSWAP";
Expand All @@ -381,6 +495,10 @@ const std::vector<CplxType> Pennylane::CSWAPGate::matrix{
0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1 };

void Pennylane::CSWAPGate::applyKernel(CplxType* state, vector<size_t>& indices, vector<CplxType>& v) {
swap(state[indices[5]], state[indices[6]]);
}

// -------------------------------------------------------------------------------------------------------------

template<class GateType>
Expand Down
Loading