Commit 872b8afc authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

adding observable and paulioperator


Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent ad2724ec
Pipeline #44140 passed with stages
in 9 minutes and 23 seconds
......@@ -70,9 +70,19 @@ endif()
message(STATUS "${BoldGreen}Installing XACC to ${CMAKE_INSTALL_PREFIX}. Override with -DCMAKE_INSTALL_PREFIX=...${ColorReset}")
#if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0.0")
# set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wimplicit-fallthrough=0")
#endif()
if (XACC_BUILD_TESTS)
enable_testing()
macro(add_xacc_test _TEST_NAME)
add_executable(${_TEST_NAME}Tester ${_TEST_NAME}Tester.cpp)
target_include_directories(${_TEST_NAME}Tester PRIVATE ${GTEST_INCLUDE_DIRS})
add_test(NAME xacc_${_TEST_NAME}Tester COMMAND ${_TEST_NAME}Tester)
target_link_libraries(${_TEST_NAME}Tester ${GTEST_LIBRARIES} xacc)
endmacro()
macro(set_cache_variable VAR_NAME VAR_DESCRIPTION)
set(${VAR_NAME} ${${VAR_NAME}} CACHE INTERNAL ${VAR_DESCRIPTION})
message(STATUS "Set ${VAR_NAME} to ${${VAR_NAME}}.")
endmacro()
endif()
include_directories(${CMAKE_BINARY_DIR}/tpls/cppmicroservices/include)
add_subdirectory(tpls)
......@@ -90,9 +100,10 @@ endif()
# and CMake loads include paths, libs, etc
configure_file("${CMAKE_SOURCE_DIR}/cmake/xacc-config.cmake.in" "${CMAKE_BINARY_DIR}/xacc-config.cmake" @ONLY)
install(FILES "${CMAKE_BINARY_DIR}/xacc-config.cmake" DESTINATION .)
install(FILES "${CMAKE_SOURCE_DIR}/cmake/Modules/tests.cmake" DESTINATION share/xacc/)
install(FILES "${CMAKE_SOURCE_DIR}/cmake/Modules/format.cmake" DESTINATION share/xacc/)
install(FILES "${CMAKE_SOURCE_DIR}/tpls/mpark-variant/variant.hpp" DESTINATION include/xacc/)
install(FILES "${CMAKE_SOURCE_DIR}/tpls/taocpp/operators.hpp" DESTINATION include/xacc/)
install( DIRECTORY "${CMAKE_SOURCE_DIR}/tpls/spdlog" DESTINATION include )
install( DIRECTORY "${CMAKE_SOURCE_DIR}/tpls/exprtk" DESTINATION include )
install( DIRECTORY "${CMAKE_SOURCE_DIR}/tpls/eigen" DESTINATION include )
......
......@@ -6,24 +6,28 @@
# XACC_INCLUDE_DIRS - include directories for XACC
# XACC_LIBRARIES - libraries to link against
# XACC_LIBRARY_DIR - the XACC library directory
include(CTest)
if (NOT XACC_ROOT)
get_filename_component(XACC_ROOT "${CMAKE_CURRENT_LIST_FILE}" PATH)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${XACC_ROOT}/share/xacc)
if (NOT TARGET format)
include(format)
endif()
set (XACC_LIBRARY_DIR "${XACC_ROOT}/lib")
link_directories("${XACC_ROOT}/lib")
set(XACC_INCLUDE_ROOT "${XACC_ROOT}/include")
set(XACC_HAS_ANTLR @XACC_HAS_ANTLR@)
set (XACC_INCLUDE_DIRS "${XACC_INCLUDE_ROOT}/antlr4-runtime;${XACC_INCLUDE_ROOT}/cppmicroservices4;${XACC_INCLUDE_ROOT}/xacc;${XACC_INCLUDE_ROOT}/quantum/gate;${XACC_INCLUDE_ROOT}/quantum/utils;${XACC_INCLUDE_ROOT}/quantum/aqc;${XACC_INCLUDE_ROOT}/spdlog;${XACC_INCLUDE_ROOT}/exprtk;${XACC_INCLUDE_ROOT}/eigen;${XACC_INCLUDE_ROOT}/rapidjson/include;${XACC_INCLUDE_ROOT};${XACC_INCLUDE_ROOT}/gtest")
set (XACC_LIBRARIES "xacc;xacc-quantum-gate;xacc-quantum-aqc;antlr4-runtime;CppMicroServices")
set (XACC_TEST_LIBRARIES "${XACC_LIBRARIES};gtest;gtest_main")
set(BOOST_ROOT ${XACC_ROOT})
macro(add_xacc_test _TEST_NAME)
add_executable(${_TEST_NAME}Tester ${_TEST_NAME}Tester.cpp)
add_test(NAME xacc_${_TEST_NAME}Tester COMMAND ${_TEST_NAME}Tester)
......
......@@ -12,6 +12,7 @@ include_directories(${CMAKE_SOURCE_DIR}/tpls/rapidjson/include)
include_directories(${CMAKE_SOURCE_DIR}/tpls/exprtk)
include_directories(${CMAKE_SOURCE_DIR}/tpls/spdlog)
include_directories(${CMAKE_SOURCE_DIR}/tpls/eigen)
include_directories(${CMAKE_SOURCE_DIR}/tpls/taocpp)
include_directories(${CMAKE_SOURCE_DIR}/quantum/utils)
include_directories(${CMAKE_SOURCE_DIR}/quantum/aqc/ir)
......@@ -35,7 +36,7 @@ add_library(_pyxacc SHARED xacc-py.cpp)
set_target_properties(_pyxacc PROPERTIES PREFIX "")
target_link_libraries(_pyxacc PUBLIC CppMicroServices xacc Boost::graph)
target_link_libraries(_pyxacc PUBLIC CppMicroServices xacc xacc-quantum-gate Boost::graph)
if(APPLE)
set_target_properties(_pyxacc PROPERTIES INSTALL_RPATH "@loader_path/lib")
......
......@@ -19,6 +19,7 @@
#include "InstructionParameter.hpp"
#include "DWGraph.hpp"
#include "EmbeddingAlgorithm.hpp"
#include "PauliOperator.hpp"
#include <pybind11/complex.h>
#include <pybind11/numpy.h>
......@@ -30,6 +31,7 @@
namespace py = pybind11;
using namespace xacc;
using namespace xacc::quantum;
// `boost::variant` as an example -- can be any `std::variant`-like container
namespace pybind11 {
......@@ -408,6 +410,45 @@ PYBIND11_MODULE(_pyxacc, m) {
"Translate the given IR Function instance to source code in this "
"Compiler's language.");
py::class_<Term>(m, "Term").def("coeff", &Term::coeff).def("ops",&Term::ops);
py::class_<PauliOperator>(m, "PauliOperator")
.def(py::init<>())
.def(py::init<std::complex<double>>())
.def(py::init<double>())
.def(py::init<std::string>())
.def(py::init<std::map<int, std::string>>())
.def(py::init<std::map<int, std::string>, double>())
.def(py::init<std::map<int, std::string>, std::complex<double>>())
.def(py::init<std::map<int, std::string>, std::string>())
.def(py::init<std::map<int, std::string>, std::complex<double>,
std::string>())
.def(py::self + py::self)
.def(py::self += py::self)
.def(py::self *= py::self)
.def(py::self *= double())
.def(py::self * py::self)
.def(py::self *= std::complex<double>())
.def(py::self -= py::self)
.def(py::self - py::self)
.def("__eq__", &PauliOperator::operator==)
.def("__repr__", &PauliOperator::toString)
.def("eval", &PauliOperator::eval)
.def("toBinaryVectors", &PauliOperator::toBinaryVectors)
.def("toXACCIR", &PauliOperator::toXACCIR)
.def("fromXACCIR", &PauliOperator::fromXACCIR)
.def("fromString", &PauliOperator::fromString)
.def("nTerms", &PauliOperator::nTerms)
.def("isClose", &PauliOperator::isClose)
.def("commutes", &PauliOperator::commutes)
.def("__len__", &PauliOperator::nTerms)
.def("nQubits", &PauliOperator::nQubits)
.def("computeActionOnKet", &PauliOperator::computeActionOnKet)
.def("computeActionOnBra", &PauliOperator::computeActionOnBra)
.def("__iter__",
[](PauliOperator &op) {
return py::make_iterator(op.begin(), op.end());
},
py::keep_alive<0, 1>());
// Expose the Program object
py::class_<xacc::Program>(
m, "Program",
......@@ -527,7 +568,7 @@ PYBIND11_MODULE(_pyxacc, m) {
m.def("getCache", &xacc::getCache, "");
m.def(
"appendCache",
(void (*)(const std::string, const std::string, InstructionParameter &)) &
(void (*)(const std::string, const std::string, InstructionParameter &,const std::string)) &
xacc::appendCache,
"");
m.def("Finalize", &xacc::Finalize, "Finalize the framework");
......
......@@ -42,8 +42,10 @@ target_include_directories(${LIBRARY_NAME}
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/quantum/utils
${CMAKE_SOURCE_DIR}/tpls/exprtk
${CMAKE_SOURCE_DIR}/tpls/rapidjson/include)
${CMAKE_SOURCE_DIR}/tpls/rapidjson/include
${CMAKE_SOURCE_DIR}/tpls/taocpp
${CMAKE_SOURCE_DIR}/tpls/eigen)
set(_bundle_name xacc_quantum_gate)
set_target_properties(${LIBRARY_NAME}
PROPERTIES # This is required for every bundle
......
......@@ -53,10 +53,14 @@ auto splitParameter = [](InstructionParameter instParam) {
std::vector<std::string> split;
InstructionParameter rawParam;
auto paramStr = mpark::get<std::string>(instParam);
std::replace(mpark::get<std::string>(instParam).begin(), mpark::get<std::string>(instParam).end(), '-', ' ');
std::replace(mpark::get<std::string>(instParam).begin(), mpark::get<std::string>(instParam).end(), '+', ' ');
std::replace(mpark::get<std::string>(instParam).begin(), mpark::get<std::string>(instParam).end(), '*', ' ');
std::replace(mpark::get<std::string>(instParam).begin(), mpark::get<std::string>(instParam).end(), '/', ' ');
std::replace(mpark::get<std::string>(instParam).begin(),
mpark::get<std::string>(instParam).end(), '-', ' ');
std::replace(mpark::get<std::string>(instParam).begin(),
mpark::get<std::string>(instParam).end(), '+', ' ');
std::replace(mpark::get<std::string>(instParam).begin(),
mpark::get<std::string>(instParam).end(), '*', ' ');
std::replace(mpark::get<std::string>(instParam).begin(),
mpark::get<std::string>(instParam).end(), '/', ' ');
auto instParamStr = instParam.as<std::string>();
split = xacc::split(instParamStr, ' ');
......@@ -68,6 +72,37 @@ auto splitParameter = [](InstructionParameter instParam) {
return rawParam;
};
const int GateFunction::nLogicalBits() {
std::set<int> local_bits;
xacc::InstructionIterator it(shared_from_this());
while (it.hasNext()) {
auto nextInst = it.next();
if (nextInst->isEnabled()) {
for (auto &i : nextInst->bits()) {
local_bits.insert(i);
}
}
}
return local_bits.size();
}
const int GateFunction::nPhysicalBits() {
int maxBitIdx = 0;
xacc::InstructionIterator it(shared_from_this());
while (it.hasNext()) {
auto nextInst = it.next();
if (nextInst->isEnabled()) {
for (auto &i : nextInst->bits()) {
if (maxBitIdx < i) {
maxBitIdx = i;
}
}
}
}
maxBitIdx++;
return maxBitIdx;
}
void GateFunction::removeInstruction(const int idx) {
auto instruction = getInstruction(idx);
// Check to see if instruction being removed is parameterized
......@@ -211,7 +246,7 @@ void GateFunction::replaceInstruction(const int idx, InstPtr replacingInst) {
void GateFunction::insertInstruction(const int idx, InstPtr newInst) {
// Check if new GateInstruction is parameterized with 1 parameter
if (newInst->isParameterized() && newInst->nParameters() <= 1) {
if (newInst->isParameterized() && !newInst->isComposite()) {
xacc::InstructionParameter param = newInst->getParameter(0);
// Check if new parameter is a string
if (param.isVariable()) {
......@@ -286,8 +321,8 @@ const std::string GateFunction::toString(const std::string &bufferVarName) {
return retStr;
}
std::shared_ptr<Function> GateFunction::
operator()(const std::vector<double> &params) {
std::shared_ptr<Function>
GateFunction::operator()(const std::vector<double> &params) {
if (params.size() != nParameters()) {
xacc::error("Invalid GateFunction evaluation: number "
"of parameters don't match. " +
......@@ -322,8 +357,7 @@ operator()(const std::vector<double> &params) {
for (auto inst : getInstructions()) {
if (inst->isComposite()) {
// If a Function, call this method recursively
auto evaled =
std::dynamic_pointer_cast<Function>(inst)->operator()(p);
auto evaled = std::dynamic_pointer_cast<Function>(inst)->operator()(p);
evaluatedFunction->addInstruction(evaled);
} else {
// If a concrete GateInstruction, then check that it
......
......@@ -91,8 +91,9 @@ public:
const int nInstructions() override;
const int nRequiredBits() const override {
XACCLogger::instance()->error("GateFunction nRequiredBits() not implemented.");
return 0;
XACCLogger::instance()->error(
"GateFunction nRequiredBits() not implemented.");
return 0;
}
InstPtr getInstruction(const int idx) override;
......@@ -121,7 +122,7 @@ public:
for (int i = 0; i < instructions.size(); i++) {
auto inst = *std::next(instructions.begin(), i);
if (inst->isAnalog()) {
return true;
return true;
}
}
return false;
......@@ -193,7 +194,7 @@ public:
*
* @return qasm The instruction as qasm
*/
const std::string toString() override {return toString("q");}
const std::string toString() override { return toString("q"); }
InstructionParameter getParameter(const int idx) const override;
......@@ -207,32 +208,26 @@ public:
const int nParameters() override;
std::shared_ptr<Function> operator()(const std::vector<double> &params) override;
std::shared_ptr<Function>
operator()(const std::vector<double> &params) override;
Graph<CircuitNode, Directed> toGraph() override;
// void fromGraph(Graph<CircuitNode>& graph) override;
// void fromGraph(std::istream& input) override;
/**
* Return the number of logical qubits.
*
* @return nLogical The number of logical qubits.
*/
const int nLogicalBits() override {
XACCLogger::instance()->error("GateFunction nLogicalBits() not implemented.");
return 0;
}
const int nLogicalBits() override;
/**
* Return the number of physical qubits.
*
* @return nPhysical The number of physical qubits.
*/
const int nPhysicalBits() override {
XACCLogger::instance()->error("GateFunction nPhysicalBits() not implemented.");
return 0;
}
const int nPhysicalBits() override;
/**
* Return true if this Instruction has
......@@ -240,7 +235,7 @@ public:
*
* @return hasOptions
*/
bool hasOptions() override {return false;}
bool hasOptions() override { return !options.empty(); }
/**
* Set the value of an option with the given name.
......@@ -249,7 +244,10 @@ public:
* @param option The value of the option
*/
void setOption(const std::string optName,
InstructionParameter option) override {}
InstructionParameter option) override {
options.insert({optName, option});
}
/**
* Get the value of an option with the given name.
*
......@@ -257,8 +255,7 @@ public:
* @return option The value of the option.
*/
InstructionParameter getOption(const std::string optName) override {
XACCLogger::instance()->error("GateFunction does not have options.");
return InstructionParameter(0);
return options[optName];
}
/**
......@@ -267,8 +264,7 @@ public:
* @return optMap The options map.
*/
std::map<std::string, InstructionParameter> getOptions() override {
XACCLogger::instance()->error("GateFunction does not have options.");
return std::map<std::string, InstructionParameter>{};
return options;
}
DEFINE_VISITABLE()
......@@ -284,6 +280,9 @@ protected:
std::vector<InstructionParameter> parameters;
std::string tag = "";
std::map<std::string, InstructionParameter> options;
};
} // namespace quantum
......
#include "PauliOperator.hpp"
#include "IRProvider.hpp"
#include <regex>
#include <set>
#include <iostream>
#include "XACC.hpp"
#include <Eigen/Core>
namespace xacc {
namespace quantum {
// const std::map<std::string, std::pair<c, std::string>> Term:: pauliProducts =
// Term::create_map();
PauliOperator::PauliOperator() {}
PauliOperator::PauliOperator(std::complex<double> c) {
terms.emplace(std::make_pair("I", c));
}
PauliOperator::PauliOperator(double c) {
terms.emplace(std::make_pair("I", c));
}
PauliOperator::PauliOperator(std::string var) {
terms.emplace(std::make_pair("I", var));
}
PauliOperator::PauliOperator(std::complex<double> c, std::string var) {
terms.emplace(std::piecewise_construct, std::forward_as_tuple("I"),
std::forward_as_tuple(c, var));
}
PauliOperator::PauliOperator(const PauliOperator &i) : terms(i.terms) {}
/**
* The Constructor, takes a vector of
* qubit-gatename pairs. Initializes coefficient to 1
*
* @param operators The pauli operators making up this SpinInstruction
*/
PauliOperator::PauliOperator(std::map<int, std::string> operators) {
terms.emplace(std::make_pair(Term::id(operators), operators));
}
PauliOperator::PauliOperator(std::map<int, std::string> operators,
std::string var) {
terms.emplace(std::piecewise_construct,
std::forward_as_tuple(Term::id(operators, var)),
std::forward_as_tuple(var, operators));
}
/**
* The Constructor, takes a vector of
* qubit-gatename pairs and this instruction's coefficient
*
* @param operators
* @param coeff
*/
PauliOperator::PauliOperator(std::map<int, std::string> operators,
std::complex<double> coeff) {
terms.emplace(std::piecewise_construct,
std::forward_as_tuple(Term::id(operators)),
std::forward_as_tuple(coeff, operators));
}
PauliOperator::PauliOperator(std::map<int, std::string> operators, double coeff)
: PauliOperator(operators, std::complex<double>(coeff, 0)) {}
PauliOperator::PauliOperator(std::map<int, std::string> operators,
std::complex<double> coeff, std::string var) {
terms.emplace(std::piecewise_construct,
std::forward_as_tuple(Term::id(operators, var)),
std::forward_as_tuple(coeff, var, operators));
}
std::vector<std::shared_ptr<Function>>
PauliOperator::observe(std::shared_ptr<Function> function) {
// Create a new GateQIR to hold the spin based terms
auto gateRegistry = xacc::getService<IRProvider>("gate");
std::vector<std::shared_ptr<Function>> observed;
int counter = 0;
auto pi = 3.141592653589793238;
// Populate GateQIR now...
for (auto &inst : terms) {
Term spinInst = inst.second;
auto gateFunction = gateRegistry->createFunction(
inst.first, {});
gateFunction->setOption("coefficient", spinInst.coeff());
for (auto& inst : function->getInstructions()) {
gateFunction->addInstruction(inst);
}
// Loop over all terms in the Spin Instruction
// and create instructions to run on the Gate QPU.
std::vector<std::shared_ptr<xacc::Instruction>> measurements;
auto termsMap = spinInst.ops();
std::vector<std::pair<int, std::string>> terms;
for (auto &kv : termsMap) {
if (kv.second != "I" && !kv.second.empty()) {
terms.push_back({kv.first, kv.second});
}
}
for (int i = terms.size() - 1; i >= 0; i--) {
auto qbit = terms[i].first;
auto gateName = terms[i].second;
auto meas =
gateRegistry->createInstruction("Measure", std::vector<int>{qbit});
xacc::InstructionParameter classicalIdx(qbit);
meas->setParameter(0, classicalIdx);
measurements.push_back(meas);
if (gateName == "X") {
auto hadamard =
gateRegistry->createInstruction("H", std::vector<int>{qbit});
gateFunction->addInstruction(hadamard);
} else if (gateName == "Y") {
auto rx = gateRegistry->createInstruction("Rx", std::vector<int>{qbit});
InstructionParameter p(pi / 2.0);
rx->setParameter(0, p);
gateFunction->addInstruction(rx);
}
}
if (!spinInst.isIdentity()) {
for (auto m : measurements) {
gateFunction->addInstruction(m);
}
}
observed.push_back(gateFunction);
counter++;
}
return observed;
}
std::pair<std::vector<int>, std::vector<int>>
Term::toBinaryVector(const int nQubits) {
// return v,w
std::vector<int> v(nQubits), w(nQubits);
for (auto &kv : ops()) {
auto site = kv.first;
auto pauli = kv.second;
if (pauli == "X") {
w[site] = 1;
} else if (pauli == "Z") {
v[site] = 1;
} else if (pauli == "Y") {
v[site] = 1;
w[site] = 1;
}
}
return {v, w};
}
std::vector<Triplet> PauliOperator::getSparseMatrixElements() {
// Get number of qubits
std::set<int> distinctSites;
for (auto &kv : terms) {
for (auto &kv2 : kv.second.ops()) {
distinctSites.insert(kv2.first);
}
}
auto nQubits = distinctSites.size();
std::vector<Triplet> triplets;
for (auto &kv : terms) {
auto termTrips = kv.second.getSparseMatrixElements(nQubits);
triplets.insert(std::end(triplets), std::begin(termTrips),
std::end(termTrips));
}
return triplets;
}
ActionResult Term::action(const std::string &bitString, ActionType type) {
auto _coeff = coeff();
auto newBits = bitString;
c i(0, 1);
for (auto &t : ops()) {
auto idx = t.first;
auto gate = t.second;
if (gate == "Z") {
_coeff *= newBits[idx] == '1' ? -1 : 1;
} else if (gate == "X") {
newBits[idx] = (newBits[idx] == '1' ? '0' : '1');
} else if (gate == "Y") {
if (type == ActionType::Bra) {
_coeff *= newBits[idx] == '1' ? i : -i;
} else {
_coeff *= newBits[idx] == '1' ? -i : i;
}
newBits[idx] = (newBits[idx] == '1' ? '0' : '1');
}
}
return {newBits, _coeff};
}
const std::vector<std::pair<std::string, std::complex<double>>>
PauliOperator::computeActionOnKet(const std::string &bitString) {
std::vector<std::pair<std::string, std::complex<double>>> ret;
std::string newBits = bitString;
std::complex<double> newCoeff(1, 0), i(0, 1);
for (auto &kv : terms) {
ret.push_back(kv.second.action(bitString, ActionType::Ket));
}
return ret;
}
const std::vector<std::pair<std::string, std::complex<double>>>
PauliOperator::computeActionOnBra(const std::string &bitString) {
std::vector<std::pair<std::string, std::complex<double>>> ret;
for (auto &kv : terms) {
ret.push_back(kv.second.action(bitString, ActionType::Bra));