Commit 1922986e authored by WrathfulSpatula's avatar WrathfulSpatula
Browse files

Qrack plugin: squash and sign-off for PR consideration


Signed-off-by: default avatarWrathfulSpatula <stranoj@gmail.com>
parent 9dcce3f1
......@@ -26,5 +26,12 @@ 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)
\ No newline at end of file
add_subdirectory(optimal_control)
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
${CMAKE_SOURCE_DIR}/tpls/eigen)
set(_bundle_name xacc_qrack)
find_package(OpenCL)
if (OpenCL_FOUND)
target_link_libraries(${LIBRARY_NAME}
PUBLIC xacc
xacc-quantum-gate
qrack
OpenCL
)
set_target_properties(${LIBRARY_NAME}
PROPERTIES COMPILE_DEFINITIONS
US_BUNDLE_NAME=${_bundle_name}
US_BUNDLE_NAME
${_bundle_name}
CL_HPP_TARGET_OPENCL_VERSION=200
CL_HPP_MINIMUM_OPENCL_VERSION=100)
else()
target_link_libraries(${LIBRARY_NAME}
PUBLIC xacc
xacc-quantum-gate
qrack
)
set_target_properties(${LIBRARY_NAME}
PROPERTIES COMPILE_DEFINITIONS
US_BUNDLE_NAME=${_bundle_name}
US_BUNDLE_NAME
${_bundle_name})
endif()
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.");
}
}
if (params.keyExists<bool>("use_opencl"))
{
m_use_opencl = params.get<bool>("shots");
}
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");
}
}
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 one
#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((Qrack::real1)angleTheta, rz.bits()[0]);
}
void QrackVisitor::visit(Ry& ry)
{
const auto angleTheta = InstructionParameterToDouble(ry.getParameter(0));
m_qReg->RY((Qrack::real1)angleTheta, ry.bits()[0]);
}
void QrackVisitor::visit(Rx& rx)
{
const auto angleTheta = InstructionParameterToDouble(rx.getParameter(0));
m_qReg->RX((Qrack::real1)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]);
}
void QrackVisitor::visit(Z& z)
{
m_qReg->Z(z.bits()[0]);
}
void QrackVisitor::visit(CY& cy)
{
m_qReg->CY(cy.bits()[0], cy.bits()[1]);
}
void QrackVisitor::visit(CZ& cz)
{
m_qReg->CZ(cz.bits()[0], cz.bits()[1]);
}
void QrackVisitor::visit(Swap& s)
{
m_qReg->Swap(s.bits()[0], s.bits()[1]);
}
void QrackVisitor::visit(CRZ& crz)
{
const auto angleTheta = InstructionParameterToDouble(crz.getParameter(0));
m_qReg->CRZ(angleTheta, crz.bits()[0], crz.bits()[1]);
}
void QrackVisitor::visit(CH& ch)
{
m_qReg->CH(ch.bits()[0], ch.bits()[1]);
}
void QrackVisitor::visit(S& s)
{
m_qReg->S(s.bits()[0]);
}
void QrackVisitor::visit(Sdg& sdg)
{
m_qReg->IS(sdg.bits()[0]);
}
void QrackVisitor::visit(T& t)
{
m_qReg->T(t.bits()[0]);
}
void QrackVisitor::visit(Tdg& tdg)
{
m_qReg->IT(tdg.bits()[0]);
}
void QrackVisitor::visit(CPhase& cphase)
{
m_qReg->CS(cphase.bits()[0], cphase.bits()[1]);
}
void QrackVisitor::visit(Identity& i)
{
// Intentionally left blank
}
void QrackVisitor::visit(U& u)
{
const auto theta = InstructionParameterToDouble(u.getParameter(0));
const auto phi = InstructionParameterToDouble(u.getParameter(1));
const auto lambda = InstructionParameterToDouble(u.getParameter(2));
m_qReg->U(u.bits()[0], (Qrack::real1)theta, (Qrack::real1)phi, (Qrack::real1)lambda);
}
void QrackVisitor::visit(iSwap& in_iSwapGate)
{
m_qReg->ISwap(in_iSwapGate.bits()[0], in_iSwapGate.bits()[1]);
}
void QrackVisitor::visit(fSim& in_fsimGate)
{
const auto theta = InstructionParameterToDouble(in_fsimGate.getParameter(0));
const auto phi = InstructionParameterToDouble(in_fsimGate.getParameter(1));
m_qReg->FSim(theta, phi, in_fsimGate.bits()[0], in_fsimGate.bits()[1]);
}
void QrackVisitor::visit(Measure& measure)
{
if (m_shotsMode || (m_shots < 1)) {
m_measureBits.push_back(measure.bits()[0]);
return;
}
bool result = m_qReg->M(measure.bits()[0]);
m_bitString.append(result ? "1" : "0");
// Add the measurement data to the acceleration buffer (e.g. for conditional execution branching)
m_buffer->measure(measure.bits()[0], result);
}
void QrackVisitor::visit(IfStmt& ifStmt)
{
ifStmt.expand({});
if (xacc::verbose)
{
std::cout << "If statement expanded to: " << ifStmt.toString() << "\n";
}
}
}}
/*******************************************************************************
* 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 one
#include "Identifiable.hpp"
#include "AllGateVisitor.hpp"
#include "AcceleratorBuffer.hpp"
#include "OptionsProvider.hpp"
#include "qrack/qfactory.hpp"
using namespace xacc;
namespace xacc {
namespace quantum {
class QrackVisitor : public AllGateVisitor, public OptionsProvider, public xacc::Cloneable<QrackVisitor> {
public:
void initialize(std::shared_ptr<AcceleratorBuffer> buffer, int shots, bool use_opencl, bool use_qunit, int device_id, bool doNormalize, double zero_threshold);
void finalize();