Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Accelerator and VQE to work with measurement bases #12

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions quantum/observable/pauli/PauliOperator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ PauliOperator::observe(std::shared_ptr<CompositeInstruction> function) {
auto it1 = basisRotations.begin();
auto it2 = terms.begin();
for (; it1 != basisRotations.end() && it2 != terms.end(); ++it1, ++it2) {

Term spinInst = (*it2).second;

auto gateFunction =
Expand Down Expand Up @@ -1138,7 +1137,9 @@ std::vector<std::shared_ptr<CompositeInstruction>> PauliOperator::getMeasurement
terms.push_back({kv.first, kv.second});
}
}

auto rotationGates = gateRegistry->createComposite(inst.first);
rotationGates->setCoefficient(spinInst.coeff());
std::vector<std::size_t> measureIdxs;
for (int i = terms.size() - 1; i >= 0; i--) {

Expand Down Expand Up @@ -1167,9 +1168,8 @@ std::vector<std::shared_ptr<CompositeInstruction>> PauliOperator::getMeasurement
meas->setBufferNames({buf_name});
rotationGates->addInstruction(meas);
}
basisRotations.push_back(rotationGates);
basisRotations.push_back(rotationGates);
}

return basisRotations;
}

Expand Down
152 changes: 100 additions & 52 deletions quantum/plugins/algorithms/vqe/vqe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
#include "xacc_service.hpp"
#include "AcceleratorDecorator.hpp"

#include <memory>
#include <iomanip>
#include <string>

using namespace xacc;

Expand Down Expand Up @@ -55,8 +53,6 @@ bool VQE::initialize(const HeterogeneousMap &parameters) {
gradientStrategy =
parameters.get<std::shared_ptr<AlgorithmGradientStrategy>>(
"gradient_strategy");
// gradientStrategy->initialize({std::make_pair("observable",
// xacc::as_shared_ptr(observable))});
}

if (parameters.stringExists("gradient_strategy") &&
Expand All @@ -82,6 +78,11 @@ bool VQE::initialize(const HeterogeneousMap &parameters) {
gradientStrategy = xacc::getService<AlgorithmGradientStrategy>("autodiff");
gradientStrategy->initialize(parameters);
}

cacheMeasurements = false;
if (parameters.keyExists<bool>("cache-measurement-basis")) {
cacheMeasurements = parameters.get<bool>("cache-measurement-basis");
}
return true;
}

Expand All @@ -98,47 +99,67 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {

std::vector<std::shared_ptr<AcceleratorBuffer>> min_child_buffers;

// auto kernels = observable->observe(xacc::as_shared_ptr(kernel));
// Cache of energy values during iterations.
std::vector<double> energies;
double last_energy = std::numeric_limits<double>::max();

if (cacheMeasurements && basisRotations.empty())
basisRotations = observable->getMeasurementBasisRotations();

// Here we just need to make a lambda kernel
// to optimize that makes calls to the targeted QPU.
OptFunction f(
[&, this](const std::vector<double> &x, std::vector<double> &dx) {
std::vector<double> coefficients;
std::vector<std::string> kernelNames;
std::vector<std::shared_ptr<CompositeInstruction>> fsToExec;
double identityCoeff = 0.0;
int nInstructionsEnergy, nInstructionsGradient = 0;

// call CompositeInstruction::operator()()
auto evaled = kernel->operator()(x);
// observe
auto kernels = observable->observe(evaled);

double identityCoeff = 0.0;
int nInstructionsEnergy = kernels.size(), nInstructionsGradient = 0;
for (auto &f : kernels) {
kernelNames.push_back(f->name());
std::complex<double> coeff = f->getCoefficient();

int nFunctionInstructions;
if (f->getInstruction(0)->isComposite()) {
nFunctionInstructions =
kernel->nInstructions() + f->nInstructions() - 1;
} else {
nFunctionInstructions = f->nInstructions();
}
// only deal with the measurement basis instead of entire circuits
if (cacheMeasurements) {

if (nFunctionInstructions > kernel->nInstructions()) {
fsToExec.push_back(f);
coefficients.push_back(std::real(coeff));
} else {
identityCoeff += std::real(coeff);
nInstructionsEnergy = basisRotations.size() - 1;
for (auto it = basisRotations.begin(); it != basisRotations.end();) {

kernelNames.push_back((*it)->name());
std::complex<double> coeff = (*it)->getCoefficient();
if ((*it)->name() == "I") {
identityCoeff += std::real(coeff);
it = basisRotations.erase(it);
}
coefficients.push_back(std::real(coeff));
++it;
}
}

} else {

// observe
auto kernels = observable->observe(evaled);
for (auto &f : kernels) {
kernelNames.push_back(f->name());
std::complex<double> coeff = f->getCoefficient();

int nFunctionInstructions;
if (f->getInstruction(0)->isComposite()) {
nFunctionInstructions =
kernel->nInstructions() + f->nInstructions() - 1;
} else {
nFunctionInstructions = f->nInstructions();
}

if (nFunctionInstructions > kernel->nInstructions()) {
fsToExec.push_back(f);
coefficients.push_back(std::real(coeff));
} else {
identityCoeff += std::real(coeff);
coefficients.push_back(std::real(coeff));
}
}
}
// Retrieve instructions for gradient, if a pointer of type
// AlgorithmGradientStrategy is given
if (gradientStrategy) {
Expand All @@ -158,7 +179,11 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
}

auto tmpBuffer = xacc::qalloc(buffer->size());
accelerator->execute(tmpBuffer, fsToExec);
if (cacheMeasurements) {
accelerator->execute(tmpBuffer, evaled, basisRotations);
} else {
accelerator->execute(tmpBuffer, fsToExec);
}
auto buffers = tmpBuffer->getChildren();

// Tag any gradient buffers;
Expand All @@ -170,7 +195,6 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
for (auto &[k, v] : tmp_buffer_extra_info) {
buffer->addExtraInfo(k, v);
}

// Create buffer child for the Identity term
auto idBuffer = xacc::qalloc(buffer->size());
idBuffer->addExtraInfo("coefficient", identityCoeff);
Expand All @@ -181,15 +205,13 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
if (accelerator->name() == "ro-error")
idBuffer->addExtraInfo("ro-fixed-exp-val-z", 1.0);
buffer->appendChild("I", idBuffer);

// Add information about the variational parameters to the child
// buffers.
// Other energy (observable-related) information will be populated by
// the Observable::postProcess().
for (auto &childBuffer : buffers) {
childBuffer->addExtraInfo("parameters", x);
}

// Special key to indicate that the buffer was processed by a
// HPC virtualization decorator.
const std::string aggregate_key =
Expand All @@ -216,7 +238,6 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
return observable->postProcess(tmpBuffer);
}
}();

// Compute the variance as well as populate any variance-related
// information to the child buffers
const double variance = [&]() {
Expand All @@ -233,7 +254,6 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
tmpBuffer, Observable::PostProcessingTask::VARIANCE_CALC);
}
}();

if (gradientStrategy) {
// gradient-based optimization
// If gradientStrategy is numerical, pass the energy
Expand All @@ -257,7 +277,6 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
for (auto &b : buffers) {
buffer->appendChild(b->name(), b);
}

std::stringstream ss;
ss << "E(" << (!x.empty() ? std::to_string(x[0]) : "");
for (int i = 1; i < x.size(); i++)
Expand All @@ -275,7 +294,6 @@ void VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer) const {
}
last_energy = energy;
}

return energy;
},
kernel->nVariables());
Expand Down Expand Up @@ -314,31 +332,61 @@ VQE::execute(const std::shared_ptr<AcceleratorBuffer> buffer,
std::vector<double> coefficients;
std::vector<std::string> kernelNames;
std::vector<std::shared_ptr<CompositeInstruction>> fsToExec;

double identityCoeff = 0.0;
auto evaled = xacc::as_shared_ptr(kernel)->operator()(x);
auto kernels = observable->observe(evaled);
for (auto &f : kernels) {
kernelNames.push_back(f->name());
std::complex<double> coeff = f->getCoefficient();

int nFunctionInstructions = 0;
if (f->getInstruction(0)->isComposite()) {
nFunctionInstructions = kernel->nInstructions() + f->nInstructions() - 1;
} else {
nFunctionInstructions = f->nInstructions();
}
int nInstructionsEnergy, nInstructionsGradient = 0;

if (nFunctionInstructions > kernel->nInstructions()) {
fsToExec.push_back(f);
// call CompositeInstruction::operator()()
auto evaled = kernel->operator()(x);

// if we want to only deal with the measurement basis instead of entire
// circuits
if (cacheMeasurements) {

nInstructionsEnergy = basisRotations.size() - 1;
for (auto it = basisRotations.begin(); it != basisRotations.end();) {

kernelNames.push_back((*it)->name());
std::complex<double> coeff = (*it)->getCoefficient();
if ((*it)->name() == "I") {
identityCoeff += std::real(coeff);
it = basisRotations.erase(it);
}
coefficients.push_back(std::real(coeff));
} else {
identityCoeff += std::real(coeff);
++it;
}

} else {

// observe
auto kernels = observable->observe(evaled);
for (auto &f : kernels) {
kernelNames.push_back(f->name());
std::complex<double> coeff = f->getCoefficient();

int nFunctionInstructions;
if (f->getInstruction(0)->isComposite()) {
nFunctionInstructions =
kernel->nInstructions() + f->nInstructions() - 1;
} else {
nFunctionInstructions = f->nInstructions();
}

if (nFunctionInstructions > kernel->nInstructions()) {
fsToExec.push_back(f);
coefficients.push_back(std::real(coeff));
} else {
identityCoeff += std::real(coeff);
coefficients.push_back(std::real(coeff));
}
}
}

auto tmpBuffer = xacc::qalloc(buffer->size());
accelerator->execute(tmpBuffer, fsToExec);
if (cacheMeasurements) {
accelerator->execute(tmpBuffer, evaled, basisRotations);
} else {
accelerator->execute(tmpBuffer, fsToExec);
}
auto buffers = tmpBuffer->getChildren();
for (auto &b : buffers) {
b->addExtraInfo("parameters", x);
Expand Down
3 changes: 2 additions & 1 deletion quantum/plugins/algorithms/vqe/vqe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class VQE : public Algorithm {
Accelerator * accelerator;
std::vector<double> initial_params;
std::shared_ptr<AlgorithmGradientStrategy> gradientStrategy;

mutable std::vector<std::shared_ptr<CompositeInstruction>> basisRotations;
bool cacheMeasurements;
HeterogeneousMap parameters;

public:
Expand Down
51 changes: 51 additions & 0 deletions quantum/plugins/qsim/accelerator/QsimAccelerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Thien Nguyen - initial API and implementation
*******************************************************************************/
#include "QsimAccelerator.hpp"
#include "xacc.hpp"
#include "xacc_plugin.hpp"
#include "IRUtils.hpp"
#include <cassert>
Expand Down Expand Up @@ -274,6 +275,56 @@ void QsimAccelerator::execute(
}
}

void QsimAccelerator::execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<CompositeInstruction> baseCircuit,
const std::vector<std::shared_ptr<CompositeInstruction>> basisRotations) {

constexpr int MAX_NUMBER_CIRCUITS_TO_ANALYZE = 100;
if (!m_vqeMode || basisRotations.size() <= 1 ||
basisRotations.size() > MAX_NUMBER_CIRCUITS_TO_ANALYZE) {
auto provider = getIRProvider("quantum");
// Cannot run VQE mode, just run each composite independently.
for (auto &b : basisRotations) {
auto f = provider->createComposite(b->name(), baseCircuit->getVariables());
auto tmpBuffer =
std::make_shared<xacc::AcceleratorBuffer>(b->name(), buffer->size());
execute(tmpBuffer, f);
buffer->appendChild(f->name(), tmpBuffer);
}
} else {
xacc::info("Running VQE mode");
QsimCircuitVisitor visitor(buffer->size());
// Walk the base IR tree, and visit each node
InstructionIterator it(baseCircuit);
while (it.hasNext()) {
auto nextInst = it.next();
if (nextInst->isEnabled() && !nextInst->isComposite()) {
nextInst->accept(&visitor);
}
}

// Run the base circuit:
auto circuit = visitor.getQsimCircuit();
StateSpace stateSpace(m_numThreads);
State state = stateSpace.Create(circuit.num_qubits);
stateSpace.SetStateZero(state);

const bool runOk = Runner::Run(m_qsimParam, Factory(m_numThreads), circuit, state);
assert(runOk);

// Now we have a wavefunction that represents execution of the ansatz.
// Run the observable sub-circuits (change of basis + measurements)
for (int i = 0; i < basisRotations.size(); ++i) {
auto tmpBuffer = std::make_shared<xacc::AcceleratorBuffer>(
basisRotations[i]->name(), buffer->size());
const double e = getExpectationValueZ(basisRotations[i], stateSpace, state);
tmpBuffer->addExtraInfo("exp-val-z", e);
buffer->appendChild(basisRotations[i]->name(), tmpBuffer);
}
}

}

double QsimAccelerator::getExpectationValueZ(
std::shared_ptr<CompositeInstruction> compositeInstruction,
const StateSpace &stateSpace, const State &state) const {
Expand Down
3 changes: 3 additions & 0 deletions quantum/plugins/qsim/accelerator/QsimAccelerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ class QsimAccelerator : public Accelerator {
virtual void execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::vector<std::shared_ptr<CompositeInstruction>>
compositeInstructions) override;
void execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<CompositeInstruction> baseCircuit,
const std::vector<std::shared_ptr<CompositeInstruction>> basisRotations) override;
virtual void apply(std::shared_ptr<AcceleratorBuffer> buffer,
std::shared_ptr<Instruction> inst) override;

Expand Down
7 changes: 7 additions & 0 deletions xacc/accelerator/Accelerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ class Accelerator : public Identifiable {
const std::vector<std::shared_ptr<CompositeInstruction>>
CompositeInstructions) = 0;

virtual void
execute(std::shared_ptr<AcceleratorBuffer> buffer,
const std::shared_ptr<CompositeInstruction> baseCircuit,
const std::vector<std::shared_ptr<CompositeInstruction>> basisRotations) {
return;
}

virtual void cancel(){};

virtual std::vector<std::pair<int, int>> getConnectivity() {
Expand Down
Loading