Unverified Commit aa31aa7b authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #123 from zpparks314/master

Updates to Python decorators and vqe
parents 8048a6cb 0f06a446
add_subdirectory(vqe)
\ No newline at end of file
add_subdirectory(vqe)
add_subdirectory(vqe-energy)
file(GLOB PYDECORATORS ${CMAKE_CURRENT_SOURCE_DIR}/vqe/python/*.py ${CMAKE_CURRENT_SOURCE_DIR}/vqe-energy/python/*.py)
install(FILES ${PYDECORATORS} DESTINATION ${CMAKE_INSTALL_PREFIX}/py-plugins)
\ No newline at end of file
set(LIBRARY_NAME xacc-algorithm-vqe-energy)
file(GLOB SRC *.cpp)
usfunctiongetresourcesource(TARGET ${LIBRARY_NAME} OUT SRC)
usfunctiongeneratebundleinit(TARGET ${LIBRARY_NAME} OUT SRC)
add_library(${LIBRARY_NAME} SHARED ${SRC})
target_include_directories(
${LIBRARY_NAME}
PUBLIC .)
target_link_libraries(${LIBRARY_NAME} PUBLIC xacc CppMicroServices)
set(_bundle_name xacc_algorithm_vqe_energy)
set_target_properties(${LIBRARY_NAME}
PROPERTIES COMPILE_DEFINITIONS
US_BUNDLE_NAME=${_bundle_name}
US_BUNDLE_NAME
${_bundle_name})
usfunctionembedresources(TARGET
${LIBRARY_NAME}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
FILES
manifest.json)
if(APPLE)
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "@loader_path/../lib")
set_target_properties(${LIBRARY_NAME}
PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
else()
set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")
set_target_properties(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-shared")
endif()
if(XACC_BUILD_TESTS)
add_subdirectory(tests)
endif()
install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins)
#include "vqe-energy.hpp"
#include "cppmicroservices/BundleActivator.h"
#include "cppmicroservices/BundleContext.h"
#include "cppmicroservices/ServiceProperties.h"
#include <memory>
#include <set>
using namespace cppmicroservices;
namespace {
/**
*/
class US_ABI_LOCAL VQEEnergyActivator : public BundleActivator {
public:
VQEEnergyActivator() {}
/**
*/
void Start(BundleContext context) {
auto c = std::make_shared<xacc::algorithm::VQEEnergy>();
context.RegisterService<xacc::Algorithm>(c);
}
/**
*/
void Stop(BundleContext /*context*/) {}
};
} // namespace
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(VQEEnergyActivator)
{
"bundle.symbolic_name" : "xacc_algorithm_vqe_energy",
"bundle.activator" : true,
"bundle.name" : "XACC VQE-Energy Algorithm",
"bundle.description" : ""
}
import unittest as test
import sys
import xacc
from xacc import PauliOperator
class TestPyVQE(test.TestCase):
def setUp(self):
self.qpu = xacc.getAccelerator('tnqvm')
self.buffer = self.qpu.createBuffer('q', 2)
self.ham = PauliOperator(5.906709445) + \
PauliOperator({0: 'X', 1: 'X'}, -2.1433) + \
PauliOperator({0: 'Y', 1: 'Y'}, -2.1433) + \
PauliOperator({0: 'Z'}, .21829) + \
PauliOperator({1: 'Z'}, -6.125)
def test_energy(self):
@xacc.qpu(algo='energy', accelerator=self.qpu, observable=self.ham)
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.71515, places=4)
if __name__ == '__main__':
xacc.Initialize()
test.main()
xacc.Finalize()
\ No newline at end of file
from pelix.ipopo.decorators import (ComponentFactory, Property, Requires,
BindField, UnbindField, Provides, Instantiate)
import xacc
import inspect
import random
import numpy as np
@ComponentFactory("wrapped_energy_factory")
@Provides("decorator_algorithm_service")
@Property("_algorithm", "algorithm", "energy")
@Property("_name", "name", "energy")
@Instantiate("wrapped_energy_instance")
class WrappedEnergyF(xacc.DecoratorFunction):
def __call__(self, *args, **kwargs):
super().__call__(*args, **kwargs)
execParams = {'accelerator': self.kwargs['accelerator'], 'ansatz': self.compiledKernel, 'observable': self.kwargs["observable"]}
if not isinstance(args[0], xacc.AcceleratorBuffer):
raise RuntimeError(
'First argument of an xacc kernel must be the Accelerator Buffer to operate on.')
buffer = args[0]
ars = args[1:]
if len(ars) > 0:
execParams['initial-parameters'] = list(ars)
else:
execParams['initial-parameters'] = random.randrange(-np.pi, np.pi)
vqe_energy = xacc.getAlgorithm('vqe-energy', execParams)
vqe_energy.execute(buffer)
return
\ No newline at end of file
include_directories(${CMAKE_BINARY_DIR})
add_xacc_test(VQEEnergy)
target_link_libraries(VQEEnergyTester xacc CppMicroServices xacc-pauli)
\ No newline at end of file
#include <gtest/gtest.h>
#include "XACC.hpp"
#include "xacc_service.hpp"
#include "Optimizer.hpp"
#include "Observable.hpp"
#include "Algorithm.hpp"
#include "PauliOperator.hpp"
using namespace xacc;
const std::string ansatz = R"a(def f(buffer, theta):
X(0)
Ry(theta, 1)
CNOT(1,0)
)a";
TEST(VQEEnergyTester, checkSimple) {
if (xacc::hasAccelerator("tnqvm") && xacc::hasCompiler("xacc-py")) {
auto acc = xacc::getAccelerator("tnqvm");
auto buffer = acc->createBuffer("q", 2);
auto compiler = xacc::getService<xacc::Compiler>("xacc-py");
auto ir = compiler->compile(ansatz, nullptr);
auto ruccsd = ir->getKernel("f");
auto optimizer = xacc::getOptimizer("nlopt");
xacc::quantum::PauliOperator observable;
observable.fromString("(-6.125,0) Z1 + (0.21829,0) Z0 + (-2.1433,0) Y0 Y1 + (5.90671,0) + (-2.1433,0) X0 X1");
auto vqe_energy = xacc::getService<Algorithm>("vqe-energy");
EXPECT_TRUE(vqe_energy->initialize({{"ansatz",ruccsd}, {"accelerator",acc}, {"observable", &observable},{"initial-parameters", {0.5}}}));//
vqe_energy->execute(buffer);
EXPECT_NEAR(-1.74916, mpark::get<double>(buffer->getInformation("opt-val")), 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;
}
#include "vqe-energy.hpp"
#include "Observable.hpp"
#include "XACC.hpp"
#include "InstructionParameter.hpp"
#include <memory>
using namespace xacc;
namespace xacc {
namespace algorithm {
bool VQEEnergy::initialize(const AlgorithmParameters &parameters) {
if (!parameters.count("observable")) {
return false;
} else if (!parameters.count("ansatz")) {
return false;
} else if (!parameters.count("accelerator")) {
return false;
}
try {
observable =
parameters.at("observable").as_no_error<std::shared_ptr<Observable>>();
} catch (std::exception &e) {
observable = std::shared_ptr<Observable>(
parameters.at("observable").as<Observable *>(), [](Observable *) {});
}
kernel = parameters.at("ansatz").as<std::shared_ptr<Function>>();
accelerator = parameters.at("accelerator").as<std::shared_ptr<Accelerator>>();
if (parameters.count("initial-parameters")) {
initial_params =
parameters.at("initial-parameters").as<std::vector<double>>();
}
return true;
}
const std::vector<std::string> VQEEnergy::requiredParameters() const {
return {"observable", "accelerator", "ansatz"};
}
void VQEEnergy::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
// void VQE::execute(xacc::Observable &observable, Optimizer &optimizer) {
auto kernels = observable->observe(kernel);
std::vector<double> coefficients;
std::vector<std::string> kernelNames;
std::vector<std::shared_ptr<Function>> fsToExec;
double identityCoeff = 0.0;
for (auto &f : kernels) {
kernelNames.push_back(f->name());
InstructionParameter p = f->getOption("coefficient");
std::complex<double> coeff = p.as<std::complex<double>>();
if (f->nInstructions() > kernel->nInstructions()) {
fsToExec.push_back(f->operator()(initial_params));
coefficients.push_back(std::real(coeff));
} else {
identityCoeff += std::real(coeff);
}
}
auto buffers = accelerator->execute(buffer, fsToExec);
double energy = identityCoeff;
for (int i = 0; i < buffers.size(); i++) {
auto expval = buffers[i]->getExpectationValueZ();
energy += expval * coefficients[i];
buffers[i]->addExtraInfo("coefficient", coefficients[i]);
buffers[i]->addExtraInfo("kernel", fsToExec[i]->name());
buffers[i]->addExtraInfo("exp-val-z", expval);
buffers[i]->addExtraInfo("parameters", initial_params);
buffer->appendChild(fsToExec[i]->name(), buffers[i]);
}
std::stringstream ss;
ss << "E(" << initial_params[0];
for (int i = 1; i < initial_params.size(); i++)
ss << "," << initial_params
[i];
ss << ") = " << energy;
xacc::info(ss.str());
buffer->addExtraInfo("opt-val", ExtraInfo(energy));
buffer->addExtraInfo("opt-params", ExtraInfo(initial_params));
return;
}
} // namespace algorithm
} // namespace xacc
\ No newline at end of file
#ifndef XACC_ALGORITHM_VQE_ENERGY_HPP_
#define XACC_ALGORITHM_VQE_ENERGY_HPP_
#include "Algorithm.hpp"
namespace xacc {
namespace algorithm {
class VQEEnergy : public Algorithm {
protected:
std::shared_ptr<Observable> observable;
std::shared_ptr<Function> kernel;
std::shared_ptr<Accelerator> accelerator;
std::vector<double> initial_params;
AlgorithmParameters parameters;
public:
bool initialize(const AlgorithmParameters &parameters) override;
const std::vector<std::string> requiredParameters() const override;
void execute(const std::shared_ptr<AcceleratorBuffer> buffer) const override;
const std::string name() const override { return "vqe-energy"; }
const std::string description() const override { return ""; }
};
} // namespace algorithm
} // namespace xacc
#endif
\ No newline at end of file
import unittest as test
import sys
import xacc
from xacc import PauliOperator
class TestPyVQE(test.TestCase):
def setUp(self):
self.qpu = xacc.getAccelerator('tnqvm')
self.buffer = self.qpu.createBuffer('q', 2)
self.ham = PauliOperator(5.906709445) + \
PauliOperator({0: 'X', 1: 'X'}, -2.1433) + \
PauliOperator({0: 'Y', 1: 'Y'}, -2.1433) + \
PauliOperator({0: 'Z'}, .21829) + \
PauliOperator({1: 'Z'}, -6.125)
def test_nlopt_default(self):
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='nlopt')
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=4)
def test_nlopt_options(self):
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='nlopt', options={"nlopt-ftol": 1e-5, "nlopt-maxeval":10, "nlopt-optimizer": "cobyla"})
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=2)
def test_scipy(self):
# causes a numpy runtime warning. I think it has to do with whatever is the default scipy optimizer method
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='scipy-opt')
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=4)
def test_scipy_options(self):
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='scipy-opt', options={"method": "COBYLA", 'options':{'maxiter':10}})
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=2)
def test_bobyqa(self):
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='bobyqa-opt')
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=4)
def test_bobyqa_options(self):
@xacc.qpu(algo='vqe', accelerator=self.qpu, observable=self.ham, optimizer='bobyqa-opt', options={'maxfun': 10})
def ansatz(buffer, t0):
X(0)
Ry(t0, 1)
CNOT(1, 0)
ansatz(self.buffer, 0.5)
self.assertAlmostEqual(self.buffer.getInformation("opt-val"), -1.74916, places=4)
if __name__ == '__main__':
xacc.Initialize()
test.main()
xacc.Finalize()
\ No newline at end of file
from abc import abstractmethod, ABC
from pelix.ipopo.decorators import (Provides, ComponentFactory, Property, Instantiate)
import xacc
import numpy as np
@Provides("vqe_optimization")
class VQEOpt(ABC):
@abstractmethod
def optimize(self, buffer, optimizer_args, execParams):
self.opt_args = optimizer_args
self.execParams = execParams
self.energies = []
self.angles = []
self.vqe_energy = xacc.getAlgorithm('vqe-energy')
self.buffer = buffer
if 'initial-parameters' in self.execParams:
self.init_args = self.execParams['initial-parameters']
else:
import random
pi = 3.141592653
self.init_args = np.array([random.uniform(-pi, pi) for _ in range(self.execParams['ansatz'].nParameters())])
# Define the objective function here
# This is a default objective function using XACC VQE
# that converges on the computed energy, computing the energy
# every iteration
@abstractmethod
def energy(self, params):
self.execParams['initial-parameters'] = params.tolist()
self.vqe_energy.initialize(self.execParams)
self.vqe_energy.execute(self.buffer)
e = self.buffer.getInformation("vqe-energy")
if 'rdm-purification' in self.execParams['accelerator'].name():
t = self.buffer.getAllUnique('parameters')
ind = len(t) - 1
children = self.buffer.getChildren('parameters', t[ind])
e = children[1].getInformation('purified-energy')
self.angles.append(','.join(map(str, params.tolist())))
self.energies.append(e)
fileName = ".persisted_buffer_%s" % (self.buffer.getInformation('accelerator')) if self.buffer.hasExtraInfoKey('accelerator') \
else ".persisted_buffer_%s" % (self.execParams['accelerator'].name())
file = open(fileName+'.ab', 'w')
file.write(str(self.buffer))
file.close()
return e
# xacc.info("The energy() method is meant to be implemented by VQEOpt subclasses!")
# exit(1)
# pass
@ComponentFactory("scipy_opt_factory")
@Property("_vqe_optimizer", "vqe_optimizer", "scipy-opt")
@Property("_name", "name", "scipy-opt")
@Instantiate("scipy_opt_instance")
class ScipyOpt(VQEOpt):
def optimize(self, buffer, optimizer_args, execParams):
super().optimize(buffer, optimizer_args, execParams)
from scipy.optimize import minimize
opt_result = minimize(self.energy, self.init_args, **self.opt_args)
# Optimizer adds the results to the buffer automatically
buffer.addExtraInfo('vqe-energies', self.energies)
buffer.addExtraInfo('vqe-params', self.angles)
optimal_angles = [float(x) for x in self.angles[self.energies.index(min(self.energies))].split(",")]
buffer.addExtraInfo('opt-params', optimal_angles)
buffer.addExtraInfo('opt-val', min(self.energies))
# Noticing something very weird if the objective energy function
# resides in the super class; looks like it needs to be
# redefined everytime (in an optimizer)
def energy(self, params):
# super().energy(params)
self.execParams['initial-parameters'] = params.tolist()
self.vqe_energy.initialize(self.execParams)
self.vqe_energy.execute(self.buffer)
e = self.buffer.getInformation("opt-val")
if 'rdm-purification' in self.execParams['accelerator'].name():
t = self.buffer.getAllUnique('parameters')
ind = len(t) - 1
children = self.buffer.getChildren('parameters', t[ind])
e = children[1].getInformation('purified-energy')
self.angles.append(','.join(map(str, params.tolist())))
self.energies.append(e)
fileName = ".persisted_buffer_%s" % (self.buffer.getInformation('accelerator')) if self.buffer.hasExtraInfoKey('accelerator') \
else ".persisted_buffer_%s" % (self.execParams['accelerator'].name())
file = open(fileName+'.ab', 'w')
file.write(str(self.buffer))
file.close()
return e
@ComponentFactory("bobyqa_opt_factory")
@Property("_vqe_optimizer", "vqe_optimizer", "bobyqa-opt")
@Property("_name", "name", "bobyqa-opt")
@Instantiate("bobyqa_opt_instance")
class BOBYQAOpt(VQEOpt):
def optimize(self, buffer, optimizer_args, execParams):
super().optimize(buffer, optimizer_args, execParams)
import pybobyqa
if 'options' in self.opt_args:
base_args = self.opt_args['options']
opt_result = pybobyqa.solve(self.energy, self.init_args, **base_args)
else:
opt_result = pybobyqa.solve(self.energy, self.init_args, **self.opt_args)
# Optimizer adds the results to the buffer automatically
buffer.addExtraInfo('vqe-energies', self.energies)
buffer.addExtraInfo('vqe-parameters', self.angles)
optimal_angles = [float(x) for x in self.angles[self.energies.index(min(self.energies))].split(",")]
buffer.addExtraInfo('opt-params', optimal_angles)
buffer.addExtraInfo('opt-val', min(self.energies))
# Noticing something very weird if the objective energy function
# resides in the super class; looks like it needs to be
# redefined everytime (in an optimizer)
def energy(self, params):
self.execParams['initial-parameters'] = params.tolist()
self.vqe_energy.initialize(self.execParams)
self.vqe_energy.execute(self.buffer)
e = self.buffer.getInformation("opt-val")
if 'rdm-purification' in self.execParams['accelerator'].name():
t = self.buffer.getAllUnique('parameters')
ind = len(t) - 1
children = self.buffer.getChildren('parameters', t[ind])
e = children[1].getInformation('purified-energy')
self.angles.append(','.join(map(str, params.tolist())))
self.energies.append(e)
fileName = ".persisted_buffer_%s" % (self.buffer.getInformation('accelerator')) if self.buffer.hasExtraInfoKey('accelerator') \
else ".persisted_buffer_%s" % (self.execParams['accelerator'].name())
file = open(fileName+