Skip to content
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ $ cmake -DQISKIT_ROOT=Path_to_qiskit ..
$ make
```

If you want to build sampler or transpiler example, you will need one of qiskit-ibm-runtime C or QRMI or SQC.
If you want to build sampler, estimator, or transpiler example, you will need one of qiskit-ibm-runtime C or QRMI or SQC.

Then example can be built by setting `QISKIT_IBM_RUNTIME_C_ROOT` or `QRMI_ROOT` or `SQC_ROOT` to cmake.

Expand All @@ -109,7 +109,7 @@ $ cmake -DQISKIT_ROOT=Path_to_qiskit -DQISKIT_IBM_RUNTIME_C_ROOT="path to qiskit
$ make
```

To run sampler example, set your account information in `$HOME/.qiskit/qiskit-ibm.json` (see https://github.com/Qiskit/qiskit-ibm-runtime?tab=readme-ov-file#save-your-account-on-disk) or setting following environment variables to access Quantum hardware.
To run sampler or estimator examples, set your account information in `$HOME/.qiskit/qiskit-ibm.json` (see https://github.com/Qiskit/qiskit-ibm-runtime?tab=readme-ov-file#save-your-account-on-disk) or setting following environment variables to access Quantum hardware.

```
QISKIT_IBM_TOKEN=<your API key>
Expand Down
5 changes: 5 additions & 0 deletions releasenotes/notes/add-backend-estimator-v2-145.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
features:
- |
Add ``BackendEstimatorV2``, ``EstimatorPub``, and ``BackendEstimatorJob`` for
Pauli expectation-value estimation via the existing sampler interface.
1 change: 1 addition & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,6 @@ add_application(parameterized_circuit_test parameterized_circuit_test.cpp)

if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT OR SQC_ROOT)
add_application(sampler_test sampler_test.cpp)
add_application(estimator_test estimator_test.cpp)
add_application(transpile_test transpile_test.cpp)
endif()
72 changes: 72 additions & 0 deletions samples/estimator_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
# This code is part of Qiskit.
#
# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
*/

// Test program for estimator

#include <iostream>
#include <cstdint>

#include "circuit/quantumcircuit.hpp"
#include "primitives/backend_estimator_v2.hpp"
#ifdef QRMI_ROOT
#include "service/qiskit_runtime_service_qrmi.hpp"
#else
#include "service/qiskit_runtime_service_c.hpp"
#endif

#include "compiler/transpiler.hpp"

using namespace Qiskit;
using namespace Qiskit::circuit;
using namespace Qiskit::primitives;
using namespace Qiskit::service;
using namespace Qiskit::compiler;

using Estimator = BackendEstimatorV2;

int main()
{
int num_qubits = 2;
auto qreg = QuantumRegister(num_qubits);
auto creg = ClassicalRegister(num_qubits, std::string("meas"));
QuantumCircuit circ(std::vector<QuantumRegister>({qreg}),
std::vector<ClassicalRegister>({creg}));

circ.h(0);
circ.cx(0, 1);
circ.measure(qreg, creg);

auto service = QiskitRuntimeService();
auto backend = service.backend("ibm_torino");
auto estimator = Estimator(backend, 1000);

auto transpiled_circ = transpile(circ, backend);

std::vector<std::pair<std::string, double>> observables = {
{"ZZ", 1.0},
{"XI", 0.5},
};

auto job = estimator.run({EstimatorPub(transpiled_circ, observables, 1000)});
if (job == nullptr) {
return -1;
}

auto result = job->result();
auto pub_result = result[0];
std::cout << "EV = " << pub_result.ev()
<< " +/- " << pub_result.stddev() << std::endl;

return 0;
}
219 changes: 219 additions & 0 deletions src/primitives/backend_estimator_job.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
*/

// job class for BackendEstimator

#ifndef __qiskitcpp_primitives_backend_estimator_job_def_hpp__
#define __qiskitcpp_primitives_backend_estimator_job_def_hpp__

#define _USE_MATH_DEFINES
#include <cmath>
#include <memory>
#include <string>
#include <vector>

#include "compiler/transpiler.hpp"
#include "primitives/backend_sampler_v2.hpp"
#include "primitives/containers/estimator_pub.hpp"
#include "primitives/containers/estimator_result.hpp"
#include "primitives/containers/sampler_pub.hpp"
#include "providers/backend.hpp"
#include "providers/jobstatus.hpp"

namespace Qiskit {
namespace primitives {

namespace {

int bit_value_from_string(const std::string& bitstring, uint_t qubit, uint_t num_qubits)
{
uint_t pos = num_qubits - 1 - qubit;
return bitstring[pos] == '1' ? 1 : 0;
}

double pauli_eigenvalue_from_bitstring(const std::string& pauli, const std::string& bitstring)
{
double value = 1.0;
uint_t n = pauli.size();

for (uint_t q = 0; q < n; q++) {
char p = pauli[n - 1 - q];

if (p == 'I') {
continue;
}

int bit = bit_value_from_string(bitstring, q, n);
value *= (bit == 0) ? 1.0 : -1.0;
}

return value;
}

void append_basis_rotation(circuit::QuantumCircuit& circ, const std::string& pauli)
{
uint_t n = pauli.size();

for (uint_t q = 0; q < n; q++) {
char p = pauli[n - 1 - q];

if (p == 'X') {
circ.h(q);
} else if (p == 'Y') {
circ.rz(-M_PI / 2.0, q);
circ.h(q);
}
}
}

void append_measurements(circuit::QuantumCircuit& circ, uint_t num_qubits)
{
for (uint_t q = 0; q < num_qubits; q++) {
circ.measure(q, q);
}
}

EstimatorPubResult estimate_pub(providers::BackendV2& backend,
uint_t default_shots,
EstimatorPub& pub)
{
BackendSamplerV2 sampler(backend, default_shots);
double total_ev = 0.0;
double variance_accum = 0.0;
uint_t shots = pub.shots() > 0 ? pub.shots() : default_shots;

for (const auto& term : pub.observables()) {
const std::string& pauli = term.first;
double coeff = term.second;
uint_t num_qubits = pauli.size();

auto meas_circ = pub.circuit().copy();
append_basis_rotation(meas_circ, pauli);

if (meas_circ.get_measure_map().empty()) {
append_measurements(meas_circ, num_qubits);
}

auto isa_circ = compiler::transpile(meas_circ, backend);
SamplerPub sampler_pub(isa_circ, shots);

auto job = sampler.run({sampler_pub});
if (job == nullptr) {
continue;
}

auto primitive_result = job->result();
auto pub_result = primitive_result[0];
auto bits = pub_result.data().get_bitstrings();

if (bits.empty()) {
continue;
}

double mean = 0.0;
for (const auto& b : bits) {
mean += pauli_eigenvalue_from_bitstring(pauli, b);
}
mean /= static_cast<double>(bits.size());

total_ev += coeff * mean;
variance_accum += coeff * coeff * (1.0 - mean * mean) / static_cast<double>(bits.size());
}

EstimatorPubResult result(pub);
result.set_ev(total_ev);
result.set_stddev(std::sqrt(variance_accum));
return result;
}

} // namespace

/// @class BackendEstimatorJob
/// @brief Job class for Backend Estimator primitive.
class BackendEstimatorJob {
protected:
providers::BackendV2& backend_;
uint_t shots_;
std::vector<EstimatorPub> pubs_;
EstimatorResult result_;
bool finished_ = false;
public:
BackendEstimatorJob(providers::BackendV2& backend,
uint_t shots,
std::vector<EstimatorPub>& pubs)
: backend_(backend), shots_(shots), pubs_(pubs)
{
}

BackendEstimatorJob(const BackendEstimatorJob& other)
: backend_(other.backend_),
shots_(other.shots_),
pubs_(other.pubs_),
result_(other.result_),
finished_(other.finished_)
{
}

const std::vector<EstimatorPub>& pubs(void) const
{
return pubs_;
}

providers::JobStatus status(void)
{
return finished_ ? providers::JobStatus::DONE : providers::JobStatus::RUNNING;
}

bool running(void)
{
return !finished_;
}

bool done(void)
{
return finished_;
}

bool cancelled(void)
{
return false;
}

bool in_final_state(void)
{
return finished_;
}

bool cancel(void)
{
return false;
}

EstimatorResult result(void)
{
if (!finished_) {
result_.allocate(pubs_.size());
for (uint_t i = 0; i < pubs_.size(); i++) {
result_[i] = estimate_pub(backend_, shots_, pubs_[i]);
}
finished_ = true;
}
return result_;
}
};

} // namespace primitives
} // namespace Qiskit

#endif //__qiskitcpp_primitives_backend_estimator_job_def_hpp__
63 changes: 63 additions & 0 deletions src/primitives/backend_estimator_v2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
*/

// estimator implementation for a backend

#ifndef __qiskitcpp_primitives_backend_estimator_v2_def_hpp__
#define __qiskitcpp_primitives_backend_estimator_v2_def_hpp__

#include <memory>
#include <vector>

#include "primitives/backend_estimator_job.hpp"
#include "primitives/containers/estimator_pub.hpp"
#include "providers/backend.hpp"

namespace Qiskit {
namespace primitives {

/// @class BackendEstimatorV2
/// @brief Implementation of EstimatorV2 on a backend
class BackendEstimatorV2 {
protected:
uint_t shots_;
providers::BackendV2& backend_;
public:
/// @brief Create a new BackendEstimatorV2
/// @param backend The backend to run circuits on
/// @param default_shots The default shots
BackendEstimatorV2(providers::BackendV2& backend, uint_t shots = 1024)
: shots_(shots), backend_(backend)
{
}

/// @brief Return reference to backend object
const providers::BackendV2& backend(void) const
{
return backend_;
}

/// @brief Run and collect expectation values from each pub.
/// @param pubs An iterable of estimator pub-like objects.
/// @return BackendEstimatorJob
std::shared_ptr<BackendEstimatorJob> run(std::vector<EstimatorPub> pubs)
{
return std::make_shared<BackendEstimatorJob>(backend_, shots_, pubs);
}
};

} // namespace primitives
} // namespace Qiskit

#endif //__qiskitcpp_primitives_backend_estimator_v2_def_hpp__
Loading
Loading