Skip to content

Commit

Permalink
Add qobj header global phase support (#931)
Browse files Browse the repository at this point in the history
* Add global phase parsing from qobj

* Add global phase to statevector and unitary states

* Add reno

* Add global phase tests

Co-authored-by: Victor Villar <59838221+vvilpas@users.noreply.github.com>
  • Loading branch information
chriseclectic and vvilpas authored Sep 21, 2020
1 parent 18c736c commit 8f59ad6
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 69 deletions.
7 changes: 7 additions & 0 deletions releasenotes/notes/global-phase-9fc2105dac076f48.yaml
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions src/controllers/qasm_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/controllers/statevector_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/controllers/unitary_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/framework/circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 20 additions & 2 deletions src/simulators/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//-----------------------------------------------------------------------

Expand Down Expand Up @@ -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
Expand All @@ -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;
};


Expand All @@ -224,6 +231,17 @@ void State<state_t>::set_config(const json_t &config) {
(ignore_argument)config;
}

template <class state_t>
void State<state_t>::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 <class state_t>
std::vector<reg_t> State<state_t>::sample_measure(const reg_t &qubits,
Expand Down
14 changes: 14 additions & 0 deletions src/simulators/statevector/statevector_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ class State : public Base::State<statevec_t> {
// Config Settings
//-----------------------------------------------------------------------

// Apply the global phase
void apply_global_phase();

// OpenMP qubit threshold
int omp_qubit_threshold_ = 14;

Expand Down Expand Up @@ -367,6 +370,7 @@ void State<statevec_t>::initialize_qreg(uint_t num_qubits) {
initialize_omp();
BaseState::qreg_.set_num_qubits(num_qubits);
BaseState::qreg_.initialize();
apply_global_phase();
}

template <class statevec_t>
Expand All @@ -379,6 +383,7 @@ void State<statevec_t>::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 <class statevec_t>
Expand All @@ -390,6 +395,7 @@ void State<statevec_t>::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 <class statevec_t>
Expand All @@ -403,6 +409,13 @@ void State<statevec_t>::initialize_omp() {
// Utility
//-------------------------------------------------------------------------

template <class statevec_t>
void State<statevec_t>::apply_global_phase() {
if (BaseState::has_global_phase_) {
BaseState::qreg_.apply_diagonal_matrix(0, {BaseState::global_phase_, BaseState::global_phase_});
}
}

template <class statevec_t>
size_t State<statevec_t>::required_memory_mb(uint_t num_qubits,
const std::vector<Operations::Op> &ops)
Expand All @@ -413,6 +426,7 @@ size_t State<statevec_t>::required_memory_mb(uint_t num_qubits,

template <class statevec_t>
void State<statevec_t>::set_config(const json_t &config) {
BaseState::set_config(config);

// Set threshold for truncating snapshots
JSON::get_value(json_chop_threshold_, "zero_threshold", config);
Expand Down
17 changes: 17 additions & 0 deletions src/simulators/unitary/unitary_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ class State : public Base::State<unitary_matrix_t> {
// Config Settings
//-----------------------------------------------------------------------

// Apply the global phase
void apply_global_phase();

// OpenMP qubit threshold
int omp_qubit_threshold_ = 6;

Expand Down Expand Up @@ -253,6 +256,8 @@ size_t State<unitary_matrix_t>::required_memory_mb(

template <class unitary_matrix_t>
void State<unitary_matrix_t>::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);

Expand All @@ -266,6 +271,7 @@ void State<unitary_matrix_t>::initialize_qreg(uint_t num_qubits) {
initialize_omp();
BaseState::qreg_.set_num_qubits(num_qubits);
BaseState::qreg_.initialize();
apply_global_phase();
}

template <class unitary_matrix_t>
Expand All @@ -281,6 +287,7 @@ void State<unitary_matrix_t>::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 <class unitary_matrix_t>
Expand All @@ -295,6 +302,7 @@ void State<unitary_matrix_t>::initialize_qreg(
initialize_omp();
BaseState::qreg_.set_num_qubits(num_qubits);
BaseState::qreg_.initialize_from_matrix(unitary);
apply_global_phase();
}

template <class unitary_matrix_t>
Expand Down Expand Up @@ -425,6 +433,15 @@ void State<unitary_matrix_t>::apply_snapshot(const Operations::Op &op,
}
}

template <class unitary_matrix_t>
void State<unitary_matrix_t>::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
Expand Down
27 changes: 25 additions & 2 deletions test/terra/backends/statevector_simulator/statevector_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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


Expand Down Expand Up @@ -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)
89 changes: 25 additions & 64 deletions test/terra/backends/unitary_simulator/unitary_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
# ---------------------------------------------------------------------
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)

0 comments on commit 8f59ad6

Please sign in to comment.