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

Fix required_memory_mb for MPS and extended stabilizer #1933

Merged
merged 3 commits into from
Sep 12, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
fixes:
- |
State::set_config was not called before calling State::required_memory_mb.
Extended stabilizer uses parameter from config to calculate required memory
so size was not correct before this fix.
Now Config is passed to required_memory_mb function.

State::required_memory_mb for MPS method returned wrong memory size.
This fix adds memory size estimation by calculating max bond dimension.
93 changes: 46 additions & 47 deletions src/controllers/aer_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class Controller {
// If `throw_except` is true an exception will be thrown on the return false
// case listing the invalid instructions in the circuit or noise model, or
// the required memory.
bool validate_method(Method method, const Circuit &circ,
bool validate_method(Method method, const Config &config, const Circuit &circ,
const Noise::NoiseModel &noise,
bool throw_except = false) const;

Expand All @@ -147,13 +147,14 @@ class Controller {
// The noise model will be modified to enable superop or kraus sampling
// methods if required by the chosen methods.
std::vector<Method>
simulation_methods(std::vector<std::shared_ptr<Circuit>> &circuits,
simulation_methods(const Config &config,
std::vector<std::shared_ptr<Circuit>> &circuits,
Noise::NoiseModel &noise_model) const;

// Return the simulation method to use based on the input circuit
// and noise model
Method
automatic_simulation_method(const Circuit &circ,
automatic_simulation_method(const Config &config, const Circuit &circ,
const Noise::NoiseModel &noise_model) const;

bool has_statevector_ops(const Circuit &circuit) const;
Expand All @@ -165,9 +166,7 @@ class Controller {
void clear_parallelization();

// Set parallelization for experiments
void set_parallelization_experiments(
const std::vector<std::shared_ptr<Circuit>> &circuits,
const Noise::NoiseModel &noise, const std::vector<Method> &methods);
void set_parallelization_experiments(const reg_t &required_memory_list);

void save_exception_to_results(Result &result, const std::exception &e) const;

Expand Down Expand Up @@ -354,12 +353,12 @@ void Controller::clear_parallelization() {
}

void Controller::set_parallelization_experiments(
const std::vector<std::shared_ptr<Circuit>> &circuits,
const Noise::NoiseModel &noise, const std::vector<Method> &methods) {
const reg_t &required_memory_mb_list) {

if (explicit_parallelization_)
return;

if (circuits.size() == 1) {
if (required_memory_mb_list.size() == 1) {
parallel_experiments_ = 1;
return;
}
Expand All @@ -378,20 +377,12 @@ void Controller::set_parallelization_experiments(
}

// If memory allows, execute experiments in parallel
std::vector<size_t> required_memory_mb_list(circuits.size());
for (size_t j = 0; j < circuits.size(); j++) {
std::shared_ptr<CircuitExecutor::Base> executor =
make_circuit_executor(methods[j]);
required_memory_mb_list[j] =
executor->required_memory_mb(*circuits[j], noise);
executor.reset();
}
std::sort(required_memory_mb_list.begin(), required_memory_mb_list.end(),
std::greater<>());
reg_t required_sorted = required_memory_mb_list;
std::sort(required_sorted.begin(), required_sorted.end(), std::greater<>());

size_t total_memory = 0;
int parallel_experiments = 0;
for (size_t required_memory_mb : required_memory_mb_list) {
for (size_t required_memory_mb : required_sorted) {
total_memory += required_memory_mb;
if (total_memory > max_memory_mb_)
break;
Expand All @@ -401,9 +392,9 @@ void Controller::set_parallelization_experiments(
if (parallel_experiments <= 0)
throw std::runtime_error(
"a circuit requires more memory than max_memory_mb.");
parallel_experiments_ =
std::min<int>({parallel_experiments, max_experiments,
max_parallel_threads_, static_cast<int>(circuits.size())});
parallel_experiments_ = std::min<int>(
{parallel_experiments, max_experiments, max_parallel_threads_,
static_cast<int>(required_memory_mb_list.size())});
}

size_t Controller::get_system_memory_mb() {
Expand Down Expand Up @@ -508,20 +499,29 @@ Result Controller::execute(std::vector<std::shared_ptr<Circuit>> &circuits,
#endif
// Determine simulation method for each circuit
// and enable required noise sampling methods
auto methods = simulation_methods(circuits, noise_model);
auto methods = simulation_methods(config, circuits, noise_model);

// Initialize Result object for the given number of experiments
Result result(circuits.size());
// Initialize circuit executors for each circuit
std::vector<std::shared_ptr<CircuitExecutor::Base>> executors(
circuits.size());
reg_t required_memory_mb_list(circuits.size());

// Execute each circuit in a try block
try {
num_process_per_experiment_ = num_processes_;

// set parallelization for experiments
try {
// catch exception raised by required_memory_mb because of invalid
// simulation method
set_parallelization_experiments(circuits, noise_model, methods);
for (int i = 0; i < circuits.size(); i++) {
executors[i] = make_circuit_executor(methods[i]);
required_memory_mb_list[i] =
executors[i]->required_memory_mb(config, *circuits[i], noise_model);
result.results[i].metadata.add(required_memory_mb_list[i],
"required_memory_mb");
}
set_parallelization_experiments(required_memory_mb_list);
} catch (std::exception &e) {
save_exception_to_results(result, e);
}
Expand Down Expand Up @@ -581,23 +581,18 @@ Result Controller::execute(std::vector<std::shared_ptr<Circuit>> &circuits,
// nested loops that causes performance degradation (DO NOT use if statement
// in #pragma omp)
if (parallel_experiments_ == 1) {
for (int j = 0; j < NUM_RESULTS; ++j) {
std::shared_ptr<CircuitExecutor::Base> executor =
make_circuit_executor(methods[j]);
executor->run_circuit(*circuits[j], noise_model, config, methods[j],
sim_device_, result.results[j]);
executor.reset();
for (int i = 0; i < NUM_RESULTS; i++) {
executors[i]->run_circuit(*circuits[i], noise_model, config, methods[i],
sim_device_, result.results[i]);
}
} else {
#pragma omp parallel for num_threads(parallel_experiments_)
for (int j = 0; j < NUM_RESULTS; ++j) {
std::shared_ptr<CircuitExecutor::Base> executor =
make_circuit_executor(methods[j]);
executor->run_circuit(*circuits[j], noise_model, config, methods[j],
sim_device_, result.results[j]);
executor.reset();
for (int i = 0; i < NUM_RESULTS; i++) {
executors[i]->run_circuit(*circuits[i], noise_model, config, methods[i],
sim_device_, result.results[i]);
}
}
executors.clear();

// Check each experiment result for completed status.
// If only some experiments completed return partial completed status.
Expand Down Expand Up @@ -755,7 +750,8 @@ Controller::make_circuit_executor(const Method method) const {
}

std::vector<Method>
Controller::simulation_methods(std::vector<std::shared_ptr<Circuit>> &circuits,
Controller::simulation_methods(const Config &config,
std::vector<std::shared_ptr<Circuit>> &circuits,
Noise::NoiseModel &noise_model) const {
// Does noise model contain kraus noise
bool kraus_noise =
Expand All @@ -769,7 +765,7 @@ Controller::simulation_methods(std::vector<std::shared_ptr<Circuit>> &circuits,
bool kraus_enabled = false;
for (const auto &_circ : circuits) {
const auto circ = *_circ;
auto method = automatic_simulation_method(circ, noise_model);
auto method = automatic_simulation_method(config, circ, noise_model);
sim_methods.push_back(method);
if (!superop_enabled &&
(method == Method::density_matrix || method == Method::superop ||
Expand Down Expand Up @@ -811,9 +807,10 @@ Controller::simulation_methods(std::vector<std::shared_ptr<Circuit>> &circuits,
}

Method Controller::automatic_simulation_method(
const Circuit &circ, const Noise::NoiseModel &noise_model) const {
const Config &config, const Circuit &circ,
const Noise::NoiseModel &noise_model) const {
// If circuit and noise model are Clifford run on Stabilizer simulator
if (validate_method(Method::stabilizer, circ, noise_model, false)) {
if (validate_method(Method::stabilizer, config, circ, noise_model, false)) {
return Method::stabilizer;
}
// For noisy simulations we enable the density matrix method if
Expand All @@ -823,7 +820,8 @@ Method Controller::automatic_simulation_method(
// dimension
if (noise_model.has_quantum_errors() && circ.num_qubits < 64 &&
circ.shots > (1ULL << circ.num_qubits) &&
validate_method(Method::density_matrix, circ, noise_model, false) &&
validate_method(Method::density_matrix, config, circ, noise_model,
false) &&
circ.can_sample) {
return Method::density_matrix;
}
Expand All @@ -837,7 +835,7 @@ Method Controller::automatic_simulation_method(
{Method::statevector, Method::density_matrix,
Method::matrix_product_state, Method::unitary, Method::superop});
for (const auto &method : methods) {
if (validate_method(method, circ, noise_model, false))
if (validate_method(method, config, circ, noise_model, false))
return method;
}

Expand Down Expand Up @@ -867,12 +865,13 @@ bool Controller::has_statevector_ops(const Circuit &circ) const {
//-------------------------------------------------------------------------
// Validation
//-------------------------------------------------------------------------
bool Controller::validate_method(Method method, const Circuit &circ,
bool Controller::validate_method(Method method, const Config &config,
const Circuit &circ,
const Noise::NoiseModel &noise_model,
bool throw_except) const {
std::shared_ptr<CircuitExecutor::Base> executor =
make_circuit_executor(method);
bool ret = executor->validate_state(circ, noise_model, throw_except);
bool ret = executor->validate_state(config, circ, noise_model, throw_except);
executor.reset();
return ret;
}
Expand Down
8 changes: 4 additions & 4 deletions src/simulators/batch_shots_executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BatchShotsExecutor : public virtual MultiStateExecutor<state_t> {

protected:
void set_config(const Config &config) override;
void set_parallelization(const Circuit &circ,
void set_parallelization(const Config &config, const Circuit &circ,
const Noise::NoiseModel &noise) override;

void run_circuit_shots(Circuit &circ, const Noise::NoiseModel &noise,
Expand Down Expand Up @@ -104,8 +104,8 @@ void BatchShotsExecutor<state_t>::set_config(const Config &config) {

template <class state_t>
void BatchShotsExecutor<state_t>::set_parallelization(
const Circuit &circ, const Noise::NoiseModel &noise) {
Base::set_parallelization(circ, noise);
const Config &config, const Circuit &circ, const Noise::NoiseModel &noise) {
Base::set_parallelization(config, circ, noise);

enable_batch_multi_shots_ = false;
if (batched_shots_gpu_ && Base::sim_device_ != Device::CPU) {
Expand Down Expand Up @@ -152,7 +152,7 @@ void BatchShotsExecutor<state_t>::run_circuit_shots(
}

Base::set_distribution(circ.shots);
Base::num_max_shots_ = Base::get_max_parallel_shots(circ, noise);
Base::num_max_shots_ = Base::get_max_parallel_shots(config, circ, noise);
if (Base::num_max_shots_ == 0)
Base::num_max_shots_ = 1;

Expand Down
Loading