diff --git a/releasenotes/notes/global-phase-9fc2105dac076f48.yaml b/releasenotes/notes/global-phase-9fc2105dac076f48.yaml new file mode 100644 index 0000000000..b63efadf16 --- /dev/null +++ b/releasenotes/notes/global-phase-9fc2105dac076f48.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds support for qobj global phase to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and statevector + methods of the :class:`~qiskit.providers.aer.QasmSimulator`. diff --git a/src/controllers/qasm_controller.hpp b/src/controllers/qasm_controller.hpp index 3272afeb45..2ff6ff719e 100755 --- a/src/controllers/qasm_controller.hpp +++ b/src/controllers/qasm_controller.hpp @@ -886,6 +886,7 @@ void QasmController::run_circuit_helper(const Circuit& circ, // Set state config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_global_phase(circ.global_phase_angle); // Rng engine RngEngine rng; diff --git a/src/controllers/statevector_controller.hpp b/src/controllers/statevector_controller.hpp index 2a3b3579a2..b4a69bf1a1 100755 --- a/src/controllers/statevector_controller.hpp +++ b/src/controllers/statevector_controller.hpp @@ -294,6 +294,7 @@ void StatevectorController::run_circuit_helper( // Set config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_global_phase(circ.global_phase_angle); // Rng engine RngEngine rng; diff --git a/src/controllers/unitary_controller.hpp b/src/controllers/unitary_controller.hpp index 5e3f99283f..74f143a71e 100755 --- a/src/controllers/unitary_controller.hpp +++ b/src/controllers/unitary_controller.hpp @@ -282,6 +282,7 @@ void UnitaryController::run_circuit_helper( // Set state config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_global_phase(circ.global_phase_angle); // Rng engine (not actually needed for unitary controller) RngEngine rng; diff --git a/src/framework/circuit.hpp b/src/framework/circuit.hpp index 4781a3ffae..171a077d17 100755 --- a/src/framework/circuit.hpp +++ b/src/framework/circuit.hpp @@ -51,7 +51,7 @@ class Circuit { uint_t shots = 1; uint_t seed; json_t header; - + double global_phase_angle = 0; // Constructor // The constructor automatically calculates the num_qubits, num_memory, num_registers @@ -184,6 +184,7 @@ Circuit::Circuit(const json_t &circ, const json_t &qobj_config) : Circuit() { // Load metadata JSON::get_value(header, "header", circ); JSON::get_value(shots, "shots", config); + JSON::get_value(global_phase_angle, "global_phase", header); // Check for specified memory slots uint_t memory_slots = 0; diff --git a/src/simulators/state.hpp b/src/simulators/state.hpp index 8e1629cb10..1860549fc9 100644 --- a/src/simulators/state.hpp +++ b/src/simulators/state.hpp @@ -128,7 +128,7 @@ class State { // Load any settings for the State class from a config JSON virtual void set_config(const json_t &config); - //----------------------------------------------------------------------- + //----------------------------------------------------------------------- // Optional: Add information to metadata //----------------------------------------------------------------------- @@ -191,13 +191,16 @@ class State { std::string name = "register") const; //----------------------------------------------------------------------- - // OpenMP thread settings + // Config Settings //----------------------------------------------------------------------- // Sets the number of threads available to the State implementation // If negative there is no restriction on the backend inline void set_parallalization(int n) {threads_ = n;} + // Set a complex global phase value exp(1j * theta) for the state + void set_global_phase(const double &phase); + protected: // The quantum state data structure @@ -212,6 +215,10 @@ class State { // Maximum threads which may be used by the backend for OpenMP multithreading // Default value is single-threaded unless overridden int threads_ = 1; + + // Set a global phase exp(1j * theta) for the state + bool has_global_phase_ = false; + complex_t global_phase_ = 1; }; @@ -224,6 +231,17 @@ void State::set_config(const json_t &config) { (ignore_argument)config; } +template +void State::set_global_phase(const double &phase_angle) { + if (Linalg::almost_equal(phase_angle, 0.0)) { + has_global_phase_ = false; + global_phase_ = 1; + } + else { + has_global_phase_ = true; + global_phase_ = std::exp(complex_t(0.0, phase_angle)); + } +} template std::vector State::sample_measure(const reg_t &qubits, diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index a21eca718b..53dcddae3f 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -275,6 +275,9 @@ class State : public Base::State { // Config Settings //----------------------------------------------------------------------- + // Apply the global phase + void apply_global_phase(); + // OpenMP qubit threshold int omp_qubit_threshold_ = 14; @@ -367,6 +370,7 @@ void State::initialize_qreg(uint_t num_qubits) { initialize_omp(); BaseState::qreg_.set_num_qubits(num_qubits); BaseState::qreg_.initialize(); + apply_global_phase(); } template @@ -379,6 +383,7 @@ void State::initialize_qreg(uint_t num_qubits, initialize_omp(); BaseState::qreg_.set_num_qubits(num_qubits); BaseState::qreg_.initialize_from_data(state.data(), 1ULL << num_qubits); + apply_global_phase(); } template @@ -390,6 +395,7 @@ void State::initialize_qreg(uint_t num_qubits, initialize_omp(); BaseState::qreg_.set_num_qubits(num_qubits); BaseState::qreg_.initialize_from_vector(state); + apply_global_phase(); } template @@ -403,6 +409,13 @@ void State::initialize_omp() { // Utility //------------------------------------------------------------------------- +template +void State::apply_global_phase() { + if (BaseState::has_global_phase_) { + BaseState::qreg_.apply_diagonal_matrix(0, {BaseState::global_phase_, BaseState::global_phase_}); + } +} + template size_t State::required_memory_mb(uint_t num_qubits, const std::vector &ops) @@ -413,6 +426,7 @@ size_t State::required_memory_mb(uint_t num_qubits, template void State::set_config(const json_t &config) { + BaseState::set_config(config); // Set threshold for truncating snapshots JSON::get_value(json_chop_threshold_, "zero_threshold", config); diff --git a/src/simulators/unitary/unitary_state.hpp b/src/simulators/unitary/unitary_state.hpp index e3102a4f30..b852bb3798 100755 --- a/src/simulators/unitary/unitary_state.hpp +++ b/src/simulators/unitary/unitary_state.hpp @@ -156,6 +156,9 @@ class State : public Base::State { // Config Settings //----------------------------------------------------------------------- + // Apply the global phase + void apply_global_phase(); + // OpenMP qubit threshold int omp_qubit_threshold_ = 6; @@ -253,6 +256,8 @@ size_t State::required_memory_mb( template void State::set_config(const json_t &config) { + BaseState::set_config(config); + // Set OMP threshold for state update functions JSON::get_value(omp_qubit_threshold_, "unitary_parallel_threshold", config); @@ -266,6 +271,7 @@ void State::initialize_qreg(uint_t num_qubits) { initialize_omp(); BaseState::qreg_.set_num_qubits(num_qubits); BaseState::qreg_.initialize(); + apply_global_phase(); } template @@ -281,6 +287,7 @@ void State::initialize_qreg( BaseState::qreg_.set_num_qubits(num_qubits); const size_t sz = 1ULL << BaseState::qreg_.size(); BaseState::qreg_.initialize_from_data(unitary.data(), sz); + apply_global_phase(); } template @@ -295,6 +302,7 @@ void State::initialize_qreg( initialize_omp(); BaseState::qreg_.set_num_qubits(num_qubits); BaseState::qreg_.initialize_from_matrix(unitary); + apply_global_phase(); } template @@ -425,6 +433,15 @@ void State::apply_snapshot(const Operations::Op &op, } } +template +void State::apply_global_phase() { + if (BaseState::has_global_phase_) { + BaseState::qreg_.apply_diagonal_matrix( + {0}, {BaseState::global_phase_, BaseState::global_phase_} + ); + } +} + //------------------------------------------------------------------------------ } // namespace QubitUnitary } // end namespace AER diff --git a/test/terra/backends/statevector_simulator/statevector_basics.py b/test/terra/backends/statevector_simulator/statevector_basics.py index 7ee187488f..a275bab78e 100644 --- a/test/terra/backends/statevector_simulator/statevector_basics.py +++ b/test/terra/backends/statevector_simulator/statevector_basics.py @@ -13,6 +13,8 @@ StatevectorSimulator Integration Tests """ +from numpy import exp, pi + from test.terra.reference import ref_measure from test.terra.reference import ref_reset from test.terra.reference import ref_initialize @@ -23,8 +25,7 @@ from test.terra.reference import ref_unitary_gate from test.terra.reference import ref_diagonal_gate -from qiskit import execute -from qiskit.compiler import assemble +from qiskit import execute, transpile, assemble from qiskit.providers.aer import StatevectorSimulator @@ -1260,3 +1261,25 @@ def test_cu3_gate_deterministic_waltz_basis_gates(self): result = job.result() self.assertSuccess(result) self.compare_statevector(result, circuits, targets) + + # --------------------------------------------------------------------- + # Test global phase + # --------------------------------------------------------------------- + + def test_qobj_global_phase(self): + """Test qobj global phase.""" + + circuits = ref_1q_clifford.h_gate_circuits_nondeterministic( + final_measure=False) + targets = ref_1q_clifford.h_gate_statevector_nondeterministic() + + qobj = assemble(transpile(circuits, self.SIMULATOR), + shots=1, backend_options=self.BACKEND_OPTS) + # Set global phases + for i, _ in enumerate(circuits): + global_phase = (-1) ** i * (pi / 4) + qobj.experiments[i].header.global_phase = global_phase + targets[i] = exp(1j * global_phase) * targets[i] + result = self.SIMULATOR.run(qobj).result() + self.assertSuccess(result) + self.compare_statevector(result, circuits, targets, global_phase=True) diff --git a/test/terra/backends/unitary_simulator/unitary_basics.py b/test/terra/backends/unitary_simulator/unitary_basics.py index bc75553f41..d79048eaa1 100644 --- a/test/terra/backends/unitary_simulator/unitary_basics.py +++ b/test/terra/backends/unitary_simulator/unitary_basics.py @@ -13,15 +13,15 @@ UnitarySimulator Integration Tests """ -import unittest -from test.terra import common +from numpy import exp, pi + from test.terra.reference import ref_1q_clifford from test.terra.reference import ref_2q_clifford from test.terra.reference import ref_non_clifford from test.terra.reference import ref_unitary_gate from test.terra.reference import ref_diagonal_gate -from qiskit import execute +from qiskit import execute, assemble, transpile from qiskit.providers.aer import UnitarySimulator @@ -588,7 +588,6 @@ def test_cz_gate_nondeterministic_minimal_basis_gates(self): self.assertSuccess(result) self.compare_unitary(result, circuits, targets) - # --------------------------------------------------------------------- # Test cu1 gate # --------------------------------------------------------------------- @@ -1064,39 +1063,6 @@ def test_diagonal_gate(self): self.assertSuccess(result) self.compare_unitary(result, circuits, targets) - # --------------------------------------------------------------------- - # Test cswap-gate (Fredkin) - # --------------------------------------------------------------------- - def test_cswap_gate_deterministic_default_basis_gates(self): - """Test cswap-gate circuits compiling to backend default basis_gates.""" - circuits = ref_non_clifford.cswap_gate_circuits_deterministic( - final_measure=False) - targets = ref_non_clifford.cswap_gate_unitary_deterministic() - job = execute(circuits, - self.SIMULATOR, - shots=1, - backend_options=self.BACKEND_OPTS) - result = job.result() - self.assertSuccess(result) - self.compare_unitary(result, circuits, targets) - - # --------------------------------------------------------------------- - # Test cu1 gate - # --------------------------------------------------------------------- - def test_cu1_gate_nondeterministic_waltz_basis_gates(self): - """Test cu1-gate gate circuits compiling to u1,u2,u3,cx""" - circuits = ref_non_clifford.cu1_gate_circuits_nondeterministic( - final_measure=False) - targets = ref_non_clifford.cu1_gate_unitary_nondeterministic() - job = execute(circuits, - self.SIMULATOR, - shots=1, - basis_gates=['u1', 'u2', 'u3', 'cx'], - backend_options=self.BACKEND_OPTS) - result = job.result() - self.assertSuccess(result) - self.compare_unitary(result, circuits, targets) - def test_cswap_gate_deterministic_minimal_basis_gates(self): """Test cswap-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.cswap_gate_circuits_deterministic( @@ -1111,20 +1077,6 @@ def test_cswap_gate_deterministic_minimal_basis_gates(self): self.assertSuccess(result) self.compare_unitary(result, circuits, targets) - def test_cu1_gate_nondeterministic_minimal_basis_gates(self): - """"Test cu1-gate gate circuits compiling to u3,cx""" - circuits = ref_non_clifford.cu1_gate_circuits_nondeterministic( - final_measure=False) - targets = ref_non_clifford.cu1_gate_unitary_nondeterministic() - job = execute(circuits, - self.SIMULATOR, - shots=1, - basis_gates=['u3', 'cx'], - backend_options=self.BACKEND_OPTS) - result = job.result() - self.assertSuccess(result) - self.compare_unitary(result, circuits, targets) - def test_cswap_gate_deterministic_waltz_basis_gates(self): """Test cswap-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.cswap_gate_circuits_deterministic( @@ -1152,19 +1104,6 @@ def test_cswap_gate_nondeterministic_default_basis_gates(self): self.assertSuccess(result) self.compare_unitary(result, circuits, targets) - def test_cu1_gate_nondeterministic_default_basis_gates(self): - """Test cu1-gate gate circuits compiling to default basis""" - circuits = ref_non_clifford.cu1_gate_circuits_nondeterministic( - final_measure=False) - targets = ref_non_clifford.cu1_gate_unitary_nondeterministic() - job = execute(circuits, - self.SIMULATOR, - shots=1, - backend_options=self.BACKEND_OPTS) - result = job.result() - self.assertSuccess(result) - self.compare_unitary(result, circuits, targets) - def test_cswap_gate_nondeterministic_minimal_basis_gates(self): """Test cswap-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.cswap_gate_circuits_nondeterministic( @@ -1192,3 +1131,25 @@ def test_cswap_gate_nondeterministic_waltz_basis_gates(self): result = job.result() self.assertSuccess(result) self.compare_unitary(result, circuits, targets) + + # --------------------------------------------------------------------- + # Test global phase + # --------------------------------------------------------------------- + + def test_qobj_global_phase(self): + """Test qobj global phase.""" + + circuits = ref_1q_clifford.h_gate_circuits_nondeterministic( + final_measure=False) + targets = ref_1q_clifford.h_gate_unitary_nondeterministic() + + qobj = assemble(transpile(circuits, self.SIMULATOR), + shots=1, backend_options=self.BACKEND_OPTS) + # Set global phases + for i, _ in enumerate(circuits): + global_phase = (-1) ** i * (pi / 4) + qobj.experiments[i].header.global_phase = global_phase + targets[i] = exp(1j * global_phase) * targets[i] + result = self.SIMULATOR.run(qobj).result() + self.assertSuccess(result) + self.compare_unitary(result, circuits, targets, global_phase=True)