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

Merge pull request #193 from tnguyen-ornl/tnguyen/qpe

Quantum Phase Estimation Algorithm
parents e33afcda 1bf0fc2f
Pipeline #103311 passed with stage
in 70 minutes and 50 seconds
......@@ -1089,6 +1089,137 @@ In Python:
print('Min QUBO value = ', buffer.getInformation('opt-val'))
Quantum Phase Estimation
++++++++++++++++++++++++
The ``QPE`` algorithm (also known as quantum eigenvalue estimation algorithm) provides
an implementation of Algorithm that estimates the phase (or eigenvalue) of an eigenvector of a unitary operator.
Here the unitary operator is called an `oracle` which is a quantum subroutine
that acts upon a set of qubits and returns the answer as a phase.
The bits precision is automatically inferred from the size of the input buffer.
+------------------------+------------------------------------------------------------------------+------------------------------------------+
| Algorithm Parameter | Parameter Description | type |
+========================+========================================================================+==========================================+
| oracle | The circuit represents the unitary operator. | pointer-like CompositeInstruction |
+------------------------+------------------------------------------------------------------------+------------------------------------------+
| accelerator | The backend quantum computer to use. | pointer-like Accelerator |
+------------------------+------------------------------------------------------------------------+------------------------------------------+
| state-preparation | The circuit to prepare the eigen state. | pointer-like CompositeInstruction |
+------------------------+------------------------------------------------------------------------+------------------------------------------+
.. code:: cpp
#include "xacc.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Accelerator:
auto acc = xacc::getAccelerator("qpp", {std::make_pair("shots", 4096)});
// In this example: we want to estimate the *phase* of an arbitrary 'oracle'
// i.e. Oracle(|State>) = exp(i*Phase)*|State>
// and we need to estimate that Phase.
// Oracle: CPhase(theta) or CU1(theta) which is defined as
// 1 0 0 0
// 0 1 0 0
// 0 0 1 0
// 0 0 0 e^(i*theta)
// The eigenstate is |11>; i.e. CPhase(theta)|11> = e^(i*theta)|11>
// Since this oracle operates on 2 qubits, we need to add more qubits to the buffer.
// The more qubits we have, the more accurate the estimate.
// Resolution := 2^(number qubits in the calculation register).
// 5-bit precision => 7 qubits in total
auto buffer = xacc::qalloc(7);
auto qpe = xacc::getService<xacc::Algorithm>("QPE");
auto compiler = xacc::getCompiler("xasm");
// Create oracle: CPhase gate with theta = 2pi/3
// i.e. the phase value to estimate is 1/3 ~ 0.33333.
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto oracle = gateRegistry->createComposite("oracle");
oracle->addInstruction(gateRegistry->createInstruction("CPhase", { 0, 1 }, { 2.0 * M_PI/ 3.0 }));
// Eigenstate preparation = |11> state
auto statePrep = compiler->compile(R"(__qpu__ void prep1(qbit q) {
X(q[0]);
X(q[1]);
})", nullptr)->getComposite("prep1");
// Initialize the Quantum Phase Estimation:
qpe->initialize({
std::make_pair("accelerator", acc),
std::make_pair("oracle", oracle),
std::make_pair("state-preparation", statePrep)
});
// Run the algorithm
qpe->execute(buffer);
// Expected result:
// The factor here is 2^5 (precision) = 32
// we expect the two most-likely bitstring is 10 and 11
// i.e. the true result is between 10/32 = 0.3125 and 11/32 = 0.34375
std::cout << "Probability of the two most-likely bitstrings 10 (theta = 0.3125) and 11 (theta = 0.34375 ): \n";
std::cout << "Probability of |11010> (11) = " << buffer->computeMeasurementProbability("11010") << "\n";
std::cout << "Probability of |01010> (10) = " << buffer->computeMeasurementProbability("01010") << "\n";
xacc::Finalize();
}
or in Python
.. code:: python
import xacc,sys, numpy as np
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp', { 'shots': 4096 })
# In this example: we want to estimate the *phase* of an arbitrary 'oracle'
# i.e. Oracle(|State>) = exp(i*Phase)*|State>
# and we need to estimate that Phase.
# The oracle is a simple T gate, and the eigenstate is |1>
# T|1> = e^(i*pi/4)|1>
# The phase value of pi/4 = 2pi * (1/8)
# i.e. if we use a 3-bit register for estimation,
# we will get the correct answer of 1 deterministically.
xacc.qasm('''.compiler xasm
.circuit oracle
.qbit q
T(q[0]);
''')
oracle = xacc.getCompiled('oracle')
# We need to prepare the eigenstate |1>
xacc.qasm('''.compiler xasm
.circuit prep
.qbit q
X(q[0]);
''')
statePrep = xacc.getCompiled('prep')
# We need 4 qubits (3-bit precision)
buffer = xacc.qalloc(4)
# Create the QPE algorithm
qpe = xacc.getAlgorithm('QPE', {
'accelerator': qpu,
'oracle': oracle,
'state-preparation': statePrep
})
qpe.execute(buffer)
# We should only get the bit string of |100> = 1
# i.e. phase value of 1/2^3 = 1/8.
print(buffer)
Accelerator Decorators
----------------------
ROErrorDecorator
......
import xacc,sys, numpy as np
# Get access to the desired QPU and
# allocate some qubits to run on
qpu = xacc.getAccelerator('qpp', { 'shots': 4096 })
# In this example: we want to estimate the *phase* of an arbitrary 'oracle'
# i.e. Oracle(|State>) = exp(i*Phase)*|State>
# and we need to estimate that Phase.
# The oracle is a simple T gate, and the eigenstate is |1>
# T|1> = e^(i*pi/4)|1>
# The phase value of pi/4 = 2pi * (1/8)
# i.e. if we use a 3-bit register for estimation,
# we will get the correct answer of 1 deterministically.
xacc.qasm('''.compiler xasm
.circuit oracle
.qbit q
T(q[0]);
''')
oracle = xacc.getCompiled('oracle')
# We need to prepare the eigenstate |1>
xacc.qasm('''.compiler xasm
.circuit prep
.qbit q
X(q[0]);
''')
statePrep = xacc.getCompiled('prep')
# We need 4 qubits (3-bit precision)
buffer = xacc.qalloc(4)
# Create the QPE algorithm
qpe = xacc.getAlgorithm('QPE', {
'accelerator': qpu,
'oracle': oracle,
'state-preparation': statePrep
})
qpe.execute(buffer)
# We should only get the bit string of |100> = 1
# i.e. phase value of 1/2^3 = 1/8.
print(buffer)
\ No newline at end of file
......@@ -35,3 +35,6 @@ target_link_libraries(nah_ucc3 PRIVATE xacc xacc-quantum-gate)
add_executable(qaoa_qubo solve_qubo_with_qaoa.cpp)
target_link_libraries(qaoa_qubo PRIVATE xacc xacc-quantum-gate)
add_executable(quantum_phase_estimation quantum_phase_estimation.cpp)
target_link_libraries(quantum_phase_estimation PRIVATE xacc xacc-quantum-gate)
/*******************************************************************************
* Copyright (c) 2020 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 "xacc.hpp"
#include "xacc_service.hpp"
int main(int argc, char **argv) {
xacc::Initialize(argc, argv);
// Accelerator:
auto acc = xacc::getAccelerator("qpp", {std::make_pair("shots", 4096)});
// In this example: we want to estimate the *phase* of an arbitrary 'oracle'
// i.e. Oracle(|State>) = exp(i*Phase)*|State>
// and we need to estimate that Phase.
// Oracle: CPhase(theta) or CU1(theta) which is defined as
// 1 0 0 0
// 0 1 0 0
// 0 0 1 0
// 0 0 0 e^(i*theta)
// The eigenstate is |11>; i.e. CPhase(theta)|11> = e^(i*theta)|11>
// Since this oracle operates on 2 qubits, we need to add more qubits to the buffer.
// The more qubits we have, the more accurate the estimate.
// Resolution := 2^(number qubits in the calculation register).
// 5-bit precision => 7 qubits in total
auto buffer = xacc::qalloc(7);
auto qpe = xacc::getService<xacc::Algorithm>("QPE");
auto compiler = xacc::getCompiler("xasm");
// Create oracle: CPhase gate with theta = 2pi/3
// i.e. the phase value to estimate is 1/3 ~ 0.33333.
auto gateRegistry = xacc::getService<xacc::IRProvider>("quantum");
auto oracle = gateRegistry->createComposite("oracle");
oracle->addInstruction(gateRegistry->createInstruction("CPhase", { 0, 1 }, { 2.0 * M_PI/ 3.0 }));
// Eigenstate preparation = |11> state
auto statePrep = compiler->compile(R"(__qpu__ void prep1(qbit q) {
X(q[0]);
X(q[1]);
})", nullptr)->getComposite("prep1");
// Initialize the Quantum Phase Estimation:
qpe->initialize({
std::make_pair("accelerator", acc),
std::make_pair("oracle", oracle),
std::make_pair("state-preparation", statePrep)
});
// Run the algorithm
qpe->execute(buffer);
// Expected result:
// The factor here is 2^5 (precision) = 32
// we expect the two most-likely bitstring is 10 and 11
// i.e. the true result is between 10/32 = 0.3125 and 11/32 = 0.34375
std::cout << "Probability of the two most-likely bitstrings 10 (theta = 0.3125) and 11 (theta = 0.34375 ): \n";
std::cout << "Probability of |11010> (11) = " << buffer->computeMeasurementProbability("11010") << "\n";
std::cout << "Probability of |01010> (10) = " << buffer->computeMeasurementProbability("01010") << "\n";
xacc::Finalize();
}
\ No newline at end of file
......@@ -16,6 +16,7 @@ add_subdirectory(ml)
add_subdirectory(rotoselect)
add_subdirectory(qpt)
add_subdirectory(qaoa)
add_subdirectory(qpe)
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-qpe)
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 xacc-quantum-gate)
set(_bundle_name xacc_algorithm_qpe)
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)
/*******************************************************************************
* 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 "ControlledGateApplicator.hpp"
namespace xacc {
namespace circuits {
bool ControlledU::expand(const xacc::HeterogeneousMap& runtimeOptions)
{
if (!runtimeOptions.keyExists<int>("control-idx"))
{
xacc::error("'control-idx' is required.");
return false;
}
const auto ctrlIdx = runtimeOptions.get<int>("control-idx");
if (!runtimeOptions.pointerLikeExists<CompositeInstruction>("U"))
{
xacc::error("'U' composite is required.");
return false;
}
auto uComposite = std::shared_ptr<CompositeInstruction>(
runtimeOptions.getPointerLike<CompositeInstruction>("U"),
xacc::empty_delete<CompositeInstruction>());
auto ctrlU = applyControl(uComposite, ctrlIdx);
for (int instId = 0; instId < ctrlU->nInstructions(); ++instId)
{
addInstruction(ctrlU->getInstruction(instId)->clone());
}
return true;
}
std::shared_ptr<xacc::CompositeInstruction> ControlledU::applyControl(const std::shared_ptr<xacc::CompositeInstruction>& in_program, int in_ctrlIdx)
{
m_gateProvider = xacc::getService<xacc::IRProvider>("quantum");
m_composite = m_gateProvider->createComposite("CTRL_" + in_program->name() + "_" + std::to_string(in_ctrlIdx));
m_ctrlIdx = in_ctrlIdx;
InstructionIterator it(in_program);
while (it.hasNext())
{
auto nextInst = it.next();
if (nextInst->isEnabled())
{
nextInst->accept(this);
}
}
return m_composite;
}
void ControlledU::visit(Hadamard& h)
{
if (h.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = h.bits()[0];
// CH
m_composite->addInstruction(m_gateProvider->createInstruction("CH", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(X& x)
{
if (x.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = x.bits()[0];
// CNOT
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(Y& y)
{
if (y.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = y.bits()[0];
// CY
m_composite->addInstruction(m_gateProvider->createInstruction("CY", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(Z& z)
{
if (z.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = z.bits()[0];
// CZ
m_composite->addInstruction(m_gateProvider->createInstruction("CZ", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(CNOT& cnot)
{
// CCNOT gate:
// We now have two control bits
const auto ctrlIdx1 = cnot.bits()[0];
const auto ctrlIdx2 = m_ctrlIdx;
// Target qubit
const auto targetIdx = cnot.bits()[1];
m_composite->addInstruction(m_gateProvider->createInstruction("H", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx1, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("Tdg", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("Tdg", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx2, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("T", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx1, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("Tdg", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx2, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("T", { ctrlIdx1 }));
m_composite->addInstruction(m_gateProvider->createInstruction("T", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("H", { targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx2, ctrlIdx1 }));
m_composite->addInstruction(m_gateProvider->createInstruction("T", { ctrlIdx2 }));
m_composite->addInstruction(m_gateProvider->createInstruction("Tdg", { ctrlIdx1 }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { ctrlIdx2, ctrlIdx1 }));
}
void ControlledU::visit(Rx& rx)
{
// CRn(theta) = Rn(theta/2) - CX - Rn(-theta/2) - CX
if (rx.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = rx.bits()[0];
const auto angle = InstructionParameterToDouble(rx.getParameter(0));
m_composite->addInstruction(m_gateProvider->createInstruction("Rx", { targetIdx }, { angle/ 2.0 }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { m_ctrlIdx, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("Rx", { targetIdx }, { -angle/ 2.0 }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(Ry& ry)
{
// CRn(theta) = Rn(theta/2) - CX - Rn(-theta/2) - CX
if (ry.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = ry.bits()[0];
const auto angle = InstructionParameterToDouble(ry.getParameter(0));
m_composite->addInstruction(m_gateProvider->createInstruction("Ry", { targetIdx }, { angle/ 2.0 }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { m_ctrlIdx, targetIdx }));
m_composite->addInstruction(m_gateProvider->createInstruction("Ry", { targetIdx }, { -angle/ 2.0 }));
m_composite->addInstruction(m_gateProvider->createInstruction("CX", { m_ctrlIdx, targetIdx }));
}
}
void ControlledU::visit(Rz& rz)
{
if (rz.bits()[0] == m_ctrlIdx)
{
xacc::error("Control bit must be different from target qubit(s).");
}
else
{
const auto targetIdx = rz.bits()[0];
const auto angle = InstructionParameterToDouble(rz.getParameter(0));
// CRz
m_composite->addInstruction(m_gateProvider->createInstruction("CRZ", { m_ctrlIdx, targetIdx }, { angle }));
}
}
void ControlledU::visit(S& s)
{
// Ctrl-S = CPhase(pi/2)
const auto targetIdx = s.bits()[0];
m_composite->addInstruction(m_gateProvider->createInstruction("CPhase", { m_ctrlIdx, targetIdx }, { M_PI_2 }));
}
void ControlledU::visit(Sdg& sdg)
{
// Ctrl-Sdg = CPhase(-pi/2)
const auto targetIdx = sdg.bits()[0];
m_composite->addInstruction(m_gateProvider->createInstruction("CPhase", { m_ctrlIdx, targetIdx }, { -M_PI_2 }));
}
void ControlledU::visit(T& t)
{
// Ctrl-T = CPhase(pi/4)
const auto targetIdx = t.bits()[0];
m_composite->addInstruction(m_gateProvider->createInstruction("CPhase", { m_ctrlIdx, targetIdx }, { M_PI_4 }));
}
void ControlledU::visit(Tdg& tdg)
{
// Ctrl-Tdg = CPhase(-pi/4)
const auto targetIdx = tdg.bits()[0];
m_composite->addInstruction(m_gateProvider->createInstruction("CPhase", { m_ctrlIdx, targetIdx }, { -M_PI_4 }));
}
void ControlledU::visit(Swap& s)
{
// Fredkin gate: controlled-swap
// Decompose Swap gate into CNOT gates;
// then re-visit those CNOT gates (becoming CCNOT).
CNOT c1(s.bits()), c2(s.bits()[1],s.bits()[0]), c3(s.bits());
visit(c1);
visit(c2);
visit(c3);
}
void ControlledU::visit(U& u)
{
// U(theta, phi, lambda) := Rz(phi)Ry(theta)Rz(lambda)
const auto theta = InstructionParameterToDouble(u.getParameter(0));
const auto phi = InstructionParameterToDouble(u.getParameter(1));
const auto lambda = InstructionParameterToDouble(u.getParameter(2));
Ry ry1(u.bits()[0], theta);
Rz rz1(u.bits()[0], lambda);
Rz rz2(u.bits()[0], phi);
// Revisit these gates: Rn -> CRn
visit(rz1);
visit(ry1);
visit(rz2);
}
void ControlledU::visit(CY& cy)
{
// controlled-Y = Sdg(target) - CX - S(target)