Skip to content

Commit

Permalink
parallelize index construction for binary search
Browse files Browse the repository at this point in the history
add debug message for performance comparison
  • Loading branch information
hhorii committed Jul 21, 2020
1 parent e87569b commit da633b0
Showing 1 changed file with 113 additions and 42 deletions.
155 changes: 113 additions & 42 deletions src/simulators/statevector/qubitvector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <chrono>

#include "simulators/statevector/indexes.hpp"
#include "framework/json.hpp"

namespace QV {

using myclock_t = std::chrono::high_resolution_clock;

template <typename T> using cvector_t = std::vector<std::complex<T>>;

//============================================================================
Expand Down Expand Up @@ -451,28 +454,12 @@ class QubitVector {
//-----------------------------------------------------------------------
// Statevector Sampler Utilities
//-----------------------------------------------------------------------

//----------------------------------------------------------------
// Function name: get_accumulated_probabilities_vector
// Description: Computes the accumulated probabilities from 0
// Parameters: qubits - the qubits for which we compute probabilities
// Returns: acc_probvector - the vector of accumulated probabilities
// index_vec - the base values whose probabilities are not 0
// For example:
// if probabilities vector is: 0.5 (00), 0.0 (01), 0.2 (10), 0.3 (11), then
// acc_probvector = 0.0, 0.5, 0.7, 1.0
// index_vec = 0 (00), 2 (10), 3(11)
//----------------------------------------------------------------
void get_accumulated_probabilities_vector(std::vector<double>& acc_probvector,
reg_t& index_vec) const;

// Return M sampled outcomes for Z-basis measurement of all qubits
// by using accumulated probabilities
reg_t sample_measure_with_bs(const std::vector<double> &rnds) const;
reg_t sample_measure_with_binary_search(const std::vector<double> &rnds) const;

// Return M sampled outcomes for Z-basis measurement of all qubits
// with consideration of memory affinity
reg_t sample_measure_with_memory_affinity(const std::vector<double> &rnds) const;
reg_t sample_measure_with_iterative_search(const std::vector<double> &rnds) const;


};
Expand Down Expand Up @@ -1956,47 +1943,48 @@ std::vector<double> QubitVector<data_t>::probabilities(const reg_t &qubits) cons
return probs;
}

template <typename data_t>
void QubitVector<data_t>::get_accumulated_probabilities_vector(std::vector<double>& acc_probvector,
reg_t& index_vec) const {
uint_t size = 1LL << num_qubits_;
uint_t j = 1;
acc_probvector.push_back(0.0);
for (uint_t i=0; i<size; i++) {
if (!AER::Linalg::almost_equal(probability(i), 0.0)) {
index_vec.push_back(i);
acc_probvector.push_back(acc_probvector[j-1] + probability(i));
j++;
}
}
}

//------------------------------------------------------------------------------
// Sample measure outcomes
//------------------------------------------------------------------------------
template <typename data_t>
reg_t QubitVector<data_t>::sample_measure(const std::vector<double> &rnds) const {

reg_t ret;
auto timer_start = myclock_t::now();
std::string sampling_method;
if (sample_measure_index_size_ < 0) {
ret = sample_measure_with_bs(rnds);
ret = sample_measure_with_binary_search(rnds);
sampling_method = "binary_search";
} else {
ret = sample_measure_with_memory_affinity(rnds);
ret = sample_measure_with_iterative_search(rnds);
sampling_method = "indexing";
}
auto timer_stop = myclock_t::now();
std::cout << "DEBUG[sample_measure]: " << sampling_method << ": " << (std::chrono::duration<double>(timer_stop - timer_start).count()) << std::endl;
return ret;

}

template <typename data_t>
reg_t QubitVector<data_t>::sample_measure_with_bs(const std::vector<double> &rnds) const {
reg_t QubitVector<data_t>::sample_measure_with_binary_search(const std::vector<double> &rnds) const {

const uint_t SHOTS = rnds.size();
reg_t samples;
samples.assign(SHOTS, 0);
std::vector<double> acc_probvector;
reg_t index_vec;
get_accumulated_probabilities_vector(acc_probvector, index_vec);

uint_t size = 1LL << num_qubits_;
uint_t j = 1;
acc_probvector.push_back(0.0);
for (uint_t i=0; i<size; i++) {
if (!AER::Linalg::almost_equal(probability(i), 0.0)) {
index_vec.push_back(i);
acc_probvector.push_back(acc_probvector[j-1] + probability(i));
j++;
}
}

uint_t accvec_size = acc_probvector.size();
uint_t rnd_index;
#pragma omp parallel for if (SHOTS > omp_threshold_ && omp_threads_ > 1) num_threads(omp_threads_)
Expand All @@ -2017,15 +2005,98 @@ reg_t QubitVector<data_t>::sample_measure_with_bs(const std::vector<double> &rnd
last = mid;
else
first = mid;
}

samples[i] = index_vec[rnd_index];
}
}
samples[i] = index_vec[rnd_index];
}

// const uint_t SHOTS = rnds.size();
// reg_t samples;
// samples.assign(SHOTS, 0);
//
// //const int INDEX_QUBITS = num_qubits_ < 10 ? 0 : 10;
// const int INDEX_QUBITS = 0;
// const int INDEX_SIZE = 1 << INDEX_QUBITS;
//
// std::vector<std::vector<std::pair<double, int_t>>> prob_indices;
// prob_indices.assign(INDEX_SIZE, std::vector<std::pair<double, int_t>>());
//
// std::vector<double> starts_probs;
// starts_probs.assign(INDEX_SIZE, 0);
// starts_probs[0] = 0.;
//
// int_t index_range = data_size_ >> INDEX_QUBITS;
//
// auto timer_start = myclock_t::now();
//#pragma omp parallel for if (INDEX_SIZE > omp_threshold_ && omp_threads_ > 1) num_threads(omp_threads_)
// for (int_t i = 0; i < INDEX_SIZE; ++i) {
// double accumulated = .0;
// for (uint_t j = index_range * i; j < index_range * (i + 1); j++) {
// if (!AER::Linalg::almost_equal(probability(j), 0.0)) {
// accumulated += probability(j);
// prob_indices[i].push_back(std::pair<double, int_t>(accumulated, j));
// }
// }
// if (i != INDEX_SIZE - 1)
// starts_probs[i + 1] = accumulated;
// }
// auto timer_stop = myclock_t::now();
// std::cout << "DEBUG[sample_measure_with_binary_search]: indexing: " << (std::chrono::duration<double>(timer_stop - timer_start).count()) << std::endl;
//
// for (int_t i = 1; i < INDEX_SIZE; ++i)
// starts_probs[i] += starts_probs[i - 1];
//
// timer_start = myclock_t::now();
//
// uint_t rnd_index = 0;
//#pragma omp parallel for if (SHOTS > omp_threshold_ && omp_threads_ > 1) num_threads(omp_threads_)
// for (int_t i = 0; i < SHOTS; ++i) {
// double rnd = rnds[i];
//
// // binary search for index
// int first = 0;
// int last = INDEX_SIZE - 1;
// int mid = 0;
//
// while (true) {
// if (first >= last - 1) {
// rnd_index = first;
// break;
// }
// mid = (first + last) / 2;
// if (rnd <= starts_probs[mid])
// last = mid;
// else
// first = mid;
// }
//
// auto starts_prob = starts_probs[rnd_index];
// auto prob_index = prob_indices[rnd_index];
//
// // binary search for which range rnd is in
// first = 0;
// last = prob_index.size() - 1;
// mid = 0;
// while (true) {
// if (first >= last - 1) {
// rnd_index = first;
// break;
// }
// mid = (first + last) / 2;
// if (rnd <= (prob_index[mid].first + starts_prob))
// last = mid;
// else
// first = mid;
// }
// samples[i] = prob_index[rnd_index].second;
// }
//
// timer_stop = myclock_t::now();
// std::cout << "DEBUG[sample_measure_with_binary_search]: search: " << (std::chrono::duration<double>(timer_stop - timer_start).count()) << std::endl;
return samples;
}

template <typename data_t>
reg_t QubitVector<data_t>::sample_measure_with_memory_affinity(const std::vector<double> &rnds) const {
reg_t QubitVector<data_t>::sample_measure_with_iterative_search(const std::vector<double> &rnds) const {

const int_t END = 1LL << num_qubits();
const int_t SHOTS = rnds.size();
Expand Down

0 comments on commit da633b0

Please sign in to comment.