Skip to content

Commit

Permalink
Map R1 to Rz for Quantinuum Base Profile after ctrl-R1 removed (#471)
Browse files Browse the repository at this point in the history
* Map r1 to rz for quantinuum base profile after ctrl-r1 removed

Signed-off-by: Alex McCaskey <[email protected]>

* cleanup and use the basis conversion pass

Signed-off-by: Alex McCaskey <[email protected]>

* also update for ionq

Signed-off-by: Alex McCaskey <[email protected]>

* address pr comments

Signed-off-by: Alex McCaskey <[email protected]>

---------

Signed-off-by: Alex McCaskey <[email protected]>
  • Loading branch information
amccaskey authored Jul 26, 2023
1 parent a37af77 commit c8ac241
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 2 deletions.
4 changes: 2 additions & 2 deletions lib/Optimizer/CodeGen/Passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ using namespace mlir;
static void addQuantinuumPipeline(OpPassManager &pm) {
using namespace cudaq::opt;
std::string basis[] = {
"h", "s", "t", "r1", "rx", "ry", "rz", "x", "y", "z", "x(1)",
"h", "s", "t", "rx", "ry", "rz", "x", "y", "z", "x(1)",
};
BasisConversionPassOptions options;
options.basis = basis;
Expand All @@ -38,7 +38,7 @@ static void addIQMPipeline(OpPassManager &pm) {
static void addIonQPipeline(OpPassManager &pm) {
using namespace cudaq::opt;
std::string basis[] = {
"h", "s", "t", "r1", "rx", "ry",
"h", "s", "t", "rx", "ry",
"rz", "x", "y", "z", "x(1)", // TODO set to ms, gpi, gpi2
};
BasisConversionPassOptions options;
Expand Down
18 changes: 18 additions & 0 deletions lib/Optimizer/Transforms/DecompositionPatterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,23 @@ struct HToPhasedRx : public OpRewritePattern<quake::HOp> {
}
};

// Naive mapping of R1 to Rz, ignoring the global phase.
// This is only expected to work with full inlining and
// quake apply specialization.
struct R1ToRz : public OpRewritePattern<quake::R1Op> {
using OpRewritePattern::OpRewritePattern;
LogicalResult matchAndRewrite(quake::R1Op r1Op,
PatternRewriter &rewriter) const override {
if (!r1Op.getControls().empty())
return failure();

rewriter.replaceOpWithNewOp<quake::RzOp>(
r1Op, r1Op.isAdj(), r1Op.getParameters(), r1Op.getControls(),
r1Op.getTargets());
return success();
}
};

// quake.swap a, b
// ───────────────────────────────────
// quake.cnot b, a;
Expand Down Expand Up @@ -1003,6 +1020,7 @@ void cudaq::populateWithAllDecompositionPatterns(RewritePatternSet &patterns) {
// R1Op patterns
CR1ToCX,
R1ToPhasedRx,
R1ToRz,
// RxOp patterns
CRxToCX,
RxToPhasedRx,
Expand Down
89 changes: 89 additions & 0 deletions test/NVQPP/phase_estimation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright (c) 2022 - 2023 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

// RUN: nvq++ %s -o %basename_t.x --target quantinuum --emulate && CUDAQ_DUMP_JIT_IR=1 ./%basename_t.x &> %basename_t.txt && cat %basename_t.txt | FileCheck %s

#include <cudaq.h>
#include <iostream>

// A pure device quantum kernel defined as a free function
// (cannot be called from host code).
__qpu__ void iqft(cudaq::qspan<> q) {
int N = q.size();
// Swap qubits
for (int i = 0; i < N / 2; ++i) {
swap(q[i], q[N - i - 1]);
}

for (int i = 0; i < N - 1; ++i) {
h(q[i]);
int j = i + 1;
for (int y = i; y >= 0; --y) {
double denom = (1UL << (j - y));
const double theta = -M_PI / denom;
r1<cudaq::ctrl>(theta, q[j], q[y]);
}
}

h(q[N - 1]);
}

// CUDA Quantum kernel call operators can be templated on
// input CUDA Quantum kernel expressions. Here we define a general
// Phase Estimation algorithm that is generic on the eigenstate
// preparation and unitary evolution steps.
struct qpe {

// Define the CUDA Quantum call expression to take user-specified eigenstate
// and unitary evolution kernels, as well as the number of qubits in the
// counting register and in the eigenstate register.
template <typename StatePrep, typename Unitary>
void operator()(const int nCountingQubits, StatePrep &&state_prep,
Unitary &&oracle) __qpu__ {
// Allocate a register of qubits
cudaq::qreg q(nCountingQubits + 1);

// Extract sub-registers, one for the counting qubits
// another for the eigenstate register
auto counting_qubits = q.front(nCountingQubits);
auto &state_register = q.back();

// Prepare the eigenstate
state_prep(state_register);

// Put the counting register into uniform superposition
h(counting_qubits);

// Perform `ctrl-U^j`
for (int i = 0; i < nCountingQubits; ++i) {
for (int j = 0; j < (1 << i); ++j) {
cudaq::control(oracle, counting_qubits[i], state_register);
}
}

// Apply inverse quantum Fourier transform
iqft(counting_qubits);

// Measure to gather sampling statistics
mz(counting_qubits);

return;
}
};

struct r1PiGate {
void operator()(cudaq::qubit &q) __qpu__ { r1(1., q); }
};

int main() {
int nQubits = 2;
auto counts = cudaq::sample(
qpe{}, nQubits, [](cudaq::qubit &q) __qpu__ { x(q); }, r1PiGate{});
}

// CHECK-NOT: __quantum__qis__r1__body

0 comments on commit c8ac241

Please sign in to comment.