Repository URL to install this package:
|
Version:
0.24.2 ▾
|
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# 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.
"""Post-processing of raw result."""
import numpy as np
from qiskit.exceptions import QiskitError
def _hex_to_bin(hexstring):
"""Convert hexadecimal readouts (memory) to binary readouts."""
return str(bin(int(hexstring, 16)))[2:]
def _bin_to_hex(bitstring):
"""Convert bitstring readouts (memory) to hexadecimal readouts."""
return hex(int(bitstring, 2))
def _pad_zeros(bitstring, memory_slots):
"""If the bitstring is truncated, pad extra zeros to make its
length equal to memory_slots"""
return format(int(bitstring, 2), f"0{memory_slots}b")
def _separate_bitstring(bitstring, creg_sizes):
"""Separate a bitstring according to the registers defined in the result header."""
substrings = []
running_index = 0
for _, size in reversed(creg_sizes):
substrings.append(bitstring[running_index : running_index + size])
running_index += size
return " ".join(substrings)
def format_counts_memory(shot_memory, header=None):
"""
Format a single bitstring (memory) from a single shot experiment.
- The hexadecimals are expanded to bitstrings
- Spaces are inserted at register divisions.
Args:
shot_memory (str): result of a single experiment.
header (dict): the experiment header dictionary containing
useful information for postprocessing. creg_sizes
are a nested list where the inner element is a list
of creg name, creg size pairs. memory_slots is an integers
specifying the number of total memory_slots in the experiment.
Returns:
dict: a formatted memory
"""
if shot_memory.startswith("0x"):
shot_memory = _hex_to_bin(shot_memory)
if header:
creg_sizes = header.get("creg_sizes", None)
memory_slots = header.get("memory_slots", None)
if memory_slots:
shot_memory = _pad_zeros(shot_memory, memory_slots)
if creg_sizes and memory_slots:
shot_memory = _separate_bitstring(shot_memory, creg_sizes)
return shot_memory
def _list_to_complex_array(complex_list):
"""Convert nested list of shape (..., 2) to complex numpy array with shape (...)
Args:
complex_list (list): List to convert.
Returns:
np.ndarray: Complex numpy array
Raises:
QiskitError: If inner most array of input nested list is not of length 2.
"""
arr = np.asarray(complex_list, dtype=np.complex_)
if not arr.shape[-1] == 2:
raise QiskitError("Inner most nested list is not of length 2.")
return arr[..., 0] + 1j * arr[..., 1]
def format_level_0_memory(memory):
"""Format an experiment result memory object for measurement level 0.
Args:
memory (list): Memory from experiment with `meas_level==1`. `avg` or
`single` will be inferred from shape of result memory.
Returns:
np.ndarray: Measurement level 0 complex numpy array
Raises:
QiskitError: If the returned numpy array does not have 2 (avg) or 3 (single)
indices.
"""
formatted_memory = _list_to_complex_array(memory)
# infer meas_return from shape of returned data.
if not 2 <= len(formatted_memory.shape) <= 3:
raise QiskitError("Level zero memory is not of correct shape.")
return formatted_memory
def format_level_1_memory(memory):
"""Format an experiment result memory object for measurement level 1.
Args:
memory (list): Memory from experiment with `meas_level==1`. `avg` or
`single` will be inferred from shape of result memory.
Returns:
np.ndarray: Measurement level 1 complex numpy array
Raises:
QiskitError: If the returned numpy array does not have 1 (avg) or 2 (single)
indices.
"""
formatted_memory = _list_to_complex_array(memory)
# infer meas_return from shape of returned data.
if not 1 <= len(formatted_memory.shape) <= 2:
raise QiskitError("Level one memory is not of correct shape.")
return formatted_memory
def format_level_2_memory(memory, header=None):
"""Format an experiment result memory object for measurement level 2.
Args:
memory (list): Memory from experiment with `meas_level==2` and `memory==True`.
header (dict): the experiment header dictionary containing
useful information for postprocessing.
Returns:
list[str]: List of bitstrings
"""
memory_list = []
for shot_memory in memory:
memory_list.append(format_counts_memory(shot_memory, header))
return memory_list
def format_counts(counts, header=None):
"""Format a single experiment result coming from backend to present
to the Qiskit user.
Args:
counts (dict): counts histogram of multiple shots
header (dict): the experiment header dictionary containing
useful information for postprocessing.
Returns:
dict: a formatted counts
"""
counts_dict = {}
for key, val in counts.items():
key = format_counts_memory(key, header)
counts_dict[key] = val
return counts_dict
def format_statevector(vec, decimals=None):
"""Format statevector coming from the backend to present to the Qiskit user.
Args:
vec (list): a list of [re, im] complex numbers.
decimals (int): the number of decimals in the statevector.
If None, no rounding is done.
Returns:
list[complex]: a list of python complex numbers.
"""
# pylint: disable=cyclic-import
from qiskit.quantum_info.states.statevector import Statevector
if isinstance(vec, Statevector):
if decimals:
return Statevector(np.around(vec.data, decimals=decimals), dims=vec.dims())
return vec
if isinstance(vec, np.ndarray):
if decimals:
return np.around(vec, decimals=decimals)
return vec
num_basis = len(vec)
if vec and isinstance(vec[0], complex):
vec_complex = np.array(vec, dtype=complex)
else:
vec_complex = np.zeros(num_basis, dtype=complex)
for i in range(num_basis):
vec_complex[i] = vec[i][0] + 1j * vec[i][1]
if decimals:
vec_complex = np.around(vec_complex, decimals=decimals)
return vec_complex
def format_unitary(mat, decimals=None):
"""Format unitary coming from the backend to present to the Qiskit user.
Args:
mat (list[list]): a list of list of [re, im] complex numbers
decimals (int): the number of decimals in the statevector.
If None, no rounding is done.
Returns:
list[list[complex]]: a matrix of complex numbers
"""
# pylint: disable=cyclic-import
from qiskit.quantum_info.operators.operator import Operator
if isinstance(mat, Operator):
if decimals:
return Operator(
np.around(mat.data, decimals=decimals),
input_dims=mat.input_dims(),
output_dims=mat.output_dims(),
)
return mat
if isinstance(mat, np.ndarray):
if decimals:
return np.around(mat, decimals=decimals)
return mat
num_basis = len(mat)
mat_complex = np.zeros((num_basis, num_basis), dtype=complex)
for i, vec in enumerate(mat):
mat_complex[i] = format_statevector(vec, decimals)
return mat_complex