Skip to content

Commit

Permalink
Set different seed for each sampling in AerStatevector (#1663)
Browse files Browse the repository at this point in the history
* update seed for multiple calls of sample_memory

* correct seed_simulator update in AerStatevector

* set seed in AerState explicitly and reuse AerState in AerStatevector for sampling

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
hhorii and mergify[bot] authored Dec 2, 2022
1 parent 58b86e1 commit a71f17f
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 15 deletions.
2 changes: 2 additions & 0 deletions qiskit_aer/backends/wrappers/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ PYBIND11_MODULE(controller_wrappers, m) {
aer_state.def("configure", &AER::AerState::configure);
aer_state.def("allocate_qubits", &AER::AerState::allocate_qubits);
aer_state.def("reallocate_qubits", &AER::AerState::reallocate_qubits);
aer_state.def("set_random_seed", &AER::AerState::set_random_seed);
aer_state.def("set_seed", &AER::AerState::set_seed);
aer_state.def("clear", &AER::AerState::clear);
aer_state.def("num_of_qubits", &AER::AerState::num_of_qubits);

Expand Down
20 changes: 20 additions & 0 deletions qiskit_aer/quantum_info/states/aer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ def __init__(self, **kwargs):
if 'method' not in kwargs:
self.configure('method', 'statevector')

def renew(self):
"""Renew AerState for reuse"""
self._assert_closed()
self._state = _STATE.INITIALIZING
self._init_data = None
self._moved_data = None
self._last_qubit = -1

def _assert_initializing(self):
if self._state != _STATE.INITIALIZING:
raise AerError('AerState was already initialized.')
Expand All @@ -84,6 +92,10 @@ def _assert_mapped_or_moved(self):
if self._state == _STATE.CLOSED:
raise AerError('AerState has already been closed.')

def _assert_closed(self):
if self._state != _STATE.CLOSED:
raise AerError('AerState is not closed.')

def _allocated(self):
if self._state != _STATE.INITIALIZING:
raise AerError('unexpected state transition: {self._state}->{_STATE.ALLOCATED}')
Expand All @@ -102,6 +114,7 @@ def _moved(self):
def _closed(self):
if self._state not in (_STATE.MOVED, _STATE.MAPPED):
raise AerError('unexpected state transition: {self._state}->{_STATE.CLOSED}')
self._state = _STATE.CLOSED

def configure(self, key, value):
"""configure AerState with options of `AerSimulator`."""
Expand Down Expand Up @@ -155,6 +168,13 @@ def _initialize_with_ndarray(self, data, copy):

self._last_qubit = num_of_qubits - 1

def set_seed(self, value=None):
"""initialize seed with a specified value"""
if value is None:
self._native_state.set_random_seed()
else:
self._native_state.set_seed(value)

def close(self):
"""Safely release all releated memory."""
self._assert_allocated_or_mapped_or_moved()
Expand Down
11 changes: 10 additions & 1 deletion qiskit_aer/quantum_info/states/aer_statevector.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ def __init__(self, data, dims=None, **configs):
self._result = None
self._configs = configs

def seed(self, value=None):
"""Set the seed for the quantum state RNG."""
if value is None or isinstance(value, int):
self._aer_state.set_seed(value)
else:
raise AerError(f'This seed is not supported: type={value.__class__}, value={value}')

def _last_result(self):
if self._result is None:
self._result = self._aer_state.last_result()
Expand Down Expand Up @@ -117,8 +124,10 @@ def sample_memory(self, shots, qargs=None):
else:
qubits = np.array(qargs)
self._aer_state.close()
self._aer_state = AerState(**self._aer_state.configuration())

self._aer_state.renew()
self._aer_state.initialize(self._data, copy=False)

samples = self._aer_state.sample_memory(qubits, shots)
self._data = self._aer_state.move_to_ndarray()
return samples
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Previously seed is not initialized in AerStatevector and then sampled results
are always same. With this commit, a seed is initialized for each sampling
and sampled results can be vary.
32 changes: 18 additions & 14 deletions src/controllers/state_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class AerState {
//-----------------------------------------------------------------------
// Constructors
//-----------------------------------------------------------------------
AerState() = default;
AerState() { set_random_seed(); };

virtual ~AerState() { };

Expand All @@ -125,9 +125,6 @@ class AerState {
// configure custatevec enabled or not
virtual bool set_custatevec(const bool& enabled);

// configure seed
virtual bool set_seed_simulator(const int& seed);

// configure number of threads to update state
virtual bool set_parallel_state_update(const uint_t& parallel_state_update);

Expand All @@ -152,7 +149,7 @@ class AerState {
// Return a number of qubits.
virtual uint_t num_of_qubits() const { return num_of_qubits_; };

// Clear all the configurations
// Clear state and buffered ops
virtual void clear();

virtual ExperimentResult& last_result() { return last_result_; };
Expand All @@ -164,6 +161,12 @@ class AerState {
// Initialize state with given configuration
void initialize();

// Initialize random number genrator
void set_random_seed();

// Initialize random number genrator with a given seed
void set_seed(int_t seed);

// Allocate qubits with inputted complex array
// method must be statevector and the length of the array must be 2^{num_qubits}
// given data will not be freed in this class
Expand Down Expand Up @@ -437,7 +440,7 @@ void AerState::configure(const std::string& _key, const std::string& _value) {
} else if (key == "custatevec_enable") {
error = !set_custatevec("true" == value);
} else if (key == "seed_simulator") {
error = !set_seed_simulator(std::stoi(value));
set_seed(std::stoi(value));
} else if (key == "parallel_state_update") {
error = !set_parallel_state_update(std::stoul(value));
} else if (key == "fusion_max_qubit") {
Expand Down Expand Up @@ -519,12 +522,6 @@ bool AerState::set_custatevec(const bool& enabled) {
return true;
};

bool AerState::set_seed_simulator(const int& seed) {
assert_not_initialized();
seed_ = seed;
return true;
};

bool AerState::set_parallel_state_update(const uint_t& parallel_state_update) {
assert_not_initialized();
parallel_state_update_ = parallel_state_update;
Expand Down Expand Up @@ -661,13 +658,21 @@ void AerState::initialize() {

state_->initialize_qreg(num_of_qubits_);
state_->initialize_creg(num_of_qubits_, num_of_qubits_);
rng_.set_seed(seed_);

clear_ops();

initialized_ = true;
};

void AerState::set_random_seed() {
set_seed(std::random_device()());
};

void AerState::set_seed(int_t seed) {
seed_ = seed;
rng_.set_seed(seed);
};

reg_t AerState::allocate_qubits(uint_t num_qubits) {
assert_not_initialized();
reg_t ret;
Expand Down Expand Up @@ -712,7 +717,6 @@ reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t* data, bo
state->initialize_creg(num_of_qubits_, num_of_qubits_);
state->initialize_statevector(num_of_qubits_, std::move(qv));
state_ = state;
rng_.set_seed(seed_);
initialized_ = true;
reg_t ret;
ret.reserve(num_of_qubits);
Expand Down
16 changes: 16 additions & 0 deletions test/terra/states/test_aer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,22 @@ def test_probabilities(self):
for idx in range(0, 2**5):
self.assertAlmostEqual(actual[idx], expected[idx])

def test_set_seed(self):
"""Test set_seed"""
init_state = random_statevector(2**5, seed=111)

state = AerState(seed_simulator=11111)
state.allocate_qubits(5)
state.initialize(init_state.data)
sample1 = state.sample_counts()
sample2 = state.sample_counts()

state.set_seed(11111)
sample3 = state.sample_counts()

self.assertNotEqual(sample1, sample2)
self.assertEqual(sample1, sample3)

def test_sampling(self):
"""Test sampling"""
init_state = random_statevector(2**5, seed=111)
Expand Down
55 changes: 55 additions & 0 deletions test/terra/states/test_aer_statevector.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,61 @@ def test_qv(self):
for e, s in zip(expected, state):
self.assertAlmostEqual(e, s)

def test_sample_randomness(self):
"""Test randomness of results of sample_counts """
circ = QuantumVolume(5, seed=1111)

state = AerStatevector(circ, seed_simulator=1)

shots = 1024
counts0 = state.sample_counts(shots, qargs=range(5))
counts1 = state.sample_counts(shots, qargs=range(5))

self.assertNotEqual(counts0, counts1)

state = AerStatevector(circ, seed_simulator=10)

shots = 1024
counts2 = state.sample_counts(shots, qargs=range(5))
counts3 = state.sample_counts(shots, qargs=range(5))

self.assertNotEqual(counts2, counts3)

self.assertNotEqual(counts0, counts2)
self.assertNotEqual(counts1, counts2)

def test_sample_with_same_seed(self):
"""Test randomness of results of sample_counts """
circ = QuantumVolume(5, seed=1111)

state = AerStatevector(circ, seed_simulator=1)

shots = 1024
counts0 = state.sample_counts(shots, qargs=range(5))
counts1 = state.sample_counts(shots, qargs=range(5))

self.assertNotEqual(counts0, counts1)

state = AerStatevector(circ, seed_simulator=1)

shots = 1024
counts2 = state.sample_counts(shots, qargs=range(5))
counts3 = state.sample_counts(shots, qargs=range(5))

self.assertNotEqual(counts2, counts3)
self.assertEqual(counts0, counts2)
self.assertEqual(counts1, counts3)

shots = 1024
state.seed(1)
counts4 = state.sample_counts(shots, qargs=range(5))
counts5 = state.sample_counts(shots, qargs=range(5))

self.assertNotEqual(counts4, counts5)
self.assertEqual(counts0, counts4)
self.assertEqual(counts1, counts5)


def test_method_and_device_properties(self):
"""Test method and device properties"""
circ = QuantumVolume(5, seed=1111)
Expand Down

0 comments on commit a71f17f

Please sign in to comment.