From cf0c9d568d851da75b6237330db1b7bd30d65236 Mon Sep 17 00:00:00 2001 From: Hiroshi Horii Date: Tue, 23 Apr 2019 17:22:29 +0900 Subject: [PATCH 1/6] add truncate optimization --- src/base/controller.hpp | 17 +-- src/base/state.hpp | 20 ++- src/framework/circuitopt.hpp | 3 +- src/framework/operations.hpp | 9 +- src/simulators/qasm/basic_optimization.hpp | 143 +++++++++++++++++- src/simulators/qasm/qasm_controller.hpp | 10 +- .../statevector/statevector_state.hpp | 16 +- 7 files changed, 183 insertions(+), 35 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 3dbac7f835..528902ccdd 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -134,13 +134,6 @@ class Controller { // Clear the current config void virtual clear_config(); - // Add circuit optimization - template - inline auto add_circuit_optimization(Type&& opt)-> typename std::enable_if_t>>::value > - { - optimizations_.push_back(std::make_shared > >(std::forward(opt))); - } - protected: //----------------------------------------------------------------------- @@ -204,9 +197,6 @@ class Controller { // Noise model Noise::NoiseModel noise_model_; - // Circuit optimization - std::vector> optimizations_; - //----------------------------------------------------------------------- // Parallelization Config //----------------------------------------------------------------------- @@ -274,9 +264,6 @@ void Controller::set_config(const json_t &config) { max_memory_mb_ = system_memory_mb / 2; } - for (std::shared_ptr opt: optimizations_) - opt->set_config(config_); - std::string path; JSON::get_value(path, "library_dir", config); // Fix for MacOS and OpenMP library double initialization crash. @@ -446,8 +433,10 @@ Circuit Controller::optimize_circuit(const Circuit &input_circ, allowed_opset.gates = state.allowed_gates(); allowed_opset.snapshots = state.allowed_snapshots(); - for (std::shared_ptr opt: optimizations_) + for (std::shared_ptr opt: state.get_circuit_optimization()) { + opt->set_config(config_); opt->optimize_circuit(working_circ, allowed_opset, data); + } return working_circ; } diff --git a/src/base/state.hpp b/src/base/state.hpp index 21c488e122..19cfa5cd28 100644 --- a/src/base/state.hpp +++ b/src/base/state.hpp @@ -13,6 +13,7 @@ #include "framework/types.hpp" #include "framework/data.hpp" #include "framework/creg.hpp" +#include "framework/circuitopt.hpp" namespace AER { namespace Base { @@ -187,6 +188,19 @@ class State { inline const state_t &qreg() const {return qreg_;} inline const auto &creg() const {return creg_;} + //----------------------------------------------------------------------- + // Circuit optimization + //----------------------------------------------------------------------- + + // Add circuit optimization + template + inline auto add_circuit_optimization(Type&& opt)-> typename std::enable_if_t>>::value > + { + optimizations_.push_back(std::make_shared > >(std::forward(opt))); + } + + std::vector> get_circuit_optimization() { return optimizations_; }; + protected: // The quantum state data structure @@ -198,6 +212,10 @@ class State { // Maximum threads which may be used by the backend for OpenMP multithreading // Default value is single-threaded unless overridden int threads_ = 1; + + // Circuit optimization + std::vector> optimizations_; + }; @@ -309,8 +327,6 @@ void State::add_creg_to_data(OutputData &data) const { data.add_register_singleshot(creg_.register_hex()); } } - - //------------------------------------------------------------------------- } // end namespace Base //------------------------------------------------------------------------- diff --git a/src/framework/circuitopt.hpp b/src/framework/circuitopt.hpp index 1b97b05322..2911d4ba5c 100644 --- a/src/framework/circuitopt.hpp +++ b/src/framework/circuitopt.hpp @@ -17,8 +17,7 @@ #include #include -#include "base/controller.hpp" -#include "base/state.hpp" +#include "framework/operations.hpp" namespace AER { diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index fedd0c779e..ca9149aead 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -121,7 +121,14 @@ struct Op { }; std::ostream& operator<<(std::ostream& s, const Op& op) { - s << op.name; + s << op.name << "["; + bool first = true; + for (size_t qubit: op.qubits) { + if (!first) s << ","; + s << qubit; + first = false; + } + s << "]"; return s; } diff --git a/src/simulators/qasm/basic_optimization.hpp b/src/simulators/qasm/basic_optimization.hpp index c9a70e46d7..1e803b59a2 100644 --- a/src/simulators/qasm/basic_optimization.hpp +++ b/src/simulators/qasm/basic_optimization.hpp @@ -16,17 +16,18 @@ using uint_t = uint_t; using op_t = Operations::Op; using optype_t = Operations::OpType; using oplist_t = std::vector; +using opset_t = Operations::OpSet; using reg_t = std::vector; class ReduceNop : public CircuitOptimization { public: void optimize_circuit(Circuit& circ, - const Operations::OpSet &opset, + const opset_t &opset, OutputData &data) const override; }; void ReduceNop::optimize_circuit(Circuit& circ, - const Operations::OpSet &allowed_opset, + const opset_t &allowed_opset, OutputData &data) const { oplist_t::iterator it = circ.ops.begin(); @@ -41,12 +42,12 @@ void ReduceNop::optimize_circuit(Circuit& circ, class Debug : public CircuitOptimization { public: void optimize_circuit(Circuit& circ, - const Operations::OpSet &opset, + const opset_t &opset, OutputData &data) const override; }; void Debug::optimize_circuit(Circuit& circ, - const Operations::OpSet &allowed_opset, + const opset_t &allowed_opset, OutputData &data) const { oplist_t::iterator it = circ.ops.begin(); @@ -63,7 +64,7 @@ class Fusion : public CircuitOptimization { void set_config(const json_t &config) override; void optimize_circuit(Circuit& circ, - const Operations::OpSet &opset, + const opset_t &opset, OutputData &data) const override; bool can_ignore(const op_t& op) const; @@ -155,7 +156,7 @@ void Fusion::set_config(const json_t &config) { #ifdef DEBUG void Fusion::dump(const Circuit& circuit) const { int idx = 0; - for (const Operations::Op& op : circuit.ops) { + for (const op_t& op : circuit.ops) { std::cout << " " << idx++ << ":\t" << op.name << " " << op.qubits << std::endl; for (const cmatrix_t& mat: op.mats) { const uint_t row = mat.GetRows(); @@ -174,12 +175,12 @@ void Fusion::dump(const Circuit& circuit) const { #endif void Fusion::optimize_circuit(Circuit& circ, - const Operations::OpSet &allowed_opset, + const opset_t &allowed_opset, OutputData &data) const { if (circ.num_qubits < threshold_ || !active_ - || allowed_opset.optypes.find(Operations::OpType::matrix_sequence) == allowed_opset.optypes.end()) + || allowed_opset.optypes.find(optype_t::matrix_sequence) == allowed_opset.optypes.end()) return; bool ret = false; @@ -463,6 +464,132 @@ cmatrix_t Fusion::matrix(const op_t& op) const { throw std::runtime_error("Fusion: unexpected operation type"); } } + +class TruncateQubits : public CircuitOptimization { +public: + + void set_config(const json_t &config) override; + + // truncate unnecessary qubits + void optimize_circuit(Circuit& circ, + const opset_t &opset, + OutputData &data) const override; + +private: + // check this optimization can be applied + bool can_apply(const Circuit& circ) const; + + // check this optimization can be applied + bool can_apply(const op_t& op) const; + + // generate a new mapping. a value of reg_t is original and its index is the new mapping + reg_t generate_mapping(const Circuit& circ) const; + + // remap qubits in an operation + op_t remap_qubits(const op_t op, const reg_t new_mapping)const; + + // show debug info + bool verbose_ = false; + + // disabled in config + bool active_ = true; +}; + +void TruncateQubits::set_config(const json_t &config) { + + CircuitOptimization::set_config(config); + + if (JSON::check_key("truncate_verbose", config_)) + JSON::get_value(verbose_, "truncate_verbose", config_); + + if (JSON::check_key("truncate_enable", config_)) + JSON::get_value(active_, "truncate_enable", config_); + + if (JSON::check_key("initial_statevector", config_)) + active_ = false; + +} + +void TruncateQubits::optimize_circuit(Circuit& circ, + const opset_t &allowed_opset, + OutputData &data) const { + + if (!active_ || !can_apply(circ)) + return; + + reg_t new_mapping = generate_mapping(circ); + + if (new_mapping.size() == circ.num_qubits) + return; + + oplist_t new_ops; + for (const op_t& old_op: circ.ops) + new_ops.push_back(remap_qubits(old_op, new_mapping)); + + circ.ops = new_ops; + circ.num_qubits = new_mapping.size(); + + if (verbose_) { + data.add_additional_data("metadata", + json_t::object({{"truncate_verbose", new_mapping}})); + } + +} + +reg_t TruncateQubits::generate_mapping(const Circuit& circ) const { + reg_t mapping; + + for (const op_t& op: circ.ops) { + for (size_t qubit: op.qubits) { + if (std::find(mapping.begin(), mapping.end(), qubit) == mapping.end()) + mapping.push_back(qubit); + } + } + + return mapping; +} + +op_t TruncateQubits::remap_qubits(const op_t op, const reg_t new_mapping) const { + op_t new_op = op; + new_op.qubits.clear(); + + for (const size_t qubit: op.qubits) { + size_t new_qubit = std::distance(new_mapping.begin(), find(new_mapping.begin(), new_mapping.end(), qubit)); + new_op.qubits.push_back(new_qubit); + } + return new_op; + +} + +bool TruncateQubits::can_apply(const Circuit& circ) const { + + for (const op_t& op: circ.ops) + if (!can_apply(op)) + return false; + + return true; +} + +bool TruncateQubits::can_apply(const op_t& op) const { + switch (op.type) { + case optype_t::gate: + case optype_t::measure: + case optype_t::reset: + case optype_t::bfunc: + case optype_t::barrier: + case optype_t::roerror: + case optype_t::initialize: + return true; + case optype_t::matrix: //TODO + case optype_t::matrix_sequence: //TODO + case optype_t::kraus: //TODO + case optype_t::snapshot: + case optype_t::noise_switch: + default: + return false; + } +} + //------------------------------------------------------------------------- } // end namespace AER //------------------------------------------------------------------------- diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 6c1bd835b1..233c672e6a 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -12,7 +12,6 @@ #include "simulators/extended_stabilizer/extended_stabilizer_state.hpp" #include "simulators/statevector/statevector_state.hpp" #include "simulators/stabilizer/stabilizer_state.hpp" -#include "simulators/qasm/basic_optimization.hpp" namespace AER { @@ -234,8 +233,6 @@ class QasmController : public Base::Controller { // Constructor //------------------------------------------------------------------------- QasmController::QasmController() { - add_circuit_optimization(ReduceNop()); - add_circuit_optimization(Fusion()); } //------------------------------------------------------------------------- @@ -477,8 +474,8 @@ void QasmController::run_circuit_with_noise(const Circuit &circ, // Sample a new noise circuit and optimize for each shot while(shots-- > 0) { Circuit noise_circ = noise_model_.sample_noise(circ, rng); - noise_circ = optimize_circuit(noise_circ, state, data); - run_single_shot(noise_circ, state, initial_state, data, rng); + Circuit opt_noise_circ = optimize_circuit(circ, state, data); + run_single_shot(opt_noise_circ, state, initial_state, data, rng); } } @@ -491,8 +488,7 @@ void QasmController::run_circuit_without_noise(const Circuit &circ, OutputData &data, RngEngine &rng) const { // Optimize circuit for state type - Circuit opt_circ; - opt_circ = optimize_circuit(circ, state, data); + Circuit opt_circ = optimize_circuit(circ, state, data); // Check if measure sampler and optimization are valid auto check = check_measure_sampling_opt(opt_circ); diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 9139be9143..181e802d59 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -16,6 +16,7 @@ #include "framework/json.hpp" #include "base/state.hpp" #include "qubitvector.hpp" +#include "simulators/qasm/basic_optimization.hpp" namespace AER { @@ -45,7 +46,7 @@ class State : public Base::State { public: using BaseState = Base::State; - State() = default; + State(); virtual ~State() = default; //----------------------------------------------------------------------- @@ -317,6 +318,16 @@ const stringmap_t State::snapshotset_({ {"register", Snapshots::cregister} }); +//------------------------------------------------------------------------- +// Constructor +//------------------------------------------------------------------------- + +template +State::State() { + BaseState::add_circuit_optimization(ReduceNop()); + BaseState::add_circuit_optimization(Fusion()); + BaseState::add_circuit_optimization(TruncateQubits()); +} //========================================================================= // Implementation: Base class method overrides @@ -381,6 +392,9 @@ size_t State::required_memory_mb(uint_t num_qubits, template void State::set_config(const json_t &config) { + // Set base controller config + BaseState::set_config(config); + // Set threshold for truncating snapshots JSON::get_value(json_chop_threshold_, "chop_threshold", config); BaseState::qreg_.set_json_chop_threshold(json_chop_threshold_); From 4f5a8ad6a8cb583827571f50a4202669cb478b33 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Mon, 22 Apr 2019 14:26:58 -0400 Subject: [PATCH 2/6] fix snapshot_type not being parsed (#163) --- src/framework/operations.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index ca9149aead..181520a5fe 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -756,14 +756,14 @@ Op json_to_op_noise_switch(const json_t &js) { //------------------------------------------------------------------------------ Op json_to_op_snapshot(const json_t &js) { - std::string type; - JSON::get_value(type, "type", js); // LEGACY: TO REMOVE - JSON::get_value(type, "snapshot_type", js); - if (type == "expectation_value_pauli" || - type == "expectation_value_pauli_with_variance") + std::string snapshot_type; + JSON::get_value(snapshot_type, "snapshot_type", js); // LEGACY: to remove in 0.3 + JSON::get_value(snapshot_type, "type", js); + if (snapshot_type == "expectation_value_pauli" || + snapshot_type == "expectation_value_pauli_with_variance") return json_to_op_snapshot_pauli(js); - if (type == "expectation_value_matrix" || - type == "expectation_value_matrix_with_variance") + if (snapshot_type == "expectation_value_matrix" || + snapshot_type == "expectation_value_matrix_with_variance") return json_to_op_snapshot_matrix(js); // Default snapshot: has "type", "label", "qubits" return json_to_op_snapshot_default(js); @@ -773,7 +773,8 @@ Op json_to_op_snapshot(const json_t &js) { Op json_to_op_snapshot_default(const json_t &js) { Op op; op.type = OpType::snapshot; - JSON::get_value(op.name, "type", js); + JSON::get_value(op.name, "type", js); // LEGACY: to remove in 0.3 + JSON::get_value(op.name, "snapshot_type", js); // If missing use "default" for label op.string_params.push_back("default"); JSON::get_value(op.string_params[0], "label", js); From 6e22bf4a1b8b66e48f53d6852e4e9dc4bb6fe651 Mon Sep 17 00:00:00 2001 From: gadial Date: Mon, 22 Apr 2019 22:55:14 +0300 Subject: [PATCH 3/6] Noise transform (#162) * Adding noise transformation to the noise package * Unified interface for dict/list/string approximating operator input * Implementation of approximate_noise_model * Now generates noise approximation using unitaries if possible * Now when possible outputs error circuits instead of Kraus/Unitary * Now represent all named operators using circuits --- qiskit/providers/aer/noise/__init__.py | 9 + .../providers/aer/noise/errors/errorutils.py | 2 +- qiskit/providers/aer/noise/utils/__init__.py | 12 + .../aer/noise/utils/noise_transformation.py | 455 ++++++++++++++++++ requirements-dev.txt | 1 + src/simulators/qasm/qasm_controller.hpp | 2 +- test/terra/noise/test_quantum_error.py | 2 +- test/terra/test_noise_transformation.py | 191 ++++++++ 8 files changed, 671 insertions(+), 3 deletions(-) create mode 100644 qiskit/providers/aer/noise/utils/__init__.py create mode 100644 qiskit/providers/aer/noise/utils/noise_transformation.py create mode 100644 test/terra/test_noise_transformation.py diff --git a/qiskit/providers/aer/noise/__init__.py b/qiskit/providers/aer/noise/__init__.py index fdad7d93ff..c5eb96032a 100644 --- a/qiskit/providers/aer/noise/__init__.py +++ b/qiskit/providers/aer/noise/__init__.py @@ -62,8 +62,17 @@ Amplitude damping error Phase damping error Combined phase and amplitude damping error + +Noise Utilities +-------------- +The `noise.utils` module contains utilities for noise models and errors including: + 'approximate_quantum_error' for approximating a general quantum error via + a provided set of errors (e.g. approximating amplitude damping via reset errors) + 'approximate_noise_model' for approximating all the errors in a nose model using + the same provided set of errors """ from .noise_model import NoiseModel from . import errors from . import device +from . import utils \ No newline at end of file diff --git a/qiskit/providers/aer/noise/errors/errorutils.py b/qiskit/providers/aer/noise/errors/errorutils.py index a344c7b71f..b0c035316b 100644 --- a/qiskit/providers/aer/noise/errors/errorutils.py +++ b/qiskit/providers/aer/noise/errors/errorutils.py @@ -622,4 +622,4 @@ def kraus2instructions(kraus_ops, standard_gates, atol=ATOL_DEFAULT): ] instructions.append(make_kraus_instruction(non_unitaries, qubits)) probabilities.append(prob_kraus) - return zip(instructions, probabilities) + return zip(instructions, probabilities) \ No newline at end of file diff --git a/qiskit/providers/aer/noise/utils/__init__.py b/qiskit/providers/aer/noise/utils/__init__.py new file mode 100644 index 0000000000..909ac057ca --- /dev/null +++ b/qiskit/providers/aer/noise/utils/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +"""Noise utils for Qiskit Aer. +""" +from .noise_transformation import NoiseTransformer +from .noise_transformation import approximate_quantum_error +from .noise_transformation import approximate_noise_model diff --git a/qiskit/providers/aer/noise/utils/noise_transformation.py b/qiskit/providers/aer/noise/utils/noise_transformation.py new file mode 100644 index 0000000000..2d81f3cafe --- /dev/null +++ b/qiskit/providers/aer/noise/utils/noise_transformation.py @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +""" +Noise transformation module + +The goal of this module is to transform one 1-qubit noise channel (given by the QuantumError class) +into another, built from specified "building blocks" (given as Kraus matrices) such that +the new channel is as close as possible to the original one in the Hilber-Schmidt metric. + +For a typical use case, consider a simulator for circuits built from the Clifford group. +Computations on such circuits can be simulated at polynomial time and space, but not all +noise channels can be used in such a simulation. To enable noisy Clifford simulation one can +transform the given noise channel into the closest one, Hilbert-Schmidt wise, that +can be used in a Clifford simulator. +""" + +import numpy +import sympy +import itertools + +from qiskit.providers.aer.noise.errors import QuantumError +from qiskit.providers.aer.noise import NoiseModel +from qiskit.providers.aer.noise.noiseerror import NoiseError +from qiskit.providers.aer.noise.errors.errorutils import single_qubit_clifford_instructions +from qiskit.quantum_info.operators.channel import Kraus +from qiskit.quantum_info.operators.channel import SuperOp + +def approximate_quantum_error(error, *, + operator_string = None, + operator_dict = None, + operator_list = None): + """Return an approximate QuantumError bases on the Hilbert-Schmidt metric. + + Args: + error (QuantumError): the error to be approximated. + operator_string (string): a name for a premade set of building blocks for the output channel + operator_dict (dict): a dictionary whose values are the building blocks for the output channel + operator_list (dict): list of building blocks for the output channel + + Additional Information: + The operator input precedence is as follows: list < dict < string + if a string is given, dict is overwritten; if a dict is given, list is overwritten + possible values for string are 'pauli', 'reset', 'clifford'; see NoiseTransformer.named_operators + + Returns: + QuantumError: the approximate quantum error. + """ + + if not isinstance(error, QuantumError): + error = QuantumError(error) + if error.number_of_qubits > 1: + raise NoiseError("Only 1-qubit noises can be converted, {}-qubit noise found in model".format( + error.number_of_qubits)) + + error_kraus_operators = Kraus(error.to_channel()).data + transformer = NoiseTransformer() + if operator_string is not None: + operator_string = operator_string.lower() + if operator_string not in transformer.named_operators.keys(): + raise RuntimeError("No information about noise type {}".format(operator_string)) + operator_dict = transformer.named_operators[operator_string] + if operator_dict is not None: + names, operator_list = zip(*operator_dict.items()) + if operator_list is not None: + op_matrix_list = [transformer.operator_matrix(operator) for operator in operator_list] + probabilities = transformer.transform_by_operator_list(op_matrix_list, error_kraus_operators) + identity_prob = 1 - sum(probabilities) + if identity_prob < 0 or identity_prob > 1: + raise RuntimeError("Approximated channel operators probabilities sum to {}".format(1-identity_prob)) + quantum_error_spec = [([{'name': 'id', 'qubits':[0]}],identity_prob)] + op_circuit_list = [transformer.operator_circuit(operator) for operator in operator_list] + for (operator, probability) in zip(op_circuit_list, probabilities): + quantum_error_spec.append((operator, probability)) + return QuantumError(quantum_error_spec) + + raise Exception("Quantum error approximation failed - no approximating operators detected") + + +def approximate_noise_model(model, *, + operator_string = None, + operator_dict = None, + operator_list = None): + """Return an approximate noise model. + + Args: + model (NoiseModel): the noise model to be approximated. + operator_string (string): a name for a premade set of building blocks for the output channel + operator_dict (dict): a dictionary whose values are the building blocks for the output channel + operator_list (dict): list of building blocks for the output channel + + Additional Information: + The operator input precedence is as follows: list < dict < string + if a string is given, dict is overwritten; if a dict is given, list is overwritten + possible values for string are 'pauli', 'reset', 'clifford'; see NoiseTransformer.named_operators + + Returns: + NoiseModel: the approximate noise model. + + Raises: + NoiseError: if the QuantumError cannot be approximated. + """ + + #We need to iterate over all the errors in the noise model. + #No nice interface for this now, easiest way is to mimic as_dict + + error_list = [] + # Add default quantum errors + for operation, errors in model._default_quantum_errors.items(): + for error in errors: + error = approximate_quantum_error(error, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) + error_dict = error.as_dict() + error_dict["operations"] = [operation] + error_list.append(error_dict) + + # Add specific qubit errors + for operation, qubit_dict in model._local_quantum_errors.items(): + for qubits_str, errors in qubit_dict.items(): + for error in errors: + error = approximate_quantum_error(error, operator_string=operator_string, operator_dict=operator_dict, + operator_list=operator_list) + error_dict = error.as_dict() + error_dict["operations"] = [operation] + error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] + error_list.append(error_dict) + + # Add non-local errors + for operation, qubit_dict in model._nonlocal_quantum_errors.items(): + for qubits_str, errors in qubit_dict.items(): + for error, noise_qubits in errors: + error = approximate_quantum_error(error, operator_string=operator_string, operator_dict=operator_dict, + operator_list=operator_list) + error_dict = error.as_dict() + error_dict["operations"] = [operation] + error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] + error_dict["noise_qubits"] = [list(noise_qubits)] + error_list.append(error_dict) + + # Add default readout error + if model._default_readout_error is not None: + error = approximate_quantum_error(model._default_readout_error, operator_string=operator_string, operator_dict=operator_dict, + operator_list=operator_list) + error_dict = error.as_dict() + error_list.append(error_dict) + + # Add local readout error + for qubits_str, error in model._local_readout_errors.items(): + error = approximate_quantum_error(error, operator_string=operator_string, operator_dict=operator_dict, + operator_list=operator_list) + error_dict = error.as_dict() + error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] + error_list.append(error_dict) + + approx_noise_model = NoiseModel.from_dict({"errors": error_list, "x90_gates": model._x90_gates}) + return approx_noise_model + +class NoiseTransformer: + """Transforms one quantum noise channel to another based on a specified criteria. + """ + + def __init__(self): + self.named_operators = { + 'pauli': {'X': [{'name': 'x', 'qubits': [0]}], + 'Y': [{'name': 'y', 'qubits': [0]}], + 'Z': [{'name': 'z', 'qubits': [0]}] + }, + 'reset': { + 'p': [{'name': 'reset','qubits': [0]}], #reset to |0> + 'q': [{'name': 'reset', 'qubits': [0]}, {'name': 'x','qubits': [0]}] #reset to |1> + }, + 'clifford': dict([(j, single_qubit_clifford_instructions(j)) for j in range(1,24)]) + } + + self.fidelity_data = None + self.use_honesty_constraint = True + self.noise_kraus_operators = None + self.transform_channel_operators = None + + def operator_matrix(self, operator): + """ + Converts an operator representation to Kraus matrix representation + Args: + operator: operator representation. Can be a noise circuit or a matrix or a list of matrices + Output: + The operator, converted to Kraus representation + """ + if isinstance(operator, list) and isinstance(operator[0], dict): + operator_error = QuantumError([(operator, 1)]) + kraus_rep = Kraus(operator_error.to_channel()).data + return kraus_rep + return operator + + def operator_circuit(self, operator): + """ + Converts an operator representation to noise circuit + Args: + operator: operator representation. Can be a noise circuit or a matrix or a list of matrices + Output: + The operator, converted to noise circuit representation + """ + if isinstance(operator, numpy.ndarray): + return [{'name': 'unitary', 'qubits': [0], 'params': [operator]}] + + if isinstance(operator, list) and isinstance(operator[0], numpy.ndarray): + if len(operator) == 1: + return [{'name': 'unitary', 'qubits': [0], 'params': operator}] + else: + return [{'name': 'kraus', 'qubits': [0], 'params': operator}] + + return operator + + + + # transformation interface methods + def transform_by_operator_list(self, transform_channel_operators, noise_kraus_operators): + """ + Args: + noise_kraus_operators: a list of matrices (Kraus operators) for the input channel + transform_channel_operators: a list of matrices or tuples of matrices + representing Kraus operators that can construct the output channel + e.g. [X,Y,Z] represent the Pauli channel + and [(|0><0|, |0><1|), |1><0|, |1><1|)] represents the relaxation channel + + + Output: + A list of amplitudes that define the output channel. + In the case the input is a list [A1, A2, ..., An] of transform matrices + and [E0, E1, ..., Em] of noise kraus operators, the output is + a list [p1, p2, ..., pn] of probabilities such that: + 1) p_i >= 0 + 2) p1 + ... + pn <= 1 + 3) [sqrt(p1)A1, sqrt(p2)A2, ..., sqrt(pn)An, sqrt(1-(p1 + ... + pn))I] is + a list of kraus operators that define the output channel + (which is "close" to the input chanel given by [E0, ..., Em]) + + This channel can be thought of as choosing the operator Ai in probability pi and applying + this operator to the quantum state. + + More generally, if the input is a list of tuples (not neccesarily of the same size): + [(A1, B1, ...), (A2, B2, ...), ... (An, Bn, ...)] then the output is + still a list [p1, p2, ..., pn] and now the output channel is defined by the operators + [sqrt(p1)A1, sqrt(p1)B1, ..., sqrt(pn)An, sqrt(pn)Bn, ..., sqrt(1-(p1 + ... + pn))I] + """ + self.noise_kraus_operators = noise_kraus_operators + self.transform_channel_operators = transform_channel_operators + full_transform_channel_operators = self.prepare_channel_operator_list(self.transform_channel_operators) + channel_matrices, const_channel_matrix = self.generate_channel_matrices(full_transform_channel_operators) + self.prepare_honesty_constraint(full_transform_channel_operators) + probabilities = self.transform_by_given_channel(channel_matrices, const_channel_matrix) + return probabilities + + # convert to sympy matrices and verify that each singleton is in a tuple; also add identity matrix + @staticmethod + def prepare_channel_operator_list(ops_list): + result = [[sympy.eye(2)]] + for ops in ops_list: + if not isinstance(ops, tuple) and not isinstance(ops, list): + ops = [ops] + result.append([sympy.Matrix(op) for op in ops]) + return result + + def prepare_honesty_constraint(self, transform_channel_operators_list): + if not self.use_honesty_constraint: + return + goal = self.fidelity(self.noise_kraus_operators) + coefficients = [self.fidelity(ops) for ops in transform_channel_operators_list] + self.fidelity_data = { + 'goal': goal, + 'coefficients': coefficients[1:] # coefficients[0] corresponds to I + } + + # methods relevant to the transformation to quadratic programming instance + + @staticmethod + def fidelity(channel): + return sum([numpy.abs(numpy.trace(E)) ** 2 for E in channel]) + + def generate_channel_matrices(self, transform_channel_operators_list): + """ + Generates a list of 4x4 symbolic matrices describing the channel defined from the given operators + + Args: + transform_channel_operators_list: a list of tuples of matrices which represent Kraus operators + The identity matrix is assumed to be the first element in the list + [(I, ), (A1, B1, ...), (A2, B2, ...), ..., (An, Bn, ...)] + e.g. for a Pauli channel, the matrices are + [(I,), (X,), (Y,), (Z,)] + for relaxation they are + [(I, ), (|0><0|, |0><1|), |1><0|, |1><1|)] + + We consider this input to symbolically represent a channel in the following manner: + define indeterminates x0, x1, ..., xn which are meant to represent probabilities + such that xi >=0 and x0 = 1-(x1 + ... + xn) + Now consider the quantum channel defined via the Kraus operators + {sqrt(x0)I, sqrt(x1)A1, sqrt(x1)B1, ..., sqrt(xn)An, sqrt(xn)Bn, ...} + This is the channel C symbolically represented by the operators + + + Output: + A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: + The matrix x1*D1 + ... + xn*Dn + E represents the operation of the channel C on the density operator + we find it easier to work with this representation of C when performing the combinatorial optimization + """ + + symbols_string = " ".join(["x{}".format(i) for i in range(len(transform_channel_operators_list))]) + symbols = sympy.symbols(symbols_string, real=True, positive=True) + exp = symbols[1] # exp will contain the symbolic expression "x1 +...+ xn" + for i in range(2, len(symbols)): + exp = symbols[i] + exp + # symbolic_operators_list is a list of lists; we flatten it the next line + symbolic_operators_list = [[sympy.sqrt(symbols[i]) * op for op in ops] + for (i, ops) in enumerate(transform_channel_operators_list)] + symbolic_operators = [op for ops in symbolic_operators_list for op in ops] + # channel_matrix_representation() peforms the required linear algebra to find the representing matrices. + operators_channel = self.channel_matrix_representation(symbolic_operators).subs(symbols[0], 1 - exp) + return self.generate_channel_quadratic_programming_matrices(operators_channel, symbols[1:]) + + @staticmethod + def compute_channel_operation(rho, operators): + """ + Given a quantum state's density function rho, the effect of the channel on this state is + rho -> \sum_{i=1}^n E_i * rho * E_i^\dagger + """ + + return sum([E * rho * E.H for E in operators], sympy.zeros(operators[0].rows)) + + @staticmethod + def flatten_matrix(m): + return [element for element in m] + + def channel_matrix_representation(self, operators): + """ + We convert the operators to a matrix by applying the channel to the four basis elements of + the 2x2 matrix space representing density operators; this is standard linear algebra + """ + standard_base = [ + sympy.Matrix([[1, 0], [0, 0]]), + sympy.Matrix([[0, 1], [0, 0]]), + sympy.Matrix([[0, 0], [1, 0]]), + sympy.Matrix([[0, 0], [0, 1]]) + ] + return (sympy.Matrix([self.flatten_matrix(self.compute_channel_operation(rho, operators)) + for rho in standard_base])) + + def generate_channel_quadratic_programming_matrices(self, channel, symbols): + """ + Args: + channel: a 4x4 symbolic matrix + symbols: the symbols x1, ..., xn which may occur in the matrix + + Output: + A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: + channel == x1*D1 + ... + xn*Dn + E + """ + return ( + [self.get_matrix_from_channel(channel, symbol) for symbol in symbols], + self.get_const_matrix_from_channel(channel, symbols) + ) + + @staticmethod + def get_matrix_from_channel(channel, symbol): + """ + Args: + channel: a 4x4 symbolic matrix. + Each entry is assumed to be a polynomial of the form a1x1 + ... + anxn + c + symbol: a symbol xi + + Output: + A 4x4 numerical matrix where for each entry, + if a1x1 + ... + anxn + c was the corresponding entry in the input channel + then the corresponding entry in the output matrix is ai. + """ + n = channel.rows + M = numpy.zeros((n, n), dtype=numpy.complex_) + for (i, j) in itertools.product(range(n), range(n)): + M[i, j] = numpy.complex(sympy.Poly(channel[i, j], symbol).coeff_monomial(symbol)) + return M + + @staticmethod + def get_const_matrix_from_channel(channel, symbols): + """ + Args: + channel: a 4x4 symbolic matrix. + Each entry is assumed to be a polynomial of the form a1x1 + ... + anxn + c + symbols: The full list [x1, ..., xn] of symbols used in the matrix + + Output: + A 4x4 numerical matrix where for each entry, + if a1x1 + ... + anxn + c was the corresponding entry in the input channel + then the corresponding entry in the output matrix is c. + """ + n = channel.rows + M = numpy.zeros((n, n), dtype=numpy.complex_) + for (i, j) in itertools.product(range(n), range(n)): + M[i, j] = numpy.complex(sympy.Poly(channel[i, j], symbols).coeff_monomial(1)) + return M + + def transform_by_given_channel(self, channel_matrices, const_channel_matrix): + """ + This method creates the quadratic programming instance for minimizing the Hilbert-Schmidt + norm of the matrix (A-B) obtained as the difference of the input noise channel and the + output channel we wish to determine. + """ + target_channel = SuperOp(Kraus(self.noise_kraus_operators)) + target_channel_matrix = target_channel._data.T + + const_matrix = const_channel_matrix - target_channel_matrix + P = self.compute_P(channel_matrices) + q = self.compute_q(channel_matrices, const_matrix) + return self.solve_quadratic_program(P, q) + + def compute_P(self, As): + vs = [numpy.array(A).flatten() for A in As] + n = len(vs) + P = sympy.zeros(n, n) + for (i, j) in itertools.product(range(n), range(n)): + P[i, j] = 2 * numpy.real(numpy.dot(vs[i], numpy.conj(vs[j]))) + return P + + def compute_q(self, As, C): + vs = [numpy.array(A).flatten() for A in As] + vC = numpy.array(C).flatten() + n = len(vs) + q = sympy.zeros(1, n) + for i in range(n): + q[i] = 2 * numpy.real(numpy.dot(numpy.conj(vC), vs[i])) + return q + + # the following method is the only place in the code where we rely on the cvxopt library + # should we consider another library, only this method needs to change + def solve_quadratic_program(self, P, q): + try: + import cvxopt + except ImportError: + raise ImportError("The CVXOPT library is required to use this module") + P = cvxopt.matrix(numpy.array(P).astype(float)) + q = cvxopt.matrix(numpy.array(q).astype(float)).T + n = len(q) + # G and h constrain: + # 1) sum of probs is less then 1 + # 2) All probs bigger than 0 + # 3) Honesty (measured using fidelity, if given) + G_data = [[1] * n] + [([-1 if i == k else 0 for i in range(n)]) for k in range(n)] + h_data = [1] + [0] * n + if self.fidelity_data is not None: + G_data.append(self.fidelity_data['coefficients']) + h_data.append(self.fidelity_data['goal']) + G = cvxopt.matrix(numpy.array(G_data).astype(float)) + h = cvxopt.matrix(numpy.array(h_data).astype(float)) + cvxopt.solvers.options['show_progress'] = False + return cvxopt.solvers.qp(P, q, G, h)['x'] \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 4abe2bf94c..59b22db41a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ cmake scikit-build cython asv +cvxopt \ No newline at end of file diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 233c672e6a..01a5ec1c89 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -474,7 +474,7 @@ void QasmController::run_circuit_with_noise(const Circuit &circ, // Sample a new noise circuit and optimize for each shot while(shots-- > 0) { Circuit noise_circ = noise_model_.sample_noise(circ, rng); - Circuit opt_noise_circ = optimize_circuit(circ, state, data); + Circuit opt_noise_circ = optimize_circuit(noise_circ, state, data); run_single_shot(opt_noise_circ, state, initial_state, data, rng); } } diff --git a/test/terra/noise/test_quantum_error.py b/test/terra/noise/test_quantum_error.py index 6e17a7a5ad..2c91c1c7de 100644 --- a/test/terra/noise/test_quantum_error.py +++ b/test/terra/noise/test_quantum_error.py @@ -712,4 +712,4 @@ def test_to_channel_circuit(self): if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/test/terra/test_noise_transformation.py b/test/terra/test_noise_transformation.py new file mode 100644 index 0000000000..6cd2bcd0e3 --- /dev/null +++ b/test/terra/test_noise_transformation.py @@ -0,0 +1,191 @@ +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. +""" +NoiseTransformer class tests +""" + +import unittest +import numpy +from qiskit.providers.aer.noise.errors.errorutils import standard_gate_unitary +from qiskit.providers.aer.noise import NoiseModel +from qiskit.providers.aer.noise.utils import NoiseTransformer +from qiskit.providers.aer.noise.utils import approximate_quantum_error +from qiskit.providers.aer.noise.utils import approximate_noise_model +from qiskit.providers.aer.noise.errors.standard_errors import amplitude_damping_error +from qiskit.providers.aer.noise.errors.standard_errors import reset_error +from qiskit.providers.aer.noise.errors.standard_errors import pauli_error + +try: + import cvxopt + has_cvxopt = True +except ImportError: + has_cvxopt = False + +@unittest.skipUnless(has_cvxopt, "Needs cvxopt to test") +class TestNoiseTransformer(unittest.TestCase): + def setUp(self): + self.ops = {'X': standard_gate_unitary('x'), + 'Y': standard_gate_unitary('y'), + 'Z': standard_gate_unitary('z'), + 'H': standard_gate_unitary('h'), + 'S': standard_gate_unitary('s') + } + self.n = NoiseTransformer() + + + def assertNoiseModelsAlmostEqual(self, lhs, rhs, places = 3): + self.assertNoiseDictsAlmostEqual(lhs._nonlocal_quantum_errors, rhs._nonlocal_quantum_errors, places=places) + self.assertNoiseDictsAlmostEqual(lhs._local_quantum_errors, rhs._local_quantum_errors, places=places) + self.assertNoiseDictsAlmostEqual(lhs._default_quantum_errors, rhs._default_quantum_errors, places=places) + self.assertNoiseDictsAlmostEqual(lhs._local_readout_errors, rhs._local_readout_errors, places=places) + if lhs._default_readout_error is not None: + self.assertTrue(rhs._default_readout_error is not None) + self.assertErrorsAlmostEqual(lhs._default_readout_error, rhs._default_readout_error, places=places) + else: + self.assertTrue(rhs._default_readout_error is None) + + def assertNoiseDictsAlmostEqual(self, lhs, rhs, places=3): + keys = set(lhs.keys()).union(set(rhs.keys())) + for key in keys: + self.assertTrue(key in lhs.keys(), msg="Key {} is missing from lhs".format(key)) + self.assertTrue(key in rhs.keys(), msg="Key {} is missing from rhs".format(key)) + for (lhs_error, rhs_error) in zip (lhs[key], rhs[key]): + self.assertErrorsAlmostEqual(lhs_error, rhs_error, places=places) + + def assertErrorsAlmostEqual(self, lhs, rhs, places = 3): + self.assertMatricesAlmostEqual(lhs.to_channel()._data, rhs.to_channel()._data, places) + + def assertDictAlmostEqual(self, lhs, rhs, places = None): + keys = set(lhs.keys()).union(set(rhs.keys())) + for key in keys: + self.assertAlmostEqual(lhs.get(key), rhs.get(key), msg = "Not almost equal for key {}: {} !~ {}".format(key, lhs.get(key), rhs.get(key)), places = places) + + def assertListAlmostEqual(self, lhs, rhs, places = None): + self.assertEqual(len(lhs), len(rhs), msg = "List lengths differ: {} != {}".format(len(lhs), len(rhs))) + for i in range(len(lhs)): + if isinstance(lhs[i], numpy.ndarray) and isinstance(rhs[i], numpy.ndarray): + self.assertMatricesAlmostEqual(lhs[i], rhs[i], places = places) + else: + self.assertAlmostEqual(lhs[i], rhs[i], places = places) + + def assertMatricesAlmostEqual(self, lhs, rhs, places = None): + self.assertEqual(lhs.shape, rhs.shape, "Marix shapes differ: {} vs {}".format(lhs, rhs)) + n, m = lhs.shape + for x in range(n): + for y in range(m): + self.assertAlmostEqual(lhs[x,y], rhs[x,y], places = places, msg="Matrices {} and {} differ on ({}, {})".format(lhs, rhs, x, y)) + + + def test_transformation_by_pauli(self): + n = NoiseTransformer() + #polarization in the XY plane; we represent via Kraus operators + X = self.ops['X'] + Y = self.ops['Y'] + Z = self.ops['Z'] + p = 0.22 + theta = numpy.pi / 5 + E0 = numpy.sqrt(1 - p) * numpy.array(numpy.eye(2)) + E1 = numpy.sqrt(p) * (numpy.cos(theta) * X + numpy.sin(theta) * Y) + results = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) + expected_results = pauli_error([('X', p*numpy.cos(theta)*numpy.cos(theta)), + ('Y', p*numpy.sin(theta)*numpy.sin(theta)), + ('Z', 0), + ('I', 1-p)]) + self.assertErrorsAlmostEqual(expected_results, results) + + + #now try again without fidelity; should be the same + n.use_honesty_constraint = False + results = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) + self.assertErrorsAlmostEqual(expected_results, results) + + def test_reset(self): + # approximating amplitude damping using relaxation operators + gamma = 0.23 + error = amplitude_damping_error(gamma) + p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 + q = 0 + expected_results = reset_error(p,q) + results = approximate_quantum_error(error, operator_string="reset") + self.assertErrorsAlmostEqual(results, expected_results) + + def test_transform(self): + X = self.ops['X'] + Y = self.ops['Y'] + Z = self.ops['Z'] + p = 0.34 + theta = numpy.pi / 7 + E0 = numpy.sqrt(1 - p) * numpy.array(numpy.eye(2)) + E1 = numpy.sqrt(p) * (numpy.cos(theta) * X + numpy.sin(theta) * Y) + + results_dict = approximate_quantum_error((E0, E1), operator_dict={"X": X, "Y": Y, "Z": Z}) + results_string = approximate_quantum_error((E0, E1), operator_string='pauli') + results_list = approximate_quantum_error((E0, E1), operator_list=[X, Y, Z]) + results_tuple = approximate_quantum_error((E0, E1), operator_list=(X, Y, Z)) + + self.assertErrorsAlmostEqual(results_dict, results_string) + self.assertErrorsAlmostEqual(results_string, results_list) + self.assertErrorsAlmostEqual(results_list, results_tuple) + + def test_fidelity(self): + n = NoiseTransformer() + expected_fidelity = {'X': 0, 'Y': 0, 'Z': 0, 'H': 0, 'S': 2} + for key in expected_fidelity: + self.assertAlmostEqual(expected_fidelity[key], n.fidelity([self.ops[key]]), msg = "Wrong fidelity for {}".format(key)) + + def test_approx_noise_model(self): + noise_model = NoiseModel() + gamma = 0.23 + p = 0.4 + q = 0.33 + ad_error = amplitude_damping_error(gamma) + r_error = reset_error(p,q) #should be approximated as-is + noise_model.add_all_qubit_quantum_error(ad_error, 'iden x y s') + noise_model.add_all_qubit_quantum_error(r_error, 'iden z h') + + result = approximate_noise_model(noise_model, operator_string="reset") + + expected_result = NoiseModel() + gamma_p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 + gamma_q = 0 + ad_error_approx = reset_error(gamma_p, gamma_q) + expected_result.add_all_qubit_quantum_error(ad_error_approx, 'iden x y s') + expected_result.add_all_qubit_quantum_error(r_error, 'iden z h') + + self.assertNoiseModelsAlmostEqual(expected_result, result) + + def test_clifford(self): + x_p = 0.17 + y_p = 0.13 + z_p = 0.34 + error = pauli_error([('X', x_p), ('Y', y_p), ('Z', z_p), ('I', 1 - (x_p + y_p + z_p))]) + results = approximate_quantum_error(error, operator_string="clifford") + self.assertErrorsAlmostEqual(error, results) + + def test_approx_names(self): + gamma = 0.23 + error = amplitude_damping_error(gamma) + results_1 = approximate_quantum_error(error, operator_string="pauli") + results_2 = approximate_quantum_error(error, operator_string="Pauli") + self.assertErrorsAlmostEqual(results_1, results_2) + + def test_errors(self): + gamma = 0.23 + error = amplitude_damping_error(gamma) + # kraus error is legit, transform_channel_operators are not + with self.assertRaisesRegex(TypeError, "takes 1 positional argument but 2 were given"): + approximate_quantum_error(error, 7) + with self.assertRaisesRegex(RuntimeError, "No information about noise type seven"): + approximate_quantum_error(error, operator_string="seven") + + #let's pretend cvxopt does not exist; the script should raise ImportError with proper message + import unittest.mock + import sys + with unittest.mock.patch.dict(sys.modules, {'cvxopt': None}): + with self.assertRaisesRegex(ImportError, "The CVXOPT library is required to use this module"): + approximate_quantum_error(error, operator_string="reset") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From ab8f9f7b4e248a6c23835871857060f7687e4d34 Mon Sep 17 00:00:00 2001 From: Hiroshi Horii Date: Wed, 24 Apr 2019 14:58:23 +0900 Subject: [PATCH 4/6] add tests for qubit-truncate optimization --- src/framework/data.hpp | 5 +- .../backends/qasm_simulator/qasm_truncate.py | 191 ++++++++++++++++++ test/terra/backends/test_qasm_simulator.py | 4 +- 3 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 test/terra/backends/qasm_simulator/qasm_truncate.py diff --git a/src/framework/data.hpp b/src/framework/data.hpp index 239d16bd54..76c4f8cbcb 100755 --- a/src/framework/data.hpp +++ b/src/framework/data.hpp @@ -227,7 +227,10 @@ template void OutputData::add_additional_data(const std::string &key, const T &data) { if (return_additional_data_) { json_t js = data; // use implicit to_json conversion function for T - additional_data_[key] = js; + if (JSON::check_key(key, additional_data_)) + additional_data_[key].update(js.begin(), js.end()); + else + additional_data_[key] = js; } } diff --git a/test/terra/backends/qasm_simulator/qasm_truncate.py b/test/terra/backends/qasm_simulator/qasm_truncate.py new file mode 100644 index 0000000000..d676984104 --- /dev/null +++ b/test/terra/backends/qasm_simulator/qasm_truncate.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +""" +QasmSimulator Integration Tests +""" +import json +from test.benchmark.tools import quantum_volume_circuit +from qiskit import execute, compile, QuantumRegister, ClassicalRegister, QuantumCircuit, Aer +from qiskit.providers.aer import QasmSimulator +from qiskit.providers.aer import noise +from qiskit.providers.aer.noise import NoiseModel +from qiskit.providers.aer.noise.errors import ReadoutError, depolarizing_error +from qiskit.providers.models import BackendProperties +from qiskit.providers.aer.noise.device import basic_device_noise_model + +class QasmQubitsTruncateTests: + """QasmSimulator Qubits Truncate tests.""" + + SIMULATOR = QasmSimulator() + BACKEND_OPTS = {'fusion_verbose': True} + + def create_circuit_for_truncate(self): + qr = QuantumRegister(4) + cr = ClassicalRegister(4) + circuit = QuantumCircuit(qr, cr) + circuit.u3(0.1,0.1,0.1,qr[1]) + circuit.barrier(qr) + circuit.x(qr[2]) + circuit.barrier(qr) + circuit.x(qr[1]) + circuit.barrier(qr) + circuit.x(qr[3]) + circuit.barrier(qr) + circuit.u3(0.1,0.1,0.1,qr[0]) + circuit.barrier(qr) + circuit.measure(qr[0], cr[0]) + circuit.measure(qr[1], cr[1]) + return circuit + + def device_properties(self): + properties = {"general": [], + "last_update_date": "2019-04-22T03:26:08+00:00", + "gates": [ + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [0]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [0]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [0]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [1]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [1]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [1]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [2]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [2]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [2]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [3]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [3]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [3]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [4]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [4]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [4]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [5]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [5]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [5]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [6]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [6]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [6]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [7]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [7]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [7]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [8]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [8]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [8]}, + {"gate": "u1", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [9]}, + {"gate": "u2", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [9]}, + {"gate": "u3", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-23T01:45:04+00:00", "unit": ""}], "qubits": [9]}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:26:00+00:00", "unit": ""}], "qubits": [0, 1], "name": "CX0_1"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:29:15+00:00", "unit": ""}], "qubits": [1, 2], "name": "CX1_2"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:32:48+00:00", "unit": ""}], "qubits": [2, 3], "name": "CX2_3"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:26:00+00:00", "unit": ""}], "qubits": [3, 4], "name": "CX3_4"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:29:15+00:00", "unit": ""}], "qubits": [4, 5], "name": "CX4_5"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:32:48+00:00", "unit": ""}], "qubits": [5, 6], "name": "CX5_6"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:26:00+00:00", "unit": ""}], "qubits": [6, 7], "name": "CX6_7"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:29:15+00:00", "unit": ""}], "qubits": [7, 8], "name": "CX7_8"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:32:48+00:00", "unit": ""}], "qubits": [8, 9], "name": "CX8_9"}, + {"gate": "cx", "parameters": [{"name": "gate_error", "value": 0.001, "date": "2019-04-22T02:26:00+00:00", "unit": ""}], "qubits": [9, 0], "name": "CX9_0"}], + "qubits": [ + [ + {"name": "T1", "value": 23.809868955712616, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 43.41142418044261, "date": "2019-04-22T01:33:33+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.032871440179164, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.03489999999999993, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 68.14048367144501, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 56.95903203933663, "date": "2019-04-22T01:34:36+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 4.896209948700639, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.19589999999999996, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 83.26776276928099, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 23.49615795695734, "date": "2019-04-22T01:31:32+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.100093544085939, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.09050000000000002, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 57.397746445609975, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 98.47976889309517, "date": "2019-04-22T01:32:32+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.238526396839902, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.24350000000000005, "date": "2019-04-20T15:31:39+00:00", "unit": ""}], + [ + {"name": "T1", "value": 23.809868955712616, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 43.41142418044261, "date": "2019-04-22T01:33:33+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.032871440179164, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.03489999999999993, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 68.14048367144501, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 56.95903203933663, "date": "2019-04-22T01:34:36+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 4.896209948700639, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.19589999999999996, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 83.26776276928099, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 23.49615795695734, "date": "2019-04-22T01:31:32+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.100093544085939, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.09050000000000002, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 57.397746445609975, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 98.47976889309517, "date": "2019-04-22T01:32:32+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.238526396839902, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.24350000000000005, "date": "2019-04-20T15:31:39+00:00", "unit": ""}], + [ + {"name": "T1", "value": 23.809868955712616, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 43.41142418044261, "date": "2019-04-22T01:33:33+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 5.032871440179164, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.03489999999999993, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + [ + {"name": "T1", "value": 68.14048367144501, "date": "2019-04-22T01:30:15+00:00", "unit": "\u00b5s"}, + {"name": "T2", "value": 56.95903203933663, "date": "2019-04-22T01:34:36+00:00", "unit": "\u00b5s"}, + {"name": "frequency", "value": 4.896209948700639, "date": "2019-04-22T03:26:08+00:00", "unit": "GHz"}, + {"name": "readout_error", "value": 0.19589999999999996, "date": "2019-04-22T01:29:47+00:00", "unit": ""}], + ], + "backend_name": "mock_4q", + "backend_version": "1.0.0"} + return BackendProperties.from_dict(properties) + + + def test_truncate(self): + """Test truncation with noise model option""" + circuit = self.create_circuit_for_truncate() + + qasm_sim = Aer.get_backend('qasm_simulator') + + result = execute(circuit, + qasm_sim, + noise_model=basic_device_noise_model(self.device_properties()), + shots=100, + coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 0]], # 10-qubit device + backend_options={"truncate_verbose": True}).result() + + self.assertTrue('truncate_verbose' in result.to_dict()['results'][0]['metadata'], msg="truncate_verbose must work.") + + def test_no_truncate(self): + """Test truncation with noise model option""" + circuit = self.create_circuit_for_truncate() + + qasm_sim = Aer.get_backend('qasm_simulator') + + result = execute(circuit, + qasm_sim, + noise_model=basic_device_noise_model(self.device_properties()), + shots=100, + coupling_map=[[1, 0], [1, 2], [1, 3], [2, 0], [2, 1], [2, 3], [3, 0], [3, 1], [3, 2]], # 4-qubit device + backend_options={"truncate_verbose": True}).result() + + self.assertFalse('truncate_verbose' in result.to_dict()['results'][0]['metadata'], msg="truncate_verbose must work.") + + + def test_truncate_disable(self): + """Test explicitly disabling truncation with noise model option""" + circuit = self.create_circuit_for_truncate() + + qasm_sim = Aer.get_backend('qasm_simulator') + + result = execute(circuit, + qasm_sim, + noise_model=basic_device_noise_model(self.device_properties()), + shots=100, + coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 0]], # 10-qubit device + backend_options={"truncate_verbose": True, "truncate_enable": False}).result() + + self.assertFalse('truncate_verbose' in result.to_dict()['results'][0]['metadata'], msg="truncate_verbose must not work.") + \ No newline at end of file diff --git a/test/terra/backends/test_qasm_simulator.py b/test/terra/backends/test_qasm_simulator.py index b9ade9e51e..08c04ef0dc 100644 --- a/test/terra/backends/test_qasm_simulator.py +++ b/test/terra/backends/test_qasm_simulator.py @@ -28,6 +28,7 @@ from test.terra.backends.qasm_simulator.qasm_extra import QasmExtraTests from test.terra.backends.qasm_simulator.qasm_thread_management import QasmThreadManagementTests from test.terra.backends.qasm_simulator.qasm_fusion import QasmFusionTests +from test.terra.backends.qasm_simulator.qasm_truncate import QasmQubitsTruncateTests class TestQasmSimulator(common.QiskitAerTestCase, @@ -47,7 +48,8 @@ class TestQasmSimulator(common.QiskitAerTestCase, QasmAlgorithmTestsMinimalBasis, QasmExtraTests, QasmThreadManagementTests, - QasmFusionTests): + QasmFusionTests, + QasmQubitsTruncateTests): """QasmSimulator automatic method tests.""" From f74f8dfa8cceeeafac6e57f18c4f68b80711dc6d Mon Sep 17 00:00:00 2001 From: Hiroshi Horii Date: Fri, 26 Apr 2019 00:59:18 +0900 Subject: [PATCH 5/6] apply truncate optimization to any state types in qasm simulator --- src/base/controller.hpp | 17 +++++++++--- src/base/state.hpp | 18 ------------- src/simulators/qasm/basic_optimization.hpp | 26 +++++++------------ src/simulators/qasm/qasm_controller.hpp | 11 +++++--- .../statevector/statevector_state.hpp | 16 +----------- 5 files changed, 32 insertions(+), 56 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 528902ccdd..3dbac7f835 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -134,6 +134,13 @@ class Controller { // Clear the current config void virtual clear_config(); + // Add circuit optimization + template + inline auto add_circuit_optimization(Type&& opt)-> typename std::enable_if_t>>::value > + { + optimizations_.push_back(std::make_shared > >(std::forward(opt))); + } + protected: //----------------------------------------------------------------------- @@ -197,6 +204,9 @@ class Controller { // Noise model Noise::NoiseModel noise_model_; + // Circuit optimization + std::vector> optimizations_; + //----------------------------------------------------------------------- // Parallelization Config //----------------------------------------------------------------------- @@ -264,6 +274,9 @@ void Controller::set_config(const json_t &config) { max_memory_mb_ = system_memory_mb / 2; } + for (std::shared_ptr opt: optimizations_) + opt->set_config(config_); + std::string path; JSON::get_value(path, "library_dir", config); // Fix for MacOS and OpenMP library double initialization crash. @@ -433,10 +446,8 @@ Circuit Controller::optimize_circuit(const Circuit &input_circ, allowed_opset.gates = state.allowed_gates(); allowed_opset.snapshots = state.allowed_snapshots(); - for (std::shared_ptr opt: state.get_circuit_optimization()) { - opt->set_config(config_); + for (std::shared_ptr opt: optimizations_) opt->optimize_circuit(working_circ, allowed_opset, data); - } return working_circ; } diff --git a/src/base/state.hpp b/src/base/state.hpp index 19cfa5cd28..3282127fc3 100644 --- a/src/base/state.hpp +++ b/src/base/state.hpp @@ -13,7 +13,6 @@ #include "framework/types.hpp" #include "framework/data.hpp" #include "framework/creg.hpp" -#include "framework/circuitopt.hpp" namespace AER { namespace Base { @@ -188,19 +187,6 @@ class State { inline const state_t &qreg() const {return qreg_;} inline const auto &creg() const {return creg_;} - //----------------------------------------------------------------------- - // Circuit optimization - //----------------------------------------------------------------------- - - // Add circuit optimization - template - inline auto add_circuit_optimization(Type&& opt)-> typename std::enable_if_t>>::value > - { - optimizations_.push_back(std::make_shared > >(std::forward(opt))); - } - - std::vector> get_circuit_optimization() { return optimizations_; }; - protected: // The quantum state data structure @@ -212,10 +198,6 @@ class State { // Maximum threads which may be used by the backend for OpenMP multithreading // Default value is single-threaded unless overridden int threads_ = 1; - - // Circuit optimization - std::vector> optimizations_; - }; diff --git a/src/simulators/qasm/basic_optimization.hpp b/src/simulators/qasm/basic_optimization.hpp index 1e803b59a2..45e4a99a27 100644 --- a/src/simulators/qasm/basic_optimization.hpp +++ b/src/simulators/qasm/basic_optimization.hpp @@ -537,14 +537,14 @@ void TruncateQubits::optimize_circuit(Circuit& circ, } reg_t TruncateQubits::generate_mapping(const Circuit& circ) const { - reg_t mapping; + size_t not_used = circ.num_qubits + 1; + reg_t mapping = reg_t(circ.num_qubits, not_used); - for (const op_t& op: circ.ops) { - for (size_t qubit: op.qubits) { - if (std::find(mapping.begin(), mapping.end(), qubit) == mapping.end()) - mapping.push_back(qubit); - } - } + for (const op_t& op: circ.ops) + for (size_t qubit: op.qubits) + mapping[qubit] = qubit; + + mapping.erase(std::remove(mapping.begin(), mapping.end(), not_used), mapping.end()); return mapping; } @@ -572,21 +572,13 @@ bool TruncateQubits::can_apply(const Circuit& circ) const { bool TruncateQubits::can_apply(const op_t& op) const { switch (op.type) { - case optype_t::gate: - case optype_t::measure: - case optype_t::reset: - case optype_t::bfunc: - case optype_t::barrier: - case optype_t::roerror: - case optype_t::initialize: - return true; - case optype_t::matrix: //TODO case optype_t::matrix_sequence: //TODO case optype_t::kraus: //TODO case optype_t::snapshot: case optype_t::noise_switch: - default: return false; + default: + return true; } } diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 01a5ec1c89..d2f9152197 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -12,6 +12,7 @@ #include "simulators/extended_stabilizer/extended_stabilizer_state.hpp" #include "simulators/statevector/statevector_state.hpp" #include "simulators/stabilizer/stabilizer_state.hpp" +#include "simulators/qasm/basic_optimization.hpp" namespace AER { @@ -233,6 +234,9 @@ class QasmController : public Base::Controller { // Constructor //------------------------------------------------------------------------- QasmController::QasmController() { + add_circuit_optimization(ReduceNop()); + add_circuit_optimization(Fusion()); + add_circuit_optimization(TruncateQubits()); } //------------------------------------------------------------------------- @@ -474,8 +478,8 @@ void QasmController::run_circuit_with_noise(const Circuit &circ, // Sample a new noise circuit and optimize for each shot while(shots-- > 0) { Circuit noise_circ = noise_model_.sample_noise(circ, rng); - Circuit opt_noise_circ = optimize_circuit(noise_circ, state, data); - run_single_shot(opt_noise_circ, state, initial_state, data, rng); + noise_circ = optimize_circuit(noise_circ, state, data); + run_single_shot(noise_circ, state, initial_state, data, rng); } } @@ -488,7 +492,8 @@ void QasmController::run_circuit_without_noise(const Circuit &circ, OutputData &data, RngEngine &rng) const { // Optimize circuit for state type - Circuit opt_circ = optimize_circuit(circ, state, data); + Circuit opt_circ; + opt_circ = optimize_circuit(circ, state, data); // Check if measure sampler and optimization are valid auto check = check_measure_sampling_opt(opt_circ); diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 181e802d59..9139be9143 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -16,7 +16,6 @@ #include "framework/json.hpp" #include "base/state.hpp" #include "qubitvector.hpp" -#include "simulators/qasm/basic_optimization.hpp" namespace AER { @@ -46,7 +45,7 @@ class State : public Base::State { public: using BaseState = Base::State; - State(); + State() = default; virtual ~State() = default; //----------------------------------------------------------------------- @@ -318,16 +317,6 @@ const stringmap_t State::snapshotset_({ {"register", Snapshots::cregister} }); -//------------------------------------------------------------------------- -// Constructor -//------------------------------------------------------------------------- - -template -State::State() { - BaseState::add_circuit_optimization(ReduceNop()); - BaseState::add_circuit_optimization(Fusion()); - BaseState::add_circuit_optimization(TruncateQubits()); -} //========================================================================= // Implementation: Base class method overrides @@ -392,9 +381,6 @@ size_t State::required_memory_mb(uint_t num_qubits, template void State::set_config(const json_t &config) { - // Set base controller config - BaseState::set_config(config); - // Set threshold for truncating snapshots JSON::get_value(json_chop_threshold_, "chop_threshold", config); BaseState::qreg_.set_json_chop_threshold(json_chop_threshold_); From a55719c393443175f4b7a8035bd18f387abb408e Mon Sep 17 00:00:00 2001 From: Hiroshi Horii Date: Fri, 26 Apr 2019 08:58:17 +0900 Subject: [PATCH 6/6] fix a merge mistake --- test/terra/backends/test_qasm_simulator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/terra/backends/test_qasm_simulator.py b/test/terra/backends/test_qasm_simulator.py index 573ea0e599..3f5456f7a3 100644 --- a/test/terra/backends/test_qasm_simulator.py +++ b/test/terra/backends/test_qasm_simulator.py @@ -52,7 +52,6 @@ class TestQasmSimulator(common.QiskitAerTestCase, QasmFusionTests, QasmQubitsTruncateTests, QasmBasicsTests): ->>>>>>> upstream/master """QasmSimulator automatic method tests."""