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 AerStatevector #1590

Merged
merged 76 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ecd5b59
initial input of aer runtime
hhorii Jun 14, 2022
e607173
support methods and refactor state classes
hhorii Jun 16, 2022
9463018
add AerStatevector that inherits quantum_info.states.Statevector
hhorii Jun 28, 2022
5258540
fix pylint errors
hhorii Jun 28, 2022
7deb3cc
set default parallelization and expose last execution result
hhorii Jun 29, 2022
b4437d7
test mps
hhorii Jun 29, 2022
f8efab0
fix configure issue
hhorii Jun 29, 2022
180b3a8
Merge remote-tracking branch 'upstream/main' into add_aer_runtime
hhorii Jul 7, 2022
3a27edc
cast to correct type of configuration
hhorii Jul 7, 2022
7a7f88b
call set_max_matrix_qubits in AerState
hhorii Jul 8, 2022
67472e9
support AerStatevector.evolve
hhorii Jul 11, 2022
da30ee8
resolve segfault and bugs in state_chunk
hhorii Jul 13, 2022
f81d111
add chunk distribution support
doichanj Jul 15, 2022
73e39ea
Merge pull request #5 from doichanj/add_aer_runtime
hhorii Jul 15, 2022
2d6b0b0
Merge branch 'main' into add_aer_runtime
hhorii Jul 16, 2022
38ed0ed
Merge remote-tracking branch 'upstream/main' into add_aer_runtime
hhorii Aug 9, 2022
000e53d
support ndarray initialization for AerStatevector
hhorii Aug 9, 2022
c4116cb
Merge branch 'main' into add_aer_runtime
hhorii Aug 15, 2022
a07bdd8
Merge branch 'main' into add_aer_runtime
hhorii Aug 22, 2022
82c5be9
Merge branch 'main' into add_aer_runtime
hhorii Aug 22, 2022
2c89336
take python parts
hhorii Aug 24, 2022
55c7a70
take a remaining python part
hhorii Aug 24, 2022
2a09297
add runtime library test
hhorii Aug 27, 2022
54f0991
add release note
hhorii Aug 27, 2022
ed0cbfb
Correctly run test in CI
hhorii Aug 27, 2022
af53035
add AerState
hhorii Aug 27, 2022
7a4759d
Merge remote-tracking branch 'upstream/main' into add_aer_runtime_cpp
hhorii Aug 31, 2022
6301a26
Merge remote-tracking branch 'upstream/main' into add_aer_state
hhorii Aug 31, 2022
1f32b38
change tests to align with qiskit_aer
hhorii Aug 31, 2022
06f7869
add init files for quantum_info
hhorii Aug 31, 2022
35f64ec
add apache header to adding files
hhorii Aug 31, 2022
b7e07da
fix lint errors
hhorii Aug 31, 2022
82e98fa
take duplicated private
hhorii Aug 31, 2022
1c288bc
Merge branch 'add_aer_state' into add_aer_statevector
hhorii Aug 31, 2022
6cd750e
add AerStatevector
hhorii Sep 1, 2022
767b621
Merge remote-tracking branch 'upstream/main' into add_aer_state
hhorii Sep 1, 2022
b17df04
Merge branch 'add_aer_state' into add_aer_statevector
hhorii Sep 1, 2022
af82155
add more tests
hhorii Sep 1, 2022
5b7e78f
add apply_global_phase
hhorii Sep 1, 2022
67ae2e2
add apply_global_phase
hhorii Sep 1, 2022
a114a69
support global phase
hhorii Sep 1, 2022
068d8ac
Merge branch 'main' into add_aer_state
hhorii Sep 1, 2022
d1d6034
Merge branch 'main' into add_aer_statevector
hhorii Sep 1, 2022
7065a7a
resolve lint errors
hhorii Sep 1, 2022
f6160cf
take a release note. use explicit state management. revert memory map…
hhorii Sep 2, 2022
9149f63
allow configure in intialization
hhorii Sep 2, 2022
5bb43cf
refactor state classes by deleting unused methods. enabled mapping on…
hhorii Sep 2, 2022
5d2bc2a
fix lint error
hhorii Sep 2, 2022
11b8c0b
Merge branch 'add_aer_state' into add_aer_statevector
hhorii Sep 2, 2022
9db4613
support copy of qubitvector
hhorii Sep 2, 2022
b3cf33e
Merge remote-tracking branch 'upstream/main' into add_aer_state
hhorii Sep 2, 2022
40b6c05
add move assignement operator to qubitvector
hhorii Sep 2, 2022
0cc642a
fix bug in sampling
hhorii Sep 2, 2022
32ac4fe
Merge branch 'add_aer_state' into add_aer_statevector
hhorii Sep 2, 2022
5e444f5
add docs of AerStatevector
hhorii Sep 2, 2022
93d2c9b
fix lint error
hhorii Sep 2, 2022
40ab386
support expectation value calculation
hhorii Sep 5, 2022
9d5ecec
support sample_memory and sample_counts
hhorii Sep 5, 2022
651a040
use original expval in AerStatevector
hhorii Sep 5, 2022
36802a6
change expval api
hhorii Sep 5, 2022
dba0afc
delete expval from AerStatevector because of its performance
hhorii Sep 5, 2022
2555383
fix lint errors
hhorii Sep 5, 2022
24a02f7
Update qiskit_aer/quantum_info/__init__.py
hhorii Sep 6, 2022
0984886
Merge branch 'main' into add_aer_state
hhorii Sep 6, 2022
27688d9
correct test cases and modified comments for AerState
hhorii Sep 6, 2022
d5288b4
Merge branch 'add_aer_state' into add_aer_statevector
hhorii Sep 6, 2022
ccba166
Merge remote-tracking branch 'upstream/main' into add_aer_statevector
hhorii Sep 7, 2022
9a96213
update test for AerStatevector
hhorii Sep 8, 2022
87e0f05
correct packaging
hhorii Sep 8, 2022
44d7ac1
return copy of configuration in AerState.
hhorii Sep 8, 2022
9994a04
refactor AerStatevector and its test
hhorii Sep 9, 2022
9d4c228
fix lint error
hhorii Sep 9, 2022
361274e
disable expval in state_controller
hhorii Sep 9, 2022
09c1aa4
Merge branch 'main' into add_aer_statevector
hhorii Sep 13, 2022
81a63f6
change copy behavior to use deepcopy and __eq__ to use Stavector.__eq…
hhorii Sep 13, 2022
1a51278
Merge branch 'add_aer_statevector' of github.com:hhorii/qiskit-aer in…
hhorii Sep 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/apidocs/aer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Qiskit Aer API Reference
aer_primitives
aer_pulse
aer_utils
aer_quantum_info
parallel
2 changes: 1 addition & 1 deletion docs/apidocs/aer_primitives.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. _aer-primitives:

.. automodule:: qiskit.providers.aer.primitives
.. automodule:: qiskit_aer.primitives
:no-members:
:no-inherited-members:
:no-special-members:
6 changes: 6 additions & 0 deletions docs/apidocs/aer_quantum_info.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. _aer-quantum_info:

.. automodule:: qiskit_aer.quantum_info
:no-members:
:no-inherited-members:
:no-special-members:
1 change: 1 addition & 0 deletions qiskit_aer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
from .backends import *
from . import library
from . import pulse
from . import quantum_info
from . import noise
from . import utils
from .version import __version__
Expand Down
4 changes: 2 additions & 2 deletions qiskit_aer/backends/wrappers/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ PYBIND11_MODULE(controller_wrappers, m) {
else
return state.probabilities(qubits);
}, py::arg("qubits") = reg_t());
aer_state.def("sample_measure", &AER::AerState::sample_measure);
aer_state.def("expval_pauli", &AER::AerState::expval_pauli);
aer_state.def("sample_memory", &AER::AerState::sample_memory);
aer_state.def("sample_counts", &AER::AerState::sample_counts);

}
13 changes: 13 additions & 0 deletions qiskit_aer/quantum_info/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,17 @@
=================================================
Aer Quantum Info (:mod:`qiskit_aer.quantum_info`)
=================================================

.. currentmodule:: qiskit_aer.quantum_info

States
======

.. autosummary::
:toctree: ../stubs/

AerStatevector

"""

from .states import AerStatevector
8 changes: 3 additions & 5 deletions qiskit_aer/quantum_info/states/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
=============================================
State (:mod:`qiskit_aer.quantum_info.states`)
=============================================
"""
"""Aer Quantum States."""

from .aer_statevector import AerStatevector
34 changes: 25 additions & 9 deletions qiskit_aer/quantum_info/states/aer_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ def __init__(self, **kwargs):
"""State that handles cpp quantum state safely"""
self._state = _STATE.INITIALIZING
self._native_state = AerStateWrapper()
self._method = 'statevector'
self._init_data = None
self._moved_data = None
self._last_qubit = -1
self._configs = {}

for key, value in kwargs.items():
self.configure(key, value)

if 'method' not in kwargs:
self.configure('method', 'statevector')

def _assert_initializing(self):
if self._state != _STATE.INITIALIZING:
raise AerError('AerState was already initialized.')
Expand Down Expand Up @@ -108,8 +111,13 @@ def configure(self, key, value):
raise AerError('AerState is configured with a str key')
if not isinstance(value, str):
value = str(value)
self._configs[key] = value
self._native_state.configure(key, value)

def configuration(self):
"""return configuration"""
return self._configs.copy()

def initialize(self, data=None, copy=True):
"""initialize state."""
self._assert_initializing()
Expand All @@ -120,7 +128,7 @@ def initialize(self, data=None, copy=True):
elif isinstance(data, np.ndarray):
self._initialize_with_ndarray(data, copy)
else:
raise AerError('unsupported init data.')
raise AerError(f'unsupported init data: {data.__class__}')

def _initialize_with_ndarray(self, data, copy):
if AerState._is_in_use(data) and not copy:
Expand All @@ -131,7 +139,7 @@ def _initialize_with_ndarray(self, data, copy):
raise AerError('length of init data must be power of two')

if (isinstance(data, np.ndarray) and
self._method == 'statevector' and
self._configs['method'] == 'statevector' and
self._native_state.initialize_statevector(num_of_qubits, data, copy)):
if not copy:
self._init_data = data
Expand Down Expand Up @@ -186,7 +194,7 @@ def allocate_qubits(self, num_of_qubits):
self._last_qubit = allocated[len(allocated) - 1]

def _assert_in_allocated_qubits(self, qubit):
if isinstance(qubit, list):
if hasattr(qubit, '__iter__'):
for q in qubit:
self._assert_in_allocated_qubits(q)
elif qubit < 0 or qubit > self._last_qubit:
Expand All @@ -195,7 +203,6 @@ def _assert_in_allocated_qubits(self, qubit):
@property
def num_qubits(self):
"""return a number of allocate qubits."""
self._assert_initializing()
return self._last_qubit + 1

def flush(self):
Expand All @@ -212,8 +219,8 @@ def last_result(self):
return self._native_state.last_result()

def apply_global_phase(self, phase):
"""apply global phase."""
self._assert_allocated_or_mapped_or_moved()
"""apply global phase"""
self._assert_allocated_or_mapped()
self._native_state.apply_global_phase(phase)

def apply_unitary(self, qubits, data):
Expand Down Expand Up @@ -353,11 +360,20 @@ def probabilities(self, qubits=None):
# retrieve probability
return self._native_state.probabilities(qubits)

def sample_measure(self, qubits=None, shots=1024):
def sample_counts(self, qubits=None, shots=1024):
"""samples all the qubits."""
self._assert_allocated_or_mapped()
if qubits is None:
qubits = range(self._last_qubit + 1)
else:
self._assert_in_allocated_qubits(qubits)
return self._native_state.sample_counts(qubits, shots)

def sample_memory(self, qubits=None, shots=1024):
"""samples all the qubits."""
self._assert_allocated_or_mapped()
if qubits is None:
qubits = range(self._last_qubit + 1)
else:
self._assert_in_allocated_qubits(qubits)
return self._native_state.sample_measure(qubits, shots)
return self._native_state.sample_memory(qubits, shots)
226 changes: 226 additions & 0 deletions qiskit_aer/quantum_info/states/aer_statevector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019, 2020, 2021, 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Statevector quantum state class.
"""
import copy
import numpy as np

from qiskit.circuit import QuantumCircuit, Instruction
from qiskit.quantum_info.states import Statevector

from qiskit_aer import AerSimulator
from .aer_state import AerState
from ...backends.aerbackend import AerError


class AerStatevector(Statevector):
"""AerStatevector class

This class inherits :class:`Statevector`, which stores probability amplitudes
in its `ndarray`. class:`AerStatevector` generates this `ndarray` by using the
same runtime with :class:`AerSimulator`.
"""

def __init__(self, data, dims=None, **configs):
"""
Args:
data (np.array or list or Statevector or QuantumCircuit or qiskit.circuit.Instruction):
Data from which the statevector can be constructed. This can be either a complex
vector, another statevector or a ``QuantumCircuit`` or ``Instruction``
(``Operator`` is not supportted in the current implementation). If the data is
a circuit or instruction, the statevector is constructed by assuming that all
qubits are initialized to the zero state.
dims (int or tuple or list): Optional. The subsystem dimension of
the state (See additional information).
configs (kwargs): configurations of :class:`AerSimulator`. `method` configuration must
be `statevector` or `matrix_product_state`.

Raises:
AerError: if input data is not valid.

Additional Information:
The ``dims`` kwarg is used to ``Statevector`` constructor.

"""
if '_aer_state' in configs:
self._aer_state = configs.pop('_aer_state')
else:
if 'method' not in configs:
configs['method'] = 'statevector'
elif configs['method'] not in ('statevector', 'matrix_product_state'):
method = configs['method']
raise AerError(f'Method {method} is not supported')
if isinstance(data, (QuantumCircuit, Instruction)):
data, aer_state = AerStatevector._from_instruction(data, None, configs)
elif isinstance(data, list):
data, aer_state = AerStatevector._from_ndarray(np.array(data, dtype=complex),
configs)
elif isinstance(data, np.ndarray):
data, aer_state = AerStatevector._from_ndarray(data, configs)
elif isinstance(data, AerStatevector):
aer_state = data._aer_state
if dims is None:
dims = data._op_shape._dims_l
data = data._data.copy()
else:
raise AerError(f'Input data is not supported: type={data.__class__}, data={data}')

self._aer_state = aer_state

super().__init__(data, dims=dims)

self._result = None
self._configs = configs

def _last_result(self):
if self._result is None:
self._result = self._aer_state.last_result()
return self._result

def metadata(self):
"""Return result metadata of an operation that executed lastly."""
if self._last_result() is None:
raise AerError('AerState was not used and metdata does not exist.')
return self._last_result()['metadata']

def __copy__(self):
ret = AerStatevector(self._data.copy(), **self._configs)
ret._op_shape = self._op_shape
ret._rng_generator = self._rng_generator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks dangerous - it'll cause two objects to hold references to the same rng instance, so they'll both be advancing state of the same thing. I don't know what's most appropriate here, but I'd expect maybe a copy of this?

return ret

def __deepcopy__(self, _memo=None):
ret = AerStatevector(self._data.copy(), **self._configs)
ret._op_shape = copy.deepcopy(self._op_shape)
ret._rng_generator = copy.deepcopy(self._rng_generator)
return ret

def __eq__(self, other):
return (isinstance(other, AerStatevector) and self.dims() == other.dims() and
np.allclose(self._data, other._data, rtol=self.rtol, atol=self.atol))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isinstance check here is likely too strict - it'll cause equality not to be commutative when comparing with a raw Statevector instance. Statevector.__eq__(AerStatevector) (for the same data) will be true (because issubclass(AerStatevector, Statevector), but AerStatevector.__eq__(Statevector) will be false.

That said, the fuzzy np.allclose already breaks equality transitivity (a == b and b == c doesn't imply a == c), but that's the fault of the base class.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that custom __eq__ does not need. The updated version uses __eq__ of the super class.


def conjugate(self):
return AerStatevector(np.conj(self._data), dims=self.dims())

def sample_memory(self, shots, qargs=None):
if qargs is None:
qubits = np.arange(self._aer_state.num_qubits)
else:
qubits = np.array(qargs)
self._aer_state.close()
self._aer_state = AerState(**self._aer_state.configuration())
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

@staticmethod
def _from_ndarray(init_data, configs):
aer_state = AerState()

options = AerSimulator._default_options()
for config_key, config_value in configs.items():
if options.get(config_key):
aer_state.configure(config_key, config_value)

if len(init_data) == 0:
raise AerError('initial data must be larger than 0')

num_qubits = int(np.log2(len(init_data)))

aer_state.allocate_qubits(num_qubits)
aer_state.initialize(data=init_data)

return aer_state.move_to_ndarray(), aer_state

@classmethod
def from_instruction(cls, instruction):
return AerStatevector(instruction)

@staticmethod
def _from_instruction(inst, init_data, configs):
aer_state = AerState()

for config_key, config_value in configs.items():
aer_state.configure(config_key, config_value)

aer_state.allocate_qubits(inst.num_qubits)
num_qubits = inst.num_qubits

if init_data is not None:
aer_state.initialize(data=init_data, copy=True)
else:
aer_state.initialize()

if isinstance(inst, QuantumCircuit) and inst.global_phase != 0:
aer_state.apply_global_phase(inst.global_phase)

if isinstance(inst, QuantumCircuit):
AerStatevector._aer_evolve_circuit(aer_state, inst, range(num_qubits))
else:
AerStatevector._aer_evolve_instruction(aer_state, inst, range(num_qubits))

return aer_state.move_to_ndarray(), aer_state

@staticmethod
def _aer_evolve_circuit(aer_state, circuit, qubits):
"""Apply circuit into aer_state"""
for instruction in circuit.data:
if instruction.clbits:
raise AerError(
f"Cannot apply instruction with classical bits: {instruction.operation.name}"
)
inst = instruction.operation
qargs = instruction.qubits
AerStatevector._aer_evolve_instruction(aer_state, inst,
[qubits[circuit.find_bit(qarg).index]
for qarg in qargs])
hhorii marked this conversation as resolved.
Show resolved Hide resolved

@staticmethod
def _aer_evolve_instruction(aer_state, inst, qubits):
"""Apply instruction into aer_state"""
params = inst.params
if inst.name in ['u', 'u3']:
aer_state.apply_mcu(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1],
params[0], params[1], params[2])
elif inst.name in ['x', 'cx', 'ccx']:
aer_state.apply_mcx(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in ['y', 'cy']:
aer_state.apply_mcy(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name in ['z', 'cz']:
aer_state.apply_mcz(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1])
elif inst.name == 'unitary':
aer_state.apply_unitary(qubits, inst.params[0])
elif inst.name == 'diagonal':
aer_state.apply_diagonal(qubits, inst.params[0])
elif inst.name == 'reset':
aer_state.apply_reset(qubits)
else:
definition = inst.definition
if definition is inst:
raise AerError('cannot decompose ' + inst.name)
if not definition:
raise AerError('cannot decompose ' + inst.name)
AerStatevector._aer_evolve_circuit(aer_state, definition, qubits)

@classmethod
def from_label(cls, label):
return AerStatevector(Statevector.from_label(label)._data)

@staticmethod
def from_int(i, dims):
size = np.product(dims)
state = np.zeros(size, dtype=complex)
state[i] = 1.0
return AerStatevector(state, dims=dims)
Loading