Commit ed13034e authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Ported nnizer transform to XACC



I realized that this was buried in an ExaTN-visitor only folder. This made it unusable for others such as ITensor (when users don't compile w/ ExaTN)
Signed-off-by: Nguyen, Thien Minh's avatarThien Nguyen <nguyentm@ornl.gov>
parent 1210455f
......@@ -13,7 +13,7 @@
set (LIBRARY_NAME xacc-circuit-optimizers)
file (GLOB_RECURSE HEADERS *.hpp)
file (GLOB SRC simple/*.cpp OptimizersActivator.cpp pulse/*.cpp gate_merge/*.cpp)
file (GLOB SRC simple/*.cpp OptimizersActivator.cpp pulse/*.cpp gate_merge/*.cpp lnn_transform/*.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 gate_merge ${CMAKE_SOURCE_DIR}/tpls/eigen ${CMAKE_SOURCE_DIR}/tpls/exprtk)
target_include_directories(${LIBRARY_NAME} PUBLIC simple pulse gate_merge lnn_transform ${CMAKE_SOURCE_DIR}/tpls/eigen ${CMAKE_SOURCE_DIR}/tpls/exprtk)
target_link_libraries(${LIBRARY_NAME} xacc xacc-quantum-gate)
if(APPLE)
......@@ -57,6 +57,7 @@ if(XACC_BUILD_TESTS)
add_subdirectory(simple/tests)
add_subdirectory(pulse/tests)
add_subdirectory(gate_merge/tests)
add_subdirectory(lnn_transform/tests)
endif()
add_subdirectory(qsearch)
\ No newline at end of file
......@@ -14,7 +14,8 @@
#include "default_placement.hpp"
#include "GateMergeOptimizer.hpp"
#include "PulseTransform.hpp"
#include "GateFusion.hpp"
#include "GateFusion.hpp"
#include "NearestNeighborTransform.hpp"
// #include "qsearch.hpp"
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
......@@ -28,40 +29,40 @@ namespace {
/**
*/
class US_ABI_LOCAL OptimizersActivator: public BundleActivator {
class US_ABI_LOCAL OptimizersActivator : public BundleActivator {
public:
OptimizersActivator() {}
OptimizersActivator() {
}
/**
*/
void Start(BundleContext context) {
auto c4 = std::make_shared<xacc::quantum::CircuitOptimizer>();
context.RegisterService<xacc::IRTransformation>(c4);
/**
*/
void Start(BundleContext context) {
auto c4 = std::make_shared<xacc::quantum::CircuitOptimizer>();
context.RegisterService<xacc::IRTransformation>(c4);
auto c5 = std::make_shared<xacc::quantum::DefaultPlacement>();
context.RegisterService<xacc::IRTransformation>(c5);
auto c5 = std::make_shared<xacc::quantum::DefaultPlacement>();
context.RegisterService<xacc::IRTransformation>(c5);
// auto c6 = std::make_shared<xacc::quantum::QsearchOptimizer>();
// context.RegisterService<xacc::IRTransformation>(c6);
context.RegisterService<xacc::IRTransformation>(c5);
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>());
context.RegisterService<xacc::IRTransformation>(
std::make_shared<xacc::quantum::NearestNeighborTransform>());
}
// auto c6 = std::make_shared<xacc::quantum::QsearchOptimizer>();
// context.RegisterService<xacc::IRTransformation>(c6);
context.RegisterService<xacc::IRTransformation>(c5);
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>());
}
/**
*/
void Stop(BundleContext /*context*/) {
}
/**
*/
void Stop(BundleContext /*context*/) {}
};
}
} // namespace
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(OptimizersActivator)
#include "NearestNeighborTransform.hpp"
#include "InstructionIterator.hpp"
namespace xacc {
namespace quantum {
void NearestNeighborTransform::apply(
std::shared_ptr<CompositeInstruction> in_program,
const std::shared_ptr<Accelerator> in_accelerator,
const HeterogeneousMap &in_options) {
// The option is qubit-distance (default is 1, adjacent qubits)
// any 2-qubit gates that are further than this distance are converted into
// Swap + Original Gate (at qubit distance) + Swap
// Strategy: meet in the middle:
// e.g. CNOT q0, q9; qubit-distance = 2 (max distance is next neighbor,
// e.g. CNOT q0, q2; CNOT q7, q9 are okay) then we will Swap q0->q4; q9->q6;
// then CNOT q4, q6; then Swap q4->q0 and Swap q6->q9
int maxDistance = 1;
if (in_options.keyExists<int>("max-distance")) {
maxDistance = in_options.get<int>("max-distance");
}
auto provider = xacc::getIRProvider("quantum");
auto flattenedProgram =
provider->createComposite(in_program->name() + "_Flattened");
InstructionIterator it(in_program);
while (it.hasNext()) {
auto nextInst = it.next();
if (nextInst->isEnabled() && !nextInst->isComposite()) {
flattenedProgram->addInstruction(nextInst->clone());
}
}
auto transformedProgram =
provider->createComposite(in_program->name() + "_Transformed");
for (int i = 0; i < flattenedProgram->nInstructions(); ++i) {
auto inst = flattenedProgram->getInstruction(i);
const auto exceedMaxDistance = [&maxDistance](int q1, int q2) -> bool {
return std::abs(q1 - q2) > maxDistance;
};
if (inst->bits().size() == 2 &&
exceedMaxDistance(inst->bits()[0], inst->bits()[1])) {
const int origLowerIdx = std::min({inst->bits()[0], inst->bits()[1]});
const int origUpperIdx = std::max({inst->bits()[0], inst->bits()[1]});
size_t lowerIdx = origLowerIdx;
size_t upperIdx = origUpperIdx;
// Insert swaps
for (;; /*Break inside*/) {
transformedProgram->addInstruction(
provider->createInstruction("Swap", {lowerIdx, lowerIdx + 1}));
lowerIdx++;
if (!exceedMaxDistance(lowerIdx, upperIdx)) {
break;
}
transformedProgram->addInstruction(
provider->createInstruction("Swap", {upperIdx, upperIdx - 1}));
upperIdx--;
if (!exceedMaxDistance(lowerIdx, upperIdx)) {
break;
}
}
// Run new gate
const bool bitCompare = inst->bits()[0] < inst->bits()[1];
inst->setBits(bitCompare ? std::vector<size_t>{lowerIdx, upperIdx}
: std::vector<size_t>{upperIdx, lowerIdx});
transformedProgram->addInstruction(inst);
// Insert swaps
for (size_t i = lowerIdx; i > origLowerIdx; --i) {
transformedProgram->addInstruction(
provider->createInstruction("Swap", {i, i - 1}));
}
for (size_t i = upperIdx; i < origUpperIdx; ++i) {
transformedProgram->addInstruction(
provider->createInstruction("Swap", {i, i + 1}));
}
} else {
transformedProgram->addInstruction(inst);
}
}
// DEBUG:
// std::cout << "After transform: \n" << transformedProgram->toString() <<
// "\n";
in_program->clear();
in_program->addInstructions(transformedProgram->getInstructions());
return;
}
} // namespace quantum
} // namespace xacc
#pragma once
#include "xacc.hpp"
#include "IRTransformation.hpp"
namespace xacc {
namespace quantum {
class NearestNeighborTransform : public IRTransformation {
public:
NearestNeighborTransform() {}
void apply(std::shared_ptr<CompositeInstruction> in_program,
const std::shared_ptr<Accelerator> in_accelerator,
const HeterogeneousMap &in_options = {}) override;
const IRTransformationType type() const override {
return IRTransformationType::Placement;
}
const std::string name() const override { return "nnizer"; }
const std::string description() const override { return ""; }
};
} // namespace quantum
} // namespace xacc
\ No newline at end of file
add_xacc_test(NearestNeighborTransform)
target_link_libraries(NearestNeighborTransformTester xacc-quantum-gate xacc-circuit-optimizers)
\ No newline at end of file
#include <memory>
#include <gtest/gtest.h>
#include "xacc.hpp"
#include "xacc_service.hpp"
namespace {
int countSwap(const std::shared_ptr<xacc::CompositeInstruction> in_program) {
int result = 0;
for (int i = 0; i < in_program->nInstructions(); ++i) {
auto inst = in_program->getInstruction(i);
if (inst->name() == "Swap") {
result++;
}
}
return result;
}
} // namespace
TEST(NearestNeighborTransformTester, checkSimple) {
auto c = xacc::getService<xacc::Compiler>("xasm");
auto f = c->compile(R"(__qpu__ void test1(qbit q) {
X(q[0]);
CNOT(q[1], q[3]);
H(q[7]);
CNOT(q[6], q[7]);
CNOT(q[7], q[3]);
X(q[6]);
Swap(q[4], q[9]);
S(q[5]);
})")
->getComposites()[0];
auto provider = xacc::getIRProvider("quantum");
auto ir = provider->createIR();
ir->addComposite(f);
auto opt = xacc::getService<xacc::IRTransformation>("nnizer");
const auto nInstBefore = f->nInstructions();
const auto nbSwapGateBefore = countSwap(f);
// No option: default max distance = 1
opt->apply(f, nullptr);
std::cout << "After LNN transform: \n" << f->toString() << "\n";
const auto nbSwapGateAfter = countSwap(f);
const auto nInstAfter = f->nInstructions();
const auto newSwapGateCount = nbSwapGateAfter - nbSwapGateBefore;
EXPECT_EQ(nInstAfter, nInstBefore + newSwapGateCount);
// Check that no 2-qubit gates have distance > 1
for (int i = 0; i < f->nInstructions(); ++i) {
auto inst = f->getInstruction(i);
if (inst->bits().size() == 2) {
const int distance = inst->bits()[0] - inst->bits()[1];
EXPECT_EQ(std::abs(distance), 1);
}
}
}
TEST(NearestNeighborTransformTester, checkDistanceConfig) {
auto c = xacc::getService<xacc::Compiler>("xasm");
auto f = c->compile(R"(__qpu__ void test2(qbit q) {
X(q[0]);
CNOT(q[1], q[9]);
H(q[7]);
CNOT(q[3], q[1]);
})")
->getComposites()[0];
auto provider = xacc::getIRProvider("quantum");
auto ir = provider->createIR();
ir->addComposite(f);
auto opt = xacc::getService<xacc::IRTransformation>("nnizer");
const auto nInstBefore = f->nInstructions();
const auto nbSwapGateBefore = countSwap(f);
// max distance = 2
opt->apply(f, nullptr, {std::make_pair("max-distance", 2)});
std::cout << "After LNN transform: \n" << f->toString() << "\n";
const auto nbSwapGateAfter = countSwap(f);
const auto nInstAfter = f->nInstructions();
const auto newSwapGateCount = nbSwapGateAfter - nbSwapGateBefore;
EXPECT_EQ(nInstAfter, nInstBefore + newSwapGateCount);
auto lastInst = f->getInstruction(nInstAfter - 1);
// The last instruction is CNOT(q[3], q[1])
// (distance = 2, within the limit, hence no conversion)
EXPECT_STREQ(lastInst->name().c_str(), "CNOT");
EXPECT_EQ(lastInst->bits()[0], 3);
EXPECT_EQ(lastInst->bits()[1], 1);
}
int main(int argc, char **argv) {
xacc::Initialize();
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
xacc::Finalize();
return ret;
}
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