Skip to content

Commit

Permalink
Merge pull request #10 from danclaudino/get_basis_rotations
Browse files Browse the repository at this point in the history
Get basis rotations
  • Loading branch information
danclaudino authored Jun 20, 2024
2 parents d669437 + 57e3b12 commit 85ba1b5
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 57 deletions.
7 changes: 3 additions & 4 deletions python/py_observable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@
* Alexander J. McCaskey - initial API and implementation
*******************************************************************************/
#include "py_observable.hpp"
//#include "Observable.hpp"
#include "Observable.hpp"
#include "PauliOperator.hpp"
#include "FermionOperator.hpp"
//#include "Utils.hpp"
#include "xacc_service.hpp"
#include "py_heterogeneous_map.hpp"
#include "ObservableTransform.hpp"
//#include <memory>

using namespace xacc::quantum;

Expand Down Expand Up @@ -46,7 +44,8 @@ void bind_observable(py::module &m) {
xacc::Observable::fromString,
"")
.def("postProcess", &xacc::Observable::postProcess,
"Post-process the execution results.");
"Post-process the execution results.")
.def("getBasisRotations", &xacc::Observable::getMeasurementBasisRotations);

m.def("getObservable",
[](const std::string &type,
Expand Down
5 changes: 5 additions & 0 deletions python/py_observable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <pybind11/functional.h>
#include <vector>

#include "Observable.hpp"
#include "xacc.hpp"

namespace py = pybind11;
Expand Down Expand Up @@ -66,6 +67,10 @@ class PyObservable : public xacc::Observable {
postProcessTask, extra_data);
}

std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() override {
PYBIND11_OVERLOAD_PURE(std::vector<std::shared_ptr<CompositeInstruction>>, xacc::Observable, getBasisRotations);
}

};

void bind_observable(py::module& m);
7 changes: 7 additions & 0 deletions quantum/observable/fermion/FermionOperator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

#include <Utils.hpp>
#include <iomanip>
#include <memory>

#include "CompositeInstruction.hpp"
#include "FermionListenerImpl.hpp"
#include "FermionOperatorLexer.h"
#include "ObservableTransform.hpp"
Expand Down Expand Up @@ -337,6 +339,11 @@ std::shared_ptr<Observable> FermionOperator::normalOrder() {
return normalOrdered;
}

std::vector<std::shared_ptr<CompositeInstruction>> FermionOperator::getMeasurementBasisRotations() {
auto transform = xacc::getService<ObservableTransform>("jw");
return transform->transform(xacc::as_shared_ptr(this))->getMeasurementBasisRotations();
}

} // namespace quantum
} // namespace xacc

Expand Down
3 changes: 3 additions & 0 deletions quantum/observable/fermion/FermionOperator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ class FermionOperator
const HeterogeneousMap &extra_data) override;

std::shared_ptr<Observable> normalOrder() override;

std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() override;

};

} // namespace quantum
Expand Down
138 changes: 86 additions & 52 deletions quantum/observable/pauli/PauliOperator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
* Alexander J. McCaskey - initial API and implementation
*******************************************************************************/
#include "PauliOperator.hpp"
#include "CompositeInstruction.hpp"
#include "IRProvider.hpp"
#include <bits/c++config.h>
#include <cassert>
#include <cmath>
#include <memory>
#include <regex>
#include <set>
#include <iostream>
#include "Instruction.hpp"
#include "Observable.hpp"
#include "xacc.hpp"
#include "xacc_service.hpp"
Expand Down Expand Up @@ -181,13 +186,14 @@ std::complex<double> PauliOperator::coefficient() {
}
return terms.begin()->second.coeff();
}

std::vector<std::shared_ptr<CompositeInstruction>>
PauliOperator::observe(std::shared_ptr<CompositeInstruction> function) {

auto basisRotations = getMeasurementBasisRotations();
// Create a new GateQIR to hold the spin based terms
auto gateRegistry = xacc::getService<IRProvider>("quantum");
std::vector<std::shared_ptr<CompositeInstruction>> observed;
int counter = 0;
auto pi = xacc::constants::pi;

// If the incoming function has instructions that have
Expand All @@ -201,13 +207,16 @@ PauliOperator::observe(std::shared_ptr<CompositeInstruction> function) {
buf_name = function->getInstruction(0)->getBufferNames()[0];
}

// Populate GateQIR now...
for (auto &inst : terms) {
assert(basisRotations.size() == terms.size());

Term spinInst = inst.second;
auto it1 = basisRotations.begin();
auto it2 = terms.begin();
for (; it1 != basisRotations.end() && it2 != terms.end(); ++it1, ++it2) {

Term spinInst = (*it2).second;

auto gateFunction =
gateRegistry->createComposite(inst.first, function->getVariables());
gateRegistry->createComposite((*it2).first, function->getVariables());

gateFunction->setCoefficient(spinInst.coeff());

Expand All @@ -219,56 +228,12 @@ PauliOperator::observe(std::shared_ptr<CompositeInstruction> function) {
gateFunction->addArgument(arg, 0);
}

// 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;
int t = qbit;
std::size_t tt = t;
auto gateName = terms[i].second;
auto meas = gateRegistry->createInstruction("Measure",
std::vector<std::size_t>{tt});
if (!buf_name.empty())
meas->setBufferNames({buf_name});
xacc::InstructionParameter classicalIdx(qbit);
meas->setParameter(0, classicalIdx);
measurements.push_back(meas);

if (gateName == "X") {
auto hadamard =
gateRegistry->createInstruction("H", std::vector<std::size_t>{tt});
if (!buf_name.empty())
hadamard->setBufferNames({buf_name});
gateFunction->addInstruction(hadamard);
} else if (gateName == "Y") {
auto rx =
gateRegistry->createInstruction("Rx", std::vector<std::size_t>{tt});
if (!buf_name.empty())
rx->setBufferNames({buf_name});
InstructionParameter p(pi / 2.0);
rx->setParameter(0, p);
gateFunction->addInstruction(rx);
}
}

if (!spinInst.isIdentity()) {
for (auto m : measurements) {
gateFunction->addInstruction(m);
}
for (auto& rot : (*it1)->getInstructions()) {
rot->setBufferNames({buf_name});
gateFunction->addInstruction(rot);
}

observed.push_back(gateFunction);
counter++;
}
return observed;
}
Expand Down Expand Up @@ -1139,6 +1104,75 @@ double PauliOperator::postProcess(std::shared_ptr<AcceleratorBuffer> buffer,
xacc::error("Unknown post-processing task: " + postProcessTask);
return 0.0;
}

std::vector<std::shared_ptr<CompositeInstruction>> PauliOperator::getMeasurementBasisRotations() {

// the idea is that, for something like VQE, we only need to know
// the qubits that need to be measure in the X or Y basis
// so we map the index of the qubit, to the corresponding
// rotation operators, then later add measure to all qubits
std::vector<std::shared_ptr<CompositeInstruction>> basisRotations;

auto pi = xacc::constants::pi;
// Create a new GateQIR to hold the spin based terms
auto gateRegistry = xacc::getService<IRProvider>("quantum");

// If the incoming function has instructions that have
// their buffer_names set, then we need to set the
// new measurement instructions buffer names to be the same.
// Here we assume that all instructions have the same buffer name
std::string buf_name = "";

// Populate GateQIR now...
for (auto &inst : terms) {

Term spinInst = inst.second;

// Loop over all terms in the Spin Instruction
// and create instructions to run on the Gate QPU.
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});
}
}
auto rotationGates = gateRegistry->createComposite(inst.first);
std::vector<std::size_t> measureIdxs;
for (int i = terms.size() - 1; i >= 0; i--) {

std::size_t qbit = terms[i].first;
measureIdxs.push_back(qbit);
auto gateName = terms[i].second;

if (gateName == "X") {
auto hadamard =
gateRegistry->createInstruction("H", {qbit});
if (!buf_name.empty())
hadamard->setBufferNames({buf_name});
rotationGates->addInstruction(hadamard);
} else if (gateName == "Y") {
auto rx =
gateRegistry->createInstruction("Rx", {qbit}, {pi / 2.0});
if (!buf_name.empty())
rx->setBufferNames({buf_name});
rotationGates->addInstruction(rx);
}

}
for(auto& mb : measureIdxs) {
auto meas = gateRegistry->createInstruction("Measure", {mb});
if (!buf_name.empty())
meas->setBufferNames({buf_name});
rotationGates->addInstruction(meas);
}
basisRotations.push_back(rotationGates);
}

return basisRotations;
}

} // namespace quantum
} // namespace xacc

Expand Down
3 changes: 3 additions & 0 deletions quantum/observable/pauli/PauliOperator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ class PauliOperator
virtual double postProcess(std::shared_ptr<AcceleratorBuffer> buffer,
const std::string &postProcessTask,
const HeterogeneousMap &extra_data) override;

std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() override;

private:
double
calcExpValFromGroupedExecution(std::shared_ptr<AcceleratorBuffer> buffer);
Expand Down
11 changes: 10 additions & 1 deletion quantum/observable/pauli/tests/PauliOperatorTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,19 @@ TEST(PauliOperatorTester, checkGroupingQaoaPostProcessMSB) {
EXPECT_NEAR(exp_val, 2.0, 0.1);
}

TEST(PauliOperatorTester, checkGetBasisRotations) {
PauliOperator op;
op.fromString("(-0.5,0) Z0 Z1 + (-0.5,0) X0 X1 + (-0.5,0) Y0 Y1");
auto rot = op.getMeasurementBasisRotations();
for(auto& r :rot) {
std::cout << r->toString() << "\n";
}
}

int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();
xacc::Finalize();
return ret;
}
}
2 changes: 2 additions & 0 deletions xacc/ir/Observable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class Observable : public Identifiable {
}
}

virtual std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() = 0;

virtual const std::string toString() = 0;
virtual void fromString(const std::string str) = 0;
virtual const int nBits() = 0;
Expand Down
5 changes: 5 additions & 0 deletions xacc/tests/AlgorithmTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Alexander J. McCaskey - initial API and implementation
*******************************************************************************/
#include <gtest/gtest.h>
#include <memory>
#include <vector>

#include "xacc.hpp"
#include "Algorithm.hpp"
Expand Down Expand Up @@ -56,6 +58,9 @@ class TestObservable : public Observable {
const HeterogeneousMap &extra_data) override {
return 0.0;
}
std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() override {
return {};
}
};

TEST(AlgorithmTester, checkSimple) {
Expand Down
3 changes: 3 additions & 0 deletions xacc/tests/HeterogeneousTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ TEST(HeterogeneousMapTester, checkVQE) {
const HeterogeneousMap &extra_data) override {
return 0.0;
}
std::vector<std::shared_ptr<CompositeInstruction>> getMeasurementBasisRotations() override {
return {};
}
};

std::shared_ptr<Observable> observable = std::make_shared<TestObservable>();
Expand Down

0 comments on commit 85ba1b5

Please sign in to comment.