diff --git a/quantum/observable/fermion/FermionOperator.cpp b/quantum/observable/fermion/FermionOperator.cpp index 467df5985..ce78aad7b 100644 --- a/quantum/observable/fermion/FermionOperator.cpp +++ b/quantum/observable/fermion/FermionOperator.cpp @@ -29,7 +29,7 @@ FermionTerm &FermionTerm::operator*=(const FermionTerm &v) noexcept { auto site = kv.first; auto c_or_a = kv.second; Operators o = ops(); - for (auto oo : o ) { + for (auto oo : o) { if (oo.first == site) { if (oo.second == c_or_a) { ops().clear(); @@ -37,8 +37,7 @@ FermionTerm &FermionTerm::operator*=(const FermionTerm &v) noexcept { ops().push_back({site, c_or_a}); } break; - } - else { + } else { ops().push_back({site, c_or_a}); break; } @@ -87,10 +86,18 @@ FermionOperator::FermionOperator(Operators operators, double coeff, void FermionOperator::clear() { terms.clear(); } -std::vector> FermionOperator::observe( - std::shared_ptr function) { - auto transform = xacc::getService("jw"); - return transform->transform(xacc::as_shared_ptr(this))->observe(function); +std::vector> +FermionOperator::observe(std::shared_ptr function, + std::string transformName) { + + auto mapping = xacc::getService(transformName); + return mapping->transform(xacc::as_shared_ptr(this))->observe(function); +} + +std::vector> +FermionOperator::observe(std::shared_ptr function) { + auto mapping = xacc::getService("jw"); + return mapping->transform(xacc::as_shared_ptr(this))->observe(function); } const std::string FermionOperator::toString() { @@ -135,7 +142,8 @@ void FermionOperator::fromString(const std::string str) { } const int FermionOperator::nBits() { auto maxInt = 0; - if (terms.empty()) return 0; + if (terms.empty()) + return 0; for (auto &kv : terms) { auto ops = kv.second.ops(); @@ -148,8 +156,8 @@ const int FermionOperator::nBits() { return maxInt + 1; } -FermionOperator &FermionOperator::operator+=( - const FermionOperator &v) noexcept { +FermionOperator & +FermionOperator::operator+=(const FermionOperator &v) noexcept { FermionOperator vv = v; for (auto &kv : v.terms) { auto termId = kv.first; @@ -171,13 +179,13 @@ FermionOperator &FermionOperator::operator+=( return *this; } -FermionOperator &FermionOperator::operator-=( - const FermionOperator &v) noexcept { +FermionOperator & +FermionOperator::operator-=(const FermionOperator &v) noexcept { return operator+=(-1.0 * v); } -FermionOperator &FermionOperator::operator*=( - const FermionOperator &v) noexcept { +FermionOperator & +FermionOperator::operator*=(const FermionOperator &v) noexcept { std::unordered_map newTerms; for (auto &kv : terms) { for (auto &vkv : v.terms) { @@ -236,16 +244,16 @@ FermionOperator &FermionOperator::operator*=(const double v) noexcept { return operator*=(std::complex(v, 0)); } -FermionOperator &FermionOperator::operator*=( - const std::complex v) noexcept { +FermionOperator & +FermionOperator::operator*=(const std::complex v) noexcept { for (auto &kv : terms) { std::get<0>(kv.second) *= v; } return *this; } -std::shared_ptr FermionOperator::commutator( - std::shared_ptr op) { +std::shared_ptr +FermionOperator::commutator(std::shared_ptr op) { FermionOperator &A = *std::dynamic_pointer_cast(op); std::shared_ptr commutatorHA = std::make_shared((*this) * A - A * (*this)); @@ -279,7 +287,7 @@ void FermionOperator::normalize() { double norm = 0.0; for (auto &kv : terms) { - norm += std::pow(std::norm(std::get<0>(kv.second)), 2); + norm += std::norm(std::get<0>(kv.second)); } norm = std::sqrt(norm); @@ -293,12 +301,19 @@ void FermionOperator::normalize() { double FermionOperator::postProcess(std::shared_ptr buffer, const std::string &postProcessTask, const HeterogeneousMap &extra_data) { - auto transform = xacc::getService("jw"); - return transform->transform(xacc::as_shared_ptr(this))->postProcess(buffer, postProcessTask, extra_data); + + std::string mapping = "jw"; + if (extra_data.stringExists("transform")) { + mapping = extra_data.getString("transform"); + } + auto transform = xacc::getService(mapping); + + return transform->transform(xacc::as_shared_ptr(this)) + ->postProcess(buffer, postProcessTask, extra_data); } } // namespace quantum -} // namespace xacc +} // namespace xacc bool operator==(const xacc::quantum::FermionOperator &lhs, const xacc::quantum::FermionOperator &rhs) { diff --git a/quantum/observable/fermion/FermionOperator.hpp b/quantum/observable/fermion/FermionOperator.hpp index 8e75292cc..641528dc9 100644 --- a/quantum/observable/fermion/FermionOperator.hpp +++ b/quantum/observable/fermion/FermionOperator.hpp @@ -34,10 +34,12 @@ using Operator = std::pair; using Operators = std::vector; // using SiteMap = std::map>; -using FermionTermTuple = std::tuple, Operators, std::string>; -class FermionTerm : public FermionTermTuple, - public tao::operators::commutative_multipliable, - public tao::operators::equality_comparable { +using FermionTermTuple = + std::tuple, Operators, std::string>; +class FermionTerm + : public FermionTermTuple, + public tao::operators::commutative_multipliable, + public tao::operators::equality_comparable { public: FermionTerm() { @@ -58,11 +60,10 @@ class FermionTerm : public FermionTermTuple, std::get<2>(*this) = ""; } - FermionTerm(double c) { - std::get<0>(*this) = std::complex(c,0.0); + FermionTerm(double c) { + std::get<0>(*this) = std::complex(c, 0.0); std::get<1>(*this) = {}; std::get<2>(*this) = ""; - } FermionTerm(std::complex c, Operators ops) { @@ -71,7 +72,7 @@ class FermionTerm : public FermionTermTuple, std::get<2>(*this) = ""; } - FermionTerm(std::complex c, Operators ops, std::string var) { + FermionTerm(std::complex c, Operators ops, std::string var) { std::get<0>(*this) = c; std::get<1>(*this) = ops; std::get<2>(*this) = var; @@ -121,7 +122,7 @@ class FermionTerm : public FermionTermTuple, Operators &ops() { return std::get<1>(*this); } std::complex &coeff() { return std::get<0>(*this); } - std::string var() {return std::get<2>(*this);} + std::string var() { return std::get<2>(*this); } FermionTerm &operator*=(const FermionTerm &v) noexcept; @@ -131,7 +132,8 @@ class FermionTerm : public FermionTermTuple, }; class FermionOperator - : public Observable, public Cloneable, + : public Observable, + public Cloneable, public std::enable_shared_from_this, public tao::operators::commutative_ring, public tao::operators::equality_comparable, @@ -144,7 +146,7 @@ class FermionOperator public: std::shared_ptr clone() override { - return std::make_shared(); + return std::make_shared(); } FermionOperator(); FermionOperator(std::complex c); @@ -158,12 +160,19 @@ class FermionOperator std::vector> observe(std::shared_ptr function) override; + + std::vector> + observe(std::shared_ptr function, + std::string transformName) override; + const std::string toString() override; void fromString(const std::string str) override; const int nBits() override; void clear(); - std::unordered_map getTerms() const { return terms; } + std::unordered_map getTerms() const { + return terms; + } FermionOperator &operator+=(const FermionOperator &v) noexcept; FermionOperator &operator-=(const FermionOperator &v) noexcept; @@ -172,16 +181,10 @@ class FermionOperator FermionOperator &operator*=(const double v) noexcept; FermionOperator &operator*=(const std::complex v) noexcept; - const std::string name() const override { - return "fermion"; - } + const std::string name() const override { return "fermion"; } - const std::string description() const override { - return ""; - } - void fromOptions(const HeterogeneousMap& options) override { - return; - } + const std::string description() const override { return ""; } + void fromOptions(const HeterogeneousMap &options) override { return; } std::shared_ptr commutator(std::shared_ptr obs) override; diff --git a/quantum/observable/pauli/PauliOperator.cpp b/quantum/observable/pauli/PauliOperator.cpp index c51307af9..3f41c9817 100644 --- a/quantum/observable/pauli/PauliOperator.cpp +++ b/quantum/observable/pauli/PauliOperator.cpp @@ -800,7 +800,7 @@ void PauliOperator::normalize() { double norm = 0.0; for (auto &kv : terms) { - norm += std::pow(std::norm(std::get<0>(kv.second)), 2); + norm += std::norm(std::get<0>(kv.second)); } norm = std::sqrt(norm); diff --git a/quantum/observable/transforms/BravyiKitaev/BK.cpp b/quantum/observable/transforms/BravyiKitaev/BK.cpp new file mode 100644 index 000000000..1463fbe37 --- /dev/null +++ b/quantum/observable/transforms/BravyiKitaev/BK.cpp @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Alexander J. McCaskey - initial API and implementation + * Daniel Claudino - porting + ******************************************************************************/ +#include "BK.hpp" +#include "FermionOperator.hpp" +#include "PauliOperator.hpp" +#include "Fenwick.hpp" +#include "xacc_observable.hpp" + +namespace xacc { +namespace quantum { + +template +bool ptr_is_a(std::shared_ptr ptr) { + return std::dynamic_pointer_cast(ptr) != nullptr; +} + +std::shared_ptr +BK::transform(std::shared_ptr Hptr_input) { + + // First we pre-process the observable to a FermionOperator + std::shared_ptr observable; + auto obs_str = Hptr_input->toString(); + if (ptr_is_a(Hptr_input)) { + observable = Hptr_input; + } else if (obs_str.find("^") != std::string::npos) { + observable = xacc::quantum::getObservable("fermion", obs_str); + } else { + XACCLogger::instance()->error("[Jordan Wigner] Error, cannot cast incoming Observable ptr to something we can process."); + } + auto fermionObservable = + std::dynamic_pointer_cast(observable); + + if (!fermionObservable) { + XACCLogger::instance()->info( + "Cannot execute Bravyi Kitaev on a non-fermion observable."); + return observable; + } + + int nQubits = observable->nBits(); + + FenwickTree tree(nQubits); + + auto result = std::make_shared(); + + auto terms = fermionObservable->getTerms(); + + // Loop over all Fermionic terms... + for (auto &kv : terms) { + + auto var = kv.second.var(); + auto coeff = kv.second.coeff(); + auto operators = kv.second.ops(); + + PauliOperator current(coeff, var); + for (auto &kv2 : operators) { + + auto isCreation = kv2.second; + int index = kv2.first; + + auto paritySet = tree.getParitySet(index); + auto ancestors = tree.getUpdateSet(index); + auto ancestorChildren = tree.getRemainderSet(index); + + std::complex dcoeff, ccoeff(.5,0); + if (isCreation) { + dcoeff = std::complex(0,-.5); + } else { + dcoeff = std::complex(0,.5); + } + + std::map dTerms{{index, "Y"}}, cTerms{{index, "X"}}; + for (auto ac : ancestorChildren) { + dTerms[ac->index] = "Z"; + } + for (auto p : paritySet) { + cTerms[p->index] = "Z"; + } + for (auto a : ancestors) { + dTerms[a->index] = "X"; + cTerms[a->index] = "X"; + } + + PauliOperator d(dTerms,dcoeff), c(cTerms,ccoeff); + + current *= (c+d); + } + + result->operator+=(current); + } + + return result; +} + +} // namespace quantum +} // namespace xacc \ No newline at end of file diff --git a/quantum/observable/transforms/BravyiKitaev/BK.hpp b/quantum/observable/transforms/BravyiKitaev/BK.hpp new file mode 100644 index 000000000..1e744bc03 --- /dev/null +++ b/quantum/observable/transforms/BravyiKitaev/BK.hpp @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Alexander J. McCaskey - initial API and implementation + * Daniel Claudino - porting + ******************************************************************************/ +#ifndef XACC_IR_OBSERVABLETRANSFORM_BK_HPP_ +#define XACC_IR_OBSERVABLETRANSFORM_BK_HPP_ +#include "ObservableTransform.hpp" + +namespace xacc { +namespace quantum { + +class BK : public ObservableTransform { +public: + std::shared_ptr + transform(std::shared_ptr obs) override; + const std::string name() const override { return "bk"; } + + const std::string description() const override { return ""; } +}; +} // namespace quantum +} // namespace xacc +#endif \ No newline at end of file diff --git a/quantum/observable/transforms/BravyiKitaev/Fenwick.hpp b/quantum/observable/transforms/BravyiKitaev/Fenwick.hpp new file mode 100644 index 000000000..3295a73ae --- /dev/null +++ b/quantum/observable/transforms/BravyiKitaev/Fenwick.hpp @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Alexander J. McCaskey - initial API and implementation + * Daniel Claudino - porting + ******************************************************************************/ + +#ifndef BK_FENWICK_HPP_ +#define BK_FENWICK_HPP_ + +#include +#include + +/** + * This class represents a Node in the Fenwick Tree. It keeps track + * of its parent, integer index, and any children it has. + */ +class Node : public std::enable_shared_from_this { + +public: + using NodePtr = std::shared_ptr; + + /** + * Reference to this Node's parent Node + */ + NodePtr parent; + + /** + * Reference to this Node's children Nodes. + */ + std::set children; + + /** + * Reference to this Node's index + */ + int index; + + /** + * The constructor + */ + Node() : index(-1), children(std::set{}) {} + + /** + * Return all predecessor Nodes. + * + * @return ancestors This Node's predecessor Nodes + */ + std::set getAncestors() { + auto node = shared_from_this(); + std::set ancestors; + while (node->parent) { + ancestors.insert(node->parent); + node = node->parent; + } + return ancestors; + } +}; + +/** + * This class models a Fenwick Tree and provides a way to + * map binary strings to binary strings such that both the + * predecessor sum and any bit flip operations have O(logN) access cost. + * + * See https://arxiv.org/pdf/1701.07072.pdf + */ +class FenwickTree { + + using NodePtr = std::shared_ptr; + +protected: + + /** + * Reference to the Nodes in this tree + */ + std::vector nodes; + + /** + * The root node of this Tree. + */ + NodePtr root; + +public: + + /** + * The Constructor. + * + * @param nQubits + */ + FenwickTree(const int nQubits) { + + // Initialize with nQubits Nodes. + for (int i = 0; i < nQubits; i++) { + nodes.push_back(std::make_shared()); + } + + if (nQubits > 0 ) { + + // Set the Root node and its index + root = nodes[nQubits-1]; + root->index = nQubits - 1; + + // Create a recursive lambda that will let + // us build up the tree. + std::function construct; + construct = + [this,&construct](const int idx1, const int idx2, NodePtr parent) { + if( idx1 >= idx2) { + return; + } else { + auto pvt = (idx1 + idx2) >> 1; + auto c = nodes[pvt]; + + c->index = pvt; + parent->children.insert(c); + c->parent = parent; + + construct(idx1, pvt, c); + construct(pvt + 1, idx2, parent); + } + }; + + // Construct the Tree! + construct(0, nQubits-1, root); + } + } + + /** + * Return the update set corresponding to Node i. + * + * @param i + * @return + */ + std::set getUpdateSet(const int i) { + return nodes[i]->getAncestors(); + } + + /** + * Return the children of Node i. + * + * @param i + * @return + */ + std::set getChildrenSet(const int i) { + return nodes[i]->children; + } + + /** + * Return the remainder set for Node i. + * + * @param i + * @return + */ + std::set getRemainderSet(const int i) { + std::set rset; + auto ancestors = getUpdateSet(i); + + for (auto a : ancestors) { + for (auto c : a->children) { + if (c->index < i) { + rset.insert(c); + } + } + } + + return rset; + } + + /** + * Return the parity set for Node i. + * + * @param i + * @return + */ + std::set getParitySet(const int i) { + auto result = getRemainderSet(i); + auto cs = getChildrenSet(i); + + std::set paritySet = result; + paritySet.insert(cs.begin(), cs.end()); + + return paritySet; + } +}; + + +#endif /* VQE_TRANSFORMATION_BK_FENWICK_HPP_ */ \ No newline at end of file diff --git a/quantum/observable/transforms/CMakeLists.txt b/quantum/observable/transforms/CMakeLists.txt index 49986bac6..844519297 100644 --- a/quantum/observable/transforms/CMakeLists.txt +++ b/quantum/observable/transforms/CMakeLists.txt @@ -2,7 +2,7 @@ set (PACKAGE_NAME "Quantum Observable Transforms") set (LIBRARY_NAME xacc-observable-transforms) file (GLOB_RECURSE HEADERS *.hpp) -file (GLOB SRC *.cpp) +file (GLOB SRC *.cpp JordanWigner/*.cpp BravyiKitaev/*.cpp) # Set up dependencies to resources to track changes usFunctionGetResourceSource(TARGET ${LIBRARY_NAME} OUT SRC) @@ -13,6 +13,8 @@ add_library(${LIBRARY_NAME} SHARED ${SRC}) target_include_directories(${LIBRARY_NAME} PUBLIC . + ./JordanWigner + ./BravyiKitaev ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/quantum/gate/observable/fermion ${CMAKE_SOURCE_DIR}/quantum/gate/observable/pauli @@ -37,7 +39,7 @@ usFunctionEmbedResources(TARGET ${LIBRARY_NAME} manifest.json ) -target_link_libraries(${LIBRARY_NAME} PUBLIC xacc PRIVATE CppMicroServices xacc-quantum-gate xacc-pauli xacc-fermion) +target_link_libraries(${LIBRARY_NAME} PUBLIC xacc PRIVATE CppMicroServices xacc-pauli xacc-fermion xacc-quantum-gate) if(APPLE) set_target_properties(${LIBRARY_NAME} PROPERTIES INSTALL_RPATH "@loader_path/../lib") @@ -53,4 +55,4 @@ endif() #install(FILES ${HEADERS} DESTINATION include/quantum/gate) -install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins) +install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins) \ No newline at end of file diff --git a/quantum/observable/transforms/JW.cpp b/quantum/observable/transforms/JordanWigner/JW.cpp similarity index 98% rename from quantum/observable/transforms/JW.cpp rename to quantum/observable/transforms/JordanWigner/JW.cpp index a0db0b263..fca7ac675 100644 --- a/quantum/observable/transforms/JW.cpp +++ b/quantum/observable/transforms/JordanWigner/JW.cpp @@ -18,17 +18,19 @@ namespace xacc { namespace quantum { + template bool ptr_is_a(std::shared_ptr ptr) { return std::dynamic_pointer_cast(ptr) != nullptr; } + std::shared_ptr JW::transform(std::shared_ptr Hptr_input) { // First we pre-process the observable to a FermionOperator std::shared_ptr observable; auto obs_str = Hptr_input->toString(); - if (ptr_is_a(Hptr_input)) { + if (ptr_is_a(Hptr_input)) { observable = Hptr_input; } else if (obs_str.find("^") != std::string::npos) { observable = xacc::quantum::getObservable("fermion", obs_str); diff --git a/quantum/observable/transforms/JW.hpp b/quantum/observable/transforms/JordanWigner/JW.hpp similarity index 100% rename from quantum/observable/transforms/JW.hpp rename to quantum/observable/transforms/JordanWigner/JW.hpp diff --git a/quantum/observable/transforms/ObservableTransformsActivator.cpp b/quantum/observable/transforms/ObservableTransformsActivator.cpp index 04a04f1f0..e98712c27 100644 --- a/quantum/observable/transforms/ObservableTransformsActivator.cpp +++ b/quantum/observable/transforms/ObservableTransformsActivator.cpp @@ -11,6 +11,7 @@ * Alexander J. McCaskey - initial API and implementation *******************************************************************************/ #include "JW.hpp" +#include "BK.hpp" #include "cppmicroservices/BundleActivator.h" #include "cppmicroservices/BundleContext.h" @@ -33,8 +34,11 @@ class US_ABI_LOCAL ObservableTransformsActivator : public BundleActivator { /** */ void Start(BundleContext context) { - auto c = std::make_shared(); - context.RegisterService(c); + auto jw = std::make_shared(); + context.RegisterService(jw); + + auto bk = std::make_shared(); + context.RegisterService(bk); } /** @@ -44,4 +48,4 @@ class US_ABI_LOCAL ObservableTransformsActivator : public BundleActivator { } // namespace -CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(ObservableTransformsActivator) +CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(ObservableTransformsActivator) \ No newline at end of file diff --git a/quantum/observable/transforms/tests/BKTester.cpp b/quantum/observable/transforms/tests/BKTester.cpp new file mode 100644 index 000000000..15a8ae046 --- /dev/null +++ b/quantum/observable/transforms/tests/BKTester.cpp @@ -0,0 +1,78 @@ +#include +#include +#include "BK.hpp" +#include "xacc.hpp" +#include +#include +#include "FermionOperator.hpp" +#include "PauliOperator.hpp" +#include "ObservableTransform.hpp" +#include "xacc_service.hpp" +#include "xacc_observable.hpp" +#include "Circuit.hpp" + +using namespace xacc::quantum; + +TEST(BravyiKitaevTransformationTester, checkBKTransform) { + + FermionOperator op("(3.17, 0) 2^ 0 + (3.17, 0) 0^ 2"); + auto fermion = std::make_shared(op); + + auto bk = xacc::getService("bk"); + auto result = bk->transform(fermion); + auto resultsStr = result->toString(); + + PauliOperator pauli("(-1.585,0) Y0 Y1 + (-1.585,0) X0 X1 Z2"); + EXPECT_TRUE( + std::dynamic_pointer_cast(result)->operator==(pauli)); +} + +TEST(BravyiKitaevTransformationTester, checkH2) { + + auto str = std::string( + "(-0.165606823582,-0) 1^ 2^ 1 2 + (0.120200490713,0) 1^ 0^ 0 1 + " + "(-0.0454063328691,-0) 0^ 3^ 1 2 + (0.168335986252,0) 2^ 0^ 0 2 + " + "(0.0454063328691,0) 1^ 2^ 3 0 + (0.168335986252,0) 0^ 2^ 2 0 + " + "(0.165606823582,0) 0^ 3^ 3 0 + (-0.0454063328691,-0) 3^ 0^ 2 1 + " + "(-0.0454063328691,-0) 1^ 3^ 0 2 + (-0.0454063328691,-0) 3^ 1^ 2 0 + " + "(0.165606823582,0) 1^ 2^ 2 1 + (-0.165606823582,-0) 0^ 3^ 0 3 + " + "(-0.479677813134,-0) 3^ 3 + (-0.0454063328691,-0) 1^ 2^ 0 3 + " + "(-0.174072892497,-0) 1^ 3^ 1 3 + (-0.0454063328691,-0) 0^ 2^ 1 3 + " + "(0.120200490713,0) 0^ 1^ 1 0 + (0.0454063328691,0) 0^ 2^ 3 1 + " + "(0.174072892497,0) 1^ 3^ 3 1 + (0.165606823582,0) 2^ 1^ 1 2 + " + "(-0.0454063328691,-0) 2^ 1^ 3 0 + (-0.120200490713,-0) 2^ 3^ 2 3 + " + "(0.120200490713,0) 2^ 3^ 3 2 + (-0.168335986252,-0) 0^ 2^ 0 2 + " + "(0.120200490713,0) 3^ 2^ 2 3 + (-0.120200490713,-0) 3^ 2^ 3 2 + " + "(0.0454063328691,0) 1^ 3^ 2 0 + (-1.2488468038,-0) 0^ 0 + " + "(0.0454063328691,0) 3^ 1^ 0 2 + (-0.168335986252,-0) 2^ 0^ 2 0 + " + "(0.165606823582,0) 3^ 0^ 0 3 + (-0.0454063328691,-0) 2^ 0^ 3 1 + " + "(0.0454063328691,0) 2^ 0^ 1 3 + (-1.2488468038,-0) 2^ 2 + " + "(0.0454063328691,0) 2^ 1^ 0 3 + (0.174072892497,0) 3^ 1^ 1 3 + " + "(-0.479677813134,-0) 1^ 1 + (-0.174072892497,-0) 3^ 1^ 3 1 + " + "(0.0454063328691,0) 3^ 0^ 1 2 + (-0.165606823582,-0) 3^ 0^ 3 0 + " + "(0.0454063328691,0) 0^ 3^ 2 1 + (-0.165606823582,-0) 2^ 1^ 2 1 + " + "(-0.120200490713,-0) 0^ 1^ 0 1 + (-0.120200490713,-0) 1^ 0^ 1 0 + " + "(0.7080240981,0)"); + + auto acc = xacc::getAccelerator("qpp"); + + auto bk = xacc::getService("bk"); + auto H = bk->transform(xacc::quantum::getObservable("fermion", str)); + + auto ansatz = + xacc::createComposite("hf", {{"ne", 2}, {"nq", 4}, {"transform", "bk"}}); + + auto vqe = xacc::getAlgorithm( + "vqe", {{"ansatz", ansatz}, {"observable", H}, {"accelerator", acc}}); + + auto q = xacc::qalloc(H->nBits()); + EXPECT_NEAR(vqe->execute(q, {})[0], -1.11633, 1e-4); +} + +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 diff --git a/quantum/observable/transforms/tests/CMakeLists.txt b/quantum/observable/transforms/tests/CMakeLists.txt index 42fed8e99..bae0d7f0f 100644 --- a/quantum/observable/transforms/tests/CMakeLists.txt +++ b/quantum/observable/transforms/tests/CMakeLists.txt @@ -12,5 +12,7 @@ include_directories(${CMAKE_SOURCE_DIR}/quantum/gate/observable/fermion) include_directories(${CMAKE_SOURCE_DIR}/quantum/gate/observable/pauli) add_xacc_test(JW) +target_link_libraries(JWTester CppMicroServices xacc-observable-transforms xacc-pauli xacc-fermion) -target_link_libraries(JWTester CppMicroServices xacc-observable-transforms xacc-pauli xacc-fermion) \ No newline at end of file +add_xacc_test(BK) +target_link_libraries(BKTester CppMicroServices xacc-observable-transforms xacc-pauli xacc-fermion xacc-quantum-gate) \ No newline at end of file diff --git a/quantum/plugins/algorithms/adapt/adapt.cpp b/quantum/plugins/algorithms/adapt/adapt.cpp index fda278a9b..f77f2d960 100644 --- a/quantum/plugins/algorithms/adapt/adapt.cpp +++ b/quantum/plugins/algorithms/adapt/adapt.cpp @@ -89,20 +89,29 @@ bool ADAPT::initialize(const HeterogeneousMap ¶meters) { parameters.get>("initial-state"); } - if (parameters.keyExists("n-electrons")) { - _nElectrons = parameters.get("n-electrons"); - } - - if ((subAlgo == "vqe" && !initialState) && - (subAlgo == "vqe" && !parameters.keyExists("n-electrons"))) { - - xacc::info("VQE requires number of electrons or initial state."); + if (pool->needsNumberOfParticles()) { + if (parameters.keyExists("n-electrons")) { + _nElectrons = parameters.get("n-electrons"); + } else { + xacc::error("Selected pool needs number of electrons."); + return false; + } } + pool->optionalParameters(parameters); - if (parameters.getString("pool") == "singlet-adapted-uccsd" && - parameters.keyExists("n-electrons")) { + if (subAlgo == "vqe" && !initialState) { - pool->optionalParameters({std::make_pair("n-electrons", _nElectrons)}); + // Now that we have JW and BK, we may want to specify it here + std::string mapping; + if (parameters.stringExists("transform")) { + mapping = parameters.getString("transform"); + } else { + xacc::warning("No transform provided. Defaulting to Jordan-Wigner."); + mapping = "jw"; + } + initialState = xacc::createComposite("hf", {{"ne", _nElectrons}, + {"nq", observable->nBits()}, + {"transform", mapping}}); } // Check if Observable is Fermion or Pauli and manipulate accordingly @@ -110,13 +119,20 @@ bool ADAPT::initialize(const HeterogeneousMap ¶meters) { // if string has ^, it's FermionOperator if (observable->toString().find("^") != std::string::npos) { - auto jw = xacc::getService("jw"); + std::shared_ptr mapping; + if (parameters.stringExists("transform")) { + mapping = xacc::getService( + parameters.getString("transform")); + } else { + mapping = xacc::getService("jw"); + } + if (std::dynamic_pointer_cast(observable)) { - observable = jw->transform(observable); + observable = mapping->transform(observable); } else { auto fermionObservable = xacc::quantum::getObservable("fermion", observable->toString()); - observable = jw->transform( + observable = mapping->transform( std::dynamic_pointer_cast(fermionObservable)); } @@ -156,31 +172,13 @@ void ADAPT::execute(const std::shared_ptr buffer) const { ansatzInstructions->addInstruction(inst); } - } else { - - if (subAlgo == "vqe") { - // Define the initial state, usually HF for chemistry problems - std::size_t j; - for (int i = 0; i < _nElectrons / 2; i++) { - j = (std::size_t)i; - auto alphaXGate = - ansatzRegistry->createInstruction("X", std::vector{j}); - ansatzInstructions->addInstruction(alphaXGate); - j = (std::size_t)(i + buffer->size() / 2); - auto betaXGate = - ansatzRegistry->createInstruction("X", std::vector{j}); - ansatzInstructions->addInstruction(betaXGate); - } - } - - if (subAlgo == "QAOA") { - std::size_t j; - for (int i = 0; i < buffer->size(); i++) { - j = (std::size_t)i; - auto H = - ansatzRegistry->createInstruction("H", std::vector{j}); - ansatzInstructions->addInstruction(H); - } + } else if (subAlgo == "QAOA") { + std::size_t j; + for (int i = 0; i < buffer->size(); i++) { + j = (std::size_t)i; + auto H = + ansatzRegistry->createInstruction("H", std::vector{j}); + ansatzInstructions->addInstruction(H); } } @@ -319,7 +317,8 @@ void ADAPT::execute(const std::shared_ptr buffer) const { // Add the ansatz to the compilation database for later retrieval xacc::appendCompiled(ansatzInstructions, true); - buffer->addExtraInfo("final-ansatz", ExtraInfo(ansatzInstructions->name())); + buffer->addExtraInfo("final-ansatz", + ExtraInfo(ansatzInstructions->name())); return; } else if (iter < _maxIter) { // Add operator and reoptimize diff --git a/quantum/plugins/algorithms/adapt/adapt_activator.cpp b/quantum/plugins/algorithms/adapt/adapt_activator.cpp index 5e81af37c..a2dcc68a6 100644 --- a/quantum/plugins/algorithms/adapt/adapt_activator.cpp +++ b/quantum/plugins/algorithms/adapt/adapt_activator.cpp @@ -17,9 +17,6 @@ #include "operator_pools/MultiQubitQAOA.hpp" #include "operator_pools/CustomPool.hpp" #include "operator_pools/SinglesDoublesPool.hpp" -#include "operator_pools/IonizationPotential.hpp" -#include "operator_pools/ElectronAttachment.hpp" -#include "operator_pools/SpinFlip.hpp" #include "cppmicroservices/BundleActivator.h" #include "cppmicroservices/BundleContext.h" @@ -58,15 +55,6 @@ class US_ABI_LOCAL ADAPT_Activator : public BundleActivator { auto sd = std::make_shared(); context.RegisterService(sd); - auto ip = std::make_shared(); - context.RegisterService(ip); - - auto ea = std::make_shared(); - context.RegisterService(ea); - - auto sf = std::make_shared(); - context.RegisterService(sf); - } void Stop(BundleContext /*context*/) {} diff --git a/quantum/plugins/algorithms/adapt/operator_pools/CustomPool.hpp b/quantum/plugins/algorithms/adapt/operator_pools/CustomPool.hpp index c4dc6b771..7359a2924 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/CustomPool.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/CustomPool.hpp @@ -38,6 +38,7 @@ class CustomPool : public OperatorPool { protected: std::vector> pool; + bool _needsNumberOfParticles = false; public: @@ -102,6 +103,14 @@ class CustomPool : public OperatorPool { pool.push_back(std::dynamic_pointer_cast((std::make_shared(op)))); } } + + void setIfNeedsNumberOfParticles(const bool needsNumberOfParticles) override { + _needsNumberOfParticles = needsNumberOfParticles; + } + + bool needsNumberOfParticles() const override { + return _needsNumberOfParticles; + } bool optionalParameters(const HeterogeneousMap parameters) override { return true; diff --git a/quantum/plugins/algorithms/adapt/operator_pools/ElectronAttachment.hpp b/quantum/plugins/algorithms/adapt/operator_pools/ElectronAttachment.hpp deleted file mode 100644 index f653a231e..000000000 --- a/quantum/plugins/algorithms/adapt/operator_pools/ElectronAttachment.hpp +++ /dev/null @@ -1,148 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 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: - * Daniel Claudino - initial API and implementation - *******************************************************************************/ -#ifndef XACC_ELECTRON_ATTACHMENT_POOL_HPP_ -#define XACC_ELECTRON_ATTACHMENT_POOL_HPP_ - -#include "OperatorPool.hpp" -#include "xacc.hpp" -#include "xacc_service.hpp" -#include "Observable.hpp" -#include "xacc_observable.hpp" -#include "FermionOperator.hpp" -#include "Circuit.hpp" -#include "ObservableTransform.hpp" - -using namespace xacc; -using namespace xacc::quantum; - -namespace xacc { -namespace quantum { - -class ElectronAttachment : public OperatorPool { - -protected: - int _nElectrons, _rank = 1; - std::vector> pool, operators; - -public: - ElectronAttachment() = default; - - bool optionalParameters(const HeterogeneousMap parameters) override { - - if (!parameters.keyExists("n-electrons")) { - xacc::info("Pool needs number of electrons."); - return false; - } - _nElectrons = parameters.get("n-electrons"); - - if (parameters.keyExists("rank")) { - _rank = parameters.get("rank"); - } - - if (_rank > 2) { - xacc::info("Cannot add more than two electrons"); - } - - return true; - } - - // generate the pool - std::vector> - getExcitationOperators(const int &nQubits) override { - - auto _nOccupied = (int)std::ceil(_nElectrons / 2.0); - auto _nVirtual = nQubits / 2 - _nOccupied; - auto _nOrbs = _nOccupied + _nVirtual; - - if (_rank == 1) { - for (int a = 0; a < _nVirtual; a++) { - int aa = a + _nOccupied; - int ab = a + _nOccupied + _nOrbs; - operators.push_back( - std::make_shared(FermionOperator({{aa, 1}}, 2.0))); - - operators.push_back( - std::make_shared(FermionOperator({{ab, 1}}, 2.0))); - } - } - - if (_rank == 2) { - - for (int a = 0; a < _nVirtual; a++) { - int aa = a + _nOccupied; - int ab = a + _nOccupied + _nOrbs; - - operators.push_back(std::make_shared( - FermionOperator({{aa, 1}, {ab, 1}}, 4.0))); - - for (int b = a + 1; b < _nVirtual; b++) { - int ba = b + _nOccupied; - int bb = b + _nOccupied + _nOrbs; - - operators.push_back(std::make_shared( - FermionOperator({{aa, 1}, {ba, 1}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{ab, 1}, {bb, 1}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{aa, 1}, {bb, 1}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{ab, 1}, {ba, 1}}, 4.0))); - } - } - } - - return operators; - } - - // generate the pool - std::vector> - generate(const int &nQubits) override { - - auto ops = getExcitationOperators(nQubits); - auto jw = xacc::getService("jw"); - for (auto &op : ops) { - auto tmp = *std::dynamic_pointer_cast(op); - tmp -= tmp.hermitianConjugate(); - tmp.normalize(); - pool.push_back(jw->transform(std::make_shared(tmp))); - } - return pool; - } - - std::string operatorString(const int index) override { - - return operators[index]->toString(); - } - - std::shared_ptr - getOperatorInstructions(const int opIdx, const int varIdx) const override { - - // Instruction service for the operator to be added to the ansatz - auto gate = std::dynamic_pointer_cast( - xacc::getService("exp_i_theta")); - - // Create instruction for new operator - gate->expand({std::make_pair("pauli", pool[opIdx]->toString()), - std::make_pair("param_id", "x" + std::to_string(varIdx))}); - - return gate; - } - - const std::string name() const override { return "electron-attachment"; } - const std::string description() const override { return ""; } -}; - -} // namespace quantum -} // namespace xacc - -#endif \ No newline at end of file diff --git a/quantum/plugins/algorithms/adapt/operator_pools/IonizationPotential.hpp b/quantum/plugins/algorithms/adapt/operator_pools/IonizationPotential.hpp deleted file mode 100644 index 6bb16ceaa..000000000 --- a/quantum/plugins/algorithms/adapt/operator_pools/IonizationPotential.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 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: - * Daniel Claudino - initial API and implementation - *******************************************************************************/ -#ifndef XACC_IONIZATION_POTENTIAL_POOL_HPP_ -#define XACC_IONIZATION_POTENTIAL_POOL_HPP_ - -#include "OperatorPool.hpp" -#include "xacc.hpp" -#include "xacc_service.hpp" -#include "Observable.hpp" -#include "xacc_observable.hpp" -#include "FermionOperator.hpp" -#include "Circuit.hpp" -#include "ObservableTransform.hpp" - -using namespace xacc; -using namespace xacc::quantum; - -namespace xacc { -namespace quantum { - -class IonizationPotential : public OperatorPool { - -protected: - int _nElectrons, _rank = 1; - std::vector> pool, operators; - -public: - IonizationPotential() = default; - - bool optionalParameters(const HeterogeneousMap parameters) override { - - if (!parameters.keyExists("n-electrons")) { - xacc::info("Pool needs number of electrons."); - return false; - } - _nElectrons = parameters.get("n-electrons"); - - if (parameters.keyExists("rank")) { - _rank = parameters.get("rank"); - } - - if (_rank > 2) { - xacc::info("Cannot remove more than two electrons"); - } - - return true; - } - - // generate the pool - std::vector> - getExcitationOperators(const int &nQubits) override { - - auto _nOccupied = (int)std::ceil(_nElectrons / 2.0); - auto _nVirtual = nQubits / 2 - _nOccupied; - auto _nOrbs = _nOccupied + _nVirtual; - - if (_rank == 1) { - for (int i = 0; i < _nOccupied; i++) { - int ia = i; - int ib = i + _nOrbs; - - operators.push_back( - std::make_shared(FermionOperator({{ia, 0}}, 2.0))); - - operators.push_back( - std::make_shared(FermionOperator({{ib, 0}}, 2.0))); - } - } - - if (_rank == 2) { - - for (int i = 0; i < _nOccupied; i++) { - int ia = i; - int ib = i + _nOrbs; - - operators.push_back(std::make_shared( - FermionOperator({{ia, 0}, {ib, 0}}, 4.0))); - - for (int j = i + 1; j < _nOccupied; j++) { - int ja = j; - int jb = j + _nOrbs; - - operators.push_back(std::make_shared( - FermionOperator({{ia, 0}, {ja, 0}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{ib, 0}, {jb, 0}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{ia, 0}, {jb, 0}}, 4.0))); - operators.push_back(std::make_shared( - FermionOperator({{ib, 0}, {ja, 0}}, 4.0))); - } - } - } - - return operators; - } - - // generate the pool - std::vector> - generate(const int &nQubits) override { - - auto ops = getExcitationOperators(nQubits); - auto jw = xacc::getService("jw"); - for (auto &op : ops) { - auto tmp = *std::dynamic_pointer_cast(op); - tmp -= tmp.hermitianConjugate(); - tmp.normalize(); - pool.push_back(jw->transform(std::make_shared(tmp))); - } - return pool; - } - - std::string operatorString(const int index) override { - - return operators[index]->toString(); - } - - std::shared_ptr - getOperatorInstructions(const int opIdx, const int varIdx) const override { - - // Instruction service for the operator to be added to the ansatz - auto gate = std::dynamic_pointer_cast( - xacc::getService("exp_i_theta")); - - // Create instruction for new operator - gate->expand({std::make_pair("pauli", pool[opIdx]->toString()), - std::make_pair("param_id", "x" + std::to_string(varIdx))}); - - return gate; - } - - const std::string name() const override { return "ionization-potential"; } - const std::string description() const override { return ""; } -}; - -} // namespace quantum -} // namespace xacc - -#endif \ No newline at end of file diff --git a/quantum/plugins/algorithms/adapt/operator_pools/MultiQubitQAOA.hpp b/quantum/plugins/algorithms/adapt/operator_pools/MultiQubitQAOA.hpp index 9a6778259..c9587e5d2 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/MultiQubitQAOA.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/MultiQubitQAOA.hpp @@ -41,6 +41,10 @@ class MultiQubitQAOA : public OperatorPool { MultiQubitQAOA() = default; + bool needsNumberOfParticles() const override { + return false; + } + // this is a required parameter for QAOA bool optionalParameters(const HeterogeneousMap parameters) override { diff --git a/quantum/plugins/algorithms/adapt/operator_pools/QubitPool.hpp b/quantum/plugins/algorithms/adapt/operator_pools/QubitPool.hpp index cb8cb384a..a342db225 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/QubitPool.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/QubitPool.hpp @@ -38,6 +38,10 @@ class QubitPool : public OperatorPool { QubitPool() = default; + bool needsNumberOfParticles() const override { + return false; + } + bool optionalParameters(const HeterogeneousMap parameters) override { return true; } diff --git a/quantum/plugins/algorithms/adapt/operator_pools/SingleQubitQAOA.hpp b/quantum/plugins/algorithms/adapt/operator_pools/SingleQubitQAOA.hpp index 37f238d75..881264bca 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/SingleQubitQAOA.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/SingleQubitQAOA.hpp @@ -40,6 +40,10 @@ class SingleQubitQAOA : public OperatorPool { SingleQubitQAOA() = default; + bool needsNumberOfParticles() const override { + return false; + } + // this is a required parameter for QAOA bool optionalParameters(const HeterogeneousMap parameters) override { diff --git a/quantum/plugins/algorithms/adapt/operator_pools/SinglesDoublesPool.hpp b/quantum/plugins/algorithms/adapt/operator_pools/SinglesDoublesPool.hpp index 8c3b77d69..28566e31b 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/SinglesDoublesPool.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/SinglesDoublesPool.hpp @@ -36,10 +36,16 @@ class SinglesDoublesPool : public OperatorPool { protected: int _nElectrons; std::vector> pool, operators; + std::shared_ptr mapping; + public: SinglesDoublesPool() = default; + bool needsNumberOfParticles() const override { + return true; + } + bool optionalParameters(const HeterogeneousMap parameters) override { if (!parameters.keyExists("n-electrons")) { @@ -49,6 +55,10 @@ class SinglesDoublesPool : public OperatorPool { _nElectrons = parameters.get("n-electrons"); + if (parameters.stringExists("transform")) { + mapping = xacc::getService(parameters.getString("transform")); + } + return true; } @@ -104,12 +114,14 @@ class SinglesDoublesPool : public OperatorPool { generate(const int &nQubits) override { auto ops = getExcitationOperators(nQubits); - auto jw = xacc::getService("jw"); + if (!mapping) { + mapping = xacc::getService("jw"); + } for (auto &op : ops) { auto tmp = *std::dynamic_pointer_cast(op); tmp -= tmp.hermitianConjugate(); tmp.normalize(); - pool.push_back(jw->transform(std::make_shared(tmp))); + pool.push_back(mapping->transform(std::make_shared(tmp))); } return pool; } diff --git a/quantum/plugins/algorithms/adapt/operator_pools/SingletAdaptedUCCSD.hpp b/quantum/plugins/algorithms/adapt/operator_pools/SingletAdaptedUCCSD.hpp index 72421c4c0..268689202 100644 --- a/quantum/plugins/algorithms/adapt/operator_pools/SingletAdaptedUCCSD.hpp +++ b/quantum/plugins/algorithms/adapt/operator_pools/SingletAdaptedUCCSD.hpp @@ -37,10 +37,15 @@ class SingletAdaptedUCCSD : public OperatorPool { protected: int _nElectrons; std::vector> pool, operators; + std::shared_ptr mapping; public: SingletAdaptedUCCSD() = default; + bool needsNumberOfParticles() const override { + return true; + } + bool optionalParameters(const HeterogeneousMap parameters) override { if (!parameters.keyExists("n-electrons")) { @@ -50,6 +55,10 @@ class SingletAdaptedUCCSD : public OperatorPool { _nElectrons = parameters.get("n-electrons"); + if (parameters.stringExists("transform")) { + mapping = xacc::getService(parameters.getString("transform")); + } + return true; } @@ -127,13 +136,16 @@ class SingletAdaptedUCCSD : public OperatorPool { generate(const int &nQubits) override { auto ops = getExcitationOperators(nQubits); - auto jw = xacc::getService("jw"); + if (!mapping) { + mapping = xacc::getService("jw"); + } + for (auto &op : ops) { auto tmp = *std::dynamic_pointer_cast(op); tmp -= tmp.hermitianConjugate(); tmp.normalize(); - pool.push_back(jw->transform(std::make_shared(tmp))); + pool.push_back(mapping->transform(std::make_shared(tmp))); } return pool; } diff --git a/quantum/plugins/algorithms/adapt/operator_pools/SpinFlip.hpp b/quantum/plugins/algorithms/adapt/operator_pools/SpinFlip.hpp deleted file mode 100644 index 2ec31dc74..000000000 --- a/quantum/plugins/algorithms/adapt/operator_pools/SpinFlip.hpp +++ /dev/null @@ -1,163 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 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: - * Daniel Claudino - initial API and implementation - *******************************************************************************/ -#ifndef XACC_SPIN_FLIP_POOL_HPP_ -#define XACC_SPIN_FLIP_POOL_HPP_ - -#include "OperatorPool.hpp" -#include "xacc.hpp" -#include "xacc_service.hpp" -#include "Observable.hpp" -#include "xacc_observable.hpp" -#include "FermionOperator.hpp" -#include "Circuit.hpp" -#include "ObservableTransform.hpp" - -using namespace xacc; -using namespace xacc::quantum; - -namespace xacc { -namespace quantum { - -class SpinFlip : public OperatorPool { - -protected: - int _nElectrons, _rank = 1; - std::vector> pool, operators; - -public: - SpinFlip() = default; - - bool optionalParameters(const HeterogeneousMap parameters) override { - - if (!parameters.keyExists("n-electrons")) { - xacc::info("Pool needs number of electrons."); - return false; - } - _nElectrons = parameters.get("n-electrons"); - - if (parameters.keyExists("rank")) { - _rank = parameters.get("rank"); - } - - if (_rank > 3) { - xacc::error("Cannot flip more than three spins"); - } - - return true; - } - - // generate the pool - std::vector> - getExcitationOperators(const int &nQubits) override { - - auto _nOccupied = (int)std::ceil(_nElectrons / 2.0); - auto _nVirtual = nQubits / 2 - _nOccupied; - auto _nOrbs = _nOccupied + _nVirtual; - - if (_rank == 1) { - for (int i = 0; i < _nOccupied; i++) { - int ia = i; - int ib = i + _nOrbs; - operators.push_back(std::make_shared( - FermionOperator({{ib, 1}, {ia, 0}}, 4.0))); - - operators.push_back(std::make_shared( - FermionOperator({{ia, 1}, {ib, 0}}, 4.0))); - } - } - - if (_rank == 2) { - - for (int i = 0; i < _nOccupied; i++) { - int ia = i; - int ib = i + _nOrbs; - for (int j = i + 1; j < _nOccupied; j++) { - int ja = j; - int jb = j + _nOrbs; - - operators.push_back(std::make_shared( - FermionOperator({{ib, 1}, {ia, 0}, {jb, 1}, {ja, 0}}, 16.0))); - - operators.push_back(std::make_shared( - FermionOperator({{ia, 1}, {ib, 0}, {ja, 1}, {jb, 0}}, 16.0))); - } - } - } - - if (_rank == 3) { - - for (int i = 0; i < _nOccupied; i++) { - int ia = i; - int ib = i + _nOrbs; - - for (int j = i + 1; j < _nOccupied; j++) { - int ja = j; - int jb = j + _nOrbs; - for (int k = j + 1; k < _nOccupied; k++) { - int ka = k; - int kb = k + _nOrbs; - - operators.push_back( - std::make_shared(FermionOperator( - {{ib, 1}, {ia, 0}, {jb, 1}, {ja, 0}, {kb, 1}, {ka, 0}}, - 16.0))); - } - } - } - } - - return operators; - } - - // generate the pool - std::vector> - generate(const int &nQubits) override { - - auto ops = getExcitationOperators(nQubits); - auto jw = xacc::getService("jw"); - for (auto &op : ops) { - auto tmp = *std::dynamic_pointer_cast(op); - tmp -= tmp.hermitianConjugate(); - tmp.normalize(); - pool.push_back(jw->transform(std::make_shared(tmp))); - } - return pool; - } - - std::string operatorString(const int index) override { - - return operators[index]->toString(); - } - - std::shared_ptr - getOperatorInstructions(const int opIdx, const int varIdx) const override { - - // Instruction service for the operator to be added to the ansatz - auto gate = std::dynamic_pointer_cast( - xacc::getService("exp_i_theta")); - - // Create instruction for new operator - gate->expand({std::make_pair("pauli", pool[opIdx]->toString()), - std::make_pair("param_id", "x" + std::to_string(varIdx))}); - - return gate; - } - - const std::string name() const override { return "spin-flip"; } - const std::string description() const override { return ""; } -}; - -} // namespace quantum -} // namespace xacc - -#endif \ No newline at end of file diff --git a/quantum/plugins/algorithms/qcmx/qcmx.cpp b/quantum/plugins/algorithms/qcmx/qcmx.cpp index 25505e611..390c7e466 100644 --- a/quantum/plugins/algorithms/qcmx/qcmx.cpp +++ b/quantum/plugins/algorithms/qcmx/qcmx.cpp @@ -63,14 +63,21 @@ bool QCMX::initialize(const HeterogeneousMap ¶meters) { maxOrder = parameters.get("cmx-order"); kernel = parameters.getPointerLike("ansatz"); - // check if Observable needs JW + // check if Observable needs ObservableTransform if (std::dynamic_pointer_cast(xacc::as_shared_ptr( parameters.getPointerLike("observable")))) { observable = xacc::as_shared_ptr( parameters.getPointerLike("observable")); } else { - auto jw = xacc::getService("jw"); - observable = jw->transform(xacc::as_shared_ptr( + + std::shared_ptr mapping; + if (parameters.stringExists("transform")) { + mapping = xacc::getService( + parameters.getString("transform")); + } else { + mapping = xacc::getService("jw"); + } + observable = mapping->transform(xacc::as_shared_ptr( parameters.getPointerLike("observable"))); } diff --git a/quantum/plugins/algorithms/qeom/qeom.cpp b/quantum/plugins/algorithms/qeom/qeom.cpp index 27d8b1ea2..70d4d0b16 100644 --- a/quantum/plugins/algorithms/qeom/qeom.cpp +++ b/quantum/plugins/algorithms/qeom/qeom.cpp @@ -69,7 +69,14 @@ bool qEOM::initialize(const HeterogeneousMap ¶meters) { parameters.get>>("operators"); } - auto jw = xacc::getService("jw"); + std::shared_ptr mapping; + if (parameters.stringExists("transform")) { + mapping = xacc::getService( + parameters.getString("transform")); + } else { + mapping = xacc::getService("jw"); + } + // if no vector was given if (parameters.keyExists("n-electrons")) { auto nOrbitals = observable->nBits(); @@ -88,18 +95,18 @@ bool qEOM::initialize(const HeterogeneousMap ¶meters) { pool->optionalParameters({{"n-electrons", nOccupied}}); for (auto &op : pool->getExcitationOperators(nOrbitals)) { - operators.push_back(jw->transform(op)); + operators.push_back(mapping->transform(op)); } } if (observable->toString().find("^") != std::string::npos) { if (std::dynamic_pointer_cast(observable)) { - observable = jw->transform(observable); + observable = mapping->transform(observable); } else { auto fermionObservable = xacc::quantum::getObservable("fermion", observable->toString()); - observable = jw->transform( + observable = mapping->transform( std::dynamic_pointer_cast(fermionObservable)); } diff --git a/quantum/plugins/algorithms/vqe/vqe.cpp b/quantum/plugins/algorithms/vqe/vqe.cpp index e7c783578..b4e29bb2b 100644 --- a/quantum/plugins/algorithms/vqe/vqe.cpp +++ b/quantum/plugins/algorithms/vqe/vqe.cpp @@ -82,6 +82,11 @@ bool VQE::initialize(const HeterogeneousMap ¶meters) { gradientStrategy = xacc::getService("autodiff"); gradientStrategy->initialize(parameters); } + + if (parameters.stringExists("transform")) { + transform = parameters.getString("transform"); + } + return true; } @@ -116,7 +121,12 @@ void VQE::execute(const std::shared_ptr buffer) const { std::reverse(tmp_x.begin(), tmp_x.end()); auto evaled = kernel->operator()(tmp_x); // observe - auto kernels = observable->observe(evaled); + std::vector> kernels; + if (transform.empty()) { + kernels = observable->observe(evaled); + } else { + kernels = observable->observe(evaled, transform); + } double identityCoeff = 0.0; int nInstructionsEnergy = kernels.size(), nInstructionsGradient = 0; @@ -273,7 +283,7 @@ void VQE::execute(const std::shared_ptr buffer) const { min_child_buffers.push_back(idBuffer); for (auto b : buffers) { min_child_buffers.push_back(b); - } + } last_energy = energy; } @@ -294,7 +304,7 @@ void VQE::execute(const std::shared_ptr buffer) const { children_coeffs.push_back( child->getInformation("coefficient").as()); children_names.push_back(child->name()); - } + } } buffer->addExtraInfo("opt-exp-vals", opt_exp_vals); @@ -320,7 +330,13 @@ VQE::execute(const std::shared_ptr buffer, auto tmp_x = x; std::reverse(tmp_x.begin(), tmp_x.end()); auto evaled = xacc::as_shared_ptr(kernel)->operator()(tmp_x); - auto kernels = observable->observe(evaled); + std::vector> kernels; + if (transform.empty()) { + kernels = observable->observe(evaled); + } else { + kernels = observable->observe(evaled, transform); + } + for (auto &f : kernels) { kernelNames.push_back(f->name()); std::complex coeff = f->getCoefficient(); @@ -378,10 +394,16 @@ VQE::execute(const std::shared_ptr buffer, resultEnergy += aggregate_value; return resultEnergy; } else { - // Normal VQE: post-proces the result with the Observable. + // Normal VQE: post-process the result with the Observable. // This will also populate meta-data to the child-buffer of // the main buffer. - return observable->postProcess(tmpBuffer); + if (transform.empty()) { + return observable->postProcess(tmpBuffer); + } else { + return observable->postProcess( + tmpBuffer, Observable::PostProcessingTask::EXP_VAL_CALC, + {{"transform", transform}}); + } } }(); @@ -397,10 +419,17 @@ VQE::execute(const std::shared_ptr buffer, } else { // This will also populate information about variance to each child // buffer. - return observable->postProcess( - tmpBuffer, Observable::PostProcessingTask::VARIANCE_CALC); + if (transform.empty()) { + return observable->postProcess( + tmpBuffer, Observable::PostProcessingTask::VARIANCE_CALC); + } else { + return observable->postProcess( + tmpBuffer, Observable::PostProcessingTask::VARIANCE_CALC, + {{"transform", transform}}); + } } }(); + // Append the child buffers from the temp. buffer // to the main buffer. // These child buffers have extra-information populate in the above diff --git a/quantum/plugins/algorithms/vqe/vqe.hpp b/quantum/plugins/algorithms/vqe/vqe.hpp index 13bce642c..b1fa6abf2 100644 --- a/quantum/plugins/algorithms/vqe/vqe.hpp +++ b/quantum/plugins/algorithms/vqe/vqe.hpp @@ -26,6 +26,7 @@ class VQE : public Algorithm { Accelerator * accelerator; std::vector initial_params; std::shared_ptr gradientStrategy; + std::string transform; HeterogeneousMap parameters; diff --git a/quantum/plugins/circuits/CMakeLists.txt b/quantum/plugins/circuits/CMakeLists.txt index 5ae34dbf5..f615d995d 100644 --- a/quantum/plugins/circuits/CMakeLists.txt +++ b/quantum/plugins/circuits/CMakeLists.txt @@ -23,6 +23,7 @@ file(GLOB SRC hwe/hwe.cpp aswap/aswap.cpp qfast/qfast.cpp kak/kak.cpp + hf/hf.cpp GeneratorsActivator.cpp) usfunctiongetresourcesource(TARGET ${LIBRARY_NAME} OUT SRC) @@ -32,8 +33,9 @@ add_library(${LIBRARY_NAME} SHARED ${SRC}) target_include_directories( ${LIBRARY_NAME} - PUBLIC . range exp hwe qft uccsd ucc1 ucc3 aswap qfast kak - ${CMAKE_SOURCE_DIR}/quantum/plugins/utils) + PUBLIC . range exp hwe qft uccsd ucc1 ucc3 aswap qfast kak hf + ${CMAKE_SOURCE_DIR}/quantum/plugins/utils + ${CMAKE_SOURCE_DIR}/tpls/eigen) target_link_libraries(${LIBRARY_NAME} PUBLIC xacc PRIVATE xacc-quantum-gate xacc-pauli xacc-fermion) @@ -74,6 +76,7 @@ if(XACC_BUILD_TESTS) add_subdirectory(aswap/tests) add_subdirectory(qfast/tests) add_subdirectory(kak/tests) + add_subdirectory(hf/tests) endif() install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins) diff --git a/quantum/plugins/circuits/GeneratorsActivator.cpp b/quantum/plugins/circuits/GeneratorsActivator.cpp index 166834424..a3a38ddbd 100644 --- a/quantum/plugins/circuits/GeneratorsActivator.cpp +++ b/quantum/plugins/circuits/GeneratorsActivator.cpp @@ -22,6 +22,7 @@ #include "aswap.hpp" #include "qfast.hpp" #include "kak.hpp" +#include "hf.hpp" #include "cppmicroservices/BundleActivator.h" #include "cppmicroservices/BundleContext.h" #include "cppmicroservices/ServiceProperties.h" @@ -45,6 +46,7 @@ class US_ABI_LOCAL GeneratorsActivator : public BundleActivator { auto qfast = std::make_shared(); auto kak = std::make_shared(); auto zyz = std::make_shared(); + auto hf = std::make_shared(); context.RegisterService(hwe); context.RegisterService(expit); @@ -58,6 +60,8 @@ class US_ABI_LOCAL GeneratorsActivator : public BundleActivator { context.RegisterService(qfast); context.RegisterService(kak); context.RegisterService(zyz); + context.RegisterService(hf); + } void Stop(BundleContext context) {} diff --git a/quantum/plugins/circuits/exp/exp.cpp b/quantum/plugins/circuits/exp/exp.cpp index 8444c8fc7..19ce8326e 100644 --- a/quantum/plugins/circuits/exp/exp.cpp +++ b/quantum/plugins/circuits/exp/exp.cpp @@ -54,9 +54,17 @@ bool Exp::expand(const HeterogeneousMap ¶meters) { if (pauli_or_fermion == "fermion") { auto fermionStr = parameters.getString("fermion"); auto op = std::make_shared(fermionStr); - terms = std::dynamic_pointer_cast( - xacc::getService("jw")->transform(op)) - ->getTerms(); + + if (parameters.stringExists("transform")) { + auto mapping = parameters.getString("transform"); + terms = std::dynamic_pointer_cast( + xacc::getService(mapping)->transform(op)) + ->getTerms(); + } else { + terms = std::dynamic_pointer_cast( + xacc::getService("jw")->transform(op)) + ->getTerms(); + } } else { auto pauliStr = parameters.getString("pauli"); PauliOperator op(pauliStr); diff --git a/quantum/plugins/circuits/hf/hf.cpp b/quantum/plugins/circuits/hf/hf.cpp new file mode 100755 index 000000000..abdb52a06 --- /dev/null +++ b/quantum/plugins/circuits/hf/hf.cpp @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Daniel Claudino - initial API and implementation + *******************************************************************************/ + +#include "hf.hpp" +#include "xacc.hpp" +#include "CommonGates.hpp" +#include +#include + +using namespace xacc; +using namespace xacc::quantum; + +namespace xacc { +namespace circuits { +const std::vector HF::requiredKeys() { + return {"transform", "ne", "nq"}; +} + +bool HF::expand(const HeterogeneousMap ¶meters) { + + if (!parameters.keyExists("ne")) { + xacc::error("HF needs number of electrons."); + return false; + } + + if (!parameters.keyExists("nq")) { + xacc::error("HF needs number of qubits/orbitals."); + return false; + } + + auto ne = parameters.get("ne"); + auto nq = parameters.get("nq"); + + std::string transform = "jw"; + if (!parameters.stringExists("transform")) { + xacc::warning("No transform provided. Defaulting to Jordan-Wigner"); + } else { + transform = parameters.getString("transform"); + } + + if (transform == "jw") { + + // JW transform acts diagonally onto the occupation number basis + for (std::size_t i = 0; i < ne / 2; i++) { + addInstruction(std::make_shared(i)); + addInstruction(std::make_shared(i + nq / 2)); + } + + } else if (transform == "bk") { + + // BK transform is not trivial (not diagonal) + // this implementation follows J. Chem. Phys. 137, 224109 (2012) + + // construct BK transform matrix + Eigen::Matrix2i I = Eigen::Matrix2i::Identity(2, 2); + Eigen::MatrixXi H, B = Eigen::Matrix2i::Identity(2, 2); + B.row(0) = Eigen::VectorXi::Ones(B.rows()); + auto nBranches = (int)std::ceil(std::log2(nq)); + + for (int i = 0; i < nBranches - 1; i++) { + H = kroneckerProduct(I, B); + H.row(0) = Eigen::VectorXi::Ones(B.rows()); + B = H; + } + + // this is necessary if nq % 2 != 0 + auto dim = (int)std::pow(2, nBranches); + auto subMatrixIndex = dim - nq; + B = B.bottomRightCorner(dim - subMatrixIndex, dim - subMatrixIndex); + + // Construct occupation vector (MSB) + Eigen::VectorXi occVec = Eigen::VectorXi::Zero(nq); + for (std::size_t i = 0; i < ne / 2; i++) { + occVec(nq - i - 1) = 1; + occVec(nq / 2 + i - 1) = 1; + } + + // transform occupation vector + Eigen::VectorXi bkOccVec = B * occVec; + + // add X gates (LSB) + for (int i = 0; i < nq; i++) { + if ((bkOccVec(nq - i - 1) % 2)) { + addInstruction(std::make_shared(i)); + } + } + } + + return true; +} + +} // namespace circuits +} // namespace xacc \ No newline at end of file diff --git a/quantum/plugins/circuits/hf/hf.hpp b/quantum/plugins/circuits/hf/hf.hpp new file mode 100755 index 000000000..62a66b2c9 --- /dev/null +++ b/quantum/plugins/circuits/hf/hf.hpp @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2021 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: + * Daniel Claudino - initial API and implementation + *******************************************************************************/ +#ifndef XACC_GENERATORS_HF_HPP_ +#define XACC_GENERATORS_HF_HPP_ + +#include "Circuit.hpp" + +namespace xacc { +namespace circuits { + +class HF : public xacc::quantum::Circuit { + +public: + HF() : Circuit("hf") {} + bool expand(const xacc::HeterogeneousMap &runtimeOptions) override; + const std::vector requiredKeys() override; + DEFINE_CLONE(HF); +}; + +} // namespace circuits +} // namespace xacc + +#endif \ No newline at end of file diff --git a/quantum/plugins/circuits/hf/tests/CMakeLists.txt b/quantum/plugins/circuits/hf/tests/CMakeLists.txt new file mode 100755 index 000000000..5a51e6d3c --- /dev/null +++ b/quantum/plugins/circuits/hf/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_xacc_test(HF) +target_link_libraries(HFTester xacc-quantum-gate) \ No newline at end of file diff --git a/quantum/plugins/circuits/hf/tests/HFTester.cpp b/quantum/plugins/circuits/hf/tests/HFTester.cpp new file mode 100755 index 000000000..6016c0e02 --- /dev/null +++ b/quantum/plugins/circuits/hf/tests/HFTester.cpp @@ -0,0 +1,28 @@ +#include "xacc.hpp" +#include +#include "Circuit.hpp" +#include "xacc_service.hpp" + +using namespace xacc; + +TEST(HFTester, checkSimple) { + + auto hf_jw = std::dynamic_pointer_cast( + xacc::getService("hf")); + HeterogeneousMap options_jw = {{"ne", 2}, {"nq", 4}, {"transform", "jw"}}; + EXPECT_TRUE(hf_jw->expand(options_jw)); + + auto hf_bk = std::dynamic_pointer_cast( + xacc::getService("hf")); + HeterogeneousMap options_bk = {{"ne", 2}, {"nq", 4}, {"transform", "bk"}}; + EXPECT_TRUE(hf_bk->expand(options_bk)); + +} + +int main(int argc, char **argv) { + xacc::Initialize(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + auto ret = RUN_ALL_TESTS(); + xacc::Finalize(); + return ret; +} diff --git a/quantum/plugins/circuits/uccsd/uccsd.cpp b/quantum/plugins/circuits/uccsd/uccsd.cpp index f2041761f..3cf4c6b08 100644 --- a/quantum/plugins/circuits/uccsd/uccsd.cpp +++ b/quantum/plugins/circuits/uccsd/uccsd.cpp @@ -52,31 +52,31 @@ bool UCCSD::expand(const xacc::HeterogeneousMap &runtimeOptions) { if (runtimeOptions.stringExists("pool")) { - auto pool = xacc::getService(runtimeOptions.getString("pool")); + auto pool = + xacc::getService(runtimeOptions.getString("pool")); pool->optionalParameters({std::make_pair("n-electrons", nElectrons)}); auto operators = pool->generate(nQubits); PauliOperator sum, refOp; int i = 0; // loop over pointers to operators - for(auto& op : operators){ + for (auto &op : operators) { - // get reference to the pointer + // get reference to the pointer refOp = *std::dynamic_pointer_cast(op); - for (auto term : refOp.getTerms()){ + for (auto term : refOp.getTerms()) { - //construct operator with parameter label - PauliOperator paramOp(term.second.ops(), term.second.coeff(), "theta" + std::to_string(i)); + // construct operator with parameter label + PauliOperator paramOp(term.second.ops(), term.second.coeff(), + "theta" + std::to_string(i)); sum += paramOp; - } // add variable only if the operator is not zero - if(!refOp.getTerms().empty()){ + if (!refOp.getTerms().empty()) { addVariable("theta" + std::to_string(i)); i++; } - } terms = sum.getTerms(); @@ -119,8 +119,8 @@ bool UCCSD::expand(const xacc::HeterogeneousMap &runtimeOptions) { auto singleParams = slice(params, 0, nSingle); auto doubleParams1 = slice(params, nSingle, 2 * nSingle); auto doubleParams2 = slice(params, 2 * nSingle); - std::vector> fs{[](int i, int n) { return i; }, - [](int i, int n) { return i + n; }}; + std::vector> fs{ + [](int i, int n) { return i; }, [](int i, int n) { return i + n; }}; using OpType = std::vector>; int count = 0; @@ -206,17 +206,22 @@ bool UCCSD::expand(const xacc::HeterogeneousMap &runtimeOptions) { count++; } - auto jw = xacc::getService("jw"); - - auto compositeResult = - jw->transform(std::shared_ptr(&myOp, [](Observable *) {})); + std::shared_ptr mapping; + if (runtimeOptions.stringExists("transform")) { + mapping = xacc::getService( + runtimeOptions.getString("transform")); + } else { + mapping = xacc::getService("jw"); + } - terms = std::dynamic_pointer_cast(compositeResult)->getTerms(); + auto compositeResult = mapping->transform( + std::shared_ptr(&myOp, [](Observable *) {})); + terms = + std::dynamic_pointer_cast(compositeResult)->getTerms(); } auto pi = xacc::constants::pi; -int co = 0; auto gateRegistry = xacc::getIRProvider("quantum"); for (auto inst : terms) { @@ -261,12 +266,11 @@ int co = 0; // this is necessary for the qubit pool // which has single Y terms - if(tempFunction->nInstructions() == 0){ + if (tempFunction->nInstructions() == 0) { tempFunction->addInstruction(rx); } else { tempFunction->insertInstruction(0, rx); } - } // Add the Rotation for the last term @@ -282,7 +286,6 @@ int co = 0; rz->setParameter(0, p); tempFunction->addInstruction(rz); } - } int counter = tempFunction->nInstructions(); @@ -299,8 +302,8 @@ int co = 0; auto cnot = gateRegistry->createInstruction( "CNOT", std::vector{qbitIdx, tmp}); - // this is necessary for the qubit pool - if(counter == tempFunction->nInstructions()){ + // this is necessary for the qubit pool + if (counter == tempFunction->nInstructions()) { tempFunction->addInstruction(cnot); } else { tempFunction->insertInstruction(counter, cnot); @@ -321,12 +324,10 @@ int co = 0; } } - // Add to the total UCCSD State Prep function for (auto inst : tempFunction->getInstructions()) { addInstruction(inst); } - } for (int i = (nElectrons / 2) - 1; i >= 0; i--) { diff --git a/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.cpp b/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.cpp index fb2185d96..155bcb22e 100644 --- a/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.cpp +++ b/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.cpp @@ -11,18 +11,23 @@ namespace xacc { -template -bool ptr_is_a(std::shared_ptr ptr) { +template bool ptr_is_a(std::shared_ptr ptr) { return std::dynamic_pointer_cast(ptr) != nullptr; } -std::shared_ptr QubitTapering::transform( - std::shared_ptr Hptr_input) { +std::shared_ptr +QubitTapering::transform(std::shared_ptr Hptr_input) { // First we pre-process the observable to a PauliOperator auto obs_str = Hptr_input->toString(); - auto fermi_to_pauli = xacc::getService("jw"); + std::shared_ptr fermi_to_pauli; + if (_options.stringExists("transform")) { + fermi_to_pauli = xacc::getService( + _options.getString("transform")); + } else { + fermi_to_pauli = xacc::getService("jw"); + } std::shared_ptr Hptr; - if (ptr_is_a(Hptr_input)) { + if (ptr_is_a(Hptr_input)) { Hptr = fermi_to_pauli->transform(Hptr_input); } else if (obs_str.find("^") != std::string::npos) { auto fermionObservable = xacc::quantum::getObservable("fermion", obs_str); @@ -34,7 +39,8 @@ std::shared_ptr QubitTapering::transform( obs_str.find("Z") != std::string::npos) { Hptr = xacc::quantum::getObservable("pauli", obs_str); } else { - xacc::error("[Qubit Tapering] Error, cannot cast incoming Observable ptr to something we can process."); + xacc::error("[Qubit Tapering] Error, cannot cast incoming Observable ptr " + "to something we can process."); } // Convert the IR into a Hamiltonian @@ -96,7 +102,8 @@ std::shared_ptr QubitTapering::transform( generators.insert(tau); counter++; - if (counter == ker_dim) break; + if (counter == ker_dim) + break; for (auto &g : generators) { std::vector s(2 * n); @@ -164,10 +171,12 @@ std::shared_ptr QubitTapering::transform( doubleNSet, nSet; // Generate range(2*n) - for (int i = 0; i < 2 * n; i++) doubleNSet.insert(i); + for (int i = 0; i < 2 * n; i++) + doubleNSet.insert(i); // Generate range(n) - for (int i = 0; i < n; i++) nSet.insert(i); + for (int i = 0; i < n; i++) + nSet.insert(i); // Create the phase_sites set which is the difference between // range(2*nQ) and the hPrimePivotSet @@ -187,7 +196,8 @@ std::shared_ptr QubitTapering::transform( std::vector> phase_configs = generateCombinations(phase_sites.size(), [&](std::vector &tmp) { for (int i = 0; i < phase_sites.size(); i++) { - if (tmp[i] == 0) tmp[i] = -1; + if (tmp[i] == 0) + tmp[i] = -1; } return; }); @@ -380,7 +390,8 @@ std::vector> QubitTapering::generateCombinations( for (int nOnes = 0; nOnes <= n; nOnes++) { std::vector test(n); - for (int k = 0; k < nOnes; k++) test[n - k - 1] = 1; + for (int k = 0; k < nOnes; k++) + test[n - k - 1] = 1; do { // Perform custom mapping on the vector before adding @@ -438,12 +449,14 @@ Eigen::MatrixXi QubitTapering::gauss(Eigen::MatrixXi &A, break; } } - if (!found_row) sc += 1; + if (!found_row) + sc += 1; } unsigned row2 = 0; for (unsigned col = 0; col < A.cols() && row2 < A.rows(); col++) { - if (std::fabs(A(row2, col)) < 1e-12) continue; + if (std::fabs(A(row2, col)) < 1e-12) + continue; pivotCols.push_back(col); row2++; @@ -452,6 +465,6 @@ Eigen::MatrixXi QubitTapering::gauss(Eigen::MatrixXi &A, return A; } -} // namespace xacc +} // namespace xacc REGISTER_PLUGIN(xacc::QubitTapering, xacc::ObservableTransform) \ No newline at end of file diff --git a/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.hpp b/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.hpp index ad81765a3..4d9cbe927 100644 --- a/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.hpp +++ b/quantum/plugins/observable_transforms/qubit-tapering/qubit_tapering.hpp @@ -20,6 +20,13 @@ class QubitTapering : public xacc::ObservableTransform { return "Reduce number of qubits required by exploiting Z2 symmetries."; } + // this is in case we want to specify the transform + // will default to JW + void setOptions(HeterogeneousMap &options) { + _options = options; + return; + } + private: Eigen::MatrixXi computeTableaux(PauliOperator &H, const int nQubits); std::vector> generateCombinations( @@ -30,5 +37,7 @@ class QubitTapering : public xacc::ObservableTransform { Eigen::MatrixXi gauss(Eigen::MatrixXi &A, std::vector &pivotCols); + HeterogeneousMap _options; + }; } // namespace xacc \ No newline at end of file diff --git a/quantum/plugins/utils/OperatorPool.hpp b/quantum/plugins/utils/OperatorPool.hpp index 54a7b62ef..41ef4f089 100644 --- a/quantum/plugins/utils/OperatorPool.hpp +++ b/quantum/plugins/utils/OperatorPool.hpp @@ -63,6 +63,10 @@ class OperatorPool : public Identifiable { exit(0); return; } + + virtual void setIfNeedsNumberOfParticles(const bool) { + return; + } // end of custom pool methods virtual bool optionalParameters(const HeterogeneousMap parameters) = 0; @@ -75,6 +79,11 @@ class OperatorPool : public Identifiable { virtual std::shared_ptr getOperatorInstructions(const int opIdx, const int varIdx) const = 0; + // this is for pools that needs number of electrons + // but should come in handy for any Hamiltonians with "fillings" + // e.g, Hubbard + virtual bool needsNumberOfParticles() const = 0; + virtual std::vector> getExcitationOperators(const int &nQubits) { XACCLogger::instance()->error("OperatorPool::getExcitationOperators(int)" diff --git a/quantum/python/xacc-quantum-py.cpp b/quantum/python/xacc-quantum-py.cpp index dafd60e08..45df0c849 100644 --- a/quantum/python/xacc-quantum-py.cpp +++ b/quantum/python/xacc-quantum-py.cpp @@ -162,6 +162,12 @@ void bind_quantum(py::module &m) { std::shared_ptr)) & xacc::Observable::observe, "") + .def("observe", + (std::vector>( + xacc::Observable::*)( + std::shared_ptr, std::string)) & + xacc::Observable::observe, + "") .def(py::init<>()) .def(py::init>()) .def(py::init()) @@ -209,6 +215,8 @@ void bind_quantum(py::module &m) { .def("generate", &OperatorPool::generate) .def("operatorString", &OperatorPool::operatorString) .def("getOperatorInstructions", &OperatorPool::getOperatorInstructions) + .def("needsNumberOfParticles", &OperatorPool::needsNumberOfParticles) + .def("setIfNeedsNumberOfParticles", &OperatorPool::setIfNeedsNumberOfParticles) .def( "optionalParameters", [](OperatorPool &op, PyHeterogeneousMap ¶ms) { diff --git a/quantum/python/xacc-quantum-py.hpp b/quantum/python/xacc-quantum-py.hpp index c2de224db..ad3cf0d89 100644 --- a/quantum/python/xacc-quantum-py.hpp +++ b/quantum/python/xacc-quantum-py.hpp @@ -85,6 +85,14 @@ class PyOperatorPool : public OperatorPool { PYBIND11_OVERLOAD_PURE(std::shared_ptr, xacc::quantum::OperatorPool, getOperatorInstructions, opIdx, varIdx); } + bool needsNumberOfParticles() const override { + PYBIND11_OVERLOAD_PURE(bool, xacc::quantum::OperatorPool, needsNumberOfParticles); + } + + void setIfNeedsNumberOfParticles(const bool needsNumberOfParticles) { + PYBIND11_OVERLOAD(void, xacc::quantum::OperatorPool, setIfNeedsNumberOfParticles, needsNumberOfParticles); + } + }; class PyAlgorithmGradientStrategy : public AlgorithmGradientStrategy { diff --git a/xacc/ir/Observable.hpp b/xacc/ir/Observable.hpp index 598576afd..04d8fac46 100644 --- a/xacc/ir/Observable.hpp +++ b/xacc/ir/Observable.hpp @@ -32,6 +32,12 @@ class SparseTriplet class Observable : public Identifiable { public: + virtual std::vector> + observe(std::shared_ptr CompositeInstruction, + std::string transformName) { + return {}; + } + virtual std::vector> observe(std::shared_ptr CompositeInstruction) = 0; @@ -56,12 +62,9 @@ class Observable : public Identifiable { return std::complex(1.0, 0.0); } - virtual std::vector - to_sparse_matrix() { - return {}; - } + virtual std::vector to_sparse_matrix() { return {}; } - virtual std::shared_ptr commutator(std::shared_ptr){ + virtual std::shared_ptr commutator(std::shared_ptr) { return nullptr; } @@ -72,8 +75,7 @@ class Observable : public Identifiable { static inline const std::string VARIANCE_CALC = "variance"; }; // Post process the execution data (stored on the AcceleratorBuffer) - virtual double - postProcess( + virtual double postProcess( std::shared_ptr buffer, const std::string &postProcessTask = PostProcessingTask::EXP_VAL_CALC, const HeterogeneousMap &extra_data = {}) = 0; diff --git a/xacc/ir/ObservableTransform.hpp b/xacc/ir/ObservableTransform.hpp index 846211f97..57208b1fe 100644 --- a/xacc/ir/ObservableTransform.hpp +++ b/xacc/ir/ObservableTransform.hpp @@ -19,6 +19,8 @@ class ObservableTransform : public Identifiable { public: virtual std::shared_ptr transform(std::shared_ptr obs) = 0; + + virtual void setOptions(HeterogeneousMap &options) { return; } }; } // namespace xacc