Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add qobj header global phase support #931

Merged
merged 9 commits into from
Sep 21, 2020
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)