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

Merge pull request #204 from tnguyen-ornl/tnguyen/qite

Implements Quantum Imaginary Time Evolution algorithm
parents 2e602b89 e04bf562
Pipeline #107174 passed with stage
in 24 minutes and 5 seconds
......@@ -1220,6 +1220,122 @@ or in Python
# i.e. phase value of 1/2^3 = 1/8.
print(buffer)
QITE
++++
The Quantum Imaginary Time Evolution (QITE) Algorithm requires the following input information:
(`Motta et al. (2020) <https://arxiv.org/pdf/1901.07653.pdf>`_)
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| Algorithm Parameter | Parameter Description | type |
+========================+=================================================================+======================================+
| observable | The hermitian operator represents the cost Hamiltonian. | std::shared_ptr<Observable> |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| accelerator | The Accelerator backend to target | std::shared_ptr<Accelerator> |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| steps | The number of Trotter steps. | int |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| step-size | The Trotter step size. | double |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
Optionally, users can provide these parameters:
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| Algorithm Parameter | Parameter Description | type |
+========================+=================================================================+======================================+
| ansatz | State preparation circuit. | std::shared_ptr<CompositeInstruction>|
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| analytical | If true, perform an analytical run rather than | boolean |
| | executing quantum circuits on the Accelerator backend. | |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
| initial-state | For `analytical` mode only, select the initial state. | int |
+------------------------+-----------------------------------------------------------------+--------------------------------------+
This Algorithm will add ``opt-val`` (``double``) which is the energy value at the final Trotter step to the provided ``AcceleratorBuffer``.
The results of the algorithm are therefore retrieved via these keys (see snippet below).
Also, energy values at each Trotter step are stored in the ``exp-vals`` field (``vector<double>``).
Note: during execution, the following line may be logged to the output console:
.. code:: cpp
warning: solve(): system seems singular; attempting approx solution
This is completely normal and can be safely ignored.
.. code:: cpp
#include "xacc.hpp"
#include "xacc_observable.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Use the Qpp simulator as the accelerator
auto acc = xacc::getAccelerator("qpp");
auto buffer = xacc::qalloc(1);
auto observable = xacc::quantum::getObservable(
"pauli",
std::string("0.7071067811865475 X0 + 0.7071067811865475 Z0"));
auto qite = xacc::getService<xacc::Algorithm>("qite");
const int nbSteps = 25;
const double stepSize = 0.1;
const bool initOk = qite->initialize({
std::make_pair("accelerator", acc),
std::make_pair("steps", nbSteps),
std::make_pair("observable", observable),
std::make_pair("step-size", stepSize)
});
qite->execute(buffer);
std::cout << "Min Energy: " << (*buffer)["opt-val"].as<double>() << "\n";
}
In Python:
.. code:: python
import xacc,sys, numpy as np
import matplotlib.pyplot as plt
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the Hamiltonian as an XACC PauliOperator
ham = xacc.getObservable('pauli', '0.70710678118 X0 + 0.70710678118 Z0')
# We just need 1 qubit
buffer = xacc.qalloc(1)
# Horizontal axis: 0 -> 2.5
# The number of Trotter steps
nbSteps = 25
# The Trotter step size
stepSize = 0.1
# Create the QITE algorithm
qite = xacc.getAlgorithm('qite', {
'accelerator': qpu,
'observable': ham,
'step-size': stepSize,
'steps': nbSteps
})
result = qite.execute(buffer)
# Expected result: ~ -1
print('Min energy value = ', buffer.getInformation('opt-val'))
E = buffer.getInformation('exp-vals')
# Plot energy vs. beta
plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro', label = 'XACC QITE')
plt.grid()
plt.show()
Accelerator Decorators
----------------------
ROErrorDecorator
......
# Simple 2-qubit demonstration of the Quatum Imaginary Time Evolution algorithm
# for Deuteron H2 Hamiltonian
# Reference: https://arxiv.org/pdf/1912.06226.pdf
# Expected minimum value: -1.74886
import xacc,sys, numpy as np
import matplotlib.pyplot as plt
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the H2 Hamiltonian
ham = xacc.getObservable('pauli', '5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1')
xacc.qasm('''.compiler xasm
.circuit prep
.qbit q
X(q[0]);
''')
prep_circuit = xacc.getCompiled('prep')
# We need 2 qubits for this case (H2)
buffer = xacc.qalloc(2)
# Horizontal axis: 0 -> 0.3 (step = 0.05)
# The number of Trotter steps
nbSteps = 6
# The Trotter step size
stepSize = 0.05
# Create the QITE algorithm
qite = xacc.getAlgorithm('qite', {
'accelerator': qpu,
'observable': ham,
'step-size': stepSize,
'steps': nbSteps,
'ansatz': prep_circuit
})
result = qite.execute(buffer)
# Expected result: ~ -1.74886
print('Min energy value = ', buffer.getInformation('opt-val'))
E = buffer.getInformation('exp-vals')
# Reproduce Fig. 3(a) of https://arxiv.org/pdf/1912.06226.pdf
plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro-', label = 'XACC QITE')
plt.ylim((-2.0, -0.25))
plt.yticks(np.arange(-2.0, -0.25, step=0.25))
plt.grid()
plt.show()
\ No newline at end of file
# Simple 3-qubit demonstration of the Quatum Imaginary Time Evolution algorithm
# for Deuteron H3 Hamiltonian
# Reference: https://arxiv.org/pdf/1912.06226.pdf
# Expected minimum value: -2.04482
import xacc,sys, numpy as np
import matplotlib.pyplot as plt
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the H3 Hamiltonian
ham = xacc.getObservable('pauli', '5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1 + 9.625 - 9.625 Z2 - 3.91 X1 X2 - 3.91 Y1 Y2')
xacc.qasm('''.compiler xasm
.circuit prep
.qbit q
X(q[0]);
''')
prep_circuit = xacc.getCompiled('prep')
# We need 3 qubits for this case (H3)
buffer = xacc.qalloc(3)
# Horizontal axis: 0 -> 0.3 (step = 0.05)
# The number of Trotter steps
nbSteps = 6
# The Trotter step size
stepSize = 0.05
# Create the QITE algorithm
qite = xacc.getAlgorithm('qite', {
'accelerator': qpu,
'observable': ham,
'step-size': stepSize,
'steps': nbSteps,
'ansatz': prep_circuit
})
result = qite.execute(buffer)
# Expected result: ~ -2.04482
print('Min energy value = ', buffer.getInformation('opt-val'))
E = buffer.getInformation('exp-vals')
# Reproduce Fig. 3(b) of https://arxiv.org/pdf/1912.06226.pdf
plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro-', label = 'XACC QITE')
plt.ylim((-2.5, -0.0))
plt.yticks(np.arange(-2.5, 0.0, step=0.5))
plt.grid()
plt.show()
\ No newline at end of file
# Simple 1-qubit demonstration of the Quatum Imaginary Time Evolution algorithm
# Reference: https://www.nature.com/articles/s41567-019-0704-4
# Target H = 1/sqrt(2)(X + Z)
# Expected minimum value: -1.0
import xacc,sys, numpy as np
import matplotlib.pyplot as plt
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp')
# Construct the Hamiltonian as an XACC PauliOperator
ham = xacc.getObservable('pauli', '0.70710678118 X0 + 0.70710678118 Z0')
# We just need 1 qubit
buffer = xacc.qalloc(1)
# See Fig. 2 (e) of https://www.nature.com/articles/s41567-019-0704-4
# Horizontal axis: 0 -> 2.5
# The number of Trotter steps
nbSteps = 25
# The Trotter step size
stepSize = 0.1
# Create the QITE algorithm
qite = xacc.getAlgorithm('qite', {
'accelerator': qpu,
'observable': ham,
'step-size': stepSize,
'steps': nbSteps
})
result = qite.execute(buffer)
# Expected result: ~ -1
print('Min energy value = ', buffer.getInformation('opt-val'))
E = buffer.getInformation('exp-vals')
# Reproduce Fig. 2(e) of https://www.nature.com/articles/s41567-019-0704-4
plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro', label = 'XACC QITE')
plt.grid()
plt.show()
\ No newline at end of file
......@@ -19,6 +19,7 @@ add_subdirectory(qpt)
add_subdirectory(qaoa)
add_subdirectory(qpe)
add_subdirectory(gradient_strategies)
add_subdirectory(qite)
file(GLOB PYDECORATORS ${CMAKE_CURRENT_SOURCE_DIR}/vqe/python/*.py
${CMAKE_CURRENT_SOURCE_DIR}/ml/ddcl/python/*.py)
......
# *******************************************************************************
# Copyright (c) 2019 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 v.10 which accompany 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:
# Thien Nguyen - initial API and implementation
# *******************************************************************************/
set(LIBRARY_NAME xacc-algorithm-qite)
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 .
${CMAKE_SOURCE_DIR}/tpls/armadillo)
target_link_libraries(${LIBRARY_NAME} PUBLIC xacc CppMicroServices xacc-quantum-gate)
set(_bundle_name xacc_algorithm_qite)
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")
# Armadillo solver needs LAPACK
find_package(LAPACK)
if(LAPACK_FOUND)
target_link_libraries(${LIBRARY_NAME} PRIVATE ${LAPACK_LIBRARIES})
else()
message(STATUS "LAPACK NOT FOUND. QITE plugin may not work.")
endif()
endif()
if(XACC_BUILD_TESTS)
add_subdirectory(tests)
endif()
install(TARGETS ${LIBRARY_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/plugins)
/*******************************************************************************
* Copyright (c) 2019 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:
* Thien Nguyen - initial API and implementation
*******************************************************************************/
#include "qite.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 QITEActivator : public BundleActivator {
public:
QITEActivator() {}
/**
*/
void Start(BundleContext context) {
auto c = std::make_shared<xacc::algorithm::QITE>();
context.RegisterService<xacc::Algorithm>(c);
}
/**
*/
void Stop(BundleContext /*context*/) {}
};
} // namespace
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR(QITEActivator)
{
"bundle.symbolic_name" : "xacc_algorithm_qite",
"bundle.activator" : true,
"bundle.name" : "XACC QITE Algorithm",
"bundle.description" : ""
}
This diff is collapsed.
/*******************************************************************************
* Copyright (c) 2019 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:
* Thien Nguyen - initial API and implementation
*******************************************************************************/
#pragma once
#include "Algorithm.hpp"
namespace xacc {
namespace algorithm {
class QITE : public Algorithm {
public:
bool initialize(const HeterogeneousMap &parameters) override;
const std::vector<std::string> requiredParameters() const override;
void execute(const std::shared_ptr<AcceleratorBuffer> buffer) const override;
std::vector<double> execute(const std::shared_ptr<AcceleratorBuffer> buffer,
const std::vector<double> &parameters) override;
const std::string name() const override { return "qite"; }
const std::string description() const override { return ""; }
DEFINE_ALGORITHM_CLONE(QITE)
private:
// Construct the Trotter propagate circuit up to current time step.
std::shared_ptr<CompositeInstruction> constructPropagateCircuit() const;
// Calculate the current energy, i.e.
// the value of the observable at the current Trotter step.
double calcCurrentEnergy(int in_nbQubits) const;
// Calculate approximate A operator observable at the current Trotter step.
// Params:
// in_kernel: the kernel to evolve the system to this time step
// in_hmTerm: the H term to be approximate by the A term
// i.e. emulate the imaginary time evolution of that H term.
std::shared_ptr<Observable> calcAOps(const std::shared_ptr<AcceleratorBuffer>& in_buffer, std::shared_ptr<CompositeInstruction> in_kernel, std::shared_ptr<Observable> in_hmTerm) const;
private:
// Number of Trotter steps
int m_nbSteps;
// dBeta, i.e. step size
double m_dBeta;
// Accelerator
std::shared_ptr<Accelerator> m_accelerator;
// Hamiltonian Observable, i.e. H = Sum(h_i)
std::shared_ptr<Observable> m_observable;
// Ansatz circuit (apply before Trotter steps)
CompositeInstruction* m_ansatz;
// List of A operators for each time step
// which approximates the imaginary-time step
// of the Hamiltonian observable
// i.e. exp(-iAt) -> exp(-Ht)
mutable std::vector<std::shared_ptr<Observable>> m_approxOps;
// Energy value achieved at a Trotter step
mutable std::vector<double> m_energyAtStep;
// If a pure analytical run is requested.
bool m_analytical;
// For analytical solver only: the initial state
// For accelerator-based simulation, the Ansatz is used to
// prepare the initial state.
int m_initialState;
};
} // namespace algorithm
} // namespace xacc
# *******************************************************************************
# Copyright (c) 2019 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 v.10 which accompany 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:
# Thien Nguyen - initial API and implementation
# *******************************************************************************/
include_directories(${CMAKE_BINARY_DIR})
add_xacc_test(QITE)
target_link_libraries(QITETester xacc xacc-pauli)
\ No newline at end of file
/*******************************************************************************
* Copyright (c) 2019 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:
* Thien Nguyen - initial API and implementation
*******************************************************************************/
#include <gtest/gtest.h>
#include "xacc.hpp"
#include "xacc_service.hpp"
#include "Optimizer.hpp"
#include "Observable.hpp"
#include "Algorithm.hpp"
#include "PauliOperator.hpp"
TEST(QITETester, checkSimple)
{
auto qite = xacc::getService<xacc::Algorithm>("qite");
std::shared_ptr<xacc::Observable> observable = std::make_shared<xacc::quantum::PauliOperator>();
observable->fromString("0.7071067811865475 X0 + 0.7071067811865475 Z0");
auto acc = xacc::getAccelerator("qpp");
const int nbSteps = 25;
const double stepSize = 0.1;
const bool initOk = qite->initialize({
std::make_pair("accelerator", acc),
std::make_pair("steps", nbSteps),
std::make_pair("observable", observable),
std::make_pair("step-size", stepSize)
});
EXPECT_TRUE(initOk);
auto buffer = xacc::qalloc(1);
qite->execute(buffer);
const double finalEnergy = (*buffer)["opt-val"].as<double>();
std::cout << "Final Energy: " << finalEnergy << "\n";
// Fig (2.e) of https://www.nature.com/articles/s41567-019-0704-4
// Minimal Energy = -1
EXPECT_NEAR(finalEnergy, -1.0, 1e-3);
const std::vector<double> energyValues = (*buffer)["exp-vals"].as<std::vector<double>>();
EXPECT_EQ(energyValues.size(), nbSteps + 1);
}
TEST(QITETester, checkDeuteuronH2)
{
auto qite = xacc::getService<xacc::Algorithm>("qite");
std::shared_ptr<xacc::Observable> observable = std::make_shared<xacc::quantum::PauliOperator>();
observable->fromString("5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1");
auto acc = xacc::getAccelerator("qpp");
const int nbSteps = 5;
const double stepSize = 0.1;
auto compiler = xacc::getCompiler("xasm");
auto ir = compiler->compile(R"(__qpu__ void f(qbit q) { X(q[0]); })", nullptr);
auto x = ir->getComposite("f");
const bool initOk = qite->initialize({
std::make_pair("accelerator", acc),
std::make_pair("steps", nbSteps),
std::make_pair("observable", observable),
std::make_pair("step-size", stepSize),
std::make_pair("ansatz", x),
});
EXPECT_TRUE(initOk);
auto buffer = xacc::qalloc(2);
qite->execute(buffer);
const double finalEnergy = (*buffer)["opt-val"].as<double>();
std::cout << "Final Energy: " << finalEnergy << "\n";
EXPECT_NEAR(finalEnergy, -1.74886, 1e-3);
}
TEST(QITETester, checkDeuteuronH2Analytical)
{
auto qite = xacc::getService<xacc::Algorithm>("qite");
std::shared_ptr<xacc::Observable> observable = std::make_shared<xacc::quantum::PauliOperator>();
observable->fromString("5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1");
auto acc = xacc::getAccelerator("qpp");
const int nbSteps = 10;
const double stepSize = 0.1;
const bool initOk = qite->initialize({
std::make_pair("accelerator", acc),
std::make_pair("steps", nbSteps),
std::make_pair("observable", observable),
std::make_pair("step-size", stepSize),
std::make_pair("analytical", true),
std::make_pair("initial-state", 1)
});
EXPECT_TRUE(initOk);
auto buffer = xacc::qalloc(2);
qite->execute(buffer);
const double finalEnergy = (*buffer)["opt-val"].as<double>();
std::cout << "Final Energy: " << finalEnergy << "\n";
EXPECT_NEAR(finalEnergy, -1.74886, 1e-3);
buffer->print();
const std::string Aop_Analytical = (*buffer)["A-op"].as<std::string>();