Unverified Commit ff8f41de authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub

Merge pull request #237 from tnguyen-ornl/tnguyen/circuit-opt

Implements gate merging optimization (#227) 
parents cf7ca388 ce9f736a
Pipeline #113027 passed with stage
in 68 minutes and 24 seconds
......@@ -372,7 +372,7 @@ public:
CZ(std::size_t srcqbit, std::size_t tgtqbit)
: CZ(std::vector<std::size_t>{srcqbit, tgtqbit}) {}
const int nRequiredBits() const override { return 1; }
const int nRequiredBits() const override { return 2; }
DEFINE_CLONE(CZ)
DEFINE_VISITABLE()
......
......@@ -44,6 +44,7 @@ public:
auto aswap = std::make_shared<xacc::circuits::ASWAP>();
auto qfast = std::make_shared<xacc::circuits::QFAST>();
auto kak = std::make_shared<xacc::circuits::KAK>();
auto zyz = std::make_shared<xacc::circuits::ZYZ>();
context.RegisterService<xacc::Instruction>(hwe);
context.RegisterService<xacc::Instruction>(expit);
......@@ -56,6 +57,7 @@ public:
context.RegisterService<xacc::Instruction>(aswap);
context.RegisterService<xacc::Instruction>(qfast);
context.RegisterService<xacc::Instruction>(kak);
context.RegisterService<xacc::Instruction>(zyz);
}
void Stop(BundleContext context) {}
......
This diff is collapsed.
......@@ -6,6 +6,18 @@
namespace xacc {
namespace circuits {
// Use Z-Y-Z decomposition of Nielsen and Chuang (Theorem 4.1).
// An arbitrary one qubit gate matrix can be writen as
// U = exp(j*a) Rz(b) Ry(c) Rz(d).
class ZYZ : public xacc::quantum::Circuit
{
public:
ZYZ() : Circuit("z-y-z") {}
bool expand(const xacc::HeterogeneousMap &runtimeOptions) override;
const std::vector<std::string> requiredKeys() override { return { "unitary" }; }
DEFINE_CLONE(ZYZ);
};
// KAK decomposition via *Magic* Bell basis transformation
// Reference:
// https://arxiv.org/pdf/quant-ph/0211002.pdf
......
......@@ -13,7 +13,7 @@
set (LIBRARY_NAME xacc-circuit-optimizers)
file (GLOB_RECURSE HEADERS *.hpp)
file (GLOB SRC simple/*.cpp OptimizersActivator.cpp pulse/*.cpp)
file (GLOB SRC simple/*.cpp OptimizersActivator.cpp pulse/*.cpp gate_merge/*.cpp)
# Set up dependencies to resources to track changes
usFunctionGetResourceSource(TARGET ${LIBRARY_NAME} OUT SRC)
......@@ -39,7 +39,7 @@ usFunctionEmbedResources(TARGET ${LIBRARY_NAME}
manifest.json
)
target_include_directories(${LIBRARY_NAME} PUBLIC simple pulse ${CMAKE_SOURCE_DIR}/tpls/eigen ${CMAKE_SOURCE_DIR}/tpls/exprtk)
target_include_directories(${LIBRARY_NAME} PUBLIC simple pulse gate_merge ${CMAKE_SOURCE_DIR}/tpls/eigen ${CMAKE_SOURCE_DIR}/tpls/exprtk)
target_link_libraries(${LIBRARY_NAME} xacc xacc-quantum-gate)
if(APPLE)
......@@ -56,5 +56,6 @@ install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins)
if(XACC_BUILD_TESTS)
add_subdirectory(simple/tests)
add_subdirectory(pulse/tests)
add_subdirectory(gate_merge/tests)
endif()
......@@ -12,12 +12,12 @@
*******************************************************************************/
#include "CircuitOptimizer.hpp"
#include "default_placement.hpp"
#include "GateMergeOptimizer.hpp"
#include "PulseTransform.hpp"
#include "GateFusion.hpp"
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/ServiceProperties.h"
#include <memory>
#include <set>
......@@ -45,6 +45,8 @@ public:
context.RegisterService<xacc::IRTransformation>(std::make_shared<xacc::quantum::PulseTransform>());
context.RegisterService<GateFuser>(std::make_shared<GateFuser>());
context.RegisterService<xacc::IRTransformation>(std::make_shared<xacc::quantum::MergeSingleQubitGatesOptimizer>());
context.RegisterService<xacc::IRTransformation>(std::make_shared<xacc::quantum::MergeTwoQubitBlockOptimizer>());
}
/**
......
This diff is collapsed.
#pragma once
#include "IRTransformation.hpp"
namespace xacc {
namespace quantum {
// Merge contiguous sequence of gates (1- or 2-qubit)
// into a more efficient gate sequence.
class MergeSingleQubitGatesOptimizer : public IRTransformation
{
public:
virtual void apply(std::shared_ptr<CompositeInstruction> program,
const std::shared_ptr<Accelerator> accelerator,
const HeterogeneousMap &options = {}) override;
virtual const IRTransformationType type() const override { return IRTransformationType::Optimization; }
const std::string name() const override { return "single-qubit-gate-merging"; }
const std::string description() const override { return ""; }
private:
std::vector<size_t> findSingleQubitGateSequence(const std::shared_ptr<CompositeInstruction> in_program, size_t in_startIdx, size_t in_lengthLimit) const;
};
// Try to merge a block containing single and two-qubit gates
// and decompose it into a more optimized representation only if *possible*.
// e.g. only if the original block has more gates than its re-synthesized version.
// We could also define a custom target for re-write
// such as reducing the number of two-qubit gates in the block
// rather than the overall gate count.
class MergeTwoQubitBlockOptimizer : public IRTransformation
{
public:
virtual void apply(std::shared_ptr<CompositeInstruction> program,
const std::shared_ptr<Accelerator> accelerator,
const HeterogeneousMap &options = {}) override;
virtual const IRTransformationType type() const override { return IRTransformationType::Optimization; }
const std::string name() const override { return "two-qubit-block-merging"; }
const std::string description() const override { return ""; }
private:
// Finds the sequence of gates (from the start index) which forms a two-qubit block (with no connections to outside the block)
std::vector<size_t> findGateSequence(const std::shared_ptr<CompositeInstruction> in_program, size_t in_startIdx, size_t in_lengthLimit, std::pair<size_t, size_t>& out_qubitPair) const;
};
}
}
\ No newline at end of file
add_xacc_test(GateMerging)
target_link_libraries(GateMergingTester xacc-quantum-gate xacc-circuit-optimizers)
\ No newline at end of file
#include <gtest/gtest.h>
#include "xacc.hpp"
#include "xacc_service.hpp"
#include "IRTransformation.hpp"
#include "GateFusion.hpp"
TEST(GateMergingTester, checkSingleQubitSimple)
{
auto c = xacc::getService<xacc::Compiler>("xasm");
// This is identity: H-X-H is equal to Z => Z-Z = I
auto f = c->compile(R"(__qpu__ void test1(qbit q) {
Z(q[0]);
H(q[0]);
X(q[0]);
H(q[0]);
})")->getComposites()[0];
auto opt = xacc::getService<xacc::IRTransformation>("single-qubit-gate-merging");
opt->apply(f, nullptr);
// No instruction after optimization
EXPECT_EQ(f->nInstructions(), 0);
}
TEST(GateMergingTester, checkSingleQubitStopAtTwoQubitGate)
{
auto c = xacc::getService<xacc::Compiler>("xasm");
auto f = c->compile(R"(__qpu__ void test2(qbit q) {
H(q[0]);
X(q[0]);
H(q[0]);
CNOT(q[0], q[1]);
H(q[0]);
})")->getComposites()[0];
auto opt = xacc::getService<xacc::IRTransformation>("single-qubit-gate-merging");
opt->apply(f, nullptr);
// Becomes: Z (Rz(pi)) - CNOT - H
EXPECT_EQ(f->nInstructions(), 3);
EXPECT_EQ(f->getInstruction(0)->name(), "Rz");
EXPECT_NEAR(f->getInstruction(0)->getParameter(0).as<double>(), M_PI, 1e-6);
EXPECT_EQ(f->getInstruction(1)->name(), "CNOT");
EXPECT_EQ(f->getInstruction(2)->name(), "H");
}
TEST(GateMergingTester, checkMixing)
{
auto c = xacc::getService<xacc::Compiler>("xasm");
auto f = c->compile(R"(__qpu__ void test2(qbit q) {
H(q[1]);
CNOT(q[1], q[0]);
H(q[0]);
H(q[1]);
X(q[0]);
// Not involved
CNOT(q[2], q[3]);
X(q[1]);
H(q[0]);
H(q[1]);
CNOT(q[0], q[2]);
Z(q[1]);
})")->getComposites()[0];
auto opt = xacc::getService<xacc::IRTransformation>("single-qubit-gate-merging");
opt->apply(f, nullptr);
std::cout << "HOWDY:\n" << f->toString() << "\n";
EXPECT_EQ(f->nInstructions(), 5);
}
TEST(GateMergingTester, checkTwoQubitSimple)
{
auto c = xacc::getService<xacc::Compiler>("xasm");
auto f = c->compile(R"(__qpu__ void test3(qbit q) {
H(q[0]);
H(q[1]);
Rz(q[0], 1.234);
Y(q[1]);
T(q[0]);
Z(q[1]);
H(q[0]);
H(q[1]);
S(q[0]);
H(q[1]);
X(q[0]);
Z(q[1]);
CNOT(q[1], q[0]);
H(q[0]);
H(q[1]);
CPhase(q[0], q[1], 1.123);
H(q[0]);
T(q[1]);
X(q[0]);
CNOT(q[0], q[1]);
T(q[0]);
//H(q[2]);
Rx(q[0], 1.234);
Ry(q[1], -2.456);
H(q[1]);
Rz(q[0], 3.124);
H(q[0]);
H(q[1]);
Y(q[0]);
H(q[0]);
H(q[1]);
T(q[0]);
X(q[1]);
})")->getComposites()[0];
auto opt = xacc::getService<xacc::IRTransformation>("two-qubit-block-merging");
const auto nbInstBefore = f->nInstructions();
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto circuitCopy = gateRegistry->createComposite("__COPY__");
for (size_t i = 0; i < f->nInstructions(); ++i)
{
circuitCopy->addInstruction(f->getInstruction(i)->clone());
}
opt->apply(f, nullptr);
std::cout << "HOWDY:\n" << f->toString() << "\n";
const auto nbInstAfter = f->nInstructions();
EXPECT_TRUE(nbInstAfter < nbInstBefore);
// Validate using gate fusion:
auto fuser = xacc::getService<xacc::quantum::GateFuser>("default");
fuser->initialize(circuitCopy);
const Eigen::MatrixXcd uMatOriginal = fuser->calcFusedGate(2);
fuser->initialize(f);
const Eigen::MatrixXcd uMatAfter = fuser->calcFusedGate(2);
// Compensate any global phase differences.
// Find index of the largest element:
size_t colIdx = 0;
size_t rowIdx = 0;
double maxVal = std::abs(uMatAfter(0,0));
for (size_t i = 0; i < uMatAfter.rows(); ++i)
{
for (size_t j = 0; j < uMatAfter.cols(); ++j)
{
if (std::abs(uMatAfter(i,j)) > maxVal)
{
maxVal = std::abs(uMatAfter(i,j));
colIdx = j;
rowIdx = i;
}
}
}
const std::complex<double> globalFactor = uMatOriginal(rowIdx, colIdx) / uMatAfter(rowIdx, colIdx);
auto uMatAfterFixedPhase = globalFactor * uMatAfter;
for (size_t i = 0; i < uMatAfter.rows(); ++i)
{
for (size_t j = 0; j < uMatAfter.cols(); ++j)
{
EXPECT_NEAR(uMatAfterFixedPhase(i, j).real(), uMatOriginal(i, j).real(), 1e-9);
EXPECT_NEAR(uMatAfterFixedPhase(i, j).imag(), uMatOriginal(i, j).imag(), 1e-9);
}
}
}
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
xacc::Finalize();
return ret;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment