Commit 19ce40eb authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Merge branch 'xacc-devel' of https://github.com/eclipse/xacc into xacc-devel

parents 7f615c18 cb8b8305
Pipeline #102739 passed with stage
in 10 minutes and 33 seconds
......@@ -26,5 +26,13 @@ add_subdirectory(qpp)
if (STAQ_DIR)
add_subdirectory(staq)
endif()
find_library(QRACK_LIBRARY NAMES qrack)
if (QRACK_LIBRARY)
message("-- Found Qrack library (find_library(QRACK_LIBRARY NAMES qrack))")
add_subdirectory(qrack)
else()
message("-- Could NOT find Qrack library (missing: find_library(QRACK_LIBRARY NAMES qrack))")
endif()
add_subdirectory(optimal_control)
add_subdirectory(optimal_control)
\ No newline at end of file
......@@ -14,6 +14,32 @@
#include "QppVisitor.hpp"
#include "xacc.hpp"
namespace {
// Add gate matrix for iSwap and fSim gates
qpp::cmat iSwapGateMat()
{
qpp::cmat gateMat(4, 4);
gateMat << 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, std::complex<double>(0, 1.0), 0.0,
0.0, std::complex<double>(0, 1.0), 0.0, 0.0,
0.0, 0.0, 0.0, 1.0;
return gateMat;
}
qpp::cmat fSimGateMat(double in_theta, double in_phi)
{
qpp::cmat gateMat(4, 4);
gateMat <<
1.0, 0.0, 0.0, 0.0,
0.0, std::cos(in_theta), std::complex<double>(0, -std::sin(in_theta)), 0.0,
0.0, std::complex<double>(0, -std::sin(in_theta)), std::cos(in_theta), 0.0,
0.0, 0.0, 0.0, std::exp(std::complex<double>(0, -in_phi));
return gateMat;
}
}
namespace xacc {
namespace quantum {
void QppVisitor::initialize(std::shared_ptr<AcceleratorBuffer> buffer, bool shotsMode)
......@@ -203,6 +229,23 @@ namespace quantum {
m_stateVec = qpp::apply(m_stateVec, uMat, { qubitIdx });
}
void QppVisitor::visit(iSwap& in_iSwapGate)
{
const auto qIdx1 = xaccIdxToQppIdx(in_iSwapGate.bits()[0]);
const auto qIdx2 = xaccIdxToQppIdx(in_iSwapGate.bits()[1]);
m_stateVec = qpp::apply(m_stateVec, iSwapGateMat(), { qIdx1, qIdx2 });
}
void QppVisitor::visit(fSim& in_fsimGate)
{
const auto qIdx1 = xaccIdxToQppIdx(in_fsimGate.bits()[0]);
const auto qIdx2 = xaccIdxToQppIdx(in_fsimGate.bits()[1]);
const auto theta = InstructionParameterToDouble(in_fsimGate.getParameter(0));
const auto phi = InstructionParameterToDouble(in_fsimGate.getParameter(1));
m_stateVec = qpp::apply(m_stateVec, fSimGateMat(theta, phi), { qIdx1, qIdx2 });
}
void QppVisitor::visit(Measure& measure)
{
if (xacc::verbose)
......
......@@ -50,7 +50,9 @@ public:
void visit(Identity& i) override;
void visit(U& u) override;
void visit(IfStmt& ifStmt) override;
void visit(iSwap& in_iSwapGate) override;
void visit(fSim& in_fsimGate) override;
virtual std::shared_ptr<QppVisitor> clone() { return std::make_shared<QppVisitor>(); }
private:
......
......@@ -397,6 +397,62 @@ TEST(QppAcceleratorTester, testConditional)
EXPECT_EQ(resultCount, nbTests);
}
TEST(QppAcceleratorTester, testISwap)
{
// Get reference to the Accelerator
xacc::set_verbose(false);
const int nbShots = 100;
auto accelerator = xacc::getAccelerator("qpp", { std::make_pair("shots", nbShots) });
auto xasmCompiler = xacc::getCompiler("xasm");
auto ir = xasmCompiler->compile(R"(__qpu__ void testISwap(qbit q) {
X(q[0]);
iSwap(q[0], q[3]);
Measure(q[0]);
Measure(q[1]);
Measure(q[2]);
Measure(q[3]);
Measure(q[4]);
})", accelerator);
auto program = ir->getComposite("testISwap");
// Allocate some qubits (5)
auto buffer = xacc::qalloc(5);
accelerator->execute(buffer, program);
// 10000 => i00010 after iswap
buffer->print();
EXPECT_EQ(buffer->getMeasurementCounts()["00010"], nbShots);
}
TEST(QppAcceleratorTester, testFsim)
{
// Get reference to the Accelerator
const int nbShots = 1000;
auto accelerator = xacc::getAccelerator("qpp", { std::make_pair("shots", nbShots) });
auto xasmCompiler = xacc::getCompiler("xasm");
auto ir = xasmCompiler->compile(R"(__qpu__ void testFsim(qbit q, double x, double y) {
X(q[0]);
fSim(q[0], q[2], x, y);
Measure(q[0]);
Measure(q[2]);
})", accelerator);
auto program = ir->getComposites()[0];
const auto angles = xacc::linspace(-xacc::constants::pi, xacc::constants::pi, 10);
for (const auto& a : angles)
{
auto buffer = xacc::qalloc(3);
auto evaled = program->operator()({ a, 0.0 });
accelerator->execute(buffer, evaled);
const auto expectedProb = std::sin(a) * std::sin(a);
std::cout << "Angle = " << a << "\n";
buffer->print();
// fSim mixes 01 and 10 states w.r.t. the theta angle.
EXPECT_NEAR(buffer->computeMeasurementProbability("01"), expectedProb, 0.1);
EXPECT_NEAR(buffer->computeMeasurementProbability("10"), 1.0 - expectedProb, 0.1);
}
}
int main(int argc, char **argv) {
xacc::Initialize();
......
set(LIBRARY_NAME xacc-qrack)
file(GLOB SRC
*.cpp
accelerator/*.cpp)
usfunctiongetresourcesource(TARGET ${LIBRARY_NAME} OUT SRC)
usfunctiongeneratebundleinit(TARGET ${LIBRARY_NAME} OUT SRC)
add_library(${LIBRARY_NAME} SHARED ${SRC})
target_include_directories(${LIBRARY_NAME}
PUBLIC .
./accelerator
./accelerator/src/include)
find_package(OpenCL)
if (OpenCL_FOUND)
target_link_libraries(${LIBRARY_NAME}
PUBLIC xacc
xacc-quantum-gate
qrack
OpenCL
)
else()
target_link_libraries(${LIBRARY_NAME}
PUBLIC xacc
xacc-quantum-gate
qrack
)
endif()
set(_bundle_name xacc_qrack)
set_target_properties(${LIBRARY_NAME}
PROPERTIES COMPILE_DEFINITIONS
US_BUNDLE_NAME=${_bundle_name}
US_BUNDLE_NAME
${_bundle_name})
usfunctionembedresources(TARGET
${LIBRARY_NAME}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
FILES
manifest.json)
if(APPLE)
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "@loader_path/../lib")
set_target_properties(${LIBRARY_NAME}
PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else()
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")
set_target_properties(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-shared")
endif()
if(XACC_BUILD_TESTS)
add_subdirectory(tests)
endif()
install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins)
/*******************************************************************************
* Copyright (c) 2020 UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompanies this
* distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
*License is available at https://eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Thien Nguyen - initial API and implementation
* Daniel Strano - adaption from Quantum++ to Qrack
*******************************************************************************/
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/ServiceProperties.h"
#include "QrackAccelerator.hpp"
using namespace cppmicroservices;
class US_ABI_LOCAL QrackActivator : public BundleActivator {
public:
QrackActivator() {}
void Start(BundleContext context) {
auto acc = std::make_shared<xacc::quantum::QrackAccelerator>();
context.RegisterService<xacc::Accelerator>(acc);
}
void Stop(BundleContext context) {}
};
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(QrackActivator)
The base repository of the Qrack framework is available here:
https://github.com/vm6502q/qrack
See that repository's README and https://qrack.readthedocs.io/en/latest/ for detailed information about Qrack and getting started.
Qrack can be installed, to be made available for XACC use.
From the root of the Qrack repository directory, run these commands:
```
mkdir _build && cd _build && cmake .. && make all install
```
XACC looks for the Qrack library with "find_library(NAMES qrack)," so it will find it for example as "libqrack.a" in /usr/local/lib.
If your installation is a non-system-standard path, "CMAKE_PREFIX_PATH" should tell CMake where it can find the library.
/*******************************************************************************
* Copyright (c) 2019-2020 UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompanies this
* distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
*License is available at https://eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Thien Nguyen - initial API and implementation
* Daniel Strano - adaption from Quantum++ to Qrack
*******************************************************************************/
#include <typeinfo>
#include "QrackAccelerator.hpp"
namespace xacc {
namespace quantum {
void QrackAccelerator::initialize(const HeterogeneousMap& params)
{
m_visitor = std::make_shared<QrackVisitor>();
if (params.keyExists<int>("shots"))
{
m_shots = params.get<int>("shots");
if (m_shots < 1)
{
xacc::error("Invalid 'shots' parameter. (Must be >= 1.)");
}
}
if (params.keyExists<bool>("use_opencl"))
{
m_use_opencl = params.get<bool>("use_opencl");
}
if (params.keyExists<bool>("use_qunit"))
{
m_use_qunit = params.get<bool>("use_qunit");
}
if (params.keyExists<int>("device_id"))
{
m_device_id = params.get<int>("device_id");
}
if (params.keyExists<bool>("do_normalize"))
{
m_do_normalize = params.get<bool>("do_normalize");
}
if (params.keyExists<double>("zero_threshold"))
{
m_zero_threshold = params.get<double>("zero_threshold");
if (m_zero_threshold < 0)
{
xacc::error("Invalid 'zero_threshold' parameter. (Must be >= 0.)");
}
}
}
void QrackAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::shared_ptr<CompositeInstruction> compositeInstruction)
{
bool canSample = true;
if (m_shots > 1) {
bool didMeasure = false;
InstructionIterator shotsIt(compositeInstruction);
while (shotsIt.hasNext())
{
auto nextInst = shotsIt.next();
if (!nextInst->isEnabled()) {
continue;
}
if (nextInst->name() == "Identity") {
continue;
}
if (nextInst->name() == "ifstmt") {
canSample = false;
break;
}
if (nextInst->name() == "Measure")
{
didMeasure = true;
}
else if (didMeasure)
{
canSample = false;
break;
}
}
if (!canSample && xacc::verbose)
{
std::cout << "Cannot sample; must repeat circuit per shot. If possible, consider removing conditionals, running 1 shot, and/or only measuring at the end of the circuit." << std::endl;
}
}
const auto runCircuit = [&](int shots){
m_visitor->initialize(buffer, shots, m_use_opencl, m_use_qunit, m_device_id, m_do_normalize, m_zero_threshold);
// Walk the IR tree, and visit each node
InstructionIterator it(compositeInstruction);
while (it.hasNext())
{
auto nextInst = it.next();
if (nextInst->isEnabled())
{
nextInst->accept(m_visitor);
}
}
m_visitor->finalize();
};
if (canSample || (m_shots < 1))
{
runCircuit(m_shots);
}
else
{
for (int i = 0; i < m_shots; ++i)
{
runCircuit(1);
}
}
}
void QrackAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::vector<std::shared_ptr<CompositeInstruction>> compositeInstructions)
{
for (auto& f : compositeInstructions)
{
auto tmpBuffer = std::make_shared<xacc::AcceleratorBuffer>(f->name(), buffer->size());
execute(tmpBuffer, f);
buffer->appendChild(f->name(), tmpBuffer);
}
}
}}
/*******************************************************************************
* Copyright (c) 2019-2020 UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompanies this
* distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
*License is available at https://eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Thien Nguyen - initial API and implementation
* Daniel Strano - adaption from Quantum++ to Qrack
*******************************************************************************/
#pragma once
#include "xacc.hpp"
#include "QrackVisitor.hpp"
namespace xacc {
namespace quantum {
class QrackAccelerator : public Accelerator {
public:
// Identifiable interface impls
virtual const std::string name() const override { return "qrack"; }
virtual const std::string description() const override { return "XACC Simulation Accelerator based on Qrack library."; }
// Accelerator interface impls
virtual void initialize(const HeterogeneousMap& params = {}) override;
virtual void updateConfiguration(const HeterogeneousMap& config) override {initialize(config);};
virtual const std::vector<std::string> configurationKeys() override { return {}; }
virtual BitOrder getBitOrder() override {return BitOrder::LSB;}
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::shared_ptr<CompositeInstruction> compositeInstruction) override;
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer, const std::vector<std::shared_ptr<CompositeInstruction>> compositeInstructions) override;
private:
std::shared_ptr<QrackVisitor> m_visitor;
int m_shots = -1;
bool m_use_opencl = true;
bool m_use_qunit = true;
int m_device_id = -1;
bool m_do_normalize = true;
double m_zero_threshold = min_norm;
};
}}
/*******************************************************************************
* Copyright (c) 2019-2020 UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompanies this
* distribution. The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
*License is available at https://eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Thien Nguyen - initial API and implementation
* Daniel Strano - adaption from Quantum++ to Qrack
*******************************************************************************/
#include "QrackVisitor.hpp"
#include "xacc.hpp"
#define MAKE_ENGINE(num_qubits, perm) Qrack::CreateQuantumInterface(qIType1, qIType2, num_qubits, perm, nullptr, Qrack::CMPLX_DEFAULT_ARG, doNormalize, false, false, device_id, true, zero_threshold)
namespace xacc {
namespace quantum {
void QrackVisitor::initialize(std::shared_ptr<AcceleratorBuffer> buffer, int shots, bool use_opencl, bool use_qunit, int device_id, bool doNormalize, double zero_threshold)
{
m_buffer = std::move(buffer);
m_measureBits.clear();
m_shots = shots;
m_shotsMode = shots > 1;
Qrack::QInterfaceEngine qIType2 = use_opencl ? Qrack::QINTERFACE_OPTIMAL : Qrack::QINTERFACE_CPU;
Qrack::QInterfaceEngine qIType1 = use_qunit ? Qrack::QINTERFACE_QUNIT : qIType2;
m_qReg = MAKE_ENGINE(m_buffer->size(), 0);
}
double QrackVisitor::calcExpectationValueZ() const
{
const auto hasEvenParity = [](size_t x, const std::vector<bitLenInt>& in_qubitIndices) -> bool {
size_t count = 0;
for (const auto& bitIdx : in_qubitIndices)
{
if (x & (1ULL << bitIdx))
{
count++;
}
}
return (count & 1) == 0;
};
std::vector<Qrack::complex> wfn((bitCapIntOcl)m_qReg->GetMaxQPower());
m_qReg->GetQuantumState(&(wfn[0]));
double result = 0.0;
for(uint64_t i = 0; i < wfn.size(); ++i)
{
result += (hasEvenParity(i, m_measureBits) ? 1.0 : -1.0) * std::norm(wfn[i]);
}
return result;
}
std::map<bitCapInt, int> QrackVisitor::measure_shots() {
bitLenInt bitCount = m_measureBits.size();
bitCapInt* qPowers = new bitCapInt[bitCount];
for (bitLenInt i = 0; i < bitCount; i++) {
qPowers[i] = Qrack::pow2(m_measureBits[i]);
}
std::map<bitCapInt, int> result = m_qReg->MultiShotMeasureMask(qPowers, bitCount, m_shots);
delete[] qPowers;
return result;
}
void QrackVisitor::finalize()
{
if (m_shots < 0)
{
const double expectedValueZ = calcExpectationValueZ();
m_buffer->addExtraInfo("exp-val-z", expectedValueZ);
m_measureBits.clear();
return;
}
if (!m_shotsMode)
{
m_buffer->appendMeasurement(m_bitString);
m_bitString.clear();
return;
}
auto measureDist = measure_shots();
for (auto it = measureDist.begin(); it != measureDist.end(); it++) {
std::string bitString = "";
for (int i = 0; i < m_measureBits.size(); i++) {
bitString.append((Qrack::pow2(i) & it->first) ? "1" : "0");
}
for (int j = 0; j < it->second; j++) {
m_buffer->appendMeasurement(bitString);
}
}
m_measureBits.clear();
}
void QrackVisitor::visit(Hadamard& h)
{
m_qReg->H(h.bits()[0]);
}
void QrackVisitor::visit(CNOT& cnot)
{
m_qReg->CNOT(cnot.bits()[0], cnot.bits()[1]);
}
void QrackVisitor::visit(Rz& rz)
{
const auto angleTheta = InstructionParameterToDouble(rz.getParameter(0));
m_qReg->RZ(angleTheta, rz.bits()[0]);
}
void QrackVisitor::visit(Ry& ry)
{
const auto angleTheta = InstructionParameterToDouble(ry.getParameter(0));
m_qReg->RY(angleTheta, ry.bits()[0]);
}
void QrackVisitor::visit(Rx& rx)
{
const auto angleTheta = InstructionParameterToDouble(rx.getParameter(0));
m_qReg->RX(angleTheta, rx.bits()[0]);
}
void QrackVisitor::visit(X& x)
{
m_qReg->X(x.bits()[0]);
}
void QrackVisitor::visit(Y& y)
{
m_qReg->Y(y.bits()[0]);
}