From f17df4984efbd852cfa55d889040282da523bd74 Mon Sep 17 00:00:00 2001 From: cjwood Date: Thu, 13 Dec 2018 20:10:27 -0500 Subject: [PATCH 1/6] Cleaning up c++ config option names --- src/base/controller.hpp | 218 ++++++++++-------- src/framework/data.hpp | 9 + src/simulators/qasm/qasm_controller.hpp | 37 +++ src/simulators/qubitunitary/qubitmatrix.hpp | 2 +- .../qubitunitary/unitary_controller.hpp | 26 +++ src/simulators/qubitunitary/unitary_state.hpp | 13 +- src/simulators/qubitvector/qubitvector.hpp | 4 +- src/simulators/qubitvector/qv_state.hpp | 31 ++- .../qubitvector/statevector_controller.hpp | 33 +++ 9 files changed, 247 insertions(+), 126 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 2068790d2..44bba356f 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -54,16 +54,45 @@ namespace Base { // It manages execution of all the circuits in a QOBJ, parallelization, // noise sampling from a noise model, and circuit optimizations. -// Parallelization: -// Parallel execution uses the OpenMP library. It may happen at three -// levels: -// 1. Parallel execution of circuits in a QOBJ -// 2. Parallel execution of shots in a Circuit -// 3. Parallelization used by the State class for performing gates. -// Options 1 and 2 are mutually exclusive: enabling circuit parallelization -// disables shot parallelization and vice versa. Option 3 is available for -// both cases but conservatively limits the number of threads since these -// are subthreads spawned by the higher level threads. +/************************************************************************** + * --------------- + * Parallelization + * --------------- + * Parallel execution uses the OpenMP library. It may happen at three levels: + * + * 1. Parallel execution of circuits in a QOBJ + * 2. Parallel execution of shots in a Circuit + * 3. Parallelization used by the State class for performing gates. + * + * Options 1 and 2 are mutually exclusive: enabling circuit parallelization + * disables shot parallelization. Option 3 is available for both cases but + * conservatively limits the number of threads since these are subthreads + * spawned by the higher level threads. If no parallelization is used for + * 1 and 2, all available threads will be used for 3. + * + * ------------------------- + * Config settings: + * + * - "noise_model" (json): A noise model to use for simulation [Default: null] + * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may + * be used across all levels of parallelization. Set to 0 for maximum + * available. [Default : 0] + * - "max_parallel_circuits" (int): Set number of circuits that may be + * executed in parallel. Set to 0 to use the number of max parallel + * threads [Default: 1] + * - "max_parallel_shots" (int): Set number of shots that maybe be executed + * in parallel for each circuit. Sset to 0 to use the number of max + * parallel threads [Default: 1]. + * + * Config settings from Data class: + * + * - "counts" (bool): Return counts objecy in circuit data [Default: True] + * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] + * - "memory" (bool): Return memory array in circuit data [Default: False] + * - "register" (bool): Return register array in circuit data [Default: False] + * - "noise_model" (json): A noise model JSON dictionary for the simulator. + * [Default: null] + **************************************************************************/ class Controller { public: @@ -84,21 +113,6 @@ public: // Load Controller, State and Data config from a JSON // config settings will be passed to the State and Data classes - // Allowed Controller config options: - // - "noise_model": NoiseModel JSON - // A noise model JSON dictionary for the simulator. - // - "max_threads": int - // Set the maximum OpenMP threads that may be used across all levels - // of parallelization. Set to -1 for maximum available (Default: -1) - // - "max_threads_circuit": int - // Set the maximum OpenMP threads that may be used for parallel - // circuit evaluation. Set to -1 for maximum available (Default: 1) - // - "max_threads_shot": int - // Set the maximum OpenMP threads that may be used for parallel - // shot evaluation. Set to -1 for maximum available (Default: 1) - // - "max_threads_state": int - // Set the maximum OpenMP threads that may be used by the State - // class. Set to -1 for maximum available (Default: -1) virtual void set_config(const json_t &config); // Clear the current config @@ -147,13 +161,11 @@ protected: int available_threads_ = 1; // The maximum number of threads to use for various levels of parallelization - int max_threads_total_ = -1; - - int max_threads_circuit_ = 1; // -1 for maximum available - - int max_threads_shot_ = 1; // -1 for maximum available - - int max_threads_state_ = -1; // -1 for maximum available + // set to 0 for maximum available + int max_threads_total_; + int max_threads_circuit_; + int max_threads_shot_; + int max_threads_state_; }; @@ -167,18 +179,22 @@ protected: //------------------------------------------------------------------------- void Controller::set_config(const json_t &config) { + // Save config for passing to State and Data classes config_ = config; - // Add noise model + + // Load noise model if (JSON::check_key("noise_model", config)) noise_model_ = Noise::NoiseModel(config["noise_model"]); - // Default: max_threads = -1 (all available threads) - JSON::get_value(max_threads_total_, "max_threads", config); - // Default: max_threads_circuit = 1 - JSON::get_value(max_threads_circuit_, "max_threads_circuit", config); - // Default: max_threads_shot = 1 - JSON::get_value(max_threads_shot_, "max_threads_shot", config); - // Default: max_threads_state = -1 (all available threads) - JSON::get_value(max_threads_shot_, "max_threads_state", config); + + // Load OpenMP maximum thread settings + JSON::get_value(max_threads_total_, "max_parallel_threads", config); + JSON::get_value(max_threads_shot_, "max_parallel_shots", config); + JSON::get_value(max_threads_circuit_, "max_parallel_circuits", config); + + // Prevent using both parallel circuits and parallel shots + // with preference given to parallel circuit execution + if (max_threads_circuit_ > 1) + max_threads_shot_ = 1; } void Controller::clear_config() { @@ -188,10 +204,10 @@ void Controller::clear_config() { } void Controller::set_threads_default() { - max_threads_total_ = -1; + max_threads_total_ = 0; + max_threads_state_ = 0; max_threads_circuit_ = 1; max_threads_shot_ = 1; - max_threads_state_ = -1; } @@ -204,10 +220,15 @@ json_t Controller::execute(const json_t &qobj_js) { // Start QOBJ timer auto timer_start = myclock_t::now(); - // Look for config and load - if (JSON::check_key("config", qobj_js)) { - set_config(qobj_js["config"]); - } + // Generate empty return JSON that matches Result spec + json_t result; + result["qobj_id"] = nullptr; + result["success"] = true; + result["status"] = nullptr; + result["backend_name"] = nullptr; + result["backend_version"] = nullptr; + result["date"] = nullptr; + result["job_id"] = nullptr; // Load QOBJ in a try block so we can catch parsing errors and still return // a valid JSON output containing the error message. @@ -216,21 +237,23 @@ json_t Controller::execute(const json_t &qobj_js) { qobj.load_qobj_from_json(qobj_js); } catch (std::exception &e) { - json_t ret; - ret["qobj_id"] = "ERROR"; - ret["success"] = false; - ret["status"] = std::string("ERROR: Failed to load qobj: ") + e.what(); - ret["backend_name"] = nullptr; - ret["backend_version"] = nullptr; - ret["date"] = nullptr; - ret["job_id"] = nullptr; - return ret; // qobj was invalid, return valid output containing error message + // qobj was invalid, return valid output containing error message + result["success"] = false; + result["status"] = std::string("ERROR: Failed to load qobj: ") + e.what(); + return result; + } + + // Get QOBJ id and pass through header to result + result["qobj_id"] = qobj.id; + if (!qobj.header.empty()) + result["header"] = qobj.header; + + // Check for config + if (JSON::check_key("config", qobj_js)) { + set_config(qobj_js["config"]); } // Qobj was loaded successfully, now we proceed - json_t ret; - bool all_success = true; - try { int num_circuits = qobj.circuits.size(); @@ -253,65 +276,59 @@ json_t Controller::execute(const json_t &qobj_js) { available_threads_ /= num_threads_circuit; // Add thread metatdata to output - ret["metadata"]["omp_enabled"] = true; - ret["metadata"]["omp_available_threads"] = omp_nthreads; - ret["metadata"]["omp_circuit_threads"] = num_threads_circuit; + result["metadata"]["omp_enabled"] = true; + result["metadata"]["omp_available_threads"] = omp_nthreads; + result["metadata"]["omp_circuit_threads"] = num_threads_circuit; #else - ret["metadata"]["omp_enabled"] = false; + result["metadata"]["omp_enabled"] = false; #endif // Initialize container to store parallel circuit output - ret["results"] = std::vector(num_circuits); + result["results"] = std::vector(num_circuits); if (num_threads_circuit > 1) { // Parallel circuit execution #pragma omp parallel for if (num_threads_circuit > 1) num_threads(num_threads_circuit) for (int j = 0; j < num_circuits; ++j) { - ret["results"][j] = execute_circuit(qobj.circuits[j]); + result["results"][j] = execute_circuit(qobj.circuits[j]); } } else { // Serial circuit execution for (int j = 0; j < num_circuits; ++j) { - ret["results"][j] = execute_circuit(qobj.circuits[j]); + result["results"][j] = execute_circuit(qobj.circuits[j]); } } // check success - for (const auto& res: ret["results"]) { - all_success &= res["success"].get(); + for (const auto& experiment: result["results"]) { + if (experiment["success"].get() == false) { + result["success"] = false; + break; + } } - - // Add success data - ret["success"] = all_success; - ret["status"] = std::string("COMPLETED"); - ret["qobj_id"] = qobj.id; - if (!qobj.header.empty()) - ret["header"] = qobj.header; - ret["backend_name"] = nullptr; - ret["backend_version"] = nullptr; - ret["date"] = nullptr; - ret["job_id"] = nullptr; + // Set status to completed + result["status"] = std::string("COMPLETED"); // Stop the timer and add total timing data auto timer_stop = myclock_t::now(); - ret["metadata"]["time_taken"] = std::chrono::duration(timer_stop - timer_start).count(); + result["metadata"]["time_taken"] = std::chrono::duration(timer_stop - timer_start).count(); } // If execution failed return valid output reporting error catch (std::exception &e) { - ret["success"] = false; - ret["status"] = std::string("ERROR: ") + e.what(); + result["success"] = false; + result["status"] = std::string("ERROR: ") + e.what(); } - return ret; + return result; } json_t Controller::execute_circuit(Circuit &circ) { - + // Start individual circuit timer auto timer_start = myclock_t::now(); // state circuit timer // Initialize circuit json return - json_t ret; + json_t result; // Execute in try block so we can catch errors and return the error message // for individual circuit failures. @@ -337,13 +354,13 @@ json_t Controller::execute_circuit(Circuit &circ) { : std::min({available_threads_ , max_threads_total_, max_threads_state_}); // Add thread information to result metadata - ret["metadata"]["omp_shot_threads"] = num_threads_shot; - ret["metadata"]["omp_state_threads"] = num_threads_state; + result["metadata"]["omp_shot_threads"] = num_threads_shot; + result["metadata"]["omp_state_threads"] = num_threads_state; #endif // Single shot thread execution if (num_threads_shot <= 1) { - ret["data"] = run_circuit(circ, circ.shots, circ.seed, num_threads_state); + result["data"] = run_circuit(circ, circ.shots, circ.seed, num_threads_state); // Parallel shot thread execution } else { // Calculate shots per thread @@ -351,7 +368,10 @@ json_t Controller::execute_circuit(Circuit &circ) { for (int j = 0; j < num_threads_shot; ++j) { subshots.push_back(circ.shots / num_threads_shot); } - subshots[0] += (circ.shots % num_threads_shot); + // If shots is not perfectly divisible by threads, assign the remaineder + for (int j=0; j < (circ.shots % num_threads_shot); ++j) { + subshots[j] += 1; + } // Vector to store parallel thread output data std::vector data(num_threads_shot); @@ -364,27 +384,27 @@ json_t Controller::execute_circuit(Circuit &circ) { data[0].combine(data[j]); } // Update output - ret["data"] = data[0]; + result["data"] = data[0]; } // Report success - ret["success"] = true; - ret["status"] = std::string("DONE"); + result["success"] = true; + result["status"] = std::string("DONE"); // Pass through circuit header and add metadata - ret["header"] = circ.header; - ret["shots"] = circ.shots; - ret["seed"] = circ.seed; + result["header"] = circ.header; + result["shots"] = circ.shots; + result["seed"] = circ.seed; // Add timer data auto timer_stop = myclock_t::now(); // stop timer double time_taken = std::chrono::duration(timer_stop - timer_start).count(); - ret["time_taken"] = time_taken; + result["time_taken"] = time_taken; } // If an exception occurs during execution, catch it and pass it to the output catch (std::exception &e) { - ret["success"] = false; - ret["status"] = std::string("ERROR: ") + e.what(); + result["success"] = false; + result["status"] = std::string("ERROR: ") + e.what(); } - return ret; + return result; } //------------------------------------------------------------------------- diff --git a/src/framework/data.hpp b/src/framework/data.hpp index a6eba88a3..c8697a254 100755 --- a/src/framework/data.hpp +++ b/src/framework/data.hpp @@ -18,6 +18,15 @@ namespace AER { // Output data class for Qiskit-Aer //============================================================================ +/************************************************************************** + * Data config options: + * + * - "counts" (bool): Return counts objecy in circuit data [Default: True] + * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] + * - "memory" (bool): Return memory array in circuit data [Default: False] + * - "register" (bool): Return register array in circuit data [Default: False] + **************************************************************************/ + class OutputData { public: //---------------------------------------------------------------- diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index ff1751c55..7e4ea1ffd 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -18,6 +18,43 @@ namespace Simulator { // QasmController class //========================================================================= +/************************************************************************** + * Config settings: + * + * From QubitVector::State class + * + * - "initial_statevector" (json complex vector): Use a custom initial + * statevector for the simulation [Default: null]. + * - "chop_threshold" (double): Threshold for truncating small values to + * zero in result data [Default: 1e-15] + * - "statevector_parallel_threshold" (int): Threshold that number of qubits + * must be greater than to enable OpenMP parallelization at State + * level [Default: 13] + * - "statevector_sample_measure_opt" (int): Threshold that number of qubits + * must be greater than to enable indexing optimization during + * measure sampling [Default: 10] + * - "statevector_hpc_gate_opt" (bool): Enable large qubit gate optimizations. + * [Default: False] + * + * From BaseController Class + * + * - "noise_model" (json): A noise model to use for simulation [Default: null] + * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may + * be used across all levels of parallelization. Set to 0 for maximum + * available. [Default : 0] + * - "max_parallel_circuits" (int): Set number of circuits that may be + * executed in parallel. Set to 0 to use the number of max parallel + * threads [Default: 1] + * - "max_parallel_shots" (int): Set number of shots that maybe be executed + * in parallel for each circuit. Sset to 0 to use the number of max + * parallel threads [Default: 1]. + * - "counts" (bool): Return counts objecy in circuit data [Default: True] + * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] + * - "memory" (bool): Return memory array in circuit data [Default: False] + * - "register" (bool): Return register array in circuit data [Default: False] + * + **************************************************************************/ + class QasmController : public Base::Controller { public: //----------------------------------------------------------------------- diff --git a/src/simulators/qubitunitary/qubitmatrix.hpp b/src/simulators/qubitunitary/qubitmatrix.hpp index 384366433..3d169db0f 100755 --- a/src/simulators/qubitunitary/qubitmatrix.hpp +++ b/src/simulators/qubitunitary/qubitmatrix.hpp @@ -181,7 +181,7 @@ protected: // Config settings //----------------------------------------------------------------------- uint_t omp_threads_ = 1; // Disable multithreading by default - uint_t omp_threshold_ = 16; // Qubit threshold for multithreading when enabled + uint_t omp_threshold_ = 6; // Qubit threshold for multithreading when enabled double json_chop_threshold_ = 0; // Threshold for choping small values // in JSON serialization diff --git a/src/simulators/qubitunitary/unitary_controller.hpp b/src/simulators/qubitunitary/unitary_controller.hpp index 9b7c36383..142dc52e4 100755 --- a/src/simulators/qubitunitary/unitary_controller.hpp +++ b/src/simulators/qubitunitary/unitary_controller.hpp @@ -18,6 +18,32 @@ namespace Simulator { // UnitaryController class //========================================================================= + +/************************************************************************** + * Config settings: + * + * From QubitUnitary::State class + * + * - "initial_unitary" (json complex matrix): Use a custom initial unitary + * matrix for the simulation [Default: null]. + * - "chop_threshold" (double): Threshold for truncating small values to + * zero in result data [Default: 1e-15] + * - "unitary_parallel_threshold" (int): Threshold that number of qubits + * must be greater than to enable OpenMP parallelization at State + * level [Default: 13] + * + * From BaseController Class + * + * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may + * be used across all levels of parallelization. Set to 0 for maximum + * available. [Default : 0] + * - "max_parallel_circuits" (int): Set number of circuits that may be + * executed in parallel. Set to 0 to use the number of max parallel + * threads [Default: 1] + * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] + * + **************************************************************************/ + class UnitaryController : public Base::Controller { public: //----------------------------------------------------------------------- diff --git a/src/simulators/qubitunitary/unitary_state.hpp b/src/simulators/qubitunitary/unitary_state.hpp index 1ed46c0f5..0b0a58289 100755 --- a/src/simulators/qubitunitary/unitary_state.hpp +++ b/src/simulators/qubitunitary/unitary_state.hpp @@ -23,7 +23,7 @@ namespace QubitUnitary { // Allowed gates enum class enum class Gates { - u0, u1, u2, u3, id, x, y, z, h, s, sdg, t, tdg, // single qubit + u1, u2, u3, id, x, y, z, h, s, sdg, t, tdg, // single qubit cx, cz, swap, // two qubit ccx // three qubit }; @@ -57,13 +57,13 @@ public: // Return the set of qobj gate instruction names supported by the State inline virtual stringset_t allowed_gates() const override { - return {"U", "CX", "u0", "u1", "u2", "u3", "cx", "cz", "swap", + return {"U", "CX", "u1", "u2", "u3", "cx", "cz", "swap", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "ccx"}; } // Return the set of qobj snapshot types supported by the State inline virtual stringset_t allowed_snapshots() const override { - return {"state", "unitary"}; + return {"unitary"}; } // Apply a sequence of operations by looping over list @@ -133,7 +133,7 @@ protected: //----------------------------------------------------------------------- // OpenMP qubit threshold - int omp_qubit_threshold_ = 7; + int omp_qubit_threshold_ = 6; // Threshold for chopping small values to zero in JSON double json_chop_threshold_ = 1e-15; @@ -160,7 +160,6 @@ const stringmap_t State::gateset_({ {"t", Gates::t}, // T-gate (sqrt(S)) {"tdg", Gates::tdg}, // Conjguate-transpose of T gate // Waltz Gates - {"u0", Gates::u0}, // idle gate in multiples of X90 {"u1", Gates::u1}, // zero-X90 pulse waltz gate {"u2", Gates::u2}, // single-X90 pulse waltz gate {"u3", Gates::u3}, // two X90 pulse waltz gate @@ -220,7 +219,7 @@ uint_t State::required_memory_mb(uint_t num_qubits, template void State::set_config(const json_t &config) { // Set OMP threshold for state update functions - JSON::get_value(omp_qubit_threshold_, "omp_qubit_threshold", config); + JSON::get_value(omp_qubit_threshold_, "unitary_parallel_threshold", config); // Set threshold for truncating snapshots JSON::get_value(json_chop_threshold_, "chop_threshold", config); @@ -303,8 +302,6 @@ void State::apply_gate(const Operations::Op &op) { case Gates::cz: BaseState::qreg_.apply_cz(op.qubits[0], op.qubits[1]); break; - case Gates::u0: // u0 = id in ideal State - break; case Gates::id: break; case Gates::x: diff --git a/src/simulators/qubitvector/qubitvector.hpp b/src/simulators/qubitvector/qubitvector.hpp index 73d2be766..78fcdac53 100755 --- a/src/simulators/qubitvector/qubitvector.hpp +++ b/src/simulators/qubitvector/qubitvector.hpp @@ -248,9 +248,9 @@ protected: // Config settings //----------------------------------------------------------------------- uint_t omp_threads_ = 1; // Disable multithreading by default - uint_t omp_threshold_ = 16; // Qubit threshold for multithreading when enabled + uint_t omp_threshold_ = 13; // Qubit threshold for multithreading when enabled int sample_measure_index_size_ = 10; // Sample measure indexing qubit size - bool gate_opt_ = false; // enable large-qubit optimized gates + bool gate_opt_ = false; // enable large-qubit optimized gates double json_chop_threshold_ = 0; // Threshold for choping small values // in JSON serialization //----------------------------------------------------------------------- diff --git a/src/simulators/qubitvector/qv_state.hpp b/src/simulators/qubitvector/qv_state.hpp index 08942a7eb..c47531a7b 100755 --- a/src/simulators/qubitvector/qv_state.hpp +++ b/src/simulators/qubitvector/qv_state.hpp @@ -23,7 +23,7 @@ namespace QubitVector { // Allowed gates enum class enum class Gates { - u0, u1, u2, u3, id, x, y, z, h, s, sdg, t, tdg, // single qubit + u1, u2, u3, id, x, y, z, h, s, sdg, t, tdg, // single qubit cx, cz, swap, // two qubit ccx // three qubit }; @@ -69,14 +69,16 @@ public: // Return the set of qobj gate instruction names supported by the State inline virtual stringset_t allowed_gates() const override { - return {"U", "CX", "u0", "u1", "u2", "u3", "cx", "cz", "swap", + return {"U", "CX", "u1", "u2", "u3", "cx", "cz", "swap", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "ccx"}; } // Return the set of qobj snapshot types supported by the State inline virtual stringset_t allowed_snapshots() const override { - return {"state", "statevector", "probabilities", "expval_pauli", - "expval_matrix", "memory", "register"}; + return {"statevector", "memory", "register", + "probabilities", "probabilities_var", + "expval_pauli", "expval_pauli_var", + "expval_matrix", "expval_matrix_var"}; } // Apply a sequence of operations by looping over list @@ -100,8 +102,6 @@ public: // Load the threshold for applying OpenMP parallelization // if the controller/engine allows threads for it - // Config: {"omp_qubit_threshold": 14} - // TODO: Check optimal default value for a desktop i7 CPU virtual void set_config(const json_t &config) override; // Sample n-measurement outcomes without applying the measure operation @@ -228,6 +228,9 @@ protected: // OpenMP qubit threshold int omp_qubit_threshold_ = 14; + // QubitVector sample measure index size + int sample_measure_index_size_ = 10; + // Threshold for chopping small values to zero in JSON double json_chop_threshold_ = 1e-15; @@ -257,7 +260,6 @@ const stringmap_t State::gateset_({ {"t", Gates::t}, // T-gate (sqrt(S)) {"tdg", Gates::tdg}, // Conjguate-transpose of T gate // Waltz Gates - {"u0", Gates::u0}, // idle gate in multiples of X90 {"u1", Gates::u1}, // zero-X90 pulse waltz gate {"u2", Gates::u2}, // single-X90 pulse waltz gate {"u3", Gates::u3}, // two X90 pulse waltz gate @@ -275,13 +277,12 @@ const stringmap_t State::gateset_({ template const stringmap_t State::snapshotset_({ {"statevector", Snapshots::statevector}, - {"state", Snapshots::statevector}, {"probabilities", Snapshots::probs}, {"expval_pauli", Snapshots::expval_pauli}, {"expval_matrix", Snapshots::expval_matrix}, - {"probabilities_with_variance", Snapshots::probs_var}, - {"expval_pauli_with_variance", Snapshots::expval_pauli_var}, - {"expval_matrix_with_variance", Snapshots::expval_matrix_var}, + {"probabilities_var", Snapshots::probs_var}, + {"expval_pauli_var", Snapshots::expval_pauli_var}, + {"expval_matrix_var", Snapshots::expval_matrix_var}, {"memory", Snapshots::cmemory}, {"register", Snapshots::cregister} }); @@ -355,17 +356,17 @@ void State::set_config(const json_t &config) { BaseState::qreg_.set_json_chop_threshold(json_chop_threshold_); // Set OMP threshold for state update functions - JSON::get_value(omp_qubit_threshold_, "omp_qubit_threshold", config); + JSON::get_value(omp_qubit_threshold_, "statevector_parallel_threshold", config); // Set the sample measure indexing size int index_size; - if (JSON::get_value(index_size, "sample_measure_index_size", config)) { + if (JSON::get_value(index_size, "statevector_sample_measure_opt", config)) { BaseState::qreg_.set_sample_measure_index_size(index_size); }; // Enable sorted gate optimzations bool gate_opt = false; - JSON::get_value(gate_opt, "gate_optimization", config); + JSON::get_value(gate_opt, "statevector_gate_opt", config); if (gate_opt) BaseState::qreg_.enable_gate_opt(); } @@ -620,8 +621,6 @@ void State::apply_gate(const Operations::Op &op) { case Gates::cz: BaseState::qreg_.apply_cz(op.qubits[0], op.qubits[1]); break; - case Gates::u0: // u0 = id in ideal State - break; case Gates::id: break; case Gates::x: diff --git a/src/simulators/qubitvector/statevector_controller.hpp b/src/simulators/qubitvector/statevector_controller.hpp index c95eaec7b..c6828f560 100755 --- a/src/simulators/qubitvector/statevector_controller.hpp +++ b/src/simulators/qubitvector/statevector_controller.hpp @@ -18,6 +18,39 @@ namespace Simulator { // StatevectorController class //========================================================================= +/************************************************************************** + * Config settings: + * + * From QubitVector::State class + * + * - "initial_statevector" (json complex vector): Use a custom initial + * statevector for the simulation [Default: null]. + * - "chop_threshold" (double): Threshold for truncating small values to + * zero in result data [Default: 1e-15] + * - "statevector_parallel_threshold" (int): Threshold that number of qubits + * must be greater than to enable OpenMP parallelization at State + * level [Default: 13] + * - "statevector_sample_measure_opt" (int): Threshold that number of qubits + * must be greater than to enable indexing optimization during + * measure sampling [Default: 10] + * - "statevector_hpc_gate_opt" (bool): Enable large qubit gate optimizations. + * [Default: False] + * + * From BaseController Class + * + * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may + * be used across all levels of parallelization. Set to 0 for maximum + * available. [Default : 0] + * - "max_parallel_circuits" (int): Set number of circuits that may be + * executed in parallel. Set to 0 to use the number of max parallel + * threads [Default: 1] + * - "counts" (bool): Return counts objecy in circuit data [Default: True] + * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] + * - "memory" (bool): Return memory array in circuit data [Default: False] + * - "register" (bool): Return register array in circuit data [Default: False] + * + **************************************************************************/ + class StatevectorController : public Base::Controller { public: //----------------------------------------------------------------------- From 0a2ca085c32f537d8d17517a31f5c0febd5a75be Mon Sep 17 00:00:00 2001 From: cjwood Date: Thu, 13 Dec 2018 20:23:04 -0500 Subject: [PATCH 2/6] rename error classes --- qiskit_aer/backends/aerbackend.py | 8 +-- .../{aersimulatorerror.py => aererror.py} | 6 +- qiskit_aer/backends/aerjob.py | 23 +++---- qiskit_aer/backends/unitary_simulator.py | 6 +- qiskit_aer/noise/errors/standard_errors.py | 56 ++++++++--------- qiskit_aer/noise/noise_model.py | 62 +++++++++---------- qiskit_aer/noise/noise_utils.py | 48 +++++++------- .../noise/{aernoiseerror.py => noiseerror.py} | 6 +- qiskit_aer/noise/quantum_error.py | 24 +++---- qiskit_aer/noise/readout_error.py | 14 ++--- test/terra/test_quantumerror.py | 20 +++--- test/terra/test_readouterror.py | 24 +++---- test/terra/test_standard_errors.py | 32 +++++----- 13 files changed, 161 insertions(+), 168 deletions(-) rename qiskit_aer/backends/{aersimulatorerror.py => aererror.py} (78%) rename qiskit_aer/noise/{aernoiseerror.py => noiseerror.py} (79%) diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 1bb952ac3..e66932a2d 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -22,7 +22,7 @@ from qiskit.qobj import QobjConfig from qiskit.result import Result from .aerjob import AerJob -from .aersimulatorerror import AerSimulatorError +from .aererror import AerError # Logger logger = logging.getLogger(__name__) @@ -66,7 +66,7 @@ class AerBackend(BaseBackend): Raises: FileNotFoundError if backend executable is not available. - QISKitError: if there is no name in the configuration + QiskitError: if there is no name in the configuration """ super().__init__(configuration, provider=provider) self._controller = controller @@ -144,9 +144,9 @@ class AerBackend(BaseBackend): # Check for error message in the failed circuit for res in output.get('results'): if not res.get('success', False): - raise AerSimulatorError(res.get("status", None)) + raise AerError(res.get("status", None)) # If no error was found check for error message at qobj level - raise AerSimulatorError(output.get("status", None)) + raise AerError(output.get("status", None)) def _validate(self, qobj): # TODO diff --git a/qiskit_aer/backends/aersimulatorerror.py b/qiskit_aer/backends/aererror.py similarity index 78% rename from qiskit_aer/backends/aersimulatorerror.py rename to qiskit_aer/backends/aererror.py index 6e2bd1976..51c876df1 100644 --- a/qiskit_aer/backends/aersimulatorerror.py +++ b/qiskit_aer/backends/aererror.py @@ -6,13 +6,13 @@ # the LICENSE.txt file in the root directory of this source tree. """ -Exception for errors raised by simulators. +Exception for errors raised by Qiskit Aer simulators backends. """ -from qiskit import QISKitError +from qiskit.qiskiterror import QiskitError -class AerSimulatorError(QISKitError): +class AerError(QiskitError): """Base class for errors raised by simulators.""" def __init__(self, *message): diff --git a/qiskit_aer/backends/aerjob.py b/qiskit_aer/backends/aerjob.py index 09e402143..70fa827d0 100644 --- a/qiskit_aer/backends/aerjob.py +++ b/qiskit_aer/backends/aerjob.py @@ -5,6 +5,8 @@ # 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. +# pylint: disable=arguments-differ + """This module implements the job class used for AerBackend objects.""" import warnings @@ -39,7 +41,7 @@ def requires_submit(func): class AerJob(BaseJob): - """Aer Job class. + """AerJob class. Attributes: _executor (futures.Executor): executor to handle asynchronous jobs @@ -118,22 +120,13 @@ class AerJob(BaseJob): elif self._future.done(): _status = JobStatus.DONE if self._future.exception() is None else JobStatus.ERROR else: - raise JobError('Unexpected behavior of {0}'.format( - self.__class__.__name__)) + # Note: There is an undocumented Future state: PENDING, that seems to show up when + # the job is enqueued, waiting for someone to pick it up. We need to deal with this + # state but there's no public API for it, so we are assuming that if the job is not + # in any of the previous states, is PENDING, ergo INITIALIZING for us. + _status = JobStatus.INITIALIZING return _status - def backend_name(self): - """ - Return the name of the backend used for this job. - - .. deprecated:: 0.6+ - After 0.6, this function is deprecated. Please use - `job.backend().name()` instead. - """ - warnings.warn('The use of `job.backend_name()` is deprecated, use ' - '`job.backend().name()` instead.', DeprecationWarning) - return self._backend.name() - def backend(self): """Return the instance of the backend used for this job.""" return self._backend diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index db09805ee..9a1516034 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -18,7 +18,7 @@ from qiskit.backends.models import BackendConfiguration from ..version import __version__ from .aerbackend import AerBackend -from .aersimulatorerror import AerSimulatorError +from .aererror import AerError from unitary_controller_wrapper import unitary_controller_execute # Logger @@ -69,9 +69,9 @@ class UnitarySimulator(AerBackend): # Check for measure or reset operations for pos, instr in reversed(list(enumerate(experiment.instructions))): if instr.name == "measure": - raise AerSimulatorError("UnitarySimulator: circuit contains measure.") + raise AerError("UnitarySimulator: circuit contains measure.") if instr.name == "reset": - raise AerSimulatorError("UnitarySimulator: circuit contains reset.") + raise AerError("UnitarySimulator: circuit contains reset.") # Set shots to 1 if getattr(experiment.config, 'shots', 1) != 1: logger.info("UnitarySimulator only supports 1 shot. " diff --git a/qiskit_aer/noise/errors/standard_errors.py b/qiskit_aer/noise/errors/standard_errors.py index 8426eae01..cde91974f 100644 --- a/qiskit_aer/noise/errors/standard_errors.py +++ b/qiskit_aer/noise/errors/standard_errors.py @@ -13,7 +13,7 @@ import numpy as np from itertools import product from qiskit.quantum_info.operators.pauli import Pauli -from ..aernoiseerror import AerNoiseError +from ..noiseerror import NoiseError from ..quantum_error import QuantumError from ..noise_utils import make_unitary_instruction from ..noise_utils import qubits_from_mat @@ -37,12 +37,12 @@ def kraus_error(noise_ops, standard_gates=True, canonical_kraus=False): QuantumError: The quantum error object. Raises: - AerNoiseError: if error parameters are invalid. + NoiseError: if error parameters are invalid. """ if not isinstance(noise_ops, (list, tuple)): - raise AerNoiseError("Invalid Kraus error input.") + raise NoiseError("Invalid Kraus error input.") if len(noise_ops) == 0: - raise AerNoiseError("Kraus error noise_ops must not be empty.") + raise NoiseError("Kraus error noise_ops must not be empty.") kraus_ops = [np.array(a, dtype=complex) for a in noise_ops] if canonical_kraus: kraus_ops = canonical_kraus_matrices(kraus_ops) @@ -65,17 +65,17 @@ def mixed_unitary_error(noise_ops, standard_gates=True): QuantumError: The quantum error object. Raises: - AerNoiseError: if error parameters are invalid. + NoiseError: if error parameters are invalid. """ # Error checking if not isinstance(noise_ops, (list, tuple, zip)): - raise AerNoiseError("Input noise ops is not a list.") + raise NoiseError("Input noise ops is not a list.") # Convert to numpy arrays noise_ops = [(np.array(op, dtype=complex), p) for op, p in noise_ops] if len(noise_ops) == 0: - raise AerNoiseError("Input noise list is empty.") + raise NoiseError("Input noise list is empty.") # Check for identity unitaries prob_identity = 0. @@ -86,9 +86,9 @@ def mixed_unitary_error(noise_ops, standard_gates=True): for unitary, prob in noise_ops: # Check unitary if qubits_from_mat(unitary) != num_qubits: - raise AerNoiseError("Input matrices different size.") + raise NoiseError("Input matrices different size.") if not is_unitary_matrix(unitary): - raise AerNoiseError("Input matrix is not unitary.") + raise NoiseError("Input matrix is not unitary.") if is_identity_matrix(unitary): prob_identity += prob else: @@ -134,15 +134,15 @@ def pauli_error(noise_ops, standard_gates=False): QuantumError: The quantum error object. Raises: - AerNoiseError: If depolarizing probability is less than 0 or greater than 1. + NoiseError: If depolarizing probability is less than 0 or greater than 1. """ # Error checking if not isinstance(noise_ops, (list, tuple, zip)): - raise AerNoiseError("Input noise ops is not a list.") + raise NoiseError("Input noise ops is not a list.") noise_ops = list(noise_ops) if len(noise_ops) == 0: - raise AerNoiseError("Input noise list is empty.") + raise NoiseError("Input noise list is empty.") num_qubits = None for op in noise_ops: pauli = op[0] @@ -151,11 +151,11 @@ def pauli_error(noise_ops, standard_gates=False): elif isinstance(pauli, str): pauli_str = pauli else: - raise AerNoiseError("Invalid Pauli input operator: {}".format(pauli)) + raise NoiseError("Invalid Pauli input operator: {}".format(pauli)) if num_qubits is None: num_qubits = len(pauli_str) elif num_qubits != len(pauli_str): - raise AerNoiseError("Pauli's are not all of the same length.") + raise NoiseError("Pauli's are not all of the same length.") # Compute Paulis as single matrix if standard_gates is False: @@ -196,7 +196,7 @@ def _pauli_error_unitary(noise_ops, num_qubits): mat = np.kron(single_pauli(s), mat) qubits.append(qubit) elif s != 'I': - raise AerNoiseError("Invalid Pauli string.") + raise NoiseError("Invalid Pauli string.") if mat is 1: prob_identity += prob else: @@ -244,7 +244,7 @@ def _pauli_error_standard(noise_ops, num_qubits): instruction["qubits"] = [qubit] circuit.append(instruction) elif s != 'I': - raise AerNoiseError("Invalid Pauli string.") + raise NoiseError("Invalid Pauli string.") if circuit == []: prob_identity += prob else: @@ -276,7 +276,7 @@ def depolarizing_error(prob, num_qubits, standard_gates=False): """ if prob < 0 or prob > 1: - raise AerNoiseError("Depolarizing probability must be in between 0 and 1.") + raise NoiseError("Depolarizing probability must be in between 0 and 1.") # Rescale completely depolarizing channel error probs # with the identity component removed @@ -312,19 +312,19 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): non-unitary Kraus error channel. """ if excited_state_population < 0: - raise AerNoiseError("Invalid excited state population " + + raise NoiseError("Invalid excited state population " + "({} < 0).".format(excited_state_population)) if excited_state_population > 1: - raise AerNoiseError("Invalid excited state population " + + raise NoiseError("Invalid excited state population " + "({} > 1).".format(excited_state_population)) if time < 0: - raise AerNoiseError("Invalid gate_time ({} < 0)".format(time)) + raise NoiseError("Invalid gate_time ({} < 0)".format(time)) if t1 <= 0: - raise AerNoiseError("Invalid T_1 relaxation time parameter: T_1 <= 0.") + raise NoiseError("Invalid T_1 relaxation time parameter: T_1 <= 0.") if t2 <= 0: - raise AerNoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.") + raise NoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.") if t2 - 2 * t1 > 0: - raise AerNoiseError("Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.") + raise NoiseError("Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.") # T1 relaxation rate if t1 == np.inf: @@ -409,19 +409,19 @@ def phase_amplitude_damping_error(param_amp, param_phase, """ if param_amp < 0: - raise AerNoiseError("Invalid amplitude damping to |0> parameter " + + raise NoiseError("Invalid amplitude damping to |0> parameter " + "({} < 0)".format(param_amp)) if param_phase < 0: - raise AerNoiseError("Invalid phase damping parameter " + + raise NoiseError("Invalid phase damping parameter " + "({} < 0)".format(param_phase)) if param_phase + param_amp > 1: - raise AerNoiseError("Invalid amplitude and phase damping parameters " + + raise NoiseError("Invalid amplitude and phase damping parameters " + "({} + {} > 1)".format(param_phase, param_amp)) if excited_state_population < 0: - raise AerNoiseError("Invalid excited state population " + + raise NoiseError("Invalid excited state population " + "({} < 0).".format(excited_state_population)) if excited_state_population > 1: - raise AerNoiseError("Invalid excited state population " + + raise NoiseError("Invalid excited state population " + "({} > 1).".format(excited_state_population)) c0 = np.sqrt(1 - excited_state_population) c1 = np.sqrt(excited_state_population) diff --git a/qiskit_aer/noise/noise_model.py b/qiskit_aer/noise/noise_model.py index 81bc7b0a2..1cc32af4e 100644 --- a/qiskit_aer/noise/noise_model.py +++ b/qiskit_aer/noise/noise_model.py @@ -11,7 +11,7 @@ Noise model class for Qiskit Aer simulators. import logging -from .aernoiseerror import AerNoiseError +from .noiseerror import NoiseError from .quantum_error import QuantumError from .readout_error import ReadoutError @@ -62,14 +62,14 @@ class NoiseModel: operations (list[str]): the operations error applies to. Raises: - AerNoiseError: if the input operations are not valid. + NoiseError: if the input operations are not valid. """ if isinstance(operations, str): operations = [operations] for op in operations: if not isinstance(op, str): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") # Add X-90 based gate to noisy gates self._noise_instructions.add(op) self._x90_gates = operations @@ -83,7 +83,7 @@ class NoiseModel: operations (str, list[str]): the operations error applies to. Raises: - AerNoiseError: if the input parameters are invalid. + NoiseError: if the input parameters are invalid. Additional Information: If the error object is ideal it will not be added to the model. @@ -97,9 +97,9 @@ class NoiseModel: try: error = QuantumError(error) except: - raise AerNoiseError("Input is not a valid quantum error.") + raise NoiseError("Input is not a valid quantum error.") if not isinstance(operations, (list, tuple)): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") # Check if error is ideal and if so don't add to the noise model if error.ideal(): @@ -108,7 +108,7 @@ class NoiseModel: # Add operations for op in operations: if not isinstance(op, str): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") self._check_number_of_qubits(error, op) if op in self._default_quantum_errors: logger.warning("WARNING: all-qubit error already exists for " + @@ -135,7 +135,7 @@ class NoiseModel: qubits (list[int]): qubits operation error applies to. Raises: - AerNoiseError: if the input parameters are invalid. + NoiseError: if the input parameters are invalid. Additional Information: If the error object is ideal it will not be added to the model. @@ -150,11 +150,11 @@ class NoiseModel: try: error = QuantumError(error) except: - raise AerNoiseError("Input is not a valid quantum error.") + raise NoiseError("Input is not a valid quantum error.") if not isinstance(qubits, (list, tuple)): - raise AerNoiseError("Qubits must be a list of integers.") + raise NoiseError("Qubits must be a list of integers.") if not isinstance(operations, (list, tuple)): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") # Check if error is ideal and if so don't add to the noise model if error.ideal(): @@ -163,7 +163,7 @@ class NoiseModel: # Add operations for op in operations: if not isinstance(op, str): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") # Check number of qubits is correct for standard operations self._check_number_of_qubits(error, op) if op in self._local_quantum_errors: @@ -174,7 +174,7 @@ class NoiseModel: # Convert qubits list to hashable string qubits_str = self._qubits2str(qubits) if error.number_of_qubits != len(qubits): - raise AerNoiseError("Number of qubits ({}) does not match".format(len(qubits)) + + raise NoiseError("Number of qubits ({}) does not match".format(len(qubits)) + " the error size ({})".format(error.number_of_qubits)) if qubits_str in qubit_dict: logger.warning("WARNING: quantum error already exists for " + @@ -206,7 +206,7 @@ class NoiseModel: should be applied to if different to the operation qubits. Raises: - AerNoiseError: if the input parameters are invalid. + NoiseError: if the input parameters are invalid. Additional Information: If the error object is ideal it will not be added to the model. @@ -221,13 +221,13 @@ class NoiseModel: try: error = QuantumError(error) except: - raise AerNoiseError("Input is not a valid quantum error.") + raise NoiseError("Input is not a valid quantum error.") if not isinstance(qubits, (list, tuple)): - raise AerNoiseError("Qubits must be a list of integers.") + raise NoiseError("Qubits must be a list of integers.") if not isinstance(noise_qubits, (list, tuple)): - raise AerNoiseError("Noise qubits must be a list of integers.") + raise NoiseError("Noise qubits must be a list of integers.") if not isinstance(operations, (list, tuple)): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") # Check if error is ideal and if so don't add to the noise model if error.ideal(): @@ -236,7 +236,7 @@ class NoiseModel: # Add operations for op in operations: if not isinstance(op, str): - raise AerNoiseError("Qobj invalid operations.") + raise NoiseError("Qobj invalid operations.") if op in self._nonlocal_quantum_errors: qubit_dict = self._nonlocal_quantum_errors[op] else: @@ -261,7 +261,7 @@ class NoiseModel: error (ReadoutError): the quantum error object. Raises: - AerNoiseError: if the input parameters are invalid. + NoiseError: if the input parameters are invalid. Additional Information: If the error object is ideal it will not be added to the model. @@ -272,7 +272,7 @@ class NoiseModel: try: error = ReadoutError(error) except: - raise AerNoiseError("Input is not a valid readout error.") + raise NoiseError("Input is not a valid readout error.") # Check if error is ideal and if so don't add to the noise model if error.ideal(): @@ -280,7 +280,7 @@ class NoiseModel: # Check number of qubits is correct for standard operations if error.number_of_qubits != 1: - raise AerNoiseError("All-qubit readout errors must defined as single-qubit errors.") + raise NoiseError("All-qubit readout errors must defined as single-qubit errors.") if self._default_readout_error is not None: logger.warning("WARNING: all-qubit readout error already exists, " + "overriding with new readout error.") @@ -303,7 +303,7 @@ class NoiseModel: qubits (list[int]): qubits operation error applies to. Raises: - AerNoiseError: if the input parameters are invalid. + NoiseError: if the input parameters are invalid. Additional Information: If the error object is ideal it will not be added to the model. @@ -314,9 +314,9 @@ class NoiseModel: try: error = ReadoutError(error) except: - raise AerNoiseError("Input is not a valid readout error.") + raise NoiseError("Input is not a valid readout error.") if not isinstance(qubits, (list, tuple)): - raise AerNoiseError("Qubits must be a list of integers.") + raise NoiseError("Qubits must be a list of integers.") # Check if error is ideal and if so don't add to the noise model if error.ideal(): @@ -326,7 +326,7 @@ class NoiseModel: qubits_str = self._qubits2str(qubits) # Check error matches qubit size if error.number_of_qubits != len(qubits): - raise AerNoiseError("Number of qubits ({}) does not match".format(len(qubits)) + + raise NoiseError("Number of qubits ({}) does not match".format(len(qubits)) + " the readout error size ({})".format(error.number_of_qubits)) # Check if we are overriding a previous error if qubits_str in self._local_readout_errors: @@ -500,7 +500,7 @@ class NoiseModel: noise_model.add_all_qubit_readout_error(roerror) # Invalid error type else: - raise AerNoiseError("Invalid error type: {}".format(error_type)) + raise NoiseError("Invalid error type: {}".format(error_type)) return noise_model def _check_number_of_qubits(self, error, operation): @@ -512,7 +512,7 @@ class NoiseModel: operation (str): qobj operation strings to apply error to. Raises: - AerNoiseError: If operation and error qubit number do not match. + NoiseError: If operation and error qubit number do not match. """ def error_message(gate_qubits): msg = "{} qubit QuantumError".format(error.number_of_qubits) + \ @@ -520,11 +520,11 @@ class NoiseModel: " operation \"{}\".".format(operation) return msg if operation in self._1qubit_operations and error.number_of_qubits != 1: - raise AerNoiseError(error_message(1)) + raise NoiseError(error_message(1)) if operation in self._2qubit_operations and error.number_of_qubits != 2: - raise AerNoiseError(error_message(2)) + raise NoiseError(error_message(2)) if operation in self._3qubit_operations and error.number_of_qubits != 3: - raise AerNoiseError(error_message(3)) + raise NoiseError(error_message(3)) def _qubits2str(self, qubits): """Convert qubits list to comma seperated qubits string.""" diff --git a/qiskit_aer/noise/noise_utils.py b/qiskit_aer/noise/noise_utils.py index cf9073aa1..3c3eddb16 100644 --- a/qiskit_aer/noise/noise_utils.py +++ b/qiskit_aer/noise/noise_utils.py @@ -10,7 +10,7 @@ Helper functions for noise model creation. """ import numpy as np -from .aernoiseerror import AerNoiseError +from .noiseerror import NoiseError def standard_gates_instructions(instructions): @@ -117,7 +117,7 @@ def single_qubit_clifford_gates(j): tuple(str): The tuple of basis gates.""" if not isinstance(j, int) or j < 0 or j > 23: - raise AerNoiseError("Index {} must be in the range [0, ..., 23]".format(j)) + raise NoiseError("Index {} must be in the range [0, ..., 23]".format(j)) labels = [ ('id',), ('s',), ('sdg',), ('z',), @@ -142,7 +142,7 @@ def single_qubit_clifford_matrix(j): np.array: The matrix for the indexed clifford.""" if not isinstance(j, int) or j < 0 or j > 23: - raise AerNoiseError("Index {} must be in the range [0, ..., 23]".format(j)) + raise NoiseError("Index {} must be in the range [0, ..., 23]".format(j)) basis_dict = { 'id': np.eye(2), @@ -174,9 +174,9 @@ def single_qubit_clifford_instructions(j, qubit=0): list(dict): The list of instructions.""" if not isinstance(j, int) or j < 0 or j > 23: - raise AerNoiseError("Index {} must be in the range [0, ..., 23]".format(j)) + raise NoiseError("Index {} must be in the range [0, ..., 23]".format(j)) if not isinstance(qubit, int) or qubit < 0: - raise AerNoiseError("qubit position must be positive integer.") + raise NoiseError("qubit position must be positive integer.") instructions = [] for gate in single_qubit_clifford_gates(j): @@ -275,10 +275,10 @@ def make_unitary_instruction(mat, qubits, standard_gates=True): dict: The qobj instruction object. Raises: - AerNoiseError: if the input is not a unitary matrix. + NoiseError: if the input is not a unitary matrix. """ if not is_unitary_matrix(mat): - raise AerNoiseError("Input matrix is not unitary.") + raise NoiseError("Input matrix is not unitary.") elif isinstance(qubits, int): qubits = [qubits] instruction = {"name": "unitary", @@ -300,10 +300,10 @@ def make_kraus_instruction(mats, qubits): dict: The qobj instruction object. Raises: - AerNoiseError: if the input is not a CPTP Kraus channel. + NoiseError: if the input is not a CPTP Kraus channel. """ if not is_cptp_kraus(mats): - raise AerNoiseError("Input Kraus matrices are not a CPTP channel.") + raise NoiseError("Input Kraus matrices are not a CPTP channel.") elif isinstance(qubits, int): qubits = [qubits] return [{"name": "kraus", "qubits": qubits, "params": mats}] @@ -315,7 +315,7 @@ def qubits_from_mat(mat): shape = arr.shape num_qubits = int(np.log2(shape[1])) if shape[1] != 2 ** num_qubits: - raise AerNoiseError("Input Kraus channel is not a multi-qubit channel.") + raise NoiseError("Input Kraus channel is not a multi-qubit channel.") return num_qubits @@ -384,9 +384,9 @@ def choi2kraus(choi, threshold=1e-10): """Convert a Choi matrix to canonical Kraus matrices""" # Check threshold if threshold < 0: - raise AerNoiseError("Threshold value cannot be negative") + raise NoiseError("Threshold value cannot be negative") if threshold > 1e-3: - raise AerNoiseError("Threshold value is too large. It should be close to zero.") + raise NoiseError("Threshold value is too large. It should be close to zero.") # Compute eigensystem of Choi matrix w, v = np.linalg.eig(choi) kraus = [] @@ -394,7 +394,7 @@ def choi2kraus(choi, threshold=1e-10): if val > threshold: kraus.append(np.sqrt(val) * vec.reshape((2, 2), order='F')) if val < -threshold: - raise AerNoiseError("Input Choi-matrix is not CP " + + raise NoiseError("Input Choi-matrix is not CP " + " (eigenvalue {} < 0)".format(val)) return kraus @@ -434,22 +434,22 @@ def kraus2instructions(kraus_ops, standard_gates, threshold): given error. Raises: - AerNoiseError: If the input Kraus channel is not CPTP. + NoiseError: If the input Kraus channel is not CPTP. """ # Check threshold if threshold < 0: - raise AerNoiseError("Threshold cannot be negative") + raise NoiseError("Threshold cannot be negative") if threshold > 1e-3: - raise AerNoiseError("Threhsold value is too large. It should be close to zero.") + raise NoiseError("Threhsold value is too large. It should be close to zero.") # Check CPTP if not is_cptp_kraus(kraus_ops): - raise AerNoiseError("Input Kraus channel is not CPTP.") + raise NoiseError("Input Kraus channel is not CPTP.") # Get number of qubits num_qubits = int(np.log2(len(kraus_ops[0]))) if len(kraus_ops[0]) != 2 ** num_qubits: - raise AerNoiseError("Input Kraus channel is not a multi-qubit channel.") + raise NoiseError("Input Kraus channel is not a multi-qubit channel.") # Check if each matrix is a: # 1. scaled identity matrix @@ -492,22 +492,22 @@ def kraus2instructions(kraus_ops, standard_gates, threshold): # Check probabilities prob_kraus = 1 - prob_unitary if prob_unitary - 1 > threshold: - raise AerNoiseError("Invalid kraus matrices: unitary probability" + + raise NoiseError("Invalid kraus matrices: unitary probability" + " {} > 1".format(prob_unitary)) if prob_unitary < -threshold: - raise AerNoiseError("Invalid kraus matrices: unitary probability" + + raise NoiseError("Invalid kraus matrices: unitary probability" + " {} < 1".format(prob_unitary)) if prob_identity - 1 > threshold: - raise AerNoiseError("Invalid kraus matrices: identity probability" + + raise NoiseError("Invalid kraus matrices: identity probability" + " {} > 1".format(prob_identity)) if prob_identity < -threshold: - raise AerNoiseError("Invalid kraus matrices: identity probability" + + raise NoiseError("Invalid kraus matrices: identity probability" + " {} < 1".format(prob_identity)) if prob_kraus - 1 > threshold: - raise AerNoiseError("Invalid kraus matrices: non-unitary probability" + + raise NoiseError("Invalid kraus matrices: non-unitary probability" + " {} > 1".format(prob_kraus)) if prob_kraus < -threshold: - raise AerNoiseError("Invalid kraus matrices: non-unitary probability" + + raise NoiseError("Invalid kraus matrices: non-unitary probability" + " {} < 1".format(prob_kraus)) # Build qobj instructions diff --git a/qiskit_aer/noise/aernoiseerror.py b/qiskit_aer/noise/noiseerror.py similarity index 79% rename from qiskit_aer/noise/aernoiseerror.py rename to qiskit_aer/noise/noiseerror.py index 0cca1f58a..3cd8b1288 100644 --- a/qiskit_aer/noise/aernoiseerror.py +++ b/qiskit_aer/noise/noiseerror.py @@ -6,13 +6,13 @@ # the LICENSE.txt file in the root directory of this source tree. """ -Exception for errors raised by Qiskit Aer. +Exception for errors raised by Qiskit Aer noise module. """ -from qiskit import QISKitError +from qiskit.qiskiterror import QiskitError -class AerNoiseError(QISKitError): +class NoiseError(QiskitError): """Class for errors raised in qiskit_aer.noise package.""" def __init__(self, *message): diff --git a/qiskit_aer/noise/quantum_error.py b/qiskit_aer/noise/quantum_error.py index 1da47a684..053e4c17b 100644 --- a/qiskit_aer/noise/quantum_error.py +++ b/qiskit_aer/noise/quantum_error.py @@ -10,7 +10,7 @@ Quantum error class for Qiskit Aer noise model """ import logging import numpy as np -from .aernoiseerror import AerNoiseError +from .noiseerror import NoiseError from .noise_utils import kraus2instructions logger = logging.getLogger(__name__) @@ -93,15 +93,15 @@ class QuantumError: # Error checking if minimum_qubits > self._number_of_qubits: - raise AerNoiseError("Input errors require {} qubits, ".format(minimum_qubits) + + raise NoiseError("Input errors require {} qubits, ".format(minimum_qubits) + "but number_of_qubits is {}".format(number_of_qubits)) if len(self._noise_circuits) != len(self._noise_probabilities): - raise AerNoiseError("Number of error circuits does not match length of probabilities") + raise NoiseError("Number of error circuits does not match length of probabilities") total_probs = np.sum(self._noise_probabilities) if abs(total_probs - 1) > threshold: - raise AerNoiseError("Probabilities are not normalized: {} != 1".format(total_probs)) + raise NoiseError("Probabilities are not normalized: {} != 1".format(total_probs)) if len([p for p in self._noise_probabilities if p < 0]) > 0: - raise AerNoiseError("Probabilities are invalid.") + raise NoiseError("Probabilities are invalid.") def __repr__(self): """Display QuantumError.""" @@ -155,13 +155,13 @@ class QuantumError: is the list of qobj instructions for the error term. Raises: - AerNoiseError: If the position is greater than the size of + NoiseError: If the position is greater than the size of the quantum error. """ if position < self.size: return self.circuits[position], self.probabilities[position] else: - raise AerNoiseError("Position {} is greater than the number".format(position) + + raise NoiseError("Position {} is greater than the number".format(position) + "of error outcomes {}".format(self.size)) def as_dict(self): @@ -192,9 +192,9 @@ class QuantumError: """ # Error checking if not isinstance(error, QuantumError): - raise AerNoiseError("error1 is not a QuantumError") + raise NoiseError("error1 is not a QuantumError") if self.number_of_qubits != error.number_of_qubits: - raise AerNoiseError("QuantumErrors are not defined on same number of qubits.") + raise NoiseError("QuantumErrors are not defined on same number of qubits.") combined_noise_circuits = [] combined_noise_probabilities = [] @@ -256,7 +256,7 @@ class QuantumError: # Error checking if not isinstance(error, QuantumError): - raise AerNoiseError("{} is not a QuantumError".format(error)) + raise NoiseError("{} is not a QuantumError".format(error)) combined_noise_circuits = [] combined_noise_probabilities = [] @@ -326,7 +326,7 @@ class QuantumError: qubits0 = kraus0['qubits'] qubits1 = kraus1['qubits'] if qubits0 != qubits1: - raise AerNoiseError("Kraus instructions are on different qubits") + raise NoiseError("Kraus instructions are on different qubits") params = [np.dot(b, a) for a in kraus0['params'] for b in kraus1['params']] return {'name': 'kraus', 'qubits': qubits0, 'params': params} @@ -336,7 +336,7 @@ class QuantumError: qubits0 = mat0['qubits'] qubits1 = mat1['qubits'] if qubits0 != qubits1: - raise AerNoiseError("Unitary instructions are on different qubits") + raise NoiseError("Unitary instructions are on different qubits") params = np.dot(mat1['params'], mat0['params']) return {'name': 'unitary', 'qubits': qubits0, 'params': params} diff --git a/qiskit_aer/noise/readout_error.py b/qiskit_aer/noise/readout_error.py index fa88e0487..0dfd88cc9 100644 --- a/qiskit_aer/noise/readout_error.py +++ b/qiskit_aer/noise/readout_error.py @@ -13,7 +13,7 @@ from numpy import array, log2, eye from numpy.linalg import norm from .noise_utils import qubits_from_mat -from .aernoiseerror import AerNoiseError +from .noiseerror import NoiseError class ReadoutError: @@ -78,23 +78,23 @@ class ReadoutError: def _check_probabilities(probabilities, threshold): """Check probabilities are valid.""" if len(probabilities) == 0: - raise AerNoiseError("Input probabilities: empty.") + raise NoiseError("Input probabilities: empty.") num_outcomes = len(probabilities[0]) num_qubits = int(log2(num_outcomes)) if 2 ** num_qubits != num_outcomes: - raise AerNoiseError("Invalid probabilities: length" + + raise NoiseError("Invalid probabilities: length" + "{} != 2**{}".format(num_outcomes, num_qubits)) if len(probabilities) != num_outcomes: - raise AerNoiseError("Invalid probabilities.") + raise NoiseError("Invalid probabilities.") for vec in probabilities: arr = array(vec) if len(arr) != num_outcomes: - raise AerNoiseError("Invalid probabilities: vectors are different lengths.") + raise NoiseError("Invalid probabilities: vectors are different lengths.") if abs(sum(arr) - 1) > threshold: - raise AerNoiseError("Invalid probabilities: sum({})".format(vec) + + raise NoiseError("Invalid probabilities: sum({})".format(vec) + " = {} is not 1.".format(sum(arr))) if len(arr[arr < 0]) > 0: - raise AerNoiseError("Invalid probabilities: {}".format(vec) + + raise NoiseError("Invalid probabilities: {}".format(vec) + " contains a negative probability.") def __repr__(self): diff --git a/test/terra/test_quantumerror.py b/test/terra/test_quantumerror.py index 7eb9e6794..746c8a882 100644 --- a/test/terra/test_quantumerror.py +++ b/test/terra/test_quantumerror.py @@ -11,7 +11,7 @@ import numpy as np from qiskit_aer.noise.noise_utils import standard_gate_unitary from qiskit_aer.noise import QuantumError -from qiskit_aer.noise.aernoiseerror import AerNoiseError +from qiskit_aer.noise.noiseerror import NoiseError # TODO: # * Test compose error for different qubit number @@ -81,38 +81,38 @@ class TestQuantumError(common.QiskitAerTestCase): """Test exception is raised for negative probabilities.""" noise_ops = [([{"name": "id", "qubits": [0]}], 1.1), ([{"name": "x", "qubits": [0]}], -0.1)] - self.assertRaises(AerNoiseError, lambda: QuantumError(noise_ops)) + self.assertRaises(NoiseError, lambda: QuantumError(noise_ops)) def test_raise_probabilities_normalized_qobj(self): """Test exception is raised for qobj probabilities greater than 1.""" noise_ops = [([{"name": "id", "qubits": [0]}], 0.9), ([{"name": "x", "qubits": [0]}], 0.2)] - self.assertRaises(AerNoiseError, lambda: QuantumError(noise_ops)) + self.assertRaises(NoiseError, lambda: QuantumError(noise_ops)) def test_raise_probabilities_normalized_unitary_kraus(self): """Test exception is raised for unitary kraus probs greater than 1.""" A0 = np.sqrt(0.9) * np.eye(2) A1 = np.sqrt(0.2) * np.diag([1, -1]) - self.assertRaises(AerNoiseError, lambda: QuantumError([A0, A1])) + self.assertRaises(NoiseError, lambda: QuantumError([A0, A1])) def test_raise_probabilities_normalized_nonunitary_kraus(self): """Test exception is raised for non-unitary kraus probs greater than 1.""" A0 = np.sqrt(0.9) * np.array([[1, 0], [0, np.sqrt(1 - 0.3)]]) A1 = np.sqrt(0.2) * np.array([[0, np.sqrt(0.3)], [0, 0]]) - self.assertRaises(AerNoiseError, lambda: QuantumError([A0, A1])) + self.assertRaises(NoiseError, lambda: QuantumError([A0, A1])) def test_raise_non_cptp_kraus(self): """Test exception is raised for non-CPTP input.""" A0 = np.array([[1, 0], [0, np.sqrt(1 - 0.3)]]) A1 = np.array([[0, 0], [np.sqrt(0.3), 0]]) - self.assertRaises(AerNoiseError, lambda: QuantumError([A0, A1])) - self.assertRaises(AerNoiseError, lambda: QuantumError([A0])) + self.assertRaises(NoiseError, lambda: QuantumError([A0, A1])) + self.assertRaises(NoiseError, lambda: QuantumError([A0])) def test_raise_non_multiqubit_kraus(self): """Test exception is raised for non-multiqubit input.""" A0 = np.sqrt(0.5) * np.diag([1, 1, 1]) A1 = np.sqrt(0.5) * np.diag([1, 1, -1]) - self.assertRaises(AerNoiseError, lambda: QuantumError([A0, A1])) + self.assertRaises(NoiseError, lambda: QuantumError([A0, A1])) def test_pauli_conversion_standard_gates(self): """Test conversion of Pauli channel kraus to gates""" @@ -246,8 +246,8 @@ class TestQuantumError(common.QiskitAerTestCase): """Test composing incompatible errors raises exception""" error0 = QuantumError([np.diag([1, 1, 1, -1])]) # 2-qubit coherent error error1 = QuantumError([np.diag([1, -1])]) # 1-qubit coherent error - self.assertRaises(AerNoiseError, lambda: error0.compose(error1)) - self.assertRaises(AerNoiseError, lambda: error1.compose(error0)) + self.assertRaises(NoiseError, lambda: error0.compose(error1)) + self.assertRaises(NoiseError, lambda: error1.compose(error0)) def test_compose_both_kraus(self): """Test composition of two kraus errors""" diff --git a/test/terra/test_readouterror.py b/test/terra/test_readouterror.py index 9ecc25dd9..6cec04eae 100644 --- a/test/terra/test_readouterror.py +++ b/test/terra/test_readouterror.py @@ -9,7 +9,7 @@ from test.terra.utils import common import unittest from qiskit_aer.noise import ReadoutError -from qiskit_aer.noise.aernoiseerror import AerNoiseError +from qiskit_aer.noise.noiseerror import NoiseError class TestReadoutError(common.QiskitAerTestCase): @@ -18,45 +18,45 @@ class TestReadoutError(common.QiskitAerTestCase): def test_probabilities_normalized_exception(self): """Test exception is raised for probabilities greater than 1.""" probs = [[0.9, 0.2], [0, 1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[0, 1], [0.9, 0.2]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) def test_probabilities_negative_exception(self): """Test exception is raised for negative probabilities.""" probs = [[1.1, -0.1], [0, 1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[0, 1], [1.1, -0.1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) def test_probabilities_dimension_exception(self): """Test exception is raised if probabilities are not multi-qubit""" probs = [[1, 0, 0], [0, 1, 0], [0, 1, 0]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) def test_probabilities_length_exception(self): """Test exception is raised if probabilities are different lengths""" probs = [[1, 0, 0, 0], [0, 1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[0, 1], [1, 0, 0, 0]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) def test_probabilities_num_outcomes_exception(self): """Test exception is raised if not enough probability vectors""" probs = [[0, 1]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[1, 0], [0, 1], [0, 0]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) probs = [[1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0]] - self.assertRaises(AerNoiseError, lambda: ReadoutError(probs)) + self.assertRaises(NoiseError, lambda: ReadoutError(probs)) def test_1qubit(self): """Test reset error noise model""" diff --git a/test/terra/test_standard_errors.py b/test/terra/test_standard_errors.py index 06fd9ee23..c39683daa 100644 --- a/test/terra/test_standard_errors.py +++ b/test/terra/test_standard_errors.py @@ -11,7 +11,7 @@ import unittest from qiskit.quantum_info.operators.pauli import Pauli from qiskit_aer.noise.noise_utils import standard_gate_unitary -from qiskit_aer.noise.aernoiseerror import AerNoiseError +from qiskit_aer.noise.noiseerror import NoiseError from qiskit_aer.noise.errors.standard_errors import kraus_error from qiskit_aer.noise.errors.standard_errors import mixed_unitary_error from qiskit_aer.noise.errors.standard_errors import coherent_unitary_error @@ -48,14 +48,14 @@ class TestNoise(common.QiskitAerTestCase): A0 = [[1, 0], [0, np.sqrt(1 - 0.3)]] A1 = [[0, 0], [0, np.sqrt(0.3)]] noise_ops = [(A0, 0.5), (A1, 0.5)] - self.assertRaises(AerNoiseError, lambda: mixed_unitary_error(noise_ops)) + self.assertRaises(NoiseError, lambda: mixed_unitary_error(noise_ops)) def test_mixed_unitary_error_raise_differnt_shape(self): """Test error is raised if input matrices different size""" unitaries = [np.eye(4), np.eye(2)] probs = [0.7, 0.4] noise_ops = [(unitaries[0], probs[0]), (unitaries[1], probs[1])] - self.assertRaises(AerNoiseError, lambda: mixed_unitary_error(noise_ops)) + self.assertRaises(NoiseError, lambda: mixed_unitary_error(noise_ops)) def test_mixed_unitary_error(self): """Test construction of mixed unitary error""" @@ -79,7 +79,7 @@ class TestNoise(common.QiskitAerTestCase): def test_pauli_error_raise_invalid(self): """Test exception for invalid Pauli string""" - self.assertRaises(AerNoiseError, lambda: pauli_error([('S', 1)])) + self.assertRaises(NoiseError, lambda: pauli_error([('S', 1)])) def test_pauli_error_1q_unitary_from_string(self): """Test single-qubit pauli error as unitary qobj from string label""" @@ -412,28 +412,28 @@ class TestNoise(common.QiskitAerTestCase): def test_amplitude_damping_error_raises_invalid_amp_param(self): """Test phase and amplitude damping error raises for invalid amp_param""" - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(-0.5, 0, 0)) - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(1.1, 0, 0)) def test_amplitude_damping_error_raises_invalid_phase_param(self): """Test phase and amplitude damping error raises for invalid amp_param""" - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(0, -0.5, 0)) - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(0, 1.1, 0)) def test_amplitude_damping_error_raises_invalid_excited_state_pop(self): """Test phase and amplitude damping error raises for invalid pop""" - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(0, 0, -0.5)) - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(0, 0, 1.1)) def test_amplitude_damping_error_raises_invalid_combined_params(self): """Test phase and amplitude damping error raises for invalid pop""" - self.assertRaises(AerNoiseError, + self.assertRaises(NoiseError, lambda: phase_amplitude_damping_error(0.5, 0.6, 0)) def test_phase_amplitude_damping_error_noncanonical(self): @@ -571,21 +571,21 @@ class TestNoise(common.QiskitAerTestCase): def test_thermal_relaxation_error_raises_invalid_t2(self): """Test raises error for invalid t2 parameters""" # T2 == 0 - self.assertRaises(AerNoiseError, lambda: thermal_relaxation_error(1, 0, 0)) + self.assertRaises(NoiseError, lambda: thermal_relaxation_error(1, 0, 0)) # T2 < 0 - self.assertRaises(AerNoiseError, lambda: thermal_relaxation_error(1, -1, 0)) + self.assertRaises(NoiseError, lambda: thermal_relaxation_error(1, -1, 0)) def test_thermal_relaxation_error_raises_invalid_t1(self): """Test raises error for invalid t1 parameters""" # T1 == 0 - self.assertRaises(AerNoiseError, lambda: thermal_relaxation_error(0, 0, 0)) + self.assertRaises(NoiseError, lambda: thermal_relaxation_error(0, 0, 0)) # T1 < 0 - self.assertRaises(AerNoiseError, lambda: thermal_relaxation_error(-0.1, 0.1, 0)) + self.assertRaises(NoiseError, lambda: thermal_relaxation_error(-0.1, 0.1, 0)) def test_thermal_relaxation_error_raises_invalid_t1_t2(self): """Test raises error for invalid t2 > 2 * t1 parameters""" # T2 > 2 * T1 - self.assertRaises(AerNoiseError, lambda: thermal_relaxation_error(1, 2.1, 0)) + self.assertRaises(NoiseError, lambda: thermal_relaxation_error(1, 2.1, 0)) def test_thermal_relaxation_error_t1_t2_inf_ideal(self): """Test t1 = t2 = inf returns identity channel""" From 8c1a493a1fa09d1bd20d295713a25fecdfa67b1f Mon Sep 17 00:00:00 2001 From: cjwood Date: Thu, 13 Dec 2018 20:35:15 -0500 Subject: [PATCH 3/6] re-arrange noise module contents --- qiskit_aer/noise/__init__.py | 28 +++++++++---------- qiskit_aer/noise/device/models.py | 2 +- .../{noise_utils.py => errors/errorutils.py} | 2 +- .../noise/{ => errors}/quantum_error.py | 4 +-- .../noise/{ => errors}/readout_error.py | 4 +-- qiskit_aer/noise/errors/standard_errors.py | 17 ++++++----- qiskit_aer/noise/noise_model.py | 4 +-- test/terra/test_noise_model.py | 7 +++-- test/terra/test_qasm_simulator.py | 5 ++-- ..._quantumerror.py => test_quantum_error.py} | 14 +++++----- ..._readouterror.py => test_readout_error.py} | 9 ++++-- test/terra/test_standard_errors.py | 10 +++++-- test/terra/test_statevector_simulator.py | 6 ++-- test/terra/test_unitary_simulator.py | 5 ++-- 14 files changed, 60 insertions(+), 57 deletions(-) rename qiskit_aer/noise/{noise_utils.py => errors/errorutils.py} (99%) rename qiskit_aer/noise/{ => errors}/quantum_error.py (99%) rename qiskit_aer/noise/{ => errors}/readout_error.py (98%) rename test/terra/{test_quantumerror.py => test_quantum_error.py} (99%) rename test/terra/{test_readouterror.py => test_readout_error.py} (97%) diff --git a/qiskit_aer/noise/__init__.py b/qiskit_aer/noise/__init__.py index 47580656b..02d145923 100644 --- a/qiskit_aer/noise/__init__.py +++ b/qiskit_aer/noise/__init__.py @@ -40,32 +40,30 @@ Custom Noise Models ------------------- Custom noise models may be constructed by adding errors to a NoiseModel object. Errors are represented using by the `QuantumError` and -`ReadoutError` classes: +`ReadoutError` classes from the `noise.errors` module: - * `QuantumErrors`: Errors that affect the quantum state during a + * `QuantumError`: Errors that affect the quantum state during a simulation. They may be applied after specific circuit gates or reset operations, or before measure operations of qubits. - * `ReadoutErrors`: Errors that apply to classical bit registers + * `ReadoutError`: Errors that apply to classical bit registers after a measurement. They do not change the quantum state of the system, only the recorded classical measurement outcome. Helper functions exist for generating standard quantum error channels in -the `errors` submodule. These functions are: +the `noise.errors` module. These functions are: - * `errors.kraus_error` - * `errors.mixed_unitary_error` - * `errors.coherent_unitary_error` - * `errors.pauli_error` - * `errors.depolarizing_error` - * `errors.thermal_relaxation_error` - * `errors.phase_amplitude_damping_error` - * `errors.amplitude_damping_error` - * `errors.phase_damping_error` + * `kraus_error` + * `mixed_unitary_error` + * `coherent_unitary_error` + * `pauli_error` + * `depolarizing_error` + * `thermal_relaxation_error` + * `phase_amplitude_damping_error` + * `amplitude_damping_error` + * `phase_damping_error` """ from .noise_model import NoiseModel -from .quantum_error import QuantumError -from .readout_error import ReadoutError from . import errors from . import device diff --git a/qiskit_aer/noise/device/models.py b/qiskit_aer/noise/device/models.py index bce61e7fb..435e59ca3 100644 --- a/qiskit_aer/noise/device/models.py +++ b/qiskit_aer/noise/device/models.py @@ -17,7 +17,7 @@ from .parameters import gate_error_values from .parameters import thermal_relaxation_values from ..noise_model import NoiseModel -from ..readout_error import ReadoutError +from ..errors.readout_error import ReadoutError from ..errors.standard_errors import depolarizing_error from ..errors.standard_errors import thermal_relaxation_error diff --git a/qiskit_aer/noise/noise_utils.py b/qiskit_aer/noise/errors/errorutils.py similarity index 99% rename from qiskit_aer/noise/noise_utils.py rename to qiskit_aer/noise/errors/errorutils.py index 3c3eddb16..68b7050d3 100644 --- a/qiskit_aer/noise/noise_utils.py +++ b/qiskit_aer/noise/errors/errorutils.py @@ -10,7 +10,7 @@ Helper functions for noise model creation. """ import numpy as np -from .noiseerror import NoiseError +from ..noiseerror import NoiseError def standard_gates_instructions(instructions): diff --git a/qiskit_aer/noise/quantum_error.py b/qiskit_aer/noise/errors/quantum_error.py similarity index 99% rename from qiskit_aer/noise/quantum_error.py rename to qiskit_aer/noise/errors/quantum_error.py index 053e4c17b..59b4eb08f 100644 --- a/qiskit_aer/noise/quantum_error.py +++ b/qiskit_aer/noise/errors/quantum_error.py @@ -10,8 +10,8 @@ Quantum error class for Qiskit Aer noise model """ import logging import numpy as np -from .noiseerror import NoiseError -from .noise_utils import kraus2instructions +from ..noiseerror import NoiseError +from .errorutils import kraus2instructions logger = logging.getLogger(__name__) diff --git a/qiskit_aer/noise/readout_error.py b/qiskit_aer/noise/errors/readout_error.py similarity index 98% rename from qiskit_aer/noise/readout_error.py rename to qiskit_aer/noise/errors/readout_error.py index 0dfd88cc9..be8d00124 100644 --- a/qiskit_aer/noise/readout_error.py +++ b/qiskit_aer/noise/errors/readout_error.py @@ -12,8 +12,8 @@ Readout error class for Qiskit Aer noise model. from numpy import array, log2, eye from numpy.linalg import norm -from .noise_utils import qubits_from_mat -from .noiseerror import NoiseError +from ..noiseerror import NoiseError +from .errorutils import qubits_from_mat class ReadoutError: diff --git a/qiskit_aer/noise/errors/standard_errors.py b/qiskit_aer/noise/errors/standard_errors.py index cde91974f..cce2781d4 100644 --- a/qiskit_aer/noise/errors/standard_errors.py +++ b/qiskit_aer/noise/errors/standard_errors.py @@ -14,15 +14,14 @@ from itertools import product from qiskit.quantum_info.operators.pauli import Pauli from ..noiseerror import NoiseError -from ..quantum_error import QuantumError -from ..noise_utils import make_unitary_instruction -from ..noise_utils import qubits_from_mat -from ..noise_utils import canonical_kraus_matrices -from ..noise_utils import choi2kraus -from ..noise_utils import standard_gate_unitary -from ..noise_utils import is_unitary_matrix -from ..noise_utils import is_identity_matrix - +from .errorutils import make_unitary_instruction +from .errorutils import qubits_from_mat +from .errorutils import canonical_kraus_matrices +from .errorutils import choi2kraus +from .errorutils import standard_gate_unitary +from .errorutils import is_unitary_matrix +from .errorutils import is_identity_matrix +from .quantum_error import QuantumError def kraus_error(noise_ops, standard_gates=True, canonical_kraus=False): """Kraus error channel. diff --git a/qiskit_aer/noise/noise_model.py b/qiskit_aer/noise/noise_model.py index 1cc32af4e..1e60abb17 100644 --- a/qiskit_aer/noise/noise_model.py +++ b/qiskit_aer/noise/noise_model.py @@ -12,8 +12,8 @@ Noise model class for Qiskit Aer simulators. import logging from .noiseerror import NoiseError -from .quantum_error import QuantumError -from .readout_error import ReadoutError +from .errors.quantum_error import QuantumError +from .errors.readout_error import ReadoutError logger = logging.getLogger(__name__) diff --git a/test/terra/test_noise_model.py b/test/terra/test_noise_model.py index 4ad0ea137..4c63013de 100644 --- a/test/terra/test_noise_model.py +++ b/test/terra/test_noise_model.py @@ -5,9 +5,12 @@ # 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. -import test.terra.utils.common as common -import unittest +""" +NoiseModel class integration tests +""" +import unittest +from test.terra.utils import common from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit import compile from qiskit_aer.noise import NoiseModel, QuantumError diff --git a/test/terra/test_qasm_simulator.py b/test/terra/test_qasm_simulator.py index c10ec78e5..f48c8ba7a 100644 --- a/test/terra/test_qasm_simulator.py +++ b/test/terra/test_qasm_simulator.py @@ -9,6 +9,7 @@ QasmSimulator Integration Tests """ +import unittest from test.terra.utils import common from test.terra.utils import ref_measure from test.terra.utils import ref_reset @@ -18,12 +19,10 @@ from test.terra.utils import ref_2q_clifford from test.terra.utils import ref_non_clifford from test.terra.utils import ref_algorithms from test.terra.utils import ref_unitary_gate -import unittest + from qiskit import execute from qiskit_aer.backends import QasmSimulator -# TODO: Enable minimal basis (U,X) tests once bugs in terra are fixed - class TestQasmSimulator(common.QiskitAerTestCase): """QasmSimulator tests.""" diff --git a/test/terra/test_quantumerror.py b/test/terra/test_quantum_error.py similarity index 99% rename from test/terra/test_quantumerror.py rename to test/terra/test_quantum_error.py index 746c8a882..faa230cfb 100644 --- a/test/terra/test_quantumerror.py +++ b/test/terra/test_quantum_error.py @@ -5,16 +5,16 @@ # 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. -from test.terra.utils import common +""" +QuantumError class tests +""" + import unittest +from test.terra.utils import common import numpy as np - -from qiskit_aer.noise.noise_utils import standard_gate_unitary -from qiskit_aer.noise import QuantumError from qiskit_aer.noise.noiseerror import NoiseError - -# TODO: -# * Test compose error for different qubit number +from qiskit_aer.noise.errors.readout_error import QuantumError +from qiskit_aer.noise.errors.errorutils import standard_gate_unitary class TestQuantumError(common.QiskitAerTestCase): diff --git a/test/terra/test_readouterror.py b/test/terra/test_readout_error.py similarity index 97% rename from test/terra/test_readouterror.py rename to test/terra/test_readout_error.py index 6cec04eae..241432ff1 100644 --- a/test/terra/test_readouterror.py +++ b/test/terra/test_readout_error.py @@ -5,11 +5,14 @@ # 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. -from test.terra.utils import common -import unittest +""" +ReadoutError class tests +""" -from qiskit_aer.noise import ReadoutError +import unittest +from test.terra.utils import common from qiskit_aer.noise.noiseerror import NoiseError +from qiskit_aer.noise.errors.readout_error import ReadoutError class TestReadoutError(common.QiskitAerTestCase): diff --git a/test/terra/test_standard_errors.py b/test/terra/test_standard_errors.py index c39683daa..5c8a1e044 100644 --- a/test/terra/test_standard_errors.py +++ b/test/terra/test_standard_errors.py @@ -5,13 +5,17 @@ # 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. -import numpy as np -from test.terra.utils import common +""" +Standard error function tests +""" + import unittest +from test.terra.utils import common +import numpy as np from qiskit.quantum_info.operators.pauli import Pauli -from qiskit_aer.noise.noise_utils import standard_gate_unitary from qiskit_aer.noise.noiseerror import NoiseError +from qiskit_aer.noise.errors.errorutils import standard_gate_unitary from qiskit_aer.noise.errors.standard_errors import kraus_error from qiskit_aer.noise.errors.standard_errors import mixed_unitary_error from qiskit_aer.noise.errors.standard_errors import coherent_unitary_error diff --git a/test/terra/test_statevector_simulator.py b/test/terra/test_statevector_simulator.py index cb44547c8..106a44736 100644 --- a/test/terra/test_statevector_simulator.py +++ b/test/terra/test_statevector_simulator.py @@ -9,6 +9,7 @@ StatevectorSimulator Integration Tests """ +import unittest from test.terra.utils import common from test.terra.utils import ref_measure from test.terra.utils import ref_reset @@ -17,13 +18,10 @@ from test.terra.utils import ref_1q_clifford from test.terra.utils import ref_2q_clifford from test.terra.utils import ref_non_clifford from test.terra.utils import ref_unitary_gate -import unittest + from qiskit import execute from qiskit_aer.backends import StatevectorSimulator -# TODO: Enable minimal basis (U,X) tests once bugs in terra are fixed -# TODO: Enable conditional tests once terra supports bfunc conditionals - class TestStatevectorSimulator(common.QiskitAerTestCase): """StatevectorSimulator tests.""" diff --git a/test/terra/test_unitary_simulator.py b/test/terra/test_unitary_simulator.py index a1db076ff..252d24c16 100644 --- a/test/terra/test_unitary_simulator.py +++ b/test/terra/test_unitary_simulator.py @@ -9,17 +9,16 @@ UnitarySimulator Integration Tests """ +import unittest from test.terra.utils import common from test.terra.utils import ref_1q_clifford from test.terra.utils import ref_2q_clifford from test.terra.utils import ref_non_clifford from test.terra.utils import ref_unitary_gate -import unittest + from qiskit import execute from qiskit_aer.backends import UnitarySimulator -# TODO: Enable minimal basis (U,X) tests once bugs in terra are fixed - class TestUnitarySimulator(common.QiskitAerTestCase): """UnitarySimulator tests.""" From 0000ee22a0950650cd354ab9499a71a8499fa3b5 Mon Sep 17 00:00:00 2001 From: cjwood Date: Thu, 13 Dec 2018 21:28:26 -0500 Subject: [PATCH 4/6] Document backend_options in Backend docstrings --- qiskit_aer/__init__.py | 3 - qiskit_aer/backends/qasm_simulator.py | 84 +++++++++++++++++-- qiskit_aer/backends/statevector_simulator.py | 67 +++++++++++++-- qiskit_aer/backends/unitary_simulator.py | 65 ++++++++++++-- src/base/controller.hpp | 4 +- src/simulators/qasm/qasm_controller.hpp | 2 +- .../qubitunitary/unitary_controller.hpp | 4 +- .../qubitvector/statevector_controller.hpp | 2 +- test/benchmarks/benchmark_tools.py | 6 +- 9 files changed, 206 insertions(+), 31 deletions(-) diff --git a/qiskit_aer/__init__.py b/qiskit_aer/__init__.py index 331777e9b..e97260f6e 100644 --- a/qiskit_aer/__init__.py +++ b/qiskit_aer/__init__.py @@ -5,9 +5,6 @@ # 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. -import os -from pathlib import Path - from .backends import Aer from . import backends from . import noise diff --git a/qiskit_aer/backends/qasm_simulator.py b/qiskit_aer/backends/qasm_simulator.py index 203126819..eeaa081d2 100644 --- a/qiskit_aer/backends/qasm_simulator.py +++ b/qiskit_aer/backends/qasm_simulator.py @@ -19,12 +19,65 @@ from qasm_controller_wrapper import qasm_controller_execute class QasmSimulator(AerBackend): - """Aer quantum circuit simulator""" + """Aer quantum circuit simulator + + Backend options: + + The following backend options may be used with in the + `backend_options` kwarg diction for `QasmSimulator.run` or + `qiskit.execute` + + * "initial_statevector" (vector_like): Sets a custom initial + statevector for the simulation instead of the all zero + initial state (Default: None). + + * "chop_threshold" (double): Sets the threshold for truncating small + values to zero in the Result data (Default: 1e-15) + + * "max_parallel_threads" (int): Sets the maximum number of CPU + cores used by OpenMP for parallelization. If set to 0 the + maximum will be set to the number of CPU cores (Default: 0). + + * "max_parallel_experiments" (int): Sets the maximum number of + qobj experiments that may be executed in parallel up to the + max_parallel_threads value. If set to 1 parallel circuit + execution will be disabled. If set to 0 the maximum will be + automatically set to max_parallel_threads (Default: 1). + + * "max_parallel_shots" (int): Sets the maximum number of + shots that may be executed in parallel during each experiment + execution, up to the max_parallel_threads value. If set to 1 + parallel shot execution wil be disabled. If set to 0 the + maximum will be automatically set to max_parallel_threads. + Note that this cannot be enabled at the same time as parallel + experiment execution (Default: 1). + + * "statevector_parallel_threshold" (int): Sets the threshold that + "n_qubits" must be greater than to enable OpenMP + parallelization for matrix multiplication during execution of + an experiment. If parallel circuit or shot execution is enabled + this will only use unallocated CPU cores up to + max_parallel_threads. Note that setting this too low can reduce + performance (Default: 12). + + * "statevector_sample_measure_opt" (int): Sets the threshold that + the number of qubits must be greater than to enable a large + qubit optimized implementation of measurement sampling. Note + that setting this two low can reduce performance (Default: 10) + + * "statevector_hpc_gate_opt" (bool): If set to True this enables + a different optimzied gate application routine that can + increase performance on systems with a large number of CPU + cores. For systems with a small number of cores it enabling + can reduce performance (Default: False). + """ + + MAX_QUBIT_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': __version__, - 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)), + 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'TODO', 'simulator': True, 'local': True, @@ -32,12 +85,33 @@ class QasmSimulator(AerBackend): 'open_pulse': False, 'memory': True, 'max_shots': 100000, - 'description': 'A C++ simulator for QASM experiments with noise', + 'description': 'A C++ simulator with realistic for qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', - 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot'], - 'gates': [{'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO'}] + 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', + 'snapshot', 'unitary'], + 'gates': [ + { + 'name': 'TODO', + 'parameters': [], + 'qasm_def': 'TODO' + } + ] } + def run(self, qobj, backend_options=None, noise_model=None): + """Run a qobj on the backend. + + Args: + qobj (Qobj): a Qobj. + backend_options (dict): backend configuration options. + noise_model (NoiseModel): noise model for simulations. + + Returns: + AerJob: the simulation job. + """ + return super().run(qobj, backend_options=backend_options, + noise_model=noise_model) + def __init__(self, configuration=None, provider=None): super().__init__(qasm_controller_execute, BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), diff --git a/qiskit_aer/backends/statevector_simulator.py b/qiskit_aer/backends/statevector_simulator.py index 6fa4ef97b..c3794f879 100644 --- a/qiskit_aer/backends/statevector_simulator.py +++ b/qiskit_aer/backends/statevector_simulator.py @@ -25,12 +25,52 @@ logger = logging.getLogger(__name__) class StatevectorSimulator(AerBackend): - """Aer statevector simulator""" + """Aer statevector simulator + + Backend options: + + The following backend options may be used with in the + `backend_options` kwarg diction for `StatevectorSimulator.run` or + `qiskit.execute` + + * "initial_statevector" (vector_like): Sets a custom initial + statevector for the simulation instead of the all zero + initial state (Default: None). + + * "chop_threshold" (double): Sets the threshold for truncating small + values to zero in the Result data (Default: 1e-15) + + * "max_parallel_threads" (int): Sets the maximum number of CPU + cores used by OpenMP for parallelization. If set to 0 the + maximum will be set to the number of CPU cores (Default: 0). + + * "max_parallel_experiments" (int): Sets the maximum number of + qobj experiments that may be executed in parallel up to the + max_parallel_threads value. If set to 1 parallel circuit + execution will be disabled. If set to 0 the maximum will be + automatically set to max_parallel_threads (Default: 1). + + * "statevector_parallel_threshold" (int): Sets the threshold that + "n_qubits" must be greater than to enable OpenMP + parallelization for matrix multiplication during execution of + an experiment. If parallel circuit or shot execution is enabled + this will only use unallocated CPU cores up to + max_parallel_threads. Note that setting this too low can reduce + performance (Default: 12). + + * "statevector_hpc_gate_opt" (bool): If set to True this enables + a different optimzied gate application routine that can + increase performance on systems with a large number of CPU + cores. For systems with a small number of cores it enabling + can reduce performance (Default: False). + """ + + MAX_QUBIT_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': __version__, - 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)), + 'n_qubits': MAX_QUBIT_MEMORY, 'url': 'TODO', 'simulator': True, 'local': True, @@ -38,10 +78,17 @@ class StatevectorSimulator(AerBackend): 'open_pulse': False, 'memory': True, 'max_shots': 1, - 'description': 'A C++ statevector simulator for QASM experiments', + 'description': 'A C++ statevector simulator for qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', - 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot'], - 'gates': [{'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO'}] + 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', + 'snapshot', 'unitary'], + 'gates': [ + { + 'name': 'TODO', + 'parameters': [], + 'qasm_def': 'TODO' + } + ] } def __init__(self, configuration=None, provider=None): @@ -50,7 +97,15 @@ class StatevectorSimulator(AerBackend): provider=provider) def run(self, qobj, backend_options=None): - """Run a qobj on the backend.""" + """Run a qobj on the backend. + + Args: + qobj (Qobj): a Qobj. + backend_options (dict): backend configuration options. + + Returns: + AerJob: the simulation job. + """ return super().run(qobj, backend_options=backend_options) def _validate(self, qobj): diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index 9a1516034..6949b83e3 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -12,7 +12,7 @@ Qiskit Aer Unitary Simulator Backend. """ import logging -from math import log2 +from math import log2, sqrt from qiskit._util import local_hardware_info from qiskit.backends.models import BackendConfiguration @@ -26,23 +26,64 @@ logger = logging.getLogger(__name__) class UnitarySimulator(AerBackend): - """Unitary circuit simulator.""" + """Unitary circuit simulator. + + Backend options: + + The following backend options may be used with in the + `backend_options` kwarg diction for `UnitarySimulator.run` or + `qiskit.execute` + + * "initial_unitary" (matrix_like): Sets a custom initial unitary + matrix for the simulation instead of identity (Default: None). + + * "chop_threshold" (double): Sets the threshold for truncating small + values to zero in the Result data (Default: 1e-15) + + * "max_parallel_threads" (int): Sets the maximum number of CPU + cores used by OpenMP for parallelization. If set to 0 the + maximum will be set to the number of CPU cores (Default: 0). + + * "max_parallel_experiments" (int): Sets the maximum number of + qobj experiments that may be executed in parallel up to the + max_parallel_threads value. If set to 1 parallel circuit + execution will be disabled. If set to 0 the maximum will be + automatically set to max_parallel_threads (Default: 1). + + * "unitary_parallel_threshold" (int): Sets the threshold that + "n_qubits" must be greater than to enable OpenMP + parallelization for matrix multiplication during execution of + an experiment. If parallel circuit execution is enabled this + will only use unallocated CPU cores up to max_parallel_threads. + Note that setting this too low can reduce performance + (Default: 6). + """ + + MAX_QUBITS_MEMORY = int(log2(sqrt(local_hardware_info()['memory'] * (1024 ** 3) / 16))) DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator', 'backend_version': __version__, - 'n_qubits': int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)), - 'url': 'TODO', + 'n_qubits': MAX_QUBITS_MEMORY, + 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': False, 'open_pulse': False, 'memory': False, 'max_shots': 1, - 'description': 'A C++ unitary simulator for QASM experiments', + 'description': 'A Python simulator for computing the unitary' + + 'matrix for experiments in qobj files', 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', - 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'snapshot'], - 'gates': [{'name': 'TODO', 'parameters': [], 'qasm_def': 'TODO'}] + 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', + 'snapshot', 'unitary'], + 'gates': [ + { + 'name': 'TODO', + 'parameters': [], + 'qasm_def': 'TODO' + } + ] } def __init__(self, configuration=None, provider=None): @@ -51,7 +92,15 @@ class UnitarySimulator(AerBackend): provider=provider) def run(self, qobj, backend_options=None): - """Run a qobj on the backend.""" + """Run a qobj on the backend. + + Args: + qobj (Qobj): a Qobj. + backend_options (dict): backend configuration options. + + Returns: + AerJob: the simulation job. + """ return super().run(qobj, backend_options=backend_options) def _validate(self, qobj): diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 44bba356f..d8cd75d0b 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -77,7 +77,7 @@ namespace Base { * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may * be used across all levels of parallelization. Set to 0 for maximum * available. [Default : 0] - * - "max_parallel_circuits" (int): Set number of circuits that may be + * - "max_parallel_experiments" (int): Set number of circuits that may be * executed in parallel. Set to 0 to use the number of max parallel * threads [Default: 1] * - "max_parallel_shots" (int): Set number of shots that maybe be executed @@ -189,7 +189,7 @@ void Controller::set_config(const json_t &config) { // Load OpenMP maximum thread settings JSON::get_value(max_threads_total_, "max_parallel_threads", config); JSON::get_value(max_threads_shot_, "max_parallel_shots", config); - JSON::get_value(max_threads_circuit_, "max_parallel_circuits", config); + JSON::get_value(max_threads_circuit_, "max_parallel_experiments", config); // Prevent using both parallel circuits and parallel shots // with preference given to parallel circuit execution diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 7e4ea1ffd..0a2651994 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -42,7 +42,7 @@ namespace Simulator { * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may * be used across all levels of parallelization. Set to 0 for maximum * available. [Default : 0] - * - "max_parallel_circuits" (int): Set number of circuits that may be + * - "max_parallel_experiments" (int): Set number of circuits that may be * executed in parallel. Set to 0 to use the number of max parallel * threads [Default: 1] * - "max_parallel_shots" (int): Set number of shots that maybe be executed diff --git a/src/simulators/qubitunitary/unitary_controller.hpp b/src/simulators/qubitunitary/unitary_controller.hpp index 142dc52e4..cf2c5dab2 100755 --- a/src/simulators/qubitunitary/unitary_controller.hpp +++ b/src/simulators/qubitunitary/unitary_controller.hpp @@ -30,14 +30,14 @@ namespace Simulator { * zero in result data [Default: 1e-15] * - "unitary_parallel_threshold" (int): Threshold that number of qubits * must be greater than to enable OpenMP parallelization at State - * level [Default: 13] + * level [Default: 6] * * From BaseController Class * * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may * be used across all levels of parallelization. Set to 0 for maximum * available. [Default : 0] - * - "max_parallel_circuits" (int): Set number of circuits that may be + * - "max_parallel_experiments" (int): Set number of circuits that may be * executed in parallel. Set to 0 to use the number of max parallel * threads [Default: 1] * - "snapshots" (bool): Return snapshots object in circuit data [Default: True] diff --git a/src/simulators/qubitvector/statevector_controller.hpp b/src/simulators/qubitvector/statevector_controller.hpp index c6828f560..6309f519f 100755 --- a/src/simulators/qubitvector/statevector_controller.hpp +++ b/src/simulators/qubitvector/statevector_controller.hpp @@ -41,7 +41,7 @@ namespace Simulator { * - "max_parallel_threads" (int): Set the maximum OpenMP threads that may * be used across all levels of parallelization. Set to 0 for maximum * available. [Default : 0] - * - "max_parallel_circuits" (int): Set number of circuits that may be + * - "max_parallel_experiments" (int): Set number of circuits that may be * executed in parallel. Set to 0 to use the number of max parallel * threads [Default: 1] * - "counts" (bool): Return counts objecy in circuit data [Default: True] diff --git a/test/benchmarks/benchmark_tools.py b/test/benchmarks/benchmark_tools.py index 839ddd6d9..212493abb 100644 --- a/test/benchmarks/benchmark_tools.py +++ b/test/benchmarks/benchmark_tools.py @@ -6,7 +6,7 @@ import time from multiprocessing import cpu_count import qiskit -from qiskit import QISKitError +from qiskit import QiskitError from qiskit import ClassicalRegister, QuantumCircuit from qiskit_aer.backends.qasm_simulator import QasmSimulator from qiskit_aer.noise import NoiseModel @@ -65,7 +65,7 @@ def benchmark_circuits(backend, circuits, shots=1): number of circuits. Raises: - QISKitError: If the simulation execution fails. + QiskitError: If the simulation execution fails. """ qobj = qiskit.compile(circuits, backend, shots=shots) start_time = time.time() @@ -76,7 +76,7 @@ def benchmark_circuits(backend, circuits, shots=1): else: average_time = (end_time - start_time) / len(circuits) if result.status != 'COMPLETED': - raise QISKitError("Simulation failed. Status: " + result.status) + raise QiskitError("Simulation failed. Status: " + result.status) return average_time From 45e1e05aec2136b8e61244f1da40c0131a8bebdd Mon Sep 17 00:00:00 2001 From: cjwood Date: Thu, 13 Dec 2018 23:47:34 -0500 Subject: [PATCH 5/6] abbreviate long names in qobj_utils --- qiskit_aer/utils/qobj_utils.py | 160 +++++++++++++-------------- test/terra/test_noise_model.py | 11 +- test/terra/test_quantum_error.py | 2 +- test/terra/utils/ref_measure.py | 26 ++--- test/terra/utils/ref_unitary_gate.py | 96 ++++++++-------- 5 files changed, 142 insertions(+), 153 deletions(-) diff --git a/qiskit_aer/utils/qobj_utils.py b/qiskit_aer/utils/qobj_utils.py index 71dbbdffc..7b38190a8 100644 --- a/qiskit_aer/utils/qobj_utils.py +++ b/qiskit_aer/utils/qobj_utils.py @@ -14,90 +14,56 @@ IS ADDED TO QISKIT TERRA. THEY WILL NOT BE SUPPORTED AFTER THAT. import copy import numpy as np -from qiskit.qobj import QobjItem +from qiskit.qobj import QobjInstruction -def qobj_append_item(qobj, exp_index, item): - """Append a QobjItem to a Qobj experiment. +def append_instr(qobj, exp_index, instruction): + """Append a QobjInstruction to a QobjExperiment. Args: - qobj (Qobj): a Qobj object - exp_index (int): The index of the experiment in the qobj - item (QobjItem): The Qobj item to insert + qobj (Qobj): a Qobj object. + exp_index (int): The index of the experiment in the qobj. + instruction (QobjInstruction): instruction to insert. """ - qobj.experiments[exp_index].instructions.append(item) + qobj.experiments[exp_index].instructions.append(instruction) return qobj -def qobj_insert_item(qobj, exp_index, item, pos): - """Insert a QobjItem into a Qobj experiment. +def insert_instr(qobj, exp_index, item, pos): + """Insert a QobjInstruction into a QobjExperiment. Args: qobj (Qobj): a Qobj object - exp_index (int): The index of the experiment in the qobj - item (QobjItem): The Qobj item to insert + exp_index (int): The index of the experiment in the qobj. + instruction(QobjInstruction): instruction to insert. pos (int): the position to insert the item. """ qobj.experiments[exp_index].instructions.insert(pos, item) return qobj -def qobj_get_item_positions(qobj, exp_index, name): - """Return all locations of QobjItem in a Qobj experiment. +def get_instr_pos(qobj, exp_index, name): + """Return all locations of QobjInstruction in a Qobj experiment. + + The return list is sorted in reverse order so iterating over it + to insert new items will work as expected. Args: qobj (Qobj): a Qobj object exp_index (int): The index of the experiment in the qobj - name (str): QobjItem name to find + name (str): QobjInstruction name to find Returns: - list[int]: A list of positions where the QobjItem is located. + list[int]: A list of positions where the QobjInstruction is located. """ # Check only the name string of the item - return [i for i, val in enumerate(qobj.experiments[exp_index].instructions) - if val.name == name] + positions = [i for i, val in enumerate(qobj.experiments[exp_index].instructions) + if val.name == name] + return positions -def qobj_get_specific_item_positions(qobj, exp_index, item): - """Return all locations of QobjItem in a Qobj experiment. - - Args: - qobj (Qobj): a Qobj object - exp_index (int): The index of the experiment in the qobj - item (QobjItem): The item to find - - Returns: - list[int]: A list of positions where the QobjItem is located. - """ - return [i for i, val in enumerate(qobj.experiments[exp_index].instructions) - if val == item] - - -def qobj_insert_snapshots_after_barriers(qobj, snapshot): - """Insert a snapshot instruction after each barrier in qobj. - - The label of the input snapshot will be appended with "i" where - "i" ranges from 0 to the 1 - number of barriers. - - Args: - snapshot (QobjItem): a snapshot instruction. - - Additional Information: - """ - if snapshot.name != "snapshot": - raise ValueError("Invalid snapshot instruction") - label = snapshot.label - for exp_index in range(len(qobj.experiments)): - positions = qobj_get_item_positions(qobj, exp_index, "barrier") - for i, pos in reversed(list(enumerate(positions))): - item = copy.copy(snapshot) - item.label = label + "{}".format(i) - qobj_insert_item(qobj, exp_index, item, pos) - return qobj - - -def qobj_unitary_item(mat, qubits, label=None): - """Create a unitary gate qobj item. +def unitary_instr(mat, qubits, label=None): + """Create a unitary gate QobjInstruction. Args: mat (matrix_like): an n-qubit unitary matrix @@ -105,7 +71,7 @@ def qobj_unitary_item(mat, qubits, label=None): label (str): optional string label for the untiary matrix Returns: - QobjItem: The qobj item for the unitary instruction. + QobjInstruction: The qobj item for the unitary instruction. Raises: ValueError: if the input matrix is not unitary @@ -131,10 +97,38 @@ def qobj_unitary_item(mat, qubits, label=None): "params": np.array(mat, dtype=complex)} if label is not None: instruction["label"] = str(label) - return QobjItem(**instruction) + return QobjInstruction(**instruction) -def qobj_snapshot_item(snapshot_type, label, qubits=None, params=None): +def measure_instr(qubits, memory, registers=None): + """Create a multi-qubit measure instruction""" + if len(qubits) != len(memory): + raise ValueError("Number of qubits does not match number of memory") + if registers is None: + return QobjInstruction(name='measure', qubits=qubits, memory=memory) + # Case where we also measure to registers + if len(qubits) != len(registers): + raise ValueError("Number of qubits does not match number of registers") + return QobjInstruction(name='measure', qubits=qubits, memory=memory, + register=registers) + + +def reset_instr(qubits): + """Create a multi-qubit reset instruction""" + return QobjInstruction(name='reset', qubits=qubits) + + +def barrier_instr(num_qubits): + """Create a barrier QobjInstruction.""" + return QobjInstruction(name='barrier', qubits=list(range(num_qubits))) + + +def iden_instr(qubit): + """Create a barrier QobjInstruction.""" + return QobjInstruction(name='id', qubits=[qubit]) + + +def snapshot_instr(snapshot_type, label, qubits=None, params=None): """Create a snapshot qobj item. Args: @@ -145,7 +139,7 @@ def qobj_snapshot_item(snapshot_type, label, qubits=None, params=None): See additional information. Returns: - QobjItem: The qobj item for the snapshot instruction. + QobjInstruction: The qobj item for the snapshot instruction. Additional Information: @@ -184,32 +178,28 @@ def qobj_snapshot_item(snapshot_type, label, qubits=None, params=None): snap["name"] = "expval_matrix" snap["params"] = [[1.0, qubits, params]] # TODO: implicit conversion for Pauli expval params - return QobjItem(**snap) + return QobjInstruction(**snap) -def qobj_measure_item(qubits, memory, registers=None): - """Create a multi-qubit measure instruction""" - if len(qubits) != len(memory): - raise ValueError("Number of qubits does not match number of memory") - if registers is None: - return QobjItem(name='measure', qubits=qubits, memory=memory) - # Case where we also measure to registers - if len(qubits) != len(registers): - raise ValueError("Number of qubits does not match number of registers") - return QobjItem(name='measure', qubits=qubits, memory=memory, - register=registers) +def insert_snapshots_after_barriers(qobj, snapshot): + """Insert a snapshot instruction after each barrier in qobj. + The label of the input snapshot will be appended with "i" where + "i" ranges from 0 to the 1 - number of barriers. -def qobj_reset_item(qubits): - """Create a multi-qubit reset instruction""" - return QobjItem(name='reset', qubits=qubits) + Args: + qobj (Qobj): a qobj to insert snapshots into + snapshot (QobjInstruction): a snapshot instruction. - -def qobj_barrier_item(num_qubits): - """Create a barrier QobjItem.""" - return QobjItem(name='barrier', qubits=list(range(num_qubits))) - - -def qobj_iden_item(qubit): - """Create a barrier QobjItem.""" - return QobjItem(name='id', qubits=[qubit]) + Additional Information: + """ + if snapshot.name != "snapshot": + raise ValueError("Invalid snapshot instruction") + label = snapshot.label + for exp_index in range(len(qobj.experiments)): + positions = get_instr_pos(qobj, exp_index, "barrier") + for i, pos in reversed(list(enumerate(positions))): + item = copy.copy(snapshot) + item.label = label + "{}".format(i) + insert_instr(qobj, exp_index, item, pos) + return qobj diff --git a/test/terra/test_noise_model.py b/test/terra/test_noise_model.py index 4c63013de..1501e806d 100644 --- a/test/terra/test_noise_model.py +++ b/test/terra/test_noise_model.py @@ -13,12 +13,13 @@ import unittest from test.terra.utils import common from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit import compile -from qiskit_aer.noise import NoiseModel, QuantumError from qiskit_aer.backends import QasmSimulator +from qiskit_aer.noise import NoiseModel +from qiskit_aer.noise.errors.quantum_error import QuantumError from qiskit_aer.noise.errors.standard_errors import pauli_error from qiskit_aer.noise.errors.standard_errors import amplitude_damping_error -from qiskit_aer.utils.qobj_utils import qobj_measure_item -from qiskit_aer.utils.qobj_utils import qobj_append_item +from qiskit_aer.utils.qobj_utils import measure_instr +from qiskit_aer.utils.qobj_utils import append_instr class TestNoise(common.QiskitAerTestCase): @@ -159,8 +160,8 @@ class TestNoise(common.QiskitAerTestCase): qobj = compile([circuit], backend, shots=shots, basis_gates=noise_model.basis_gates) # Add measure to qobj - item = qobj_measure_item([0, 1], [0, 1]) - qobj_append_item(qobj, 0, item) + item = measure_instr([0, 1], [0, 1]) + append_instr(qobj, 0, item) # Execute result = backend.run(qobj, noise_model=noise_model).result() self.is_completed(result) diff --git a/test/terra/test_quantum_error.py b/test/terra/test_quantum_error.py index faa230cfb..c684467ab 100644 --- a/test/terra/test_quantum_error.py +++ b/test/terra/test_quantum_error.py @@ -13,7 +13,7 @@ import unittest from test.terra.utils import common import numpy as np from qiskit_aer.noise.noiseerror import NoiseError -from qiskit_aer.noise.errors.readout_error import QuantumError +from qiskit_aer.noise.errors.quantum_error import QuantumError from qiskit_aer.noise.errors.errorutils import standard_gate_unitary diff --git a/test/terra/utils/ref_measure.py b/test/terra/utils/ref_measure.py index b1f203936..a8faf83e2 100644 --- a/test/terra/utils/ref_measure.py +++ b/test/terra/utils/ref_measure.py @@ -17,9 +17,9 @@ from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit # direclty by qobj instructions until terra compiler supports them from qiskit import compile from qiskit_aer.backends import QasmSimulator -from qiskit_aer.utils.qobj_utils import qobj_insert_item -from qiskit_aer.utils.qobj_utils import qobj_measure_item -from qiskit_aer.utils.qobj_utils import qobj_iden_item +from qiskit_aer.utils.qobj_utils import insert_instr +from qiskit_aer.utils.qobj_utils import measure_instr +from qiskit_aer.utils.qobj_utils import iden_instr # ========================================================================== @@ -182,9 +182,9 @@ def measure_circuits_qobj_deterministic(allow_sampling=True): circuit.x(qr[1]) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_measure_item([0, 1], [0, 1]), -1) + insert_instr(qobj, 0, measure_instr([0, 1], [0, 1]), -1) if not allow_sampling: - qobj_insert_item(qobj, 0, qobj_iden_item(0), -1) + insert_instr(qobj, 0, iden_instr(0), -1) final_qobj.experiments.append(qobj.experiments[0]) # 3-qubit measure |101> @@ -195,9 +195,9 @@ def measure_circuits_qobj_deterministic(allow_sampling=True): circuit.x(qr[2]) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_measure_item([0, 1, 2], [0, 1, 2]), -1) + insert_instr(qobj, 0, measure_instr([0, 1, 2], [0, 1, 2]), -1) if not allow_sampling: - qobj_insert_item(qobj, 0, qobj_iden_item(0), -1) + insert_instr(qobj, 0, iden_instr(0), -1) final_qobj.experiments.append(qobj.experiments[0]) # 4-qubit measure |1010> @@ -208,9 +208,9 @@ def measure_circuits_qobj_deterministic(allow_sampling=True): circuit.x(qr[3]) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_measure_item([0, 1, 2, 3], [0, 1, 2, 3]), -1) + insert_instr(qobj, 0, measure_instr([0, 1, 2, 3], [0, 1, 2, 3]), -1) if not allow_sampling: - qobj_insert_item(qobj, 0, qobj_iden_item(0), -1) + insert_instr(qobj, 0, iden_instr(0), -1) final_qobj.experiments.append(qobj.experiments[0]) return final_qobj @@ -264,9 +264,9 @@ def measure_circuits_qobj_nondeterministic(allow_sampling=True): circuit.h(qr[1]) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_measure_item([0, 1], [0, 1]), -1) + insert_instr(qobj, 0, measure_instr([0, 1], [0, 1]), -1) if not allow_sampling: - qobj_insert_item(qobj, 0, qobj_iden_item(0), -1) + insert_instr(qobj, 0, iden_instr(0), -1) final_qobj.experiments.append(qobj.experiments[0]) # 3-qubit measure |++0> @@ -277,9 +277,9 @@ def measure_circuits_qobj_nondeterministic(allow_sampling=True): circuit.h(qr[1]) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_measure_item([0, 1, 2], [0, 1, 2]), -1) + insert_instr(qobj, 0, measure_instr([0, 1, 2], [0, 1, 2]), -1) if not allow_sampling: - qobj_insert_item(qobj, 0, qobj_iden_item(0), -1) + insert_instr(qobj, 0, iden_instr(0), -1) final_qobj.experiments.append(qobj.experiments[0]) return final_qobj diff --git a/test/terra/utils/ref_unitary_gate.py b/test/terra/utils/ref_unitary_gate.py index 83c7930bb..aa10d9e02 100644 --- a/test/terra/utils/ref_unitary_gate.py +++ b/test/terra/utils/ref_unitary_gate.py @@ -12,11 +12,10 @@ Test circuits and reference outputs for measure instruction. import numpy as np from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, compile - from qiskit_aer.backends import QasmSimulator -from qiskit_aer.utils.qobj_utils import qobj_unitary_item -from qiskit_aer.utils.qobj_utils import qobj_insert_item -from qiskit_aer.utils.qobj_utils import qobj_measure_item +from qiskit_aer.utils.qobj_utils import unitary_instr +from qiskit_aer.utils.qobj_utils import append_instr +from qiskit_aer.utils.qobj_utils import measure_instr # ========================================================================== @@ -51,64 +50,64 @@ def unitary_gate_circuits_real_deterministic(final_measure=True): circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10, |00> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX01.(X^I), |10> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(x_mat, [1]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(x_mat, [1])) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10.(I^X), |01> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(x_mat, [0]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(x_mat, [0])) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX01.(I^X), |11> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(x_mat, [0]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(x_mat, [0])) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10.(X^I), |11> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(x_mat, [1]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(x_mat, [1])) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) return final_qobj @@ -217,65 +216,64 @@ def unitary_gate_circuits_complex_deterministic(final_measure=True): circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10, |00> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) - # CX01.(Y^I), |10> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(y_mat, [1]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(y_mat, [1])) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10.(I^Y), |01> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(y_mat, [0]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(y_mat, [0])) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX01.(I^Y), |11> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(y_mat, [0]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [0, 1]), -1) + append_instr(qobj, 0, unitary_instr(y_mat, [0])) + append_instr(qobj, 0, unitary_instr(cx_mat, [0, 1])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) # CX10.(Y^I), |11> state circuit = QuantumCircuit(*regs) circuit.barrier(qr) qobj = compile(circuit, QasmSimulator(), shots=1) - qobj_insert_item(qobj, 0, qobj_unitary_item(y_mat, [1]), -1) - qobj_insert_item(qobj, 0, qobj_unitary_item(cx_mat, [1, 0]), -1) + append_instr(qobj, 0, unitary_instr(y_mat, [1])) + append_instr(qobj, 0, unitary_instr(cx_mat, [1, 0])) if final_measure: - qobj_insert_item(qobj, 0, qobj_measure_item([0], [0]), -1) - qobj_insert_item(qobj, 0, qobj_measure_item([1], [1]), -1) + append_instr(qobj, 0, measure_instr([0], [0])) + append_instr(qobj, 0, measure_instr([1], [1])) final_qobj.experiments.append(qobj.experiments[0]) return final_qobj From d64af5d762f1889ed6ebaf4ee832f85a8ae522ea Mon Sep 17 00:00:00 2001 From: cjwood Date: Fri, 14 Dec 2018 13:19:19 -0500 Subject: [PATCH 6/6] make snapshot types more descriptive --- src/framework/operations.hpp | 17 +++++++++-------- src/simulators/qubitvector/qv_state.hpp | 16 ++++++++-------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index d7b73e8bf..8737c7e98 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -428,9 +428,11 @@ 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); - if (type == "expval_pauli") + if (type == "expectation_value_pauli" || + type == "expectation_value_pauli_with_variance") return json_to_op_snapshot_pauli(js); - if (type == "expval_matrix") + if (type == "expectation_value_matrix" || + 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); @@ -455,8 +457,7 @@ Op json_to_op_snapshot_default(const json_t &js) { Op json_to_op_snapshot_pauli(const json_t &js) { // Load default snapshot parameters Op op = json_to_op_snapshot_default(js); - // override name for depreciated "pauli_observable" - op.name = "expval_pauli"; + // Check qubits are valid check_empty_qubits(op); check_duplicate_qubits(op); @@ -481,7 +482,8 @@ Op json_to_op_snapshot_pauli(const json_t &js) { if (std::abs(coeff) > threshold) { std::string pauli = comp[1]; if (pauli.size() != op.qubits.size()) { - throw std::invalid_argument("Invalid Pauli snapshot (Pauli label does not match qubit number.)."); + throw std::invalid_argument(std::string("Invalid Pauli expectation value snapshot ") + + "(Pauli label does not match qubit number.)."); } // make tuple and add to components op.params_expval_pauli.push_back(std::make_pair(coeff, pauli)); @@ -497,8 +499,6 @@ Op json_to_op_snapshot_pauli(const json_t &js) { Op json_to_op_snapshot_matrix(const json_t &js) { // Load default snapshot parameters Op op = json_to_op_snapshot_default(js); - // override name for depreciated "pauli_observable" - op.name = "expval_matrix"; const auto threshold = 1e-10; // drop small components // Get matrix operator components @@ -538,7 +538,8 @@ Op json_to_op_snapshot_matrix(const json_t &js) { } } // end component loop } else { - throw std::invalid_argument("Invalid matrix snapshot (\"params\" field missing)."); + throw std::invalid_argument(std::string("Invalid matrix expectation value snapshot ") + + "(\"params\" field missing)."); } return op; } diff --git a/src/simulators/qubitvector/qv_state.hpp b/src/simulators/qubitvector/qv_state.hpp index c47531a7b..ba69903d2 100755 --- a/src/simulators/qubitvector/qv_state.hpp +++ b/src/simulators/qubitvector/qv_state.hpp @@ -76,9 +76,9 @@ public: // Return the set of qobj snapshot types supported by the State inline virtual stringset_t allowed_snapshots() const override { return {"statevector", "memory", "register", - "probabilities", "probabilities_var", - "expval_pauli", "expval_pauli_var", - "expval_matrix", "expval_matrix_var"}; + "probabilities", "probabilities_with_variance", + "expectation_value_pauli", "expectation_value_pauli_with_variance", + "expectation_value_matrix", "expectation_value_matrix_with_variance"}; } // Apply a sequence of operations by looping over list @@ -278,11 +278,11 @@ template const stringmap_t State::snapshotset_({ {"statevector", Snapshots::statevector}, {"probabilities", Snapshots::probs}, - {"expval_pauli", Snapshots::expval_pauli}, - {"expval_matrix", Snapshots::expval_matrix}, - {"probabilities_var", Snapshots::probs_var}, - {"expval_pauli_var", Snapshots::expval_pauli_var}, - {"expval_matrix_var", Snapshots::expval_matrix_var}, + {"expectation_value_pauli", Snapshots::expval_pauli}, + {"expectation_value_matrix", Snapshots::expval_matrix}, + {"probabilities_with_variance", Snapshots::probs_var}, + {"expectation_value_pauli_with_variance", Snapshots::expval_pauli_var}, + {"expectation_value_matrix_with_variance", Snapshots::expval_matrix_var}, {"memory", Snapshots::cmemory}, {"register", Snapshots::cregister} });