diff --git a/qiskit_aer/noise/device/models.py b/qiskit_aer/noise/device/models.py index ec67dc7d84..657e74c8ec 100644 --- a/qiskit_aer/noise/device/models.py +++ b/qiskit_aer/noise/device/models.py @@ -16,7 +16,7 @@ """ import logging -from warnings import warn, catch_warnings, filterwarnings +from warnings import warn from numpy import inf, exp, allclose @@ -85,7 +85,6 @@ def basic_device_gate_errors(properties=None, gate_lengths=None, gate_length_units='ns', temperature=0, - standard_gates=None, warnings=None, target=None): """ @@ -114,15 +113,12 @@ def basic_device_gate_errors(properties=None, Can be 'ns', 'ms', 'us', or 's' (Default: 'ns'). temperature (double): qubit temperature in milli-Kelvin (mK) (Default: 0). - standard_gates (bool): DEPRECATED, If true return errors as standard - qobj gates. If false return as unitary - qobj instructions (Default: None). - warnings (bool): PLAN TO BE DEPRECATED, Display warnings (Default: None). + warnings (bool): DEPRECATED, Display warnings (Default: None). target (Target): device backend target (Default: None). When this is supplied, several options are disabled: `properties`, `gate_lengths` and `gate_length_units` are not used during the construction of gate errors. - Default values are always used for `standard_gates` and `warnings`. + Default values are always used for `warnings`. Returns: list: A list of tuples ``(label, qubits, QuantumError)``, for gates @@ -135,23 +131,18 @@ def basic_device_gate_errors(properties=None, if properties is None and target is None: raise NoiseError("Either properties or target must be supplied.") - if standard_gates is not None: - warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - if warnings is not None: warn( - '"warnings" argument will be deprecated as part of the qiskit-aer 0.12.0 and ' - 'subsequently removed', - PendingDeprecationWarning, stacklevel=2) + '"warnings" argument has been deprecated as of qiskit-aer 0.12.0 ' + 'and will be removed no earlier than 3 months from that release date. ' + 'Use the warnings filter in Python standard library instead.', + DeprecationWarning, stacklevel=2) else: warnings = True if target is not None: - if standard_gates is not None or not warnings: - warn("When `target` is supplied, `standard_gates` and `warnings` are ignored," + if not warnings: + warn("When `target` is supplied, `warnings` are ignored," " and they are always set to true.", UserWarning) if gate_lengths: @@ -210,14 +201,8 @@ def basic_device_gate_errors(properties=None, # Get depolarizing error channel if gate_error: - with catch_warnings(): - filterwarnings( - "ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils" - ) - depol_error = _device_depolarizing_error( - qubits, error_param, relax_error, standard_gates) + depol_error = _device_depolarizing_error( + qubits, error_param, relax_error) # Combine errors combined_error = _combine_depol_and_relax_error(depol_error, relax_error) @@ -287,8 +272,7 @@ def _basic_device_target_gate_errors(target, def _device_depolarizing_error(qubits, error_param, - relax_error=None, - standard_gates=True): + relax_error=None): """Construct a depolarizing_error for device. If un-physical parameters are supplied, they are truncated to the theoretical bound values.""" @@ -322,8 +306,7 @@ def _device_depolarizing_error(qubits, # Check if reported error param is un-physical # The minimum average gate fidelity is F_min = 1 / (dim + 1) # So the maximum gate error is 1 - F_min = dim / (dim + 1) - if error_param > error_max: - error_param = error_max + error_param = min(error_param, error_max) # Model gate error entirely as depolarizing error num_qubits = len(qubits) dim = 2 ** num_qubits @@ -331,14 +314,7 @@ def _device_depolarizing_error(qubits, max_param = 4**num_qubits / (4**num_qubits - 1) if depol_param > max_param: depol_param = min(depol_param, max_param) - with catch_warnings(): - filterwarnings( - "ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.device.models" - ) - return depolarizing_error( - depol_param, num_qubits, standard_gates=standard_gates) + return depolarizing_error(depol_param, num_qubits) return None diff --git a/qiskit_aer/noise/errors/errorutils.py b/qiskit_aer/noise/errors/errorutils.py deleted file mode 100644 index 69ba78f060..0000000000 --- a/qiskit_aer/noise/errors/errorutils.py +++ /dev/null @@ -1,914 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2019, 2021. -# -# 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. -""" -Helper functions for noise model creation. -""" -import warnings - -import numpy as np - -from qiskit.circuit import Reset -from qiskit.circuit.library.standard_gates import ( - XGate, YGate, ZGate, TGate, TdgGate, CXGate, CZGate, SwapGate, - IGate, SGate, SdgGate, HGate, CCXGate -) -from qiskit.quantum_info.operators.channel.kraus import Kraus -from qiskit.quantum_info.operators.channel.superop import SuperOp -from qiskit.quantum_info.operators.operator import Operator -from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT -from qiskit.quantum_info.operators.predicates import is_identity_matrix -from qiskit.quantum_info.operators.predicates import is_unitary_matrix -from qiskit.quantum_info.operators.predicates import matrix_equal -from ..noiseerror import NoiseError - - -def standard_gates_instructions(instructions): - """Convert a list with unitary matrix instructions into standard gates. - - Args: - instructions (list): A list of qobj instructions. - - Returns: - list: a list of qobj instructions equivalent to in input instruction. - """ - warnings.warn( - 'standard_gates_instructions has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - output_instructions = [] - for instruction in instructions: - output_instructions += standard_gate_instruction(instruction) - return output_instructions - - -def _standard_gates_instructions(instructions): - """Temporary function to create Instruction objects from json strings, - which is necessary for creating a new QuantumError object from deprecated - json-based input. Note that the type of returned object is different from - standard_gates_instructions. - TODO: to be removed after deprecation period. - - Args: - instructions (list): A list of qobj instructions or ordinary instruction. - - Returns: - list: a list of ordinary instructions equivalent to in input instruction. - """ - output_instructions = [] - for instruction in instructions: - if isinstance(instruction, dict): - output_instructions += _standard_gate_instruction(instruction) - else: - output_instructions.append(instruction) - return output_instructions - - -# pylint: disable=too-many-return-statements -def standard_gate_instruction(instruction, ignore_phase=True): - """Convert a unitary matrix instruction into a standard gate instruction. - Args: - instruction (dict): A qobj instruction. - ignore_phase (bool): Ignore global phase on unitary matrix in - comparison to canonical unitary. - Returns: - list: a list of qobj instructions equivalent to in input instruction. - """ - warnings.warn( - 'standard_gate_instruction has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - name = instruction.get("name", None) - if name not in ["mat", "unitary", "kraus"]: - return [instruction] - qubits = instruction["qubits"] - params = instruction["params"] - - # Check for single-qubit reset Kraus - if name == "kraus": - if len(qubits) == 1: - superop = SuperOp(Kraus(params)) - # Check if reset to |0> - reset0 = reset_superop(1) - if superop == reset0: - return [{"name": "reset", "qubits": qubits}] - # Check if reset to |1> - reset1 = reset0.compose(Operator(standard_gate_unitary('x'))) - if superop == reset1: - return [{"name": "reset", "qubits": qubits}, {"name": "x", "qubits": qubits}] - # otherwise just return the kraus instruction - return [instruction] - - # Check single qubit gates - mat = params[0] - if len(qubits) == 1: - # Check clifford gates - for j in range(24): - if matrix_equal( - mat, - single_qubit_clifford_matrix(j), - ignore_phase=ignore_phase): - return single_qubit_clifford_instructions(j, qubit=qubits[0]) - # Check t gates - for name in ["t", "tdg"]: - if matrix_equal( - mat, - standard_gate_unitary(name), - ignore_phase=ignore_phase): - return [{"name": name, "qubits": qubits}] - # TODO: u1,u2,u3 decomposition - # Check two qubit gates - if len(qubits) == 2: - for name in ["cx", "cz", "swap"]: - if matrix_equal( - mat, - standard_gate_unitary(name), - ignore_phase=ignore_phase): - return [{"name": name, "qubits": qubits}] - # Check reversed CX - if matrix_equal( - mat, - standard_gate_unitary("cx_10"), - ignore_phase=ignore_phase): - return [{"name": "cx", "qubits": [qubits[1], qubits[0]]}] - # Check 2-qubit Pauli's - paulis = ["id", "x", "y", "z"] - for pauli0 in paulis: - for pauli1 in paulis: - pmat = np.kron( - standard_gate_unitary(pauli1), - standard_gate_unitary(pauli0)) - if matrix_equal(mat, pmat, ignore_phase=ignore_phase): - if pauli0 == "id": - return [{"name": pauli1, "qubits": [qubits[1]]}] - elif pauli1 == "id": - return [{"name": pauli0, "qubits": [qubits[0]]}] - else: - return [{ - "name": pauli0, - "qubits": [qubits[0]] - }, { - "name": pauli1, - "qubits": [qubits[1]] - }] - # Check three qubit toffoli - if len(qubits) == 3: - if matrix_equal( - mat, - standard_gate_unitary("ccx_012"), - ignore_phase=ignore_phase): - return [{"name": "ccx", "qubits": qubits}] - if matrix_equal( - mat, - standard_gate_unitary("ccx_021"), - ignore_phase=ignore_phase): - return [{ - "name": "ccx", - "qubits": [qubits[0], qubits[2], qubits[1]] - }] - if matrix_equal( - mat, - standard_gate_unitary("ccx_120"), - ignore_phase=ignore_phase): - return [{ - "name": "ccx", - "qubits": [qubits[1], qubits[2], qubits[0]] - }] - - # Else return input in - return [instruction] - - -def _standard_gate_instruction(instruction, ignore_phase=True): - """Temporary function to create Instruction objects from a json string, - which is necessary for creating a new QuantumError object from deprecated - json-based input. Note that the type of returned object is different from - the deprecated standard_gate_instruction. - TODO: to be removed after deprecation period. - - Args: - instruction (dict): A qobj instruction. - ignore_phase (bool): Ignore global phase on unitary matrix in - comparison to canonical unitary. - - Returns: - list: a list of (instructions, qubits) equivalent to in input instruction. - """ - gate = { - "id": IGate(), - "x": XGate(), - "y": YGate(), - "z": ZGate(), - "h": HGate(), - "s": SGate(), - "sdg": SdgGate(), - "t": TGate(), - "tdg": TdgGate(), - "cx": CXGate(), - "cz": CZGate(), - "swap": SwapGate() - } - - name = instruction.get("name", None) - qubits = instruction["qubits"] - if name in gate: - return [(gate[name], qubits)] - - if name not in ["mat", "unitary", "kraus"]: - return [instruction] - - params = instruction["params"] - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils") - - # Check for single-qubit reset Kraus - if name == "kraus": - if len(qubits) == 1: - superop = SuperOp(Kraus(params)) - # Check if reset to |0> - reset0 = reset_superop(1) - if superop == reset0: - return [(Reset(), [0])] - # Check if reset to |1> - reset1 = reset0.compose(Operator(standard_gate_unitary('x'))) - if superop == reset1: - return [(Reset(), [0]), (XGate(), [0])] - return [instruction] - - # Check single qubit gates - mat = params[0] - if len(qubits) == 1: - # Check clifford gates - for j in range(24): - if matrix_equal( - mat, - single_qubit_clifford_matrix(j), - ignore_phase=ignore_phase): - return [(gate, [0]) for gate in _CLIFFORD_GATES[j]] - # Check t gates - for name in ["t", "tdg"]: - if matrix_equal( - mat, - standard_gate_unitary(name), - ignore_phase=ignore_phase): - return [(gate[name], qubits)] - # TODO: u1,u2,u3 decomposition - # Check two qubit gates - if len(qubits) == 2: - for name in ["cx", "cz", "swap"]: - if matrix_equal( - mat, - standard_gate_unitary(name), - ignore_phase=ignore_phase): - return [(gate[name], qubits)] - # Check reversed CX - if matrix_equal( - mat, - standard_gate_unitary("cx_10"), - ignore_phase=ignore_phase): - return [(CXGate(), [qubits[1], qubits[0]])] - # Check 2-qubit Pauli's - paulis = ["id", "x", "y", "z"] - for pauli0 in paulis: - for pauli1 in paulis: - pmat = np.kron( - standard_gate_unitary(pauli1), - standard_gate_unitary(pauli0)) - if matrix_equal(mat, pmat, ignore_phase=ignore_phase): - if pauli0 == "id": - return [(gate[pauli1], [qubits[1]])] - elif pauli1 == "id": - return [(gate[pauli0], [qubits[0]])] - else: - return [(gate[pauli0], [qubits[0]]), (gate[pauli1], [qubits[1]])] - # Check three qubit toffoli - if len(qubits) == 3: - if matrix_equal( - mat, - standard_gate_unitary("ccx_012"), - ignore_phase=ignore_phase): - return [(CCXGate(), qubits)] - if matrix_equal( - mat, - standard_gate_unitary("ccx_021"), - ignore_phase=ignore_phase): - return [(CCXGate(), [qubits[0], qubits[2], qubits[1]])] - if matrix_equal( - mat, - standard_gate_unitary("ccx_120"), - ignore_phase=ignore_phase): - return [(CCXGate(), [qubits[1], qubits[2], qubits[0]])] - - # Else return input in - return [instruction] - - -def single_qubit_clifford_gates(j): - """Return a QASM gate names for a single qubit Clifford. - The labels are returned in a basis set consisting of - ('id', 's', 'sdg', 'z', 'h', x', 'y') gates decomposed to - use the minimum number of X-90 pulses in a (u1, u2, u3) - decomposition. - Args: - j (int): Clifford index 0, ..., 23. - Returns: - tuple(str): The tuple of basis gates. - Raises: - NoiseError: If index is out of range [0, 23]. - """ - warnings.warn( - 'single_qubit_clifford_gates has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - if not isinstance(j, int) or j < 0 or j > 23: - raise NoiseError( - "Index {} must be in the range [0, ..., 23]".format(j)) - - labels = [ - ('id', ), - ('s', ), - ('sdg', ), - ('z', ), - # u2 gates - ( - 'h', ), - ('h', 'z'), - ('z', 'h'), - ('h', 's'), - ('s', 'h'), - ('h', 'sdg'), - ('sdg', 'h'), - ('s', 'h', 's'), - ('sdg', 'h', 's'), - ('z', 'h', 's'), - ('s', 'h', 'sdg'), - ('sdg', 'h', 'sdg'), - ('z', 'h', 'sdg'), - ('s', 'h', 'z'), - ('sdg', 'h', 'z'), - ('z', 'h', 'z'), - # u3 gates - ( - 'x', ), - ('y', ), - ('s', 'x'), - ('sdg', 'x') - ] - return labels[j] - - -_CLIFFORD_GATES = [ - (IGate(), ), - (SGate(), ), - (SdgGate(), ), - (ZGate(), ), - # u2 gates - (HGate(), ), - (HGate(), ZGate()), - (ZGate(), HGate()), - (HGate(), SGate()), - (SGate(), HGate()), - (HGate(), SdgGate()), - (SdgGate(), HGate()), - (SGate(), HGate(), SGate()), - (SdgGate(), HGate(), SGate()), - (ZGate(), HGate(), SGate()), - (SGate(), HGate(), SdgGate()), - (SdgGate(), HGate(), SdgGate()), - (ZGate(), HGate(), SdgGate()), - (SGate(), HGate(), ZGate()), - (SdgGate(), HGate(), ZGate()), - (ZGate(), HGate(), ZGate()), - # u3 gates - (XGate(), ), - (YGate(), ), - (SGate(), XGate()), - (SdgGate(), XGate()) -] - - -def single_qubit_clifford_matrix(j): - """Return Numpy array for a single qubit Clifford. - Args: - j (int): Clifford index 0, ..., 23. - Returns: - np.array: The matrix for the indexed clifford. - Raises: - NoiseError: If index is out of range [0, 23]. - """ - warnings.warn( - 'single_qubit_clifford_matrix has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - if not isinstance(j, int) or j < 0 or j > 23: - raise NoiseError( - "Index {} must be in the range [0, ..., 23]".format(j)) - - basis_dict = { - 'id': np.eye(2), - 'x': np.array([[0, 1], [1, 0]], dtype=complex), - 'y': np.array([[0, -1j], [1j, 0]], dtype=complex), - 'z': np.array([[1, 0], [0, -1]], dtype=complex), - 'h': np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2), - 's': np.array([[1, 0], [0, 1j]], dtype=complex), - 'sdg': np.array([[1, 0], [0, -1j]], dtype=complex) - } - mat = np.eye(2) - for gate in single_qubit_clifford_gates(j): - mat = np.dot(basis_dict[gate], mat) - return mat - - -# pylint: disable=invalid-name -def single_qubit_clifford_instructions(index, qubit=0): - """Return a list of qobj instructions for a single qubit Cliffords. - The instructions are returned in a basis set consisting of - ('id', 's', 'sdg', 'z', 'h', x', 'y') gates decomposed to - use the minimum number of X-90 pulses in a (u1, u2, u3) - decomposition. - Args: - index (int): Clifford index 0, ..., 23. - qubit (int): the qubit to apply the Clifford to. - Returns: - list(dict): The list of instructions. - Raises: - NoiseError: If index is out of range [0, 23] or qubit invalid. - """ - warnings.warn( - 'single_qubit_clifford_instructions has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - if not isinstance(index, int) or index < 0 or index > 23: - raise NoiseError( - "Index {} must be in the range [0, ..., 23]".format(index)) - if not isinstance(qubit, int) or qubit < 0: - raise NoiseError("qubit position must be positive integer.") - - instructions = [] - for gate in single_qubit_clifford_gates(index): - instructions.append({"name": gate, "qubits": [qubit]}) - return instructions - - -def standard_gate_unitary(name): - """Return the unitary matrix for a standard gate.""" - warnings.warn( - 'standard_gate_unitary has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - unitary_matrices = { - ("id", "I"): - np.eye(2, dtype=complex), - ("x", "X"): - np.array([[0, 1], [1, 0]], dtype=complex), - ("y", "Y"): - np.array([[0, -1j], [1j, 0]], dtype=complex), - ("z", "Z"): - np.array([[1, 0], [0, -1]], dtype=complex), - ("h", "H"): - np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2), - ("s", "S"): - np.array([[1, 0], [0, 1j]], dtype=complex), - ("sdg", "Sdg"): - np.array([[1, 0], [0, -1j]], dtype=complex), - ("t", "T"): - np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex), - ("tdg", "Tdg"): - np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex), - ("cx", "CX", "cx_01"): - np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), - ("cx_10",): - np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), - ("cz", "CZ"): - np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex), - ("swap", "SWAP"): - np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex), - ("ccx", "CCX", "ccx_012", "ccx_102"): - np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 0, 0]], - dtype=complex), - ("ccx_021", "ccx_201"): - np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0]], - dtype=complex), - ("ccx_120", "ccx_210"): - np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]], - dtype=complex) - } - - return next((value for key, value in unitary_matrices.items() if name in key), None) - - -def reset_superop(num_qubits): - """Return a N-qubit reset SuperOp.""" - warnings.warn( - 'reset_superop has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - reset = SuperOp( - np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])) - if num_qubits == 1: - return reset - reset_n = reset - for _ in range(num_qubits - 1): - reset_n.tensor(reset) - return reset_n - - -def standard_instruction_operator(instr): - """Return the Operator for a standard gate instruction.""" - warnings.warn( - 'standard_instruction_operator has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - # Convert to dict (for QobjInstruction types) - if hasattr(instr, 'as_dict'): - instr = instr.as_dict() - # Get name and parameters - name = instr.get('name', "") - params = instr.get('params', []) - # Check if standard unitary gate name - mat = standard_gate_unitary(name) - if isinstance(mat, np.ndarray): - return Operator(mat) - - # Check if standard parameterized waltz gates - if name == 'u1': - lam = params[0] - mat = np.diag([1, np.exp(1j * lam)]) - return Operator(mat) - if name == 'u2': - phi = params[0] - lam = params[1] - mat = np.array([[1, -np.exp(1j * lam)], - [np.exp(1j * phi), - np.exp(1j * (phi + lam))]]) / np.sqrt(2) - return Operator(mat) - if name == 'u3': - theta = params[0] - phi = params[1] - lam = params[2] - mat = np.array( - [[np.cos(theta / 2), -np.exp(1j * lam) * np.sin(theta / 2)], - [np.exp(1j * phi) * np.sin(theta / 2), np.exp(1j * (phi + lam)) * np.cos(theta / 2)]]) - return Operator(mat) - - # Check if unitary instruction - if name == 'unitary': - return Operator(params[0]) - - # Otherwise return None if we cannot convert instruction - return None - - -def standard_instruction_channel(instr): - """Return the SuperOp channel for a standard instruction.""" - warnings.warn( - 'standard_instruction_channel has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - # Check if standard operator - oper = standard_instruction_operator(instr) - if oper is not None: - return SuperOp(oper) - - # Convert to dict (for QobjInstruction types) - if hasattr(instr, 'as_dict'): - instr = instr.as_dict() - # Get name and parameters - name = instr.get('name', "") - - # Check if reset instruction - if name == 'reset': - # params should be the number of qubits being reset - num_qubits = len(instr['qubits']) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - res = reset_superop(num_qubits) - return res - # Check if Kraus instruction - if name == 'kraus': - params = instr['params'] - return SuperOp(Kraus(params)) - return None - - -def circuit2superop(circuit, min_qubits=1): - """Return the SuperOp for a standard instruction.""" - warnings.warn( - 'circuit2superop has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - # Get number of qubits - max_qubits = 1 - for instr in circuit: - qubits = [] - if hasattr(instr, 'qubits'): - qubits = instr.qubits - elif isinstance(instr, dict): - qubits = instr.get('qubits', []) - max_qubits = max(max_qubits, 1 + max(qubits)) - - num_qubits = max(max_qubits, min_qubits) - - # Initialize N-qubit identity superoperator - superop = SuperOp(np.eye(4**num_qubits)) - # compose each circuit element with the superoperator - for instr in circuit: - instr_op = standard_instruction_channel(instr) - if instr_op is None: - raise NoiseError('Cannot convert instruction {} to SuperOp'.format(instr)) - if hasattr(instr, 'qubits'): - qubits = instr.qubits - else: - qubits = instr['qubits'] - superop = superop.compose(instr_op, qubits) - return superop - - -def make_unitary_instruction(mat, qubits, standard_gates=True): - """Return a qobj instruction for a unitary matrix gate. - Args: - mat (matrix): A square or diagonal unitary matrix. - qubits (list[int]): The qubits the matrix is applied to. - standard_gates (bool): Check if the matrix instruction is a - standard instruction. - Returns: - dict: The qobj instruction object. - Raises: - NoiseError: if the input is not a unitary matrix. - """ - warnings.warn( - 'make_unitary_instruction has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - if not is_unitary_matrix(mat): - raise NoiseError("Input matrix is not unitary.") - - if isinstance(qubits, int): - qubits = [qubits] - - instruction = {"name": "unitary", "qubits": qubits, "params": [mat]} - if standard_gates: - return standard_gate_instruction(instruction) - else: - return [instruction] - - -def _make_unitary_instruction(mat, qubits, standard_gates=True): - """Temporary function to create Instruction objects from a unitary matrix, - which is necessary for creating a new QuantumError with the deprecated - standard_gates option. Note that the type of returned object is different from - the deprecated make_unitary_instruction. - TODO: to be removed after deprecation period. - - Args: - mat (matrix): A square or diagonal unitary matrix. - qubits (list[int]): The qubits the matrix is applied to. - standard_gates (bool): Check if the matrix instruction is a - standard instruction. - - Returns: - list: The list of instructions. - - Raises: - NoiseError: if the input is not a unitary matrix. - """ - if not is_unitary_matrix(mat): - raise NoiseError("Input matrix is not unitary.") - - if isinstance(qubits, int): - qubits = [qubits] - - instruction = {"name": "unitary", "qubits": qubits, "params": [mat]} - if standard_gates: - if isinstance(instruction, dict): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils") - res = _standard_gate_instruction(instruction) - else: - res = [instruction] - return res - else: - return [instruction] - - -def make_kraus_instruction(mats, qubits): - """Return a qobj instruction for a Kraus error. - - Args: - mats (list[matrix]): A list of square or diagonal Kraus matrices. - qubits (list[int]): The qubits the matrix is applied to. - Returns: - dict: The qobj instruction object. - - Raises: - NoiseError: if the input is not a CPTP Kraus channel. - """ - warnings.warn( - 'make_kraus_instruction has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - kraus = Kraus(mats) - if not kraus.is_cptp() or kraus._input_dim != kraus._output_dim: - raise NoiseError("Input Kraus matrices are not a CPTP channel.") - if isinstance(qubits, int): - qubits = [qubits] - return [{"name": "kraus", "qubits": qubits, "params": kraus.data}] - - -def qubits_from_mat(mat): - """Return the number of qubits for a multi-qubit matrix.""" - warnings.warn( - 'qubits_from_mat has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - arr = np.array(mat) - shape = arr.shape - num_qubits = int(np.log2(shape[1])) - if shape[1] != 2**num_qubits: - raise NoiseError("Input matrix is not a multi-qubit matrix.") - return num_qubits - - -def is_matrix_diagonal(mat): - """Test if row-vector representation of diagonal matrix.""" - warnings.warn( - 'is_matrix_diagonal has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - mat = np.array(mat) - shape = mat.shape - return len(shape) == 2 and shape[0] == 1 - - -def kraus2instructions(kraus_ops, standard_gates, atol=ATOL_DEFAULT): - """ - Convert a list of Kraus matrices into qobj circuits. - - If any Kraus operators are a unitary matrix they will be converted - into unitary qobj instructions. Identity unitary matrices will also be - converted into identity qobj instructions. - - Args: - kraus_ops (list[matrix]): A list of Kraus matrices for a CPTP map. - standard_gates (bool): Check if the matrix instruction is a - standard instruction (default: True). - atol (double): Threshold for testing if probabilities are zero. - - - Returns: - list: A list of pairs (p, circuit) where `circuit` is a list of qobj - instructions, and `p` is the probability of that circuit for the - given error. - - Raises: - NoiseError: If the input Kraus channel is not CPTP. - """ - warnings.warn( - 'kraus2instructions has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - # Check threshold - if atol < 0: - raise NoiseError("atol cannot be negative") - if atol > 1e-5: - raise NoiseError( - "atol value is too large. It should be close to zero.") - - # Check CPTP - if not Kraus(kraus_ops).is_cptp(atol=atol): - raise NoiseError("Input Kraus channel is not CPTP.") - - # Get number of qubits - num_qubits = int(np.log2(len(kraus_ops[0]))) - if len(kraus_ops[0]) != 2**num_qubits: - raise NoiseError("Input Kraus channel is not a multi-qubit channel.") - - # Check if each matrix is a: - # 1. scaled identity matrix - # 2. scaled non-identity unitary matrix - # 3. a non-unitary Kraus operator - - # Probabilities - prob_identity = 0 - prob_unitary = 0 # total probability of all unitary ops (including id) - prob_kraus = 0 # total probability of non-unitary ops - probabilities = [] # initialize with probability of Identity - - # Matrices - unitaries = [] # non-identity unitaries - non_unitaries = [] # non-unitary Kraus matrices - - for mat in kraus_ops: - # Get the value of the maximum diagonal element - # of op.H * op for rescaling - # pylint: disable=no-member - prob = abs(max(np.diag(np.conj(np.transpose(mat)).dot(mat)))) - if prob > 0.0: - if abs(prob - 1) > 0.0: - # Rescale the operator by square root of prob - rescaled_mat = np.array(mat) / np.sqrt(prob) - else: - rescaled_mat = mat - # Check if identity operator - if is_identity_matrix(rescaled_mat, ignore_phase=True): - prob_identity += prob - prob_unitary += prob - # Check if unitary - elif is_unitary_matrix(rescaled_mat): - probabilities.append(prob) - prob_unitary += prob - unitaries.append(rescaled_mat) - # Non-unitary op - else: - non_unitaries.append(mat) - - # Check probabilities - prob_kraus = 1 - prob_unitary - if prob_unitary - 1 > atol: - raise NoiseError("Invalid kraus matrices: unitary probability " - "{} > 1".format(prob_unitary)) - if prob_unitary < -atol: - raise NoiseError("Invalid kraus matrices: unitary probability " - "{} < 1".format(prob_unitary)) - if prob_identity - 1 > atol: - raise NoiseError("Invalid kraus matrices: identity probability " - "{} > 1".format(prob_identity)) - if prob_identity < -atol: - raise NoiseError("Invalid kraus matrices: identity probability " - "{} < 1".format(prob_identity)) - if prob_kraus - 1 > atol: - raise NoiseError("Invalid kraus matrices: non-unitary probability " - "{} > 1".format(prob_kraus)) - if prob_kraus < -atol: - raise NoiseError("Invalid kraus matrices: non-unitary probability " - "{} < 1".format(prob_kraus)) - - # Build qobj instructions - instructions = [] - qubits = list(range(num_qubits)) - - # Add unitary instructions - for unitary in unitaries: - instructions.append( - _make_unitary_instruction( - unitary, qubits, standard_gates=standard_gates)) - - # Add identity instruction - if prob_identity > atol: - if abs(prob_identity - 1) < atol: - probabilities.append(1) - else: - probabilities.append(prob_identity) - instructions.append([(IGate(), [0])]) - - # Add Kraus - if prob_kraus < atol: - # No Kraus operators - return zip(instructions, probabilities) - if prob_kraus < 1: - # Rescale kraus operators by probabilities - non_unitaries = [ - np.array(op) / np.sqrt(prob_kraus) for op in non_unitaries - ] - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils") - instructions.append(make_kraus_instruction(non_unitaries, qubits)) - probabilities.append(prob_kraus) - # Normalize probabilities to account for any rounding errors - probabilities = list(np.array(probabilities) / np.sum(probabilities)) - return zip(instructions, probabilities) diff --git a/qiskit_aer/noise/errors/quantum_error.py b/qiskit_aer/noise/errors/quantum_error.py index 7bfa6645b9..5d8efd335c 100644 --- a/qiskit_aer/noise/errors/quantum_error.py +++ b/qiskit_aer/noise/errors/quantum_error.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019, 2021. +# (C) Copyright IBM 2018-2023. # # 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 @@ -15,7 +15,6 @@ import copy import numbers import uuid -import warnings from typing import Iterable import numpy as np @@ -25,16 +24,12 @@ from qiskit.circuit.library.generalized_gates import PauliGate from qiskit.circuit.library.standard_gates import IGate from qiskit.exceptions import QiskitError -from qiskit.extensions import UnitaryGate from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.channel import Kraus, SuperOp from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel from qiskit.quantum_info.operators.mixins import TolerancesMixin from qiskit.quantum_info.operators.predicates import is_identity_matrix from qiskit.quantum_info.operators.symplectic import Clifford -from .errorutils import _standard_gates_instructions -from .errorutils import kraus2instructions -from .errorutils import standard_gate_unitary from ..noiseerror import NoiseError @@ -50,10 +45,7 @@ class QuantumError(BaseOperator, TolerancesMixin): """ def __init__(self, - noise_ops, - number_of_qubits=None, - standard_gates=None, - atol=None): + noise_ops): """ Create a quantum error for a noise model. @@ -96,12 +88,6 @@ def __init__(self, ``QuantumCircuit``, ``(Instruction, qargs)`` and a list of ``(Instruction, qargs)``. Note that ``qargs`` should be a list of integers and can be omitted (default qubits are used in that case). See also examples above. - number_of_qubits (int): [DEPRECATED] specify the number of qubits for the - error. If None this will be determined - automatically (default None). - standard_gates (bool): [DEPRECATED] Check if input matrices are standard gates. - atol (double): [DEPRECATED] Threshold for testing if probabilities are - equal to 0 or 1 (Default: ``QuantumError.atol``). Raises: NoiseError: If input noise_ops is invalid, e.g. it's not a CPTP map. """ @@ -115,38 +101,6 @@ def __init__(self, super().__init__(num_qubits=noise_ops.num_qubits) return - if atol is not None: - warnings.warn( - '"atol" option in the constructor of QuantumError has been deprecated' - ' as of qiskit-aer 0.10.0 and will be removed no earlier than 3 months' - ' from that release date. Use QuantumError.atol = value instead.', - DeprecationWarning, stacklevel=2) - else: - atol = QuantumError.atol - - # Convert list of arrarys to kraus instruction (for old API support) TODO: to be removed - if isinstance(noise_ops, (list, tuple)) and \ - len(noise_ops) > 0 and isinstance(noise_ops[0], np.ndarray): - warnings.warn( - 'Constructing QuantumError with list of arrays representing a Kraus channel' - ' has been deprecated as of qiskit-aer 0.10.0 and will be removed no earlier than' - ' 3 months from that release date. Use QuantumError(Kraus(mats)) instead.', - DeprecationWarning, stacklevel=2) - if standard_gates: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils") - noise_ops = kraus2instructions( - noise_ops, standard_gates, atol=atol) - else: - try: - noise_ops = Kraus(noise_ops) - noise_ops = [((noise_ops.to_instruction(), - list(range(noise_ops.num_qubits))), 1.0)] - except QiskitError as err: - raise NoiseError("Fail to convert Kraus to Instruction") from err - # Single circuit case if not isinstance(noise_ops, Iterable) or \ (isinstance(noise_ops, tuple) and isinstance(noise_ops[0], Instruction)): @@ -163,7 +117,7 @@ def __init__(self, _, p = pair # pylint: disable=invalid-name if not isinstance(p, numbers.Real): raise NoiseError(f"Invalid type of probability: {p}") - if p < -1 * atol: + if p < -1 * QuantumError.atol: raise NoiseError(f"Negative probability is invalid: {p}") # Remove zero probability circuits @@ -176,19 +130,9 @@ def __init__(self, ops, probs = zip(*noise_ops) # unzip - if standard_gates is not None: - warnings.warn( - '"standard_gates" option in the constructor of QuantumError has been deprecated' - ' as of qiskit-aer 0.10.0 in favor of externalizing such an unrolling functionality' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - if standard_gates: - if isinstance(ops[0], list): - ops = [_standard_gates_instructions(op) for op in ops] - # Initialize internal variables with error checking total_probs = sum(probs) - if not np.isclose(total_probs - 1, 0, atol=atol): + if not np.isclose(total_probs - 1, 0, atol=QuantumError.atol): raise NoiseError(f"Probabilities are not normalized: {total_probs} != 1") # Rescale probabilities if their sum is ok to avoid accumulation of rounding errors self._probs = list(np.array(probs) / total_probs) @@ -197,14 +141,6 @@ def __init__(self, circs = [self._to_circuit(op) for op in ops] num_qubits = max(qc.num_qubits for qc in circs) - if number_of_qubits is not None: - num_qubits = number_of_qubits - warnings.warn( - '"number_of_qubits" in the constructor of QuantumError has been deprecated' - ' as of qiskit-aer 0.10.0 in favor of determining it automatically' - ' and will be removed no earlier than 3 months from that release date.' - ' Specify number of qubits in the quantum circuit passed to the init if necessary.', - DeprecationWarning, stacklevel=2) self._circs = [self._enlarge_qreg(qc, num_qubits) for qc in circs] # Check validity of circuits @@ -269,44 +205,6 @@ def _to_circuit(cls, op): f" not appendable to circuit." ) from err return circ - # Support for old-style json-like input TODO: to be removed - elif all(isinstance(aop, dict) for aop in op): - warnings.warn( - 'Constructing QuantumError with list of dict representing a mixed channel' - ' has been deprecated as of qiskit-aer 0.10.0 and will be removed' - ' no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=3) - # Convert json-like to non-kraus Instruction - num_qubits = max([max(dic['qubits']) for dic in op]) + 1 - circ = QuantumCircuit(num_qubits) - for dic in op: - if dic['name'] == 'reset': - # pylint: disable=import-outside-toplevel - from qiskit.circuit import Reset - circ.append(Reset(), qargs=dic['qubits']) - elif dic['name'] == 'kraus': - circ.append(Instruction(name='kraus', - num_qubits=len(dic['qubits']), - num_clbits=0, - params=dic['params']), - qargs=dic['qubits']) - elif dic['name'] == 'unitary': - circ.append(UnitaryGate(data=dic['params'][0]), - qargs=dic['qubits']) - elif dic['name'] == 'pauli': - circ.append(PauliGate(dic['params'][0]), - qargs=dic['qubits']) - else: - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils" - ) - circ.append(UnitaryGate(label=dic['name'], - data=standard_gate_unitary(dic['name'])), - qargs=dic['qubits']) - return circ else: raise NoiseError(f"Invalid type of op list: {op}") @@ -343,40 +241,11 @@ def copy(self): # The constructor of subclasses from raw data should be a copy return copy.deepcopy(self) - @classmethod - def set_atol(cls, value): - """Set the class default absolute tolerance parameter for float comparisons.""" - warnings.warn( - 'QuantumError.set_atol(value) has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.' - ' Use QuantumError.atol = value instead.', - DeprecationWarning, stacklevel=2) - QuantumError.atol = value - - @classmethod - def set_rtol(cls, value): - """Set the class default relative tolerance parameter for float comparisons.""" - warnings.warn( - 'QuantumError.set_rtol(value) has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.' - ' Use QuantumError.rtol = value instead.', - DeprecationWarning, stacklevel=2) - QuantumError.rtol = value - @property def size(self): """Return the number of error circuit.""" return len(self.circuits) - @property - def number_of_qubits(self): - """Return the number of qubits for the error.""" - warnings.warn( - "The `number_of_qubits` property has been deprecated as of" - " qiskit-aer 0.10.0. Use the `num_qubits` property instead.", - DeprecationWarning) - return self.num_qubits - @property def circuits(self): """Return the list of error circuits.""" diff --git a/qiskit_aer/noise/errors/standard_errors.py b/qiskit_aer/noise/errors/standard_errors.py index a52252e7c6..cbb01e8667 100644 --- a/qiskit_aer/noise/errors/standard_errors.py +++ b/qiskit_aer/noise/errors/standard_errors.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2019, 2021. +# (C) Copyright IBM 2018-2023. # # 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 @@ -14,30 +14,27 @@ """ import itertools as it -import warnings import numpy as np -from qiskit.circuit import QuantumCircuit, Reset -from qiskit.circuit.library.standard_gates import IGate, XGate, YGate, ZGate + +from qiskit.circuit import Reset +from qiskit.circuit.library.standard_gates import IGate, XGate, ZGate from qiskit.exceptions import QiskitError from qiskit.extensions import UnitaryGate -from qiskit.quantum_info.operators import Operator, Pauli +from qiskit.quantum_info.operators import Pauli from qiskit.quantum_info.operators.channel import Choi, Kraus from qiskit.quantum_info.operators.predicates import is_identity_matrix from qiskit.quantum_info.operators.predicates import is_unitary_matrix - -from .errorutils import _make_unitary_instruction from .quantum_error import QuantumError from ..noiseerror import NoiseError -def kraus_error(noise_ops, standard_gates=None, canonical_kraus=False): +def kraus_error(noise_ops, canonical_kraus=False): """ Return a Kraus quantum error channel. Args: noise_ops (list[matrix]): Kraus matrices. - standard_gates (bool): DEPRECATED, Check if input matrices are standard gates. canonical_kraus (bool): Convert input Kraus matrices into the canonical Kraus representation (default: False) @@ -52,12 +49,6 @@ def kraus_error(noise_ops, standard_gates=None, canonical_kraus=False): if not noise_ops: raise NoiseError("Kraus error noise_ops must not be empty.") - if standard_gates is not None: - warnings.warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - kraus = Kraus(noise_ops) if canonical_kraus: # Convert to Choi and back to get canonical Kraus @@ -65,7 +56,7 @@ def kraus_error(noise_ops, standard_gates=None, canonical_kraus=False): return QuantumError(kraus) -def mixed_unitary_error(noise_ops, standard_gates=None): +def mixed_unitary_error(noise_ops): """ Return a mixed unitary quantum error channel. @@ -75,7 +66,6 @@ def mixed_unitary_error(noise_ops, standard_gates=None): Args: noise_ops (list[pair[matrix, double]]): unitary error matrices. - standard_gates (bool): DEPRECATED, Check if input matrices are standard gates. Returns: QuantumError: The quantum error object. @@ -83,13 +73,6 @@ def mixed_unitary_error(noise_ops, standard_gates=None): Raises: NoiseError: if error parameters are invalid. """ - if standard_gates is not None: - warnings.warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.' - ' Use directly init e.g. QuantumError([(IGate(), prob1), (ZGate(), prob2)]) instead.', - DeprecationWarning, stacklevel=2) - # Error checking if not isinstance(noise_ops, (list, tuple, zip)): raise NoiseError("Input noise ops is not a list.") @@ -115,19 +98,11 @@ def mixed_unitary_error(noise_ops, standard_gates=None): if is_identity_matrix(unitary): prob_identity += prob else: - if standard_gates: # TODO: to be removed after deprecation period - qubits = list(range(num_qubits)) - instr = _make_unitary_instruction( - unitary, qubits, standard_gates=standard_gates) - else: - instr = UnitaryGate(unitary) + instr = UnitaryGate(unitary) instructions.append(instr) instructions_probs.append(prob) if prob_identity > 0: - if standard_gates: # TODO: to be removed after deprecation period - instructions.append([{"name": "id", "qubits": [0]}]) - else: - instructions.append(IGate()) + instructions.append(IGate()) instructions_probs.append(prob_identity) return QuantumError(zip(instructions, instructions_probs)) @@ -145,7 +120,7 @@ def coherent_unitary_error(unitary): return mixed_unitary_error([(unitary, 1)]) -def pauli_error(noise_ops, standard_gates=None): +def pauli_error(noise_ops): """ Return a mixed Pauli quantum error channel. @@ -156,10 +131,6 @@ def pauli_error(noise_ops, standard_gates=None): Args: noise_ops (list[pair[Pauli, double]]): Pauli error terms. - standard_gates (bool): DEPRECATED, if True return the operators as standard qobj - Pauli gate instructions. If false return as - unitary matrix qobj instructions. - (Default: None) Returns: QuantumError: The quantum error object. @@ -193,20 +164,10 @@ def to_pauli(op): if num_qubits != pauli.num_qubits: raise NoiseError("Pauli's are not all of the same length.") - if standard_gates is not None: - warnings.warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - if num_qubits > 1: - paulis = [pauli.to_instruction().definition for pauli in paulis] - if not standard_gates: - paulis = [Operator(pauli).to_instruction() for pauli in paulis] - return QuantumError(zip(paulis, probs)) -def depolarizing_error(param, num_qubits, standard_gates=None): +def depolarizing_error(param, num_qubits): r""" Return a depolarizing quantum error channel. @@ -231,9 +192,6 @@ def depolarizing_error(param, num_qubits, standard_gates=None): Args: param (double): depolarizing error parameter. num_qubits (int): the number of qubits for the error channel. - standard_gates (bool): DEPRECATED, if True return the operators as - Pauli gates. If false return as unitary gates. - (Default: None) Returns: QuantumError: The quantum error object. @@ -256,21 +214,6 @@ def depolarizing_error(param, num_qubits, standard_gates=None): prob_pauli = param / num_terms probs = [prob_iden] + (num_terms - 1) * [prob_pauli] - if standard_gates is not None: - warnings.warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - circs = [] - for pauli_list in it.product([IGate(), XGate(), YGate(), ZGate()], repeat=num_qubits): - qc = QuantumCircuit(num_qubits) - for q, pauli in enumerate(pauli_list): - if not standard_gates: - pauli = UnitaryGate(pauli.to_matrix()) - qc.append(pauli, qargs=[q]) - circs.append(qc) - return QuantumError(zip(circs, probs)) - # Generate pauli strings. The order doesn't matter as long # as the all identity string is first. paulis = [Pauli("".join(tup)) for tup in it.product(['I', 'X', 'Y', 'Z'], repeat=num_qubits)] diff --git a/qiskit_aer/noise/noise_model.py b/qiskit_aer/noise/noise_model.py index 5ff51411d5..fa2aa6f11d 100644 --- a/qiskit_aer/noise/noise_model.py +++ b/qiskit_aer/noise/noise_model.py @@ -18,9 +18,13 @@ from typing import Optional from warnings import warn, catch_warnings, filterwarnings -from numpy import ndarray +import numpy as np from qiskit.circuit import Instruction, Delay +from qiskit.circuit import QuantumCircuit +from qiskit.circuit import Reset +from qiskit.circuit.library.generalized_gates import PauliGate +from qiskit.extensions import UnitaryGate from qiskit.providers import QubitProperties from qiskit.providers.exceptions import BackendPropertyError from qiskit.providers.models import BackendProperties @@ -50,7 +54,7 @@ class AerJSONEncoder(json.JSONEncoder): # pylint: disable=method-hidden,arguments-differ def default(self, obj): - if isinstance(obj, ndarray): + if isinstance(obj, np.ndarray): return obj.tolist() if isinstance(obj, complex): return [obj.real, obj.imag] @@ -163,8 +167,7 @@ def __init__(self, basis_gates=None): # Store gates with a noise model defined self._noise_instructions = set() # Store qubits referenced in noise model. - # These include gate qubits in local quantum and readout errors, - # and both gate and noise qubits for non-local quantum errors. + # These include gate qubits in local quantum and readout errors. self._noise_qubits = set() # Default (all-qubit) quantum errors are stored as: # dict(str: QuantumError) @@ -175,12 +178,6 @@ def __init__(self, basis_gates=None): # where the outer keys are the instruction str label and the # inner dict keys are the gate qubits self._local_quantum_errors = {} - # Non-local quantum errors are stored as: - # dict(str: dict(tuple: dict(tuple: QuantumError))) - # where the outer keys are the instruction str label, the middle dict - # keys are the gate qubits, and the inner most dict keys are - # the noise qubits. - self._nonlocal_quantum_errors = {} # Default (all-qubit) readout error is stored as a single # ReadoutError object since there may only be one defined. self._default_readout_error = None @@ -215,7 +212,6 @@ def from_backend(cls, backend, temperature=0, gate_lengths=None, gate_length_units='ns', - standard_gates=None, warnings=None): """Return a noise model derived from a devices backend properties. @@ -283,7 +279,7 @@ def from_backend(cls, backend, If non-default values are used gate_lengths should be a list Args: - backend (Backend): backend. For BackendV2, `standard_gates` and `warnings` + backend (Backend): backend. For BackendV2, `warnings` options are ignored, and their default values are used. gate_error (bool): Include depolarizing gate errors (Default: True). readout_error (Bool): Include readout errors in model @@ -298,10 +294,7 @@ def from_backend(cls, backend, gate_length_units (str): Time units for gate length values in gate_lengths. Can be 'ns', 'ms', 'us', or 's' (Default: 'ns'). - standard_gates (bool): DEPRECATED, If true return errors as standard - qobj gates. If false return as unitary - qobj instructions (Default: None) - warnings (bool): PLAN TO BE DEPRECATED, Display warnings (Default: None). + warnings (bool): DEPRECATED, Display warnings (Default: None). Returns: NoiseModel: An approximate noise model for the device backend. @@ -309,17 +302,12 @@ def from_backend(cls, backend, Raises: NoiseError: If the input backend is not valid. """ - if standard_gates is not None: - warn( - '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - if warnings is not None: warn( - '"warnings" argument will be deprecated as part of the qiskit-aer 0.12.0 and ' - 'subsequently removed', - PendingDeprecationWarning, stacklevel=2) + '"warnings" argument has been deprecated as of qiskit-aer 0.12.0 ' + 'and will be removed no earlier than 3 months from that release date. ' + 'Use the warnings filter in Python standard library instead.', + DeprecationWarning, stacklevel=2) else: warnings = True @@ -328,27 +316,9 @@ def from_backend(cls, backend, backend_interface_version = 0 target = None - if isinstance(backend, BackendProperties): - warn( - 'Passing BackendProperties instead of a "backend" object ' - 'has been deprecated as of qiskit-aer 0.10.0 and will be ' - 'removed no earlier than 3 months from that release date. ' - 'Duration dependent delay relaxation noise requires a ' - 'backend object.', DeprecationWarning, stacklevel=2) - properties = backend - basis_gates = set() - for prop in properties.gates: - basis_gates.add(prop.gate) - basis_gates = list(basis_gates) - num_qubits = len(properties.qubits) - all_qubit_properties = [QubitProperties(t1=properties.t1(q), - t2=properties.t2(q), - frequency=properties.frequency(q)) - for q in range(num_qubits)] - dt = 0 # disable delay noise if dt is unknown - elif backend_interface_version == 2: - if standard_gates is not None or not warnings: - warn("When a BackendV2 is supplied, `standard_gates` and `warnings`" + if backend_interface_version == 2: + if not warnings: + warn("When a BackendV2 is supplied, `warnings`" " are ignored, and their default values are used.", UserWarning) properties = None basis_gates = backend.operation_names @@ -375,11 +345,9 @@ def from_backend(cls, backend, for q in range(configuration.num_qubits)] dt = getattr(configuration, "dt", 0) if not properties: - raise NoiseError('Qiskit backend {} does not have a ' - 'BackendProperties'.format(backend)) + raise NoiseError(f'Qiskit backend {backend} does not have a BackendProperties') else: - raise NoiseError('{} is not a Qiskit backend or' - ' BackendProperties'.format(backend)) + raise NoiseError(f'{backend} is not a Qiskit backend') noise_model = NoiseModel(basis_gates=basis_gates) @@ -395,11 +363,6 @@ def from_backend(cls, backend, category=DeprecationWarning, module="qiskit_aer.noise" ) - filterwarnings( - "ignore", - category=PendingDeprecationWarning, - module="qiskit_aer.noise" - ) gate_errors = basic_device_gate_errors( properties, gate_error=gate_error, @@ -407,7 +370,6 @@ def from_backend(cls, backend, gate_lengths=gate_lengths, gate_length_units=gate_length_units, temperature=temperature, - standard_gates=standard_gates, warnings=warnings, target=target, ) @@ -555,8 +517,6 @@ def is_ideal(self): # pylint: disable=too-many-return-statements return False if self._local_readout_errors: return False - if self._nonlocal_quantum_errors: - return False if self._custom_noise_passes: return False return True @@ -590,13 +550,6 @@ def __str__(self): if tmp not in local_error_ops: local_error_ops.append(tmp) - # Get nonlocal errors - nonlocal_error_ops = [] - for inst, dic in self._nonlocal_quantum_errors.items(): - for qubits, errors in dic.items(): - for noise_qubits in errors: - nonlocal_error_ops.append((inst, qubits, noise_qubits)) - output = "NoiseModel:" output += "\n Basis gates: {}".format(self.basis_gates) if self._noise_instructions: @@ -610,9 +563,6 @@ def __str__(self): if local_error_ops: output += "\n Specific qubit errors: {}".format( local_error_ops) - if nonlocal_error_ops: - output += "\n Non-local specific qubit errors: {}".format( - nonlocal_error_ops) return output def __eq__(self, other): @@ -634,8 +584,6 @@ def __eq__(self, other): return False if not self._local_quantum_errors_equal(other): return False - if not self._nonlocal_quantum_errors_equal(other): - return False # If we made it here they are equal return True @@ -643,7 +591,7 @@ def reset(self): """Reset the noise model.""" self.__init__() - def add_basis_gates(self, instructions, warnings=False): + def add_basis_gates(self, instructions): """Add additional gates to the noise model basis_gates. This should be used to add any gates that are identified by a @@ -652,8 +600,6 @@ def add_basis_gates(self, instructions, warnings=False): Args: instructions (list[str] or list[Instruction]): the instructions error applies to. - warnings (bool): [DEPRECATED] display warning if instruction is not in - QasmSimulator basis_gates (Default: False). """ for name, _ in self._instruction_names_labels(instructions): # If the instruction is in the default basis gates for the @@ -662,13 +608,6 @@ def add_basis_gates(self, instructions, warnings=False): if name not in ['measure', 'reset', 'initialize', 'kraus', 'superop', 'roerror']: self._basis_gates.add(name) - elif warnings: - warn('"warnings" option has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - logger.warning( - "Warning: Adding a gate \"%s\" to basis_gates which is " - "not in AerSimulator basis_gates.", name) def add_all_qubit_quantum_error(self, error, instructions, warnings=True): """ @@ -799,91 +738,6 @@ def add_quantum_error(self, error, instructions, qubits, warnings=True): self._noise_instructions.add(label) self.add_basis_gates(name) - def add_nonlocal_quantum_error(self, - error, - instructions, - qubits, - noise_qubits, - warnings=True): - """ - Add a non-local quantum error to the noise model (DEPRECATED). - - .. deprecated:: 0.9.0 - - Adding nonlocal noise to a noise model is deprecated and will - be removed no earlier than 3 months from the qiskit-aer 0.9.0 - release date. To add non-local noise to a circuit you should - write a custom qiskit transpiler pass. - - Args: - error (QuantumError): the quantum error object. - instructions (str or list[str] or - Instruction or - list[Instruction]): the instructions error applies to. - qubits (Sequence[int]): qubits instruction error applies to. - noise_qubits (Sequence[int]): Specify the exact qubits the error - should be applied to if different - to the instruction qubits. - warnings (bool): Display warning if appending to an instruction that - already has an error (Default: True). - - Raises: - NoiseError: if the input parameters are invalid. - - Additional Information: - If the error object is ideal it will not be added to the model. - """ - warn('Adding nonlocal noise to a noise model is deprecated as of' - ' qiskit-aer 0.9.0 and will be removed no earlier than 3' - ' months from that release date. To add non-local noise to' - ' a circuit you should write a custom qiskit transpiler pass.', - DeprecationWarning) - - # Error checking - if not isinstance(error, QuantumError): - try: - error = QuantumError(error) - except NoiseError as ex: - raise NoiseError("Input is not a valid quantum error.") from ex - try: - qubits = tuple(qubits) - noise_qubits = tuple(noise_qubits) - except TypeError as ex: - raise NoiseError("Qubits must be convertible to a tuple of integers") from ex - # Check if error is ideal and if so don't add to the noise model - if error.ideal(): - return - # Add noise qubits - for qubit in qubits: - self._noise_qubits.add(qubit) - for qubit in noise_qubits: - self._noise_qubits.add(qubit) - # Add instructions - for name, label in self._instruction_names_labels(instructions): - if label in self._nonlocal_quantum_errors: - gate_qubit_dict = self._nonlocal_quantum_errors[label] - else: - gate_qubit_dict = {} - if qubits in gate_qubit_dict: - noise_qubit_dict = gate_qubit_dict[qubits] - if noise_qubits in noise_qubit_dict: - new_error = noise_qubit_dict[noise_qubits].compose(error) - noise_qubit_dict[noise_qubits] = new_error - else: - noise_qubit_dict[noise_qubits] = error - gate_qubit_dict[qubits] = noise_qubit_dict - if warnings: - logger.warning( - "Warning: nonlocal error already exists for " - "instruction \"%s\" on qubits %s." - "Composing additional error.", label, qubits) - else: - gate_qubit_dict[qubits] = {noise_qubits: error} - # Add updated dictionary - self._nonlocal_quantum_errors[label] = gate_qubit_dict - self._noise_instructions.add(label) - self.add_basis_gates(name, warnings=False) - def add_all_qubit_readout_error(self, error, warnings=True): """ Add a single-qubit readout error that applies measure on all qubits. @@ -1018,16 +872,6 @@ def to_dict(self, serializable=False): error_dict["gate_qubits"] = [qubits] error_list.append(error_dict) - # Add non-local errors - for name, qubit_dict in self._nonlocal_quantum_errors.items(): - for qubits, noise_qubit_dict in qubit_dict.items(): - for noise_qubits, error in noise_qubit_dict.items(): - error_dict = error.to_dict() - error_dict["operations"] = [name] - error_dict["gate_qubits"] = [qubits] - error_dict["noise_qubits"] = [noise_qubits] - error_list.append(error_dict) - # Add default readout error if self._default_readout_error is not None: error_dict = self._default_readout_error.to_dict() @@ -1062,6 +906,37 @@ def from_dict(noise_dict): warn('from_dict has been deprecated as of qiskit-aer 0.10.0' ' and will be removed no earlier than 3 months from that release date.', DeprecationWarning, stacklevel=2) + + def inst_dic_list_to_circuit(dic_list): + num_qubits = max([max(dic['qubits']) for dic in dic_list]) + 1 + circ = QuantumCircuit(num_qubits) + for dic in dic_list: + if dic['name'] == 'reset': + circ.append(Reset(), qargs=dic['qubits']) + elif dic['name'] == 'kraus': + circ.append(Instruction(name='kraus', + num_qubits=len(dic['qubits']), + num_clbits=0, + params=dic['params']), + qargs=dic['qubits']) + elif dic['name'] == 'unitary': + circ.append(UnitaryGate(data=dic['params'][0]), + qargs=dic['qubits']) + elif dic['name'] == 'pauli': + circ.append(PauliGate(dic['params'][0]), + qargs=dic['qubits']) + else: + with catch_warnings(): + filterwarnings( + "ignore", + category=DeprecationWarning, + module="qiskit_aer.noise.errors.errorutils" + ) + circ.append(UnitaryGate(label=dic['name'], + data=_standard_gate_unitary(dic['name'])), + qargs=dic['qubits']) + return circ + # Return noise model noise_model = NoiseModel() @@ -1073,35 +948,20 @@ def from_dict(noise_dict): # Add QuantumError if error_type == 'qerror': - noise_ops = tuple( - zip(error['instructions'], error['probabilities'])) + circuits = [inst_dic_list_to_circuit(dics) for dics in error['instructions']] + noise_ops = tuple(zip(circuits, error['probabilities'])) + qerror = QuantumError(noise_ops) + qerror._id = error.get('id', None) or qerror.id instruction_names = error['operations'] all_gate_qubits = error.get('gate_qubits', None) - all_noise_qubits = error.get('noise_qubits', None) - with catch_warnings(): - filterwarnings("ignore", - category=DeprecationWarning, - module="qiskit_aer.noise") - qerror = QuantumError(noise_ops) - qerror._id = error.get('id', None) or qerror.id if all_gate_qubits is not None: for gate_qubits in all_gate_qubits: - # Load non-local quantum error - if all_noise_qubits is not None: - for noise_qubits in all_noise_qubits: - noise_model.add_nonlocal_quantum_error( - qerror, - instruction_names, - gate_qubits, - noise_qubits, - warnings=False) # Add local quantum error - else: - noise_model.add_quantum_error( - qerror, - instruction_names, - gate_qubits, - warnings=False) + noise_model.add_quantum_error( + qerror, + instruction_names, + gate_qubits, + warnings=False) else: # Add all-qubit quantum error noise_model.add_all_qubit_quantum_error( @@ -1218,26 +1078,6 @@ def _local_quantum_errors_equal(self, other): return False return True - def _nonlocal_quantum_errors_equal(self, other): - """Check two noise models have equal non-local quantum errors""" - if sorted(self._nonlocal_quantum_errors.keys()) != sorted( - other._nonlocal_quantum_errors.keys()): - return False - for key in self._nonlocal_quantum_errors: - inner_dict1 = self._nonlocal_quantum_errors[key] - inner_dict2 = other._nonlocal_quantum_errors[key] - if sorted(inner_dict1.keys()) != sorted(inner_dict2.keys()): - return False - for inner_key in inner_dict1: - iinner_dict1 = inner_dict1[inner_key] - iinner_dict2 = inner_dict2[inner_key] - if sorted(iinner_dict1.keys()) != sorted(iinner_dict2.keys()): - return False - for iinner_key in iinner_dict1: - if iinner_dict1[iinner_key] != iinner_dict2[iinner_key]: - return False - return True - def _pass_manager(self) -> Optional[PassManager]: """ Return the pass manager that add custom noises defined as noise passes @@ -1249,3 +1089,55 @@ def _pass_manager(self) -> Optional[PassManager]: if len(passes) > 0: return PassManager(passes) return None + + +def _standard_gate_unitary(name): + # To be removed with from_dict + unitary_matrices = { + ("id", "I"): + np.eye(2, dtype=complex), + ("x", "X"): + np.array([[0, 1], [1, 0]], dtype=complex), + ("y", "Y"): + np.array([[0, -1j], [1j, 0]], dtype=complex), + ("z", "Z"): + np.array([[1, 0], [0, -1]], dtype=complex), + ("h", "H"): + np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2), + ("s", "S"): + np.array([[1, 0], [0, 1j]], dtype=complex), + ("sdg", "Sdg"): + np.array([[1, 0], [0, -1j]], dtype=complex), + ("t", "T"): + np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex), + ("tdg", "Tdg"): + np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex), + ("cx", "CX", "cx_01"): + np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + ("cx_10",): + np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + ("cz", "CZ"): + np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex), + ("swap", "SWAP"): + np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex), + ("ccx", "CCX", "ccx_012", "ccx_102"): + np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 0, 0]], + dtype=complex), + ("ccx_021", "ccx_201"): + np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0]], + dtype=complex), + ("ccx_120", "ccx_210"): + np.array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]], + dtype=complex) + } + + return next((value for key, value in unitary_matrices.items() if name in key), None) diff --git a/qiskit_aer/utils/__init__.py b/qiskit_aer/utils/__init__.py index 26fb383918..e386ca97f1 100644 --- a/qiskit_aer/utils/__init__.py +++ b/qiskit_aer/utils/__init__.py @@ -22,35 +22,23 @@ using noise models. -Classes -======= - -.. autosummary:: - :toctree: ../stubs/ - - NoiseTransformer - - Functions ========= .. autosummary:: :toctree: ../stubs/ - transform_noise_model - transpile_noise_model - transpile_quantum_error - remap_noise_model insert_noise approximate_quantum_error approximate_noise_model + transform_noise_model + transpile_noise_model + transpile_quantum_error """ -from .noise_remapper import remap_noise_model +from .noise_model_inserter import insert_noise +from .noise_transformation import approximate_noise_model +from .noise_transformation import approximate_quantum_error from .noise_transformation import transform_noise_model from .noise_transformation import transpile_noise_model from .noise_transformation import transpile_quantum_error -from .noise_transformation import NoiseTransformer -from .noise_transformation import approximate_quantum_error -from .noise_transformation import approximate_noise_model -from .noise_model_inserter import insert_noise diff --git a/qiskit_aer/utils/noise_model_inserter.py b/qiskit_aer/utils/noise_model_inserter.py index aeb91395d7..3ab5ea266e 100644 --- a/qiskit_aer/utils/noise_model_inserter.py +++ b/qiskit_aer/utils/noise_model_inserter.py @@ -38,7 +38,6 @@ def insert_noise(circuits, noise_model, transpile=False): is_circuits_list = isinstance(circuits, (list, tuple)) circuits = circuits if is_circuits_list else [circuits] result_circuits = [] - nonlocal_errors = noise_model._nonlocal_quantum_errors local_errors = noise_model._local_quantum_errors default_errors = noise_model._default_quantum_errors for circuit in circuits: @@ -54,11 +53,8 @@ def insert_noise(circuits, noise_model, transpile=False): result_circuit.data.append((inst, qargs, cargs)) qubits = tuple(qubit_indices[q] for q in qargs) # Priority for error model used: - # nonlocal error > local error > default error - if inst.name in nonlocal_errors and qubits in nonlocal_errors[inst.name]: - for noise_qubits, error in nonlocal_errors[inst.name][qubits].items(): - result_circuit.append(error.to_instruction(), noise_qubits) - elif inst.name in local_errors and qubits in local_errors[inst.name]: + # local error > default error + if inst.name in local_errors and qubits in local_errors[inst.name]: error = local_errors[inst.name][qubits] result_circuit.append(error.to_instruction(), qargs) elif inst.name in default_errors.keys(): diff --git a/qiskit_aer/utils/noise_remapper.py b/qiskit_aer/utils/noise_remapper.py deleted file mode 100644 index 85b9d07b07..0000000000 --- a/qiskit_aer/utils/noise_remapper.py +++ /dev/null @@ -1,130 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2019. -# -# 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. - -""" -Remap qubits in a NoiseModel. -""" - -import logging -from warnings import warn - -from ..noise.noise_model import NoiseModel -from ..noise.noiseerror import NoiseError - -logger = logging.getLogger(__name__) - - -def remap_noise_model(noise_model, remapping, discard_qubits=False, warnings=True): - """[Deprecated] Remap qubits in a noise model. - - This remaps the specified gate qubits for local quantum errors, the gate - and noise qubits for non-local quantum errors, and the gate qubits for - local ReadoutErrors. All-qubit quantum and readout errors are unaffected. - - Args: - noise_model (NoiseModel): a noise model to remap qubits. - remapping (list): list or remappings of old qubit to new qubit. - See Additional Information. - discard_qubits (bool): if True discard qubits not in remapping keys, - if False an identity mapping wil be assumed - for unnamed qubits (Default: False). - warnings (bool): display warnings if qubits being remapped are not - in the input noise model (Default: True). - - Returns: - NoiseModel: a new noise model with the same errors but remapped - gate and noise qubits for local and non-local errors. - - Raises: - NoiseError: if remapping has duplicate qubits in the remapped qubits. - - Additional Information: - * The remapping map be specified as either a list of pairs: - ``[(old, new), ...]``, or a list of old qubits where the new qubit is - inferred from the position: ``[old0, old1, ...]`` is treated as - ``[(old0, 0), (old1, 1), ...]``. - - * If ``discard_qubits`` is ``False``, any qubits in the noise model not - specified in the list of old qubits will be added to the remapping as - a trivial mapping ``(qubit, qubit)``. - """ - warn( - 'The "remap_noise_model" function has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - if not isinstance(noise_model, NoiseModel): - raise NoiseError("Input must be a NoiseModel.") - - if warnings: - # Warning if remapped qubit isn't in noise model - for qubit in remapping: - if qubit not in noise_model.noise_qubits: - logger.warning('Warning: qubit %s is not in noise model', qubit) - - # Convert remapping into a inverse mapping dict - inv_map = {} - for pos, item in enumerate(remapping): - if isinstance(item, (list, tuple)) and len(item) == 2: - inv_map[int(item[0])] = int(item[1]) - elif isinstance(item, int): - inv_map[item] = pos - # Add noise model qubits not in mapping as identity mapping - if not discard_qubits: - for qubit in noise_model.noise_qubits: - if qubit not in inv_map: - inv_map[qubit] = qubit - - # Check mapping doesn't have duplicate qubits in output - new_qubits = list(inv_map.values()) - if len(set(new_qubits)) != len(new_qubits): - raise NoiseError('Duplicate qubits in remapping: {}'.format(inv_map)) - - # qubits not to be remapped - discarded_qubits = set(noise_model.noise_qubits) - set(inv_map.keys()) - - def in_target(qubits): # check if qubits are to be remapped or not - if not discard_qubits: - return True - for q in qubits: - if q in discarded_qubits: - return False - return True - - def remapped(qubits): - return tuple(inv_map[q] for q in qubits) - - # Copy from original noise model - new_model = NoiseModel() - new_model._basis_gates = noise_model._basis_gates - # No remapping for default errors - for inst_name, noise in noise_model._default_quantum_errors.items(): - new_model.add_all_qubit_quantum_error(noise, inst_name) - if noise_model._default_readout_error: - new_model.add_all_qubit_readout_error(noise_model._default_readout_error) - # Remapping - for inst_name, noise_dic in noise_model._local_quantum_errors.items(): - for qubits, noise in noise_dic.items(): - if in_target(qubits): - new_model.add_quantum_error(noise, inst_name, remapped(qubits)) - for inst_name, outer_dic in noise_model._nonlocal_quantum_errors.items(): - for qubits, inner_dic in outer_dic.items(): - if in_target(qubits): - for noise_qubits, noise in inner_dic.items(): - if in_target(noise_qubits): - new_model.add_nonlocal_quantum_error( - noise, inst_name, remapped(qubits), remapped(noise_qubits) - ) - for qubits, noise in noise_model._local_readout_errors.items(): - if in_target(qubits): - new_model.add_readout_error(noise, remapped(qubits)) - return new_model diff --git a/qiskit_aer/utils/noise_transformation.py b/qiskit_aer/utils/noise_transformation.py index b058259170..dd00df0947 100644 --- a/qiskit_aer/utils/noise_transformation.py +++ b/qiskit_aer/utils/noise_transformation.py @@ -28,23 +28,22 @@ # pylint: disable=import-outside-toplevel import copy -import itertools import logging -import warnings from typing import Sequence, List, Union, Callable import numpy as np from qiskit.circuit import Reset -from qiskit.circuit.library.standard_gates import IGate, XGate, YGate, ZGate +from qiskit.circuit.library.standard_gates import ( + IGate, XGate, YGate, ZGate, + HGate, SGate, SdgGate, +) from qiskit.compiler import transpile from qiskit.exceptions import MissingOptionalLibraryError from qiskit.quantum_info.operators.channel import Kraus, SuperOp, Chi from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel from qiskit.transpiler.exceptions import TranspilerError from ..noise.errors import QuantumError -from ..noise.errors.errorutils import _CLIFFORD_GATES -from ..noise.errors.errorutils import single_qubit_clifford_matrix from ..noise.noise_model import NoiseModel from ..noise.noiseerror import NoiseError @@ -80,11 +79,6 @@ def transform_noise_model(noise_model: NoiseModel, func: Callable) -> NoiseModel for qubits, error in noise_dic.items(): new_noise._local_quantum_errors[inst_name][qubits] = func(error) - for inst_name, outer_dic in new_noise._nonlocal_quantum_errors.items(): - for qubits, inner_dic in outer_dic.items(): - for noise_qubits, error in inner_dic.items(): - new_noise._nonlocal_quantum_errors[inst_name][qubits][noise_qubits] = func(error) - return new_noise @@ -185,20 +179,7 @@ def approximate_quantum_error(error, *, The ``'clifford'`` does not support 2-qubit errors. """ if not isinstance(error, (QuantumError, QuantumChannel)): - warnings.warn( - 'Support of types other than QuantumError or QuantumChannel for error' - ' to be approximated has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - if isinstance(error, tuple) and isinstance(error[0], np.ndarray): - error = list(error) - if isinstance(error, list) or \ - (isinstance(error, tuple) and isinstance(error[0], list)): - # first case for ordinary Kraus [A_i, B_i] - # second case for generalized Kraus ([A_i], [B_i]) - error = Kraus(error) - else: - raise NoiseError(f"Invalid input error type: {error.__class__.__name__}") + raise NoiseError(f"Invalid input error type: {error.__class__.__name__}") if error.num_qubits > 2: raise NoiseError("Only 1-qubit and 2-qubit noises can be converted, " @@ -212,29 +193,19 @@ def approximate_quantum_error(error, *, f"It must be one of {valid_operator_strings}") try: operator_list = _PRESET_OPERATOR_TABLE[operator_string][error.num_qubits] - except KeyError: + except KeyError as err: raise NoiseError(f"Preset '{operator_string}' operators do not support the " - f"approximation of errors with {error.num_qubits} qubits") + f"approximation of errors with {error.num_qubits} qubits") from err if operator_dict is not None: _, operator_list = zip(*operator_dict.items()) if operator_list is not None: if not isinstance(operator_list, Sequence): - raise NoiseError("operator_list is not a sequence: {}".format(operator_list)) - if operator_list and isinstance(operator_list[0], Sequence) and isinstance( - operator_list[0][0], np.ndarray): - warnings.warn( - 'Accepting a sequence of Kraus matrices (list of numpy arrays)' - ' as an operator_list has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.' - ' Please use a sequence of Kraus objects instead.', - DeprecationWarning, stacklevel=2) - operator_list = [Kraus(op) for op in operator_list] - + raise NoiseError(f"operator_list is not a sequence: {operator_list}") try: channel_list = [op if isinstance(op, QuantumChannel) else QuantumError([(op, 1)]) for op in operator_list] # preserve operator_list - except NoiseError: - raise NoiseError(f"Invalid type found in operator list: {operator_list}") + except NoiseError as err: + raise NoiseError(f"Invalid type found in operator list: {operator_list}") from err probabilities = _transform_by_operator_list(channel_list, error)[1:] identity_prob = np.round(1 - sum(probabilities), 9) @@ -308,6 +279,35 @@ def approximate(noise): _RESET_Q1_TO_1 = [(Reset(), [1]), (XGate(), [1])] _RESET_Q1 = [[(IGate(), [1])], _RESET_Q1_TO_0, _RESET_Q1_TO_1] _RESET_Q0Q1 = [op_q0 + op_q1 for op_q0 in _RESET_Q0 for op_q1 in _RESET_Q1] +# clifford operators +_CLIFFORD_GATES = [ + (IGate(), ), + (SGate(), ), + (SdgGate(), ), + (ZGate(), ), + # u2 gates + (HGate(), ), + (HGate(), ZGate()), + (ZGate(), HGate()), + (HGate(), SGate()), + (SGate(), HGate()), + (HGate(), SdgGate()), + (SdgGate(), HGate()), + (SGate(), HGate(), SGate()), + (SdgGate(), HGate(), SGate()), + (ZGate(), HGate(), SGate()), + (SGate(), HGate(), SdgGate()), + (SdgGate(), HGate(), SdgGate()), + (ZGate(), HGate(), SdgGate()), + (SGate(), HGate(), ZGate()), + (SdgGate(), HGate(), ZGate()), + (ZGate(), HGate(), ZGate()), + # u3 gates + (XGate(), ), + (YGate(), ), + (SGate(), XGate()), + (SdgGate(), XGate()) +] # preset operator table _PRESET_OPERATOR_TABLE = { "pauli": { @@ -417,552 +417,3 @@ def _hs_norm(A): # pylint: disable=invalid-name def _hs_inner_prod_real(A, B): # pylint: disable=invalid-name # Re[Tr(A^† B)] return np.trace(np.conj(A.T) @ B).real - - -# ================== Deprecated interfaces ================== # -# TODO: remove after deprecation period -def pauli_operators(): - """[Deprecated] Return a list of Pauli operators for 1 and 2 qubits.""" - warnings.warn( - '"pauli_operators" has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - pauli_1_qubit = { - 'X': [{'name': 'x', 'qubits': [0]}], - 'Y': [{'name': 'y', 'qubits': [0]}], - 'Z': [{'name': 'z', 'qubits': [0]}] - } - pauli_2_qubit = { - 'XI': [{'name': 'x', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], - 'YI': [{'name': 'y', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], - 'ZI': [{'name': 'z', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], - 'IX': [{'name': 'id', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], - 'IY': [{'name': 'id', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], - 'IZ': [{'name': 'id', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], - 'XX': [{'name': 'x', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], - 'YY': [{'name': 'y', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], - 'ZZ': [{'name': 'z', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], - 'XY': [{'name': 'x', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], - 'XZ': [{'name': 'x', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], - 'YX': [{'name': 'y', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], - 'YZ': [{'name': 'y', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], - 'ZX': [{'name': 'z', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], - 'ZY': [{'name': 'z', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], - } - return [pauli_1_qubit, pauli_2_qubit] - - -def reset_operators(): - """[Deprecated] Return a list of reset operators for 1 and 2 qubits.""" - warnings.warn( - '"reset_operators" has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - - reset_0_to_0 = [{'name': 'reset', 'qubits': [0]}] - reset_0_to_1 = [{'name': 'reset', 'qubits': [0]}, {'name': 'x', 'qubits': [0]}] - reset_1_to_0 = [{'name': 'reset', 'qubits': [1]}] - reset_1_to_1 = [{'name': 'reset', 'qubits': [1]}, {'name': 'x', 'qubits': [1]}] - id_0 = [{'name': 'id', 'qubits': [0]}] - id_1 = [{'name': 'id', 'qubits': [1]}] - - reset_1_qubit = { - 'p': reset_0_to_0, - 'q': reset_0_to_1 - } - - reset_2_qubit = { - 'p0': reset_0_to_0 + id_1, - 'q0': reset_0_to_1 + id_1, - 'p1': reset_1_to_0 + id_0, - 'q1': reset_1_to_1 + id_0, - 'p0_p1': reset_0_to_0 + reset_1_to_0, - 'p0_q1': reset_0_to_0 + reset_1_to_1, - 'q0_p1': reset_0_to_1 + reset_1_to_0, - 'q0_q1': reset_0_to_1 + reset_1_to_1, - } - return [reset_1_qubit, reset_2_qubit] - - -class NoiseTransformer: - """[Deprecated] Transforms one quantum channel to another based on a specified criteria.""" - - def __init__(self): - warnings.warn( - '"NoiseTransformer" class has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) - self.named_operators = { - 'pauli': pauli_operators(), - 'reset': reset_operators(), - 'clifford': [{j: single_qubit_clifford_matrix(j) for j in range(1, 24)}] - } - self.fidelity_data = None - self.use_honesty_constraint = True - self.noise_kraus_operators = None - self.transform_channel_operators = None - - def operator_matrix(self, operator): - """Converts an operator representation to Kraus matrix representation - - Args: - operator (operator): operator representation. Can be a noise - circuit or a matrix or a list of matrices. - - Returns: - Kraus: the operator, converted to Kraus representation. - """ - if isinstance(operator, list) and isinstance(operator[0], dict): - operator_error = QuantumError([(operator, 1)]) - kraus_rep = Kraus(operator_error.to_quantumchannel()).data - return kraus_rep - return operator - - def operator_circuit(self, operator): - """Converts an operator representation to noise circuit. - - Args: - operator (operator): operator representation. Can be a noise - circuit or a matrix or a list of matrices. - Returns: - List: The operator, converted to noise circuit representation. - """ - if isinstance(operator, np.ndarray): - return [{'name': 'unitary', 'qubits': [0], 'params': [operator]}] - - if isinstance(operator, list) and isinstance(operator[0], - np.ndarray): - if len(operator) == 1: - return [{'name': 'unitary', 'qubits': [0], 'params': operator}] - else: - return [{'name': 'kraus', 'qubits': [0], 'params': operator}] - - return operator - - # transformation interface methods - def transform_by_operator_list(self, transform_channel_operators, - noise_kraus_operators): - r""" - Transform input Kraus operators. - - Allows approximating a set of input Kraus operators as in terms of - a different set of Kraus matrices. - - For example, setting :math:`[X, Y, Z]` allows approximating by a - Pauli channel, and :math:`[(|0 \langle\rangle 0|, - |0\langle\rangle 1|), |1\langle\rangle 0|, |1 \langle\rangle 1|)]` - represents the relaxation channel - - In the case the input is a list :math:`[A_1, A_2, ..., A_n]` of - transform matrices and :math:`[E_0, E_1, ..., E_m]` of noise Kraus - operators, the output is a list :math:`[p_1, p_2, ..., p_n]` of - probabilities such that: - - 1. :math:`p_i \ge 0` - 2. :math:`p_1 + ... + p_n \le 1` - 3. :math:`[\sqrt(p_1) A_1, \sqrt(p_2) A_2, ..., \sqrt(p_n) A_n, - \sqrt(1-(p_1 + ... + p_n))I]` is a list of Kraus operators that - define the output channel (which is "close" to the input channel - given by :math:`[E_0, ..., E_m]`.) - - This channel can be thought of as choosing the operator :math:`A_i` - in probability :math:`p_i` and applying this operator to the - quantum state. - - More generally, if the input is a list of tuples (not necessarily - of the same size): :math:`[(A_1, B_1, ...), (A_2, B_2, ...), - ..., (A_n, B_n, ...)]` then the output is still a list - :math:`[p_1, p_2, ..., p_n]` and now the output channel is defined - by the operators: - :math:`[\sqrt(p_1)A1, \sqrt(p_1)B_1, ..., \sqrt(p_n)A_n, - \sqrt(p_n)B_n, ..., \sqrt(1-(p_1 + ... + p_n))I]` - - Args: - noise_kraus_operators (List): a list of matrices (Kraus operators) - for the input channel. - transform_channel_operators (List): a list of matrices or tuples - of matrices representing Kraus operators that can construct the output channel. - - Returns: - List: A list of amplitudes that define the output channel. - """ - self.noise_kraus_operators = noise_kraus_operators - # pylint: disable=invalid-name - self.transform_channel_operators = transform_channel_operators - full_transform_channel_operators = self.prepare_channel_operator_list( - self.transform_channel_operators) - channel_matrices, const_channel_matrix = self.generate_channel_matrices( - full_transform_channel_operators) - self.prepare_honesty_constraint(full_transform_channel_operators) - probabilities = self.transform_by_given_channel( - channel_matrices, const_channel_matrix) - return probabilities - - @staticmethod - def prepare_channel_operator_list(ops_list): - """ - Prepares a list of channel operators. - - Args: - ops_list (List): The list of operators to prepare - - Returns: - List: The channel operator list - """ - from sympy import Matrix, eye - # convert to sympy matrices and verify that each singleton is - # in a tuple; also add identity matrix - result = [] - for ops in ops_list: - if not isinstance(ops, tuple) and not isinstance(ops, list): - ops = [ops] - result.append([Matrix(op) for op in ops]) - n = result[0][0].shape[0] # grab the dimensions from the first element - result = [[eye(n)]] + result - return result - - # pylint: disable=invalid-name - def prepare_honesty_constraint(self, transform_channel_operators_list): - """ - Prepares the honesty constraint. - - Args: - transform_channel_operators_list (list): A list of tuples of matrices which represent - Kraus operators. - """ - if not self.use_honesty_constraint: - return - - goal = self.fidelity(self.noise_kraus_operators) - coefficients = [ - self.fidelity(ops) for ops in transform_channel_operators_list - ] - self.fidelity_data = { - 'goal': goal, - 'coefficients': - coefficients[1:] # coefficients[0] corresponds to I - } - - # methods relevant to the transformation to quadratic programming instance - - @staticmethod - def fidelity(channel): - """ Calculates channel fidelity """ - return sum([np.abs(np.trace(E)) ** 2 for E in channel]) - - # pylint: disable=invalid-name - def generate_channel_matrices(self, transform_channel_operators_list): - r""" - Generate symbolic channel matrices. - - Generates a list of 4x4 symbolic matrices describing the channel - defined from the given operators. The identity matrix is assumed - to be the first element in the list: - - .. code-block:: python - - [(I, ), (A1, B1, ...), (A2, B2, ...), ..., (An, Bn, ...)] - - E.g. for a Pauli channel, the matrices are: - - .. code-block:: python - - [(I,), (X,), (Y,), (Z,)] - - For relaxation they are: - - .. code-block:: python - - [(I, ), (|0><0|, |0><1|), |1><0|, |1><1|)] - - We consider this input to symbolically represent a channel in the - following manner: define indeterminates :math:`x_0, x_1, ..., x_n` - which are meant to represent probabilities such that - :math:`x_i \ge 0` and :math:`x0 = 1-(x_1 + ... + x_n)`. - - Now consider the quantum channel defined via the Kraus operators - :math:`{\sqrt(x_0)I, \sqrt(x_1) A_1, \sqrt(x1) B_1, ..., - \sqrt(x_m)A_n, \sqrt(x_n) B_n, ...}` - This is the channel C symbolically represented by the operators. - - Args: - transform_channel_operators_list (list): A list of tuples of - matrices which represent Kraus operators. - - Returns: - list: A list of 4x4 complex matrices ``([D1, D2, ..., Dn], E)`` - such that the matrix :math:`x_1 D_1 + ... + x_n D_n + E` - represents the operation of the channel C on the density - operator. we find it easier to work with this representation - of C when performing the combinatorial optimization. - """ - - from sympy import symbols as sp_symbols, sqrt - symbols_string = " ".join([ - "x{}".format(i) - for i in range(len(transform_channel_operators_list)) - ]) - symbols = sp_symbols(symbols_string, real=True, positive=True) - exp = symbols[ - 1] # exp will contain the symbolic expression "x1 +...+ xn" - for i in range(2, len(symbols)): - exp = symbols[i] + exp - # symbolic_operators_list is a list of lists; we flatten it the next line - symbolic_operators_list = [[ - sqrt(symbols[i]) * op for op in ops - ] for (i, ops) in enumerate(transform_channel_operators_list)] - symbolic_operators = [ - op for ops in symbolic_operators_list for op in ops - ] - # channel_matrix_representation() performs the required linear - # algebra to find the representing matrices. - operators_channel = self.channel_matrix_representation( - symbolic_operators).subs(symbols[0], 1 - exp) - return self.generate_channel_quadratic_programming_matrices( - operators_channel, symbols[1:]) - - @staticmethod - def compute_channel_operation(rho, operators): - """ - Given a quantum state's density function rho, the effect of the - channel on this state is: - rho -> sum_{i=1}^n E_i * rho * E_i^dagger - - Args: - rho (number): Density function - operators (list): List of operators - - Returns: - number: The result of applying the list of operators - """ - from sympy import zeros - return sum([E * rho * E.H for E in operators], - zeros(operators[0].rows)) - - @staticmethod - def flatten_matrix(m): - """ - Args: - m (Matrix): The matrix to flatten - - Returns: - list: A row vector repesenting the flattened matrix - """ - return list(m) - - def channel_matrix_representation(self, operators): - """ - We convert the operators to a matrix by applying the channel to - the four basis elements of the 2x2 matrix space representing - density operators; this is standard linear algebra - - Args: - operators (list): The list of operators to transform into a Matrix - - Returns: - sympy.Matrix: The matrx representation of the operators - """ - from sympy import Matrix, zeros - shape = operators[0].shape - standard_base = [] - for i in range(shape[0]): - for j in range(shape[1]): - basis_element_ij = zeros(*shape) - basis_element_ij[(i, j)] = 1 - standard_base.append(basis_element_ij) - - return (Matrix([ - self.flatten_matrix( - self.compute_channel_operation(rho, operators)) - for rho in standard_base - ])) - - def generate_channel_quadratic_programming_matrices( - self, channel, symbols): - """ - Generate matrices for quadratic program. - - Args: - channel (Matrix): a 4x4 symbolic matrix - symbols (list): the symbols x1, ..., xn which may occur in the matrix - - Returns: - list: A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: - channel == x1*D1 + ... + xn*Dn + E - """ - return ( - [self.get_matrix_from_channel(channel, symbol) for symbol in symbols], - self.get_const_matrix_from_channel(channel, symbols) - ) - - @staticmethod - def get_matrix_from_channel(channel, symbol): - """ - Extract the numeric parameter matrix. - - Args: - channel (matrix): a 4x4 symbolic matrix. - symbol (list): a symbol xi - - Returns: - matrix: a 4x4 numeric matrix. - - Additional Information: - Each entry of the 4x4 symbolic input channel matrix is assumed to - be a polynomial of the form a1x1 + ... + anxn + c. The corresponding - entry in the output numeric matrix is ai. - """ - from sympy import Poly - n = channel.rows - M = np.zeros((n, n), dtype=np.complex_) - for (i, j) in itertools.product(range(n), range(n)): - M[i, j] = complex( - Poly(channel[i, j], symbol).coeff_monomial(symbol)) - return M - - @staticmethod - def get_const_matrix_from_channel(channel, symbols): - """ - Extract the numeric constant matrix. - - Args: - channel (matrix): a 4x4 symbolic matrix. - symbols (list): The full list [x1, ..., xn] of symbols - used in the matrix. - - Returns: - matrix: a 4x4 numeric matrix. - - Additional Information: - Each entry of the 4x4 symbolic input channel matrix is assumed to - be a polynomial of the form a1x1 + ... + anxn + c. The corresponding - entry in the output numeric matrix is c. - """ - from sympy import Poly - n = channel.rows - M = np.zeros((n, n), dtype=np.complex_) - for (i, j) in itertools.product(range(n), range(n)): - M[i, j] = complex( - Poly(channel[i, j], symbols).coeff_monomial(1)) - return M - - def transform_by_given_channel(self, channel_matrices, - const_channel_matrix): - """ - Transform by by quantum channels. - - This method creates objective function representing the - Hilbert-Schmidt norm of the matrix (A-B) obtained - as the difference of the input noise channel and the output - channel we wish to determine. - - This function is represented by a matrix P and a vector q, such that - f(x) = 1/2(x*P*x)+q*x - where x is the vector we wish to minimize, where x represents - probabilities for the noise operators that construct the output channel - - Args: - channel_matrices (list): A list of 4x4 symbolic matrices - const_channel_matrix (matrix): a 4x4 constant matrix - - Returns: - list: a list of the optimal probabilities for the channel matrices, - determined by the quadratic program solver - """ - target_channel = SuperOp(Kraus(self.noise_kraus_operators)) - target_channel_matrix = target_channel._data.T - - const_matrix = const_channel_matrix - target_channel_matrix - P = self.compute_P(channel_matrices) - q = self.compute_q(channel_matrices, const_matrix) - return self.solve_quadratic_program(P, q) - - def compute_P(self, As): - """ - This method creates the matrix P in the - f(x) = 1/2(x*P*x)+q*x - representation of the objective function - Args: - As (list): list of symbolic matrices repersenting the channel matrices - - Returns: - matrix: The matrix P for the description of the quadaric program - """ - from sympy import zeros - vs = [np.array(A).flatten() for A in As] - n = len(vs) - P = zeros(n, n) - for (i, j) in itertools.product(range(n), range(n)): - P[i, j] = 2 * np.real(np.dot(vs[i], np.conj(vs[j]))) - return P - - def compute_q(self, As, C): - """ - This method creates the vector q for the - f(x) = 1/2(x*P*x)+q*x - representation of the objective function - Args: - As (list): list of symbolic matrices repersenting the quadratic program - C (matrix): matrix representing the the constant channel matrix - - Returns: - list: The vector q for the description of the quadaric program - """ - from sympy import zeros - vs = [np.array(A).flatten() for A in As] - vC = np.array(C).flatten() - n = len(vs) - q = zeros(1, n) - for i in range(n): - q[i] = 2 * np.real(np.dot(np.conj(vC), vs[i])) - return q - - def solve_quadratic_program(self, P, q): - """ - Solve the quadratic program optimization problem. - - This function solved the quadratic program to minimize the objective function - f(x) = 1/2(x*P*x)+q*x - subject to the additional constraints - Gx <= h - - Where P, q are given and G,h are computed to ensure that x represents - a probability vector and subject to honesty constraints if required - Args: - P (matrix): A matrix representing the P component of the objective function - q (list): A vector representing the q component of the objective function - - Returns: - list: The solution of the quadratic program (represents probabilities) - - Additional information: - This method is the only place in the code where we rely on the cvxpy library - should we consider another library, only this method needs to change. - """ - try: - import cvxpy - except ImportError: - logger.error("cvxpy module needs to be installed to use this feature.") - - P = np.array(P).astype(float) - q = np.array(q).astype(float).T - n = len(q) - # G and h constrain: - # 1) sum of probs is less then 1 - # 2) All probs bigger than 0 - # 3) Honesty (measured using fidelity, if given) - G_data = [[1] * n] + [([-1 if i == k else 0 for i in range(n)]) - for k in range(n)] - h_data = [1] + [0] * n - if self.fidelity_data is not None: - G_data.append(self.fidelity_data['coefficients']) - h_data.append(self.fidelity_data['goal']) - G = np.array(G_data).astype(float) - h = np.array(h_data).astype(float) - x = cvxpy.Variable(n) - prob = cvxpy.Problem( - cvxpy.Minimize((1 / 2) * cvxpy.quad_form(x, P) + q.T @ x), - [G @ x <= h]) - prob.solve() - return x.value diff --git a/releasenotes/notes/remove-deprecated-noise-functions-52128d161d3327e9.yaml b/releasenotes/notes/remove-deprecated-noise-functions-52128d161d3327e9.yaml new file mode 100644 index 0000000000..1409f8439e --- /dev/null +++ b/releasenotes/notes/remove-deprecated-noise-functions-52128d161d3327e9.yaml @@ -0,0 +1,72 @@ +--- +upgrade: + - | + A deprecated method :meth:`add_nonlocal_quantum_error` in :class:`~.NoiseModel` has been + removed. No alternative method is available. If you want to add non-local quantum errors, + you should write a transpiler pass that inserts your own quantum error into a circuit, + and run the pass just before running the circuit on Aer simulator. + - | + The :meth:`.NoiseModel.from_backend` now has changed not to accept ``BackendProperties`` + object as a ``backend`` argument. Use newly added :meth:`.NoiseModel.from_backend_properties` + method instead. + - | + A deprecated ``standard_gates`` argument broadly used in several methods and functions + (listed below) across :mod:`~.noise` module has been removed. + + * :meth:`NoiseModel.from_backend` and :func:`noise.device.basic_device_gate_errors` + * :func:`kraus_error`, :func:`mixed_unitary_error`, :func:`pauli_error` and + :func:`depolarizing_error` in :mod:`noise.errors.standard_errors` + * :meth:`QuantumError.__init__` + + No alternative means are available because the user should be agnostic about + how the simulator represents noises (quantum errors) internally. + - | + The constructor of :class:`~.QuantumError` has now dropped the support of deprecated + json-like input for ``noise_ops`` argument. + Use the new styple input for ``noise_ops`` argument instead, for example, + + .. code-block:: python + + from qiskit.circuit.library import IGate, XGate + from qiskit_aer.noise import QuantumError + + error = QuantumError([ + ((IGate(), [1]), 0.9), + ((XGate(), [1]), 0.1), + ]) + + # json-like input is no longer accepted (the following code fails) + # error = QuantumError([ + # ([{"name": "I", "qubits": [1]}], 0.9), + # ([{"name": "X", "qubits": [1]}], 0.1), + # ]) + + Also it has dropped deprecated arguments: + + * ``number_of_qubits``: Use ``QuantumCircuit`` to define ``noise_ops`` instead. + * ``atol``: Use :attr:`QuantumError.atol` attribute instead. + * ``standard_gates``: No alternative is available (users should not too much care about + internal representation of quantum errors). + + - | + The deprecated :mod:`noise.errors.errorutils` module has been entirely removed + and no alternatives are available. + All functions in the module were helper functions meant to be used + only for implementing functions in :mod:`~.noise.errors.standard_errors` + (i.e. they should have been provided as private functions) + and no longer used in it. + - | + The deprecated :mod:`utils.noise_remapper` have been entirely removed and no alternatives + are available since the C++ code now automatically truncates and remaps noise models + if it truncates circuits. + - | + All deprecated functions (:func:`pauli_operators` and :func:`reset_operators`) + and class (:class:`NoiseTransformer`) in :mod:`utils.noise_transformation` module + have been removed, and no alternatives are available. + They were in fact private functions/class used only for implementing + :func:`approximate_quantum_error` and should not have been public. +deprecations: + - | + A ``warnings`` argument broadly used in several methods and functions + across :mod:`~.noise` module has been deprecated in favor of + the use of filtering functions in Python's standard ``warnings`` library. diff --git a/test/terra/backends/aer_simulator/test_noise.py b/test/terra/backends/aer_simulator/test_noise.py index 382020b122..caedb4f887 100644 --- a/test/terra/backends/aer_simulator/test_noise.py +++ b/test/terra/backends/aer_simulator/test_noise.py @@ -73,8 +73,7 @@ def test_pauli_gate_noise(self, method, device): backend = self.backend(method=method, device=device) shots = 1000 circuits = ref_pauli_noise.pauli_gate_error_circuits() - with self.assertWarns(DeprecationWarning): - noise_models = ref_pauli_noise.pauli_gate_error_noise_models() + noise_models = ref_pauli_noise.pauli_gate_error_noise_models() targets = ref_pauli_noise.pauli_gate_error_counts(shots) for circuit, noise_model, target in zip(circuits, noise_models, @@ -92,8 +91,7 @@ def test_pauli_reset_noise(self, method, device): backend = self.backend(method=method, device=device) shots = 1000 circuits = ref_pauli_noise.pauli_reset_error_circuits() - with self.assertWarns(DeprecationWarning): - noise_models = ref_pauli_noise.pauli_reset_error_noise_models() + noise_models = ref_pauli_noise.pauli_reset_error_noise_models() targets = ref_pauli_noise.pauli_reset_error_counts(shots) for circuit, noise_model, target in zip(circuits, noise_models, @@ -109,8 +107,7 @@ def test_pauli_measure_noise(self, method, device): backend = self.backend(method=method, device=device) shots = 1000 circuits = ref_pauli_noise.pauli_measure_error_circuits() - with self.assertWarns(DeprecationWarning): - noise_models = ref_pauli_noise.pauli_measure_error_noise_models() + noise_models = ref_pauli_noise.pauli_measure_error_noise_models() targets = ref_pauli_noise.pauli_measure_error_counts(shots) for circuit, noise_model, target in zip(circuits, noise_models, diff --git a/test/terra/noise/test_noise_inserter.py b/test/terra/noise/test_noise_inserter.py index e7ed620f2c..725024ea30 100644 --- a/test/terra/noise/test_noise_inserter.py +++ b/test/terra/noise/test_noise_inserter.py @@ -91,26 +91,6 @@ def test_local_quantum_errors(self): self.assertEqual(SuperOp(target_circuit), SuperOp(result_circuit)) - def test_nonlocal_quantum_errors(self): - qr = QuantumRegister(3, 'qr') - circuit = QuantumCircuit(qr) - circuit.x(qr[0]) - circuit.x(qr[2]) - - error_x = pauli_error([('Y', 0.25), ('I', 0.75)]) - noise_model = NoiseModel() - with self.assertWarns(DeprecationWarning): - noise_model.add_nonlocal_quantum_error(error_x, 'x', [0], [1]) - - target_circuit = QuantumCircuit(qr) - target_circuit.x(qr[0]) - target_circuit.append(error_x.to_instruction(), [qr[1]]) - target_circuit.x(qr[2]) - - result_circuit = insert_noise(circuit, noise_model) - - self.assertEqual(SuperOp(target_circuit), SuperOp(result_circuit)) - def test_transpiling(self): qr = QuantumRegister(3, 'qr') circuit = QuantumCircuit(qr) diff --git a/test/terra/noise/test_noise_model.py b/test/terra/noise/test_noise_model.py index af42100972..35a32b32dd 100644 --- a/test/terra/noise/test_noise_model.py +++ b/test/terra/noise/test_noise_model.py @@ -149,13 +149,6 @@ def test_noise_model_noise_qubits(self): target = sorted([0, 1]) self.assertEqual(model.noise_qubits, target) - # Check adding a non-local error adds to noise qubits - model = NoiseModel() - with self.assertWarns(DeprecationWarning): - model.add_nonlocal_quantum_error(pauli_error([['XX', 1]]), ['label'], [0], [1, 2], False) - target = sorted([0, 1, 2]) - self.assertEqual(model.noise_qubits, target) - # Check adding a default error isn't added to noise qubits model = NoiseModel() model.add_all_qubit_readout_error([[0.9, 0.1], [0, 1]], False) @@ -177,16 +170,12 @@ def test_noise_models_equal(self): model1 = NoiseModel() model1.add_all_qubit_quantum_error(error1, ['u3'], False) model1.add_quantum_error(error1, ['u3'], [2], False) - with self.assertWarns(DeprecationWarning): - model1.add_nonlocal_quantum_error(error1, ['cx'], [0, 1], [3], False) model1.add_all_qubit_readout_error(roerror, False) model1.add_readout_error(roerror, [0], False) model2 = NoiseModel() model2.add_all_qubit_quantum_error(error2, ['u3'], False) model2.add_quantum_error(error2, ['u3'], [2], False) - with self.assertWarns(DeprecationWarning): - model2.add_nonlocal_quantum_error(error2, ['cx'], [0, 1], [3], False) model2.add_all_qubit_readout_error(roerror, False) model2.add_readout_error(roerror, [0], False) self.assertEqual(model1, model2) @@ -401,10 +390,10 @@ def test_from_dict(self): noise_model = NoiseModel() with self.assertWarns(DeprecationWarning): - noise_model.add_quantum_error(QuantumError(noise_ops_1q, 1), 'h', [0]) - noise_model.add_quantum_error(QuantumError(noise_ops_1q, 1), 'h', [1]) - noise_model.add_quantum_error(QuantumError(noise_ops_2q, 2), 'cx', [0, 1]) - noise_model.add_quantum_error(QuantumError(noise_ops_2q, 2), 'cx', [1, 0]) + noise_model.add_quantum_error(QuantumError(noise_ops_1q), 'h', [0]) + noise_model.add_quantum_error(QuantumError(noise_ops_1q), 'h', [1]) + noise_model.add_quantum_error(QuantumError(noise_ops_2q), 'cx', [0, 1]) + noise_model.add_quantum_error(QuantumError(noise_ops_2q), 'cx', [1, 0]) deserialized = NoiseModel.from_dict(noise_model.to_dict()) self.assertEqual(noise_model, deserialized) diff --git a/test/terra/noise/test_noise_remapper.py b/test/terra/noise/test_noise_remapper.py deleted file mode 100644 index 148537f2e0..0000000000 --- a/test/terra/noise/test_noise_remapper.py +++ /dev/null @@ -1,146 +0,0 @@ -""" -NoiseModel class integration tests -""" - -import unittest - -from qiskit_aer.noise import NoiseModel -from qiskit_aer.noise.errors import depolarizing_error -from qiskit_aer.noise.noiseerror import NoiseError -from qiskit_aer.utils import remap_noise_model -from test.terra.common import QiskitAerTestCase - - -class TestNoiseRemapper(QiskitAerTestCase): - """Testing remap_noise_model function""" - - def test_raises_duplicate_qubits(self): - """Test duplicate qubits raises exception""" - model = NoiseModel() - with self.assertRaises(NoiseError): - with self.assertWarns(DeprecationWarning): - remap_noise_model(model, [[0, 1], [2, 1]], warnings=False) - model = NoiseModel() - error = depolarizing_error(0.5, 1) - model.add_quantum_error(error, ['u3'], [2], False) - with self.assertRaises(NoiseError): - with self.assertWarns(DeprecationWarning): - remap_noise_model(model, [[3, 2]], warnings=False) - - def test_remap_all_qubit_quantum_errors(self): - """Test remapper doesn't effect all-qubit quantum errors.""" - model = NoiseModel() - error1 = depolarizing_error(0.5, 1) - error2 = depolarizing_error(0.5, 2) - model.add_all_qubit_quantum_error(error1, ['u3'], False) - model.add_all_qubit_quantum_error(error2, ['cx'], False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [[0, 1], [1, 0]], warnings=False) - self.assertEqual(model, remapped_model) - - def test_remap_quantum_errors(self): - """Test remapping of quantum errors.""" - model = NoiseModel() - error1 = depolarizing_error(0.5, 1) - error2 = depolarizing_error(0.5, 2) - model.add_quantum_error(error1, ['u3'], [0], False) - model.add_quantum_error(error2, ['cx'], [1, 2], False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [[0, 1], [1, 2], [2, 0]], warnings=False) - target = NoiseModel() - target.add_quantum_error(error1, ['u3'], [1], False) - target.add_quantum_error(error2, ['cx'], [2, 0], False) - self.assertEqual(remapped_model, target) - - def test_remap_nonlocal_quantum_errors(self): - """Test remapping of non-local quantum errors.""" - model = NoiseModel() - error1 = depolarizing_error(0.5, 1) - error2 = depolarizing_error(0.5, 2) - with self.assertWarns(DeprecationWarning): - model.add_nonlocal_quantum_error(error1, ['u3'], [0], [1], False) - model.add_nonlocal_quantum_error(error2, ['cx'], [1, 2], [3, 0], False) - remapped_model = remap_noise_model(model, [[0, 1], [1, 2], [2, 0]], warnings=False) - target = NoiseModel() - target.add_nonlocal_quantum_error(error1, ['u3'], [1], [2], False) - target.add_nonlocal_quantum_error(error2, ['cx'], [2, 0], [3, 1], False) - self.assertEqual(remapped_model, target) - - def test_remap_all_qubit_readout_errors(self): - """Test remapping of all-qubit readout errors.""" - model = NoiseModel() - error1 = [[0.9, 0.1], [0.5, 0.5]] - model.add_all_qubit_readout_error(error1, False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [[0, 1], [1, 2], [2, 0]], warnings=False) - self.assertEqual(remapped_model, model) - - def test_remap_readout_errors(self): - """Test remapping of readout errors.""" - model = NoiseModel() - error1 = [[0.9, 0.1], [0.5, 0.5]] - error2 = [[0.8, 0.2, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0.1, 0.9]] - model.add_readout_error(error1, [1], False) - model.add_readout_error(error2, [0, 2], False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [[0, 1], [1, 2], [2, 0]], warnings=False) - target = NoiseModel() - target.add_readout_error(error1, [2], False) - target.add_readout_error(error2, [1, 0], False) - self.assertEqual(remapped_model, target) - - def test_reduce_noise_model(self): - """Test reduction mapping of noise model.""" - error1 = depolarizing_error(0.5, 1) - error2 = depolarizing_error(0.5, 2) - roerror1 = [[0.9, 0.1], [0.5, 0.5]] - roerror2 = [[0.8, 0.2, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0.1, 0.9]] - - model = NoiseModel() - model.add_all_qubit_quantum_error(error1, ['u3'], False) - model.add_quantum_error(error1, ['u3'], [1], False) - with self.assertWarns(DeprecationWarning): - model.add_nonlocal_quantum_error(error2, ['cx'], [2, 0], [3, 1], False) - model.add_all_qubit_readout_error(roerror1, False) - model.add_readout_error(roerror2, [0, 2], False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [0, 1, 2], discard_qubits=True, warnings=False) - target = NoiseModel() - target.add_all_qubit_quantum_error(error1, ['u3'], False) - target.add_quantum_error(error1, ['u3'], [1], False) - target.add_all_qubit_readout_error(roerror1, False) - target.add_readout_error(roerror2, [0, 2], False) - self.assertEqual(remapped_model, target) - - def test_reduce_remapped_noise_model(self): - """Test reduction and remapping of noise model.""" - error1 = depolarizing_error(0.5, 1) - error2 = depolarizing_error(0.5, 2) - roerror1 = [[0.9, 0.1], [0.5, 0.5]] - roerror2 = [[0.8, 0.2, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0.1, 0.9]] - - model = NoiseModel() - model.add_all_qubit_quantum_error(error1, ['u3'], False) - model.add_quantum_error(error1, ['u3'], [1], False) - with self.assertWarns(DeprecationWarning): - model.add_nonlocal_quantum_error(error2, ['cx'], [2, 0], [3, 1], False) - model.add_all_qubit_readout_error(roerror1, False) - model.add_readout_error(roerror2, [0, 2], False) - - with self.assertWarns(DeprecationWarning): - remapped_model = remap_noise_model(model, [2, 0, 1], discard_qubits=True, warnings=False) - target = NoiseModel() - target.add_all_qubit_quantum_error(error1, ['u3'], False) - target.add_quantum_error(error1, ['u3'], [2], False) - target.add_all_qubit_readout_error(roerror1, False) - target.add_readout_error(roerror2, [1, 0], False) - self.assertEqual(remapped_model, target) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/terra/noise/test_noise_transformation.py b/test/terra/noise/test_noise_transformation.py index 80e5a9d500..a7e4e00a36 100644 --- a/test/terra/noise/test_noise_transformation.py +++ b/test/terra/noise/test_noise_transformation.py @@ -12,25 +12,26 @@ """ NoiseTransformer class tests """ +from test.terra.common import QiskitAerTestCase import unittest import numpy + +from qiskit.circuit import Reset +from qiskit.circuit.library.standard_gates import IGate +from qiskit.circuit.library.standard_gates import XGate, YGate, ZGate, HGate, SGate +from qiskit.extensions import UnitaryGate +from qiskit.quantum_info.operators.channel import Kraus +from qiskit.quantum_info.random import random_unitary from qiskit_aer.noise import NoiseModel from qiskit_aer.noise.errors.quantum_error import QuantumError from qiskit_aer.noise.errors.standard_errors import amplitude_damping_error from qiskit_aer.noise.errors.standard_errors import pauli_error from qiskit_aer.noise.errors.standard_errors import reset_error from qiskit_aer.noise.noiseerror import NoiseError -from qiskit_aer.utils import NoiseTransformer from qiskit_aer.utils import approximate_noise_model from qiskit_aer.utils import approximate_quantum_error -from test.terra.common import QiskitAerTestCase - -from qiskit.circuit import Reset -from qiskit.circuit.library.standard_gates import IGate -from qiskit.circuit.library.standard_gates import XGate, YGate, ZGate, HGate, SGate -from qiskit.quantum_info.operators.channel import Kraus try: import cvxpy @@ -52,10 +53,6 @@ def setUp(self): } def assertNoiseModelsAlmostEqual(self, lhs, rhs, places=3): - self.assertNoiseDictsAlmostEqual( - lhs._nonlocal_quantum_errors, - rhs._nonlocal_quantum_errors, - places=places) self.assertNoiseDictsAlmostEqual( lhs._local_quantum_errors, rhs._local_quantum_errors, @@ -163,9 +160,6 @@ def test_transformation_by_kraus(self): expected_probs = [1 - p, p, 0] self.assertListAlmostEqual(expected_probs, actual.probabilities) - with self.assertWarns(DeprecationWarning): - approximate_quantum_error(error, operator_list=[reset_to_0, reset_to_1]) - def test_reset(self): # approximating amplitude damping using relaxation operators gamma = 0.23 @@ -201,16 +195,6 @@ def test_transform(self): self.assertErrorsAlmostEqual(results_string, results_list) self.assertErrorsAlmostEqual(results_list, results_tuple) - def test_fidelity(self): - expected_fidelity = {'X': 0, 'Y': 0, 'Z': 0, 'H': 0, 'S': 2} - for key in expected_fidelity: - with self.assertWarns(DeprecationWarning): - actual_fidelity = NoiseTransformer().fidelity([self.ops[key]]) - self.assertAlmostEqual( - expected_fidelity[key], - actual_fidelity, - msg="Wrong fidelity for {}".format(key)) - def test_approx_noise_model(self): noise_model = NoiseModel() gamma = 0.23 @@ -324,117 +308,6 @@ def test_approx_random_mixed_unitary_channel_2q(self): for opstr in ['pauli', 'reset']: approximate_quantum_error(noise, operator_string=opstr) -# ================== Tests using old interfaces ================== # -# TODO: Delete after deprecation of old noise transformer -import warnings -from qiskit.extensions import UnitaryGate -from qiskit.quantum_info import process_fidelity -from qiskit.quantum_info.random import random_unitary -@unittest.skipUnless(HAS_CVXPY, 'cvxpy is required to run these tests') -class TestCompareOldAndNewNoiseTransformer(QiskitAerTestCase): - def setUp(self): - super().setUp() - warnings.filterwarnings("ignore", category=DeprecationWarning) - - @staticmethod - def old_approximate_quantum_error(error, *, - operator_string=None, - operator_dict=None, - operator_list=None): - if not isinstance(error, QuantumError): - error = QuantumError(error) - if error.number_of_qubits > 2: - raise NoiseError("Only 1-qubit and 2-qubit noises can be converted, {}-qubit " - "noise found in model".format(error.number_of_qubits)) - - error_kraus_operators = Kraus(error.to_quantumchannel()).data - transformer = NoiseTransformer() - if operator_string is not None: - no_info_error = "No information about noise type {}".format(operator_string) - operator_string = operator_string.lower() - if operator_string not in transformer.named_operators.keys(): - raise RuntimeError(no_info_error) - operator_lists = transformer.named_operators[operator_string] - if len(operator_lists) < error.number_of_qubits: - raise RuntimeError( - no_info_error + " for {} qubits".format(error.number_of_qubits)) - operator_dict = operator_lists[error.number_of_qubits - 1] - if operator_dict is not None: - _, operator_list = zip(*operator_dict.items()) - if operator_list is not None: - op_matrix_list = [ - transformer.operator_matrix(operator) for operator in operator_list - ] - probabilities = transformer.transform_by_operator_list( - op_matrix_list, error_kraus_operators) - identity_prob = numpy.round(1 - sum(probabilities), 9) - if identity_prob < 0 or identity_prob > 1: - raise RuntimeError( - "Channel probabilities sum to {}".format(1 - identity_prob)) - quantum_error_spec = [([{'name': 'id', 'qubits': [0]}], identity_prob)] - op_circuit_list = [ - transformer.operator_circuit(operator) - for operator in operator_list - ] - for (operator, probability) in zip(op_circuit_list, probabilities): - quantum_error_spec.append((operator, probability)) - return QuantumError(quantum_error_spec) - - raise NoiseError( - "Quantum error approximation failed - no approximating operators detected" - ) - - def test_approx_random_unitary_channel_1q(self): - noise = Kraus(random_unitary(2, seed=123)) - for opstr in ['pauli', 'reset']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertEqual(new_result, old_result) - for opstr in ['clifford']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertGreaterEqual(process_fidelity(noise, new_result), - process_fidelity(noise, old_result)) - - def test_approx_random_unitary_channel_2q(self): - noise = Kraus(random_unitary(4, seed=123)) - for opstr in ['pauli']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertEqual(new_result, old_result) - for opstr in ['reset']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertGreaterEqual(process_fidelity(noise, new_result), - process_fidelity(noise, old_result)) - - def test_approx_random_mixed_unitary_channel_1q(self): - noise1 = UnitaryGate(random_unitary(2, seed=123)) - noise2 = UnitaryGate(random_unitary(2, seed=456)) - noise = QuantumError([(noise1, 0.7), (noise2, 0.3)]) - for opstr in ['pauli', 'reset']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertEqual(new_result, old_result) - for opstr in ['clifford']: - # cannot compare due to error in old implementation - with self.assertRaises(NoiseError): - self.old_approximate_quantum_error(noise, operator_string=opstr) - - def test_approx_random_mixed_unitary_channel_2q(self): - noise1 = UnitaryGate(random_unitary(4, seed=123)) - noise2 = UnitaryGate(random_unitary(4, seed=456)) - noise = QuantumError([(noise1, 0.7), (noise2, 0.3)]) - for opstr in ['pauli']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertEqual(new_result, old_result) - for opstr in ['reset']: - new_result = approximate_quantum_error(noise, operator_string=opstr) - old_result = self.old_approximate_quantum_error(noise, operator_string=opstr) - self.assertGreaterEqual(process_fidelity(noise, new_result), - process_fidelity(noise, old_result)) - if __name__ == '__main__': unittest.main() diff --git a/test/terra/noise/test_quantum_error.py b/test/terra/noise/test_quantum_error.py index 00bad55dd5..62c7ad6b66 100644 --- a/test/terra/noise/test_quantum_error.py +++ b/test/terra/noise/test_quantum_error.py @@ -12,19 +12,18 @@ """ QuantumError class tests """ +from test.terra.common import QiskitAerTestCase import unittest -import ddt import numpy as np + from qiskit.circuit import QuantumCircuit, Reset, Measure -from qiskit.circuit.library.standard_gates import * +from qiskit.circuit.library.standard_gates import IGate, XGate, YGate, ZGate from qiskit.extensions import UnitaryGate +from qiskit.quantum_info.operators import SuperOp, Kraus, Pauli from qiskit_aer.noise import QuantumError -from qiskit_aer.noise.errors.errorutils import standard_gate_unitary from qiskit_aer.noise.noiseerror import NoiseError -from qiskit.quantum_info.operators import SuperOp, Kraus, Pauli -from test.terra.common import QiskitAerTestCase class TestQuantumError(QiskitAerTestCase): @@ -33,7 +32,7 @@ class TestQuantumError(QiskitAerTestCase): def test_empty(self): """Test construction with empty noise_ops.""" with self.assertRaises(TypeError): - QuantumError() + QuantumError() # pylint: disable=no-value-for-parameter with self.assertRaises(NoiseError): QuantumError([]) @@ -232,434 +231,5 @@ def test_tensor_with_different_type_of_operator(self): self.assertEqual(actual, expected) -# ================== Tests for old interfaces ================== # -# TODO: remove after deprecation period -@ddt.ddt -class TestQuantumErrorOldInterface(QiskitAerTestCase): - """Testing the deprecating interface of QuantumError class""" - - def assertKrausWarning(self): - return self.assertWarns( - DeprecationWarning, - msg=r"Constructing QuantumError with list of arrays representing a Kraus channel .* qiskit-aer 0\.10\.0.*", - ) - - def kraus_error(self, param): - """Return a Kraus class, and the associated QuantumError, constructed by - passing the list of operators directly, without first wrapping in the - ``Kraus()`` class.""" - krauses = [ - np.array([[1, 0], [0, np.sqrt(1 - param)]], dtype=complex), - np.array([[0, 0], [0, np.sqrt(param)]], dtype=complex) - ] - with self.assertKrausWarning(): - # The point here is to test the path where you construct - # QuantumError using a list of Kraus operators, _without_ wrapping - # it in the Kraus class first, despite the deprecation. - error = QuantumError(krauses) - return Kraus(krauses), error - - def mixed_unitary_error(self, probs, labels, **kwargs): - """Return a Kraus class with the given unitaries (represented by the - labels) at the given probabilities, and the same result unitary error list""" - with self.assertWarns( - DeprecationWarning, - msg=r"standard_gate_unitary has been deprecated as of qiskit-aer 0\.10\.0 .*", - ): - unitaries = [ - np.sqrt(prob) * standard_gate_unitary(label) - for prob, label in zip(probs, labels) - ] - with self.assertKrausWarning(): - error = QuantumError(unitaries, **kwargs) - return Kraus(unitaries), error - - def depol_error(self, param, **kwargs): - """Return depol error unitary list""" - return self.mixed_unitary_error( - [1 - param * 0.75, param * 0.25, param * 0.25, param * 0.25], - ['id', 'x', 'y', 'z'], - **kwargs, - ) - - @staticmethod - def qubits_list(circ, qargs): - return [circ.find_bit(q).index for q in qargs] - - @ddt.data( - ('id', np.eye(2)), - ('x', np.array([[0, 1], [1, 0]])), - ('y', np.array([[0, -1j], [1j, 0]])), - ('z', np.array([[1, 0], [0, -1]])), - ('h', np.sqrt(0.5) * np.array([[1, 1], [1, -1]])), - ('s', np.array([[1, 0], [0, 1j]])), - ('sdg', np.array([[1, 0], [0, -1j]])), - ('t', np.array([[1, 0], [0, np.sqrt(0.5) * (1 + 1j)]])), - ('tdg', np.array([[1, 0], [0, np.sqrt(0.5) * (1 - 1j)]])), - ( - 'cx', - np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]), - ), - ('cz', np.diag([1, 1, 1, -1])), - ( - 'swap', - np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]), - ), - ( - 'ccx', - np.array([ - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - ]), - ), - ) - @ddt.unpack - def test_standard_gate_unitary(self, label, matrix): - """Test standard gates are correct""" - with self.assertWarns( - DeprecationWarning, - msg=r"standard_gate_unitary has been deprecated as of qiskit-aer 0\.10\.0.*", - ): - created = standard_gate_unitary(label) - self.assertLess(np.linalg.norm(created - matrix), 1e-15) - - def test_raise_probabilities_negative(self): - """Test exception is raised for negative probabilities.""" - noise_ops = [ - ([{"name": "id", "qubits": [0]}], 1.1), - ([{"name": "x", "qubits": [0]}], -0.1), - ] - with self.assertRaises(NoiseError): - QuantumError(noise_ops) - - def test_raise_probabilities_normalized_qobj(self): - """Test exception is raised for qobj probabilities greater than 1.""" - noise_ops = [ - ([{"name": "id", "qubits": [0]}], 0.9), - ([{"name": "x", "qubits": [0]}], 0.2), - ] - with self.assertRaises(NoiseError): - QuantumError(noise_ops) - - def test_raise_probabilities_normalized_unitary_kraus(self): - """Test exception is raised for unitary kraus probs greater than 1.""" - a_0 = np.sqrt(0.9) * np.eye(2) - a_1 = np.sqrt(0.2) * np.diag([1, -1]) - with self.assertRaises(NoiseError), self.assertKrausWarning(): - QuantumError([a_0, a_1]) - - def test_raise_probabilities_normalized_nonunitary_kraus(self): - """Test exception is raised for non-unitary kraus probs greater than 1.""" - a_0 = np.sqrt(0.9) * np.array([[1, 0], [0, np.sqrt(1 - 0.3)]]) - a_1 = np.sqrt(0.2) * np.array([[0, np.sqrt(0.3)], [0, 0]]) - with self.assertRaises(NoiseError), self.assertKrausWarning(): - QuantumError([a_0, a_1]) - - def test_raise_non_cptp_kraus(self): - """Test exception is raised for non-CPTP input.""" - a_0 = np.array([[1, 0], [0, np.sqrt(1 - 0.3)]], dtype=complex) - a_1 = np.array([[0, 0], [np.sqrt(0.3), 0]], dtype=complex) - with self.assertRaises(NoiseError), self.assertKrausWarning(): - QuantumError([a_0, a_1]) - with self.assertRaises(NoiseError), self.assertKrausWarning(): - QuantumError([a_0]) - - def test_raise_non_multiqubit_kraus(self): - """Test exception is raised for non-multiqubit input.""" - a_0 = np.sqrt(0.5) * np.diag([1, 1, 1]) - a_1 = np.sqrt(0.5) * np.diag([1, 1, -1]) - with self.assertRaises(NoiseError), self.assertKrausWarning(): - QuantumError([a_0, a_1]) - - def test_pauli_conversion_standard_gates(self): - """Test conversion of Pauli channel kraus to gates""" - kraus, error = self.depol_error(1, standard_gates=True) - for j in range(4): - instr, _ = error.error_term(j) - self.assertEqual(len(instr), 1) - self.assertIn(instr[0][0].name, ['x', 'y', 'z', 'id']) - self.assertEqual(self.qubits_list(instr, instr[0][1]), [0]) - target = SuperOp(kraus) - self.assertEqual(target, SuperOp(error)) - - def test_tensor_both_kraus(self): - """Test tensor of two kraus errors""" - kraus0, error_kraus0 = self.kraus_error(0.3) - kraus1, error_kraus1 = self.kraus_error(0.5) - error = error_kraus0.tensor(error_kraus1) - target = SuperOp(kraus0).tensor(kraus1) - - circ, prob = error.error_term(0) - self.assertEqual(prob, 1) - self.assertEqual(circ[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(circ, circ.qubits), [0, 1]) - self.assertEqual(target, SuperOp(error), msg="Incorrect tensor kraus") - - def test_tensor_both_unitary_standard_gates(self): - """Test tensor of two unitary standard gate errors""" - kraus0, error0 = self.mixed_unitary_error( - [0.9, 0.1], ['z', 's'], standard_gates=True, - ) - kraus1, error1 = self.mixed_unitary_error( - [0.6, 0.4], ['x', 'y'], standard_gates=True, - ) - error = error0.tensor(error1) - target = SuperOp(kraus0).tensor(kraus1) - - for j in range(4): - circ, _ = error.error_term(j) - self.assertEqual(len(circ), 2) - for instr, qargs, _ in circ: - self.assertIn(instr.name, ['s', 'x', 'y', 'z']) - self.assertIn(self.qubits_list(circ, qargs), [[0], [1]]) - self.assertEqual(SuperOp(error), target) - - def test_tensor_kraus_and_unitary(self): - """Test tensor of a kraus and unitary error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_kraus.tensor(error_unitaries) - target = SuperOp(kraus).tensor(kraus_unitaries) - - circ, prob = error.error_term(0) - self.assertEqual(self.qubits_list(circ, circ.qubits), [0, 1]) - self.assertEqual(target, SuperOp(error)) - - def test_tensor_unitary_and_kraus(self): - """Test tensor of a unitary and kraus error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_unitaries.tensor(error_kraus) - target = SuperOp(kraus_unitaries).tensor(kraus) - - circ, prob = error.error_term(0) - self.assertEqual(self.qubits_list(circ, circ.qubits), [0, 1]) - self.assertEqual(target, SuperOp(error)) - - def test_expand_both_kraus(self): - """Test expand of two kraus errors""" - kraus0, error0 = self.kraus_error(0.3) - kraus1, error1 = self.kraus_error(0.5) - error = error0.expand(error1) - target = SuperOp(kraus0).expand(kraus1) - - circ, prob = error.error_term(0) - self.assertEqual(prob, 1) - self.assertEqual(circ[0][0].name, 'kraus') - self.assertEqual(circ[1][0].name, 'kraus') - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(self.qubits_list(circ, circ[1][1]), [1]) - self.assertEqual(target, SuperOp(error)) - - def test_expand_both_unitary_standard_gates(self): - """Test expand of two unitary standard gate errors""" - kraus0, error0 = self.mixed_unitary_error( - [0.9, 0.1], ['z', 's'], standard_gates=True, - ) - kraus1, error1 = self.mixed_unitary_error( - [0.6, 0.4], ['x', 'y'], standard_gates=True, - ) - error = error0.expand(error1) - target = SuperOp(kraus0).expand(kraus1) - - for j in range(4): - circ, _ = error.error_term(j) - self.assertEqual(len(circ), 2) - for instr, qargs, _ in circ: - self.assertIn(instr.name, ['s', 'x', 'y', 'z']) - self.assertIn(self.qubits_list(circ, qargs), [[0], [1]]) - self.assertEqual(SuperOp(error), target) - - def test_expand_kraus_and_unitary(self): - """Test expand of a kraus and unitary error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_kraus.expand(error_unitaries) - target = SuperOp(kraus).expand(kraus_unitaries) - - circ, prob = error.error_term(0) - # self.assertEqual(prob, 1) - self.assertEqual(circ[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(circ, circ.qubits), [0, 1]) - self.assertEqual(target, SuperOp(error)) - - def test_expand_unitary_and_kraus(self): - """Test expand of a unitary and kraus error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_unitaries.expand(error_kraus) - target = SuperOp(kraus_unitaries).expand(kraus) - - circ, prob = error.error_term(0) - self.assertEqual(self.qubits_list(circ, circ.qubits), [0, 1]) - self.assertEqual(target, SuperOp(error)) - - def test_compose_both_kraus(self): - """Test compose of two kraus errors""" - kraus0, error0 = self.kraus_error(0.3) - kraus1, error1 = self.kraus_error(0.5) - error = error0.compose(error1) - target = SuperOp(kraus0).compose(kraus1) - - kraus, prob = error.error_term(0) - self.assertEqual(prob, 1) - self.assertEqual(kraus[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(kraus, kraus[0][1]), [0]) - self.assertEqual(target, SuperOp(error), msg="Incorrect tensor kraus") - - def test_compose_both_unitary_standard_gates(self): - """Test compose of two unitary standard gate errors""" - kraus0, error0 = self.mixed_unitary_error( - [0.9, 0.1], ['z', 's'], standard_gates=True, - ) - kraus1, error1 = self.mixed_unitary_error( - [0.6, 0.4], ['x', 'y'], standard_gates=True, - ) - error = error0.compose(error1) - target = SuperOp(kraus0).compose(kraus1) - - for j in range(4): - circ, _ = error.error_term(j) - self.assertIn(circ[0][0].name, ['s', 'x', 'y', 'z']) - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(SuperOp(error), target) - - def test_compose_kraus_and_unitary(self): - """Test compose of a kraus and unitary error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_kraus.compose(error_unitaries) - target = SuperOp(kraus).compose(kraus_unitaries) - - circ, prob = error.error_term(0) - # self.assertEqual(prob, 1) - self.assertEqual(circ[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(target, SuperOp(error)) - - def test_compose_unitary_and_kraus(self): - """Test compose of a unitary and kraus error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_unitaries.compose(error_kraus) - target = SuperOp(kraus_unitaries).compose(kraus) - - circ, prob = error.error_term(0) - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(target, SuperOp(error)) - - def test_dot_both_kraus(self): - """Test dot of two kraus errors""" - kraus0, error0 = self.kraus_error(0.3) - kraus1, error1 = self.kraus_error(0.5) - error = error0.dot(error1) - target = SuperOp(kraus0).dot(kraus1) - - kraus, prob = error.error_term(0) - self.assertEqual(prob, 1) - self.assertEqual(kraus[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(kraus, kraus[0][1]), [0]) - self.assertEqual(target, SuperOp(error), msg="Incorrect dot kraus") - - def test_dot_both_unitary_standard_gates(self): - """Test dot of two unitary standard gate errors""" - kraus0, error0 = self.mixed_unitary_error( - [0.9, 0.1], ['z', 's'], standard_gates=True, - ) - kraus1, error1 = self.mixed_unitary_error( - [0.6, 0.4], ['x', 'y'], standard_gates=True, - ) - error = error0.dot(error1) - target = SuperOp(kraus0).dot(kraus1) - - for j in range(4): - circ, _ = error.error_term(j) - self.assertIn(circ[0][0].name, ['s', 'x', 'y', 'z']) - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(SuperOp(error), target) - - def test_dot_kraus_and_unitary(self): - """Test dot of a kraus and unitary error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_kraus.dot(error_unitaries) - target = SuperOp(kraus).dot(kraus_unitaries) - - circ, prob = error.error_term(0) - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(target, SuperOp(error)) - - def test_dot_unitary_and_kraus(self): - """Test dot of a unitary and kraus error.""" - kraus, error_kraus = self.kraus_error(0.4) - kraus_unitaries, error_unitaries = self.depol_error(0.1) - error = error_unitaries.dot(error_kraus) - target = SuperOp(kraus_unitaries).dot(kraus) - - circ, prob = error.error_term(0) - # self.assertEqual(prob, 1) - self.assertEqual(circ[0][0].name, 'kraus') - self.assertEqual(self.qubits_list(circ, circ[0][1]), [0]) - self.assertEqual(target, SuperOp(error)) - - def test_to_quantumchannel_kraus(self): - """Test to_quantumchannel for Kraus inputs.""" - a_0 = np.array([[1, 0], [0, np.sqrt(1 - 0.3)]], dtype=complex) - a_1 = np.array([[0, 0], [0, np.sqrt(0.3)]], dtype=complex) - b_0 = np.array([[1, 0], [0, np.sqrt(1 - 0.5)]], dtype=complex) - b_1 = np.array([[0, 0], [0, np.sqrt(0.5)]], dtype=complex) - target = SuperOp(Kraus([a_0, a_1])).tensor(SuperOp(Kraus([b_0, b_1]))) - with self.assertWarns( - DeprecationWarning, - msg=r"Constructing QuantumError .* Kraus channel .* qiskit-aer 0\.10\.0 .*", - ): - error = QuantumError([a_0, a_1]).tensor(QuantumError([b_0, b_1])) - self.assertEqual(target, error.to_quantumchannel()) - - def test_to_quantumchannel_circuit(self): - """Test to_quantumchannel for circuit inputs.""" - noise_ops = [ - ([{'name': 'reset', 'qubits': [0]}], 0.2), - ([{'name': 'reset', 'qubits': [1]}], 0.3), - ([{'name': 'id', 'qubits': [0]}], 0.5), - ] - with self.assertWarns( - DeprecationWarning, - msg=r"Constructing QuantumError .* list of dict .* qiskit-aer 0\.10\.0 .*", - ): - error = QuantumError(noise_ops) - reset = SuperOp( - np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) - ) - iden = SuperOp(np.eye(4)) - target = ( - 0.2 * iden.tensor(reset) - + 0.3 * reset.tensor(iden) - + 0.5 * iden.tensor(iden) - ) - self.assertEqual(target, error.to_quantumchannel()) - - def test_equal(self): - """Test two quantum errors are equal""" - with self.assertWarns( - DeprecationWarning, - msg=r"standard_gate_unitary is deprecated as of qiskit-aer 0\.10\.0.*", - ): - a_i = np.sqrt(0.25) * standard_gate_unitary('id') - a_x = np.sqrt(0.25) * standard_gate_unitary('x') - a_y = np.sqrt(0.25) * standard_gate_unitary('y') - a_z = np.sqrt(0.25) * standard_gate_unitary('z') - with self.assertKrausWarning(): - error1 = QuantumError([a_i, a_x, a_y, a_z], standard_gates=True) - error2 = QuantumError([a_i, a_x, a_y, a_z], standard_gates=False) - self.assertEqual(error1, error2) - - if __name__ == '__main__': unittest.main() diff --git a/test/terra/noise/test_standard_errors.py b/test/terra/noise/test_standard_errors.py index c6aea52c3f..dcebc9369a 100644 --- a/test/terra/noise/test_standard_errors.py +++ b/test/terra/noise/test_standard_errors.py @@ -442,136 +442,5 @@ def test_thermal_relaxation_error_kraus(self): self.assertEqual(circ[0][1], [circ.qubits[0]]) -# ================== Tests for old interfaces ================== # -# TODO: remove after deprecation period -class TestNoiseOldInterface(QiskitAerTestCase): - """Testing the deprecating interface of standard_error""" - - def test_pauli_error_1q_unitary_from_pauli(self): - """Test single-qubit pauli error as unitary qobj from Pauli obj""" - from qiskit.extensions import UnitaryGate - paulis = [qi.Pauli(s) for s in ['I', 'X', 'Y', 'Z']] - probs = [0.4, 0.3, 0.2, 0.1] - with self.assertWarns(DeprecationWarning): - actual = pauli_error(zip(paulis, probs), standard_gates=False) - - target_unitaries = [qi.Pauli("I").to_matrix(), - qi.Pauli("X").to_matrix(), - qi.Pauli("Y").to_matrix(), - qi.Pauli("Z").to_matrix()] - expected = QuantumError([(UnitaryGate(mat), p) for mat, p in zip(target_unitaries, probs)]) - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertEqual(circ, expected_circ) - self.assertAlmostEqual(prob, expected_prob) - - def test_pauli_error_2q_unitary_from_pauli(self): - """Test two-qubit pauli error as unitary qobj from Pauli obj""" - from qiskit.extensions import UnitaryGate - paulis = [qi.Pauli(s) for s in ['XY', 'YZ', 'ZX']] - probs = [0.5, 0.3, 0.2] - with self.assertWarns(DeprecationWarning): - actual = pauli_error(zip(paulis, probs), standard_gates=False) - - X = qi.Pauli("X").to_matrix() - Y = qi.Pauli("Y").to_matrix() - Z = qi.Pauli("Z").to_matrix() - target_unitaries = [np.kron(X, Y), np.kron(Y, Z), np.kron(Z, X)] - expected = QuantumError([(UnitaryGate(mat), p) for mat, p in zip(target_unitaries, probs)]) - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertEqual(circ, expected_circ) - self.assertAlmostEqual(prob, expected_prob) - - def test_pauli_error_2q_gate_from_string(self): - """Test two-qubit pauli error as gate qobj from string label""" - paulis = ['XZ', 'YX', 'ZY'] - probs = [0.5, 0.3, 0.2] - with self.assertWarns(DeprecationWarning): - actual = pauli_error(zip(paulis, probs), standard_gates=True) - - target_circs = [[{"name": "z", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "z", "qubits": [1]}]] - with self.assertWarns(DeprecationWarning): - expected = QuantumError(zip(target_circs, probs), standard_gates=True) - - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertEqual(circ, expected_circ) - self.assertAlmostEqual(prob, expected_prob) - - def test_pauli_error_2q_gate_from_string_1qonly(self): - """Test two-qubit pauli error as gate qobj from string label""" - paulis = ['XI', 'YI', 'ZI'] - probs = [0.5, 0.3, 0.2] - with self.assertWarns(DeprecationWarning): - actual = pauli_error(zip(paulis, probs), standard_gates=True) - - target_circs = [[{"name": "id", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "id", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "id", "qubits": [0]}, {"name": "z", "qubits": [1]}]] - with self.assertWarns(DeprecationWarning): - expected = QuantumError(zip(target_circs, probs), standard_gates=True) - - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertTrue(qi.Operator(circ).equiv(qi.Operator(expected_circ))) - self.assertAlmostEqual(prob, expected_prob) - - def test_pauli_error_2q_gate_from_pauli(self): - """Test two-qubit pauli error as gate qobj from Pauli obj""" - paulis = [qi.Pauli(s) for s in ['XZ', 'YX', 'ZY']] - probs = [0.5, 0.3, 0.2] - with self.assertWarns(DeprecationWarning): - actual = pauli_error(zip(paulis, probs), standard_gates=True) - - target_circs = [[{"name": "z", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "z", "qubits": [1]}]] - with self.assertWarns(DeprecationWarning): - expected = QuantumError(zip(target_circs, probs), standard_gates=True) - - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertEqual(circ, expected_circ) - self.assertAlmostEqual(prob, expected_prob) - - def test_depolarizing_error_2q_gate(self): - """Test 2-qubit depolarizing error as gate qobj""" - p_depol = 0.3 - with self.assertWarns(DeprecationWarning): - actual = depolarizing_error(p_depol, 2, standard_gates=True) - target_circs = [[{"name": "id", "qubits": [0]}, {"name": "id", "qubits": [1]}], - [{"name": "id", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "id", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "id", "qubits": [0]}, {"name": "z", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "id", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "x", "qubits": [0]}, {"name": "z", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "id", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "y", "qubits": [0]}, {"name": "z", "qubits": [1]}], - [{"name": "z", "qubits": [0]}, {"name": "id", "qubits": [1]}], - [{"name": "z", "qubits": [0]}, {"name": "x", "qubits": [1]}], - [{"name": "z", "qubits": [0]}, {"name": "y", "qubits": [1]}], - [{"name": "z", "qubits": [0]}, {"name": "z", "qubits": [1]}]] - target_probs = [1 - p_depol * 15 / 16] + [p_depol / 16] * 15 - with self.assertWarns(DeprecationWarning): - expected = QuantumError(zip(target_circs, target_probs), standard_gates=True) - for i in range(actual.size): - circ, prob = actual.error_term(i) - expected_circ, expected_prob = expected.error_term(i) - self.assertEqual(circ, expected_circ, msg=f"Incorrect {i}-th circuit") - self.assertAlmostEqual(prob, expected_prob, msg=f"Incorrect {i}-th probability") - - if __name__ == '__main__': unittest.main() diff --git a/test/terra/reference/ref_pauli_noise.py b/test/terra/reference/ref_pauli_noise.py index 8b5dbc6351..683b92c8eb 100644 --- a/test/terra/reference/ref_pauli_noise.py +++ b/test/terra/reference/ref_pauli_noise.py @@ -78,17 +78,6 @@ def pauli_gate_error_circuits(): circuit.measure(qr, cr) circuits.append(circuit) - # 25% Pauli-X error on spectator for CX gate on [0, 1] - qr = QuantumRegister(3, 'qr') - cr = ClassicalRegister(3, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.cx(qr[0], qr[1]) - circuit.barrier(qr) - circuit.cx(qr[1], qr[0]) - circuit.barrier(qr) - circuit.measure(qr, cr) - circuits.append(circuit) - return circuits @@ -132,12 +121,6 @@ def pauli_gate_error_noise_models(): noise_model.add_all_qubit_quantum_error(error, 'x') noise_models.append(noise_model) - # 25% Pauli-X error on spectator for CX gate on [0, 1] - error = pauli_error([('XII', 0.25), ('III', 0.75)]) - noise_model = NoiseModel() - noise_model.add_nonlocal_quantum_error(error, 'cx', [0, 1], [0, 1, 2]) - noise_models.append(noise_model) - return noise_models @@ -169,10 +152,6 @@ def pauli_gate_error_counts(shots, hex_counts=True): counts = 4 * [shots / 4] counts_lists.append(counts) - # 25% Pauli-X error on spectator for CX gate on [0, 1] - counts = [3 * shots / 4, 0, 0, 0, shots / 4, 0, 0, 0] - counts_lists.append(counts) - return [list2dict(i, hex_counts) for i in counts_lists] @@ -197,11 +176,6 @@ def pauli_measure_error_circuits(): circuit.measure(qr, cr) circuits.append(circuit) - # 25 % non-local Pauli error on qubit 1 for measure of qubit-1 - circuit = QuantumCircuit(qr, cr) - circuit.measure(qr, cr) - circuits.append(circuit) - return circuits @@ -221,12 +195,6 @@ def pauli_measure_error_noise_models(): noise_model.add_quantum_error(error, 'measure', [1]) noise_models.append(noise_model) - # 25 % non-local Pauli error on qubit 1 for measure of qubit-1 - error = pauli_error([('X', 0.25), ('I', 0.75)]) - noise_model = NoiseModel() - noise_model.add_nonlocal_quantum_error(error, 'measure', [0], [1]) - noise_models.append(noise_model) - return noise_models @@ -242,10 +210,6 @@ def pauli_measure_error_counts(shots, hex_counts=True): counts = [3 * shots / 4, 0, shots / 4, 0] counts_lists.append(counts) - # 25 % non-local Pauli error on qubit 1 for measure of qubit-1 - counts = [3 * shots / 4, 0, shots / 4, 0] - counts_lists.append(counts) - # Convert to counts dict return [list2dict(i, hex_counts) for i in counts_lists] @@ -278,16 +242,6 @@ def pauli_reset_error_circuits(): circuit.measure(qr, cr) circuits.append(circuit) - # 25 % non-local Pauli error on qubit 1 for reset of qubit-0 - circuit = QuantumCircuit(qr, cr) - circuit.barrier(qr) - circuit.reset(qr[1]) - circuit.barrier(qr) - circuit.reset(qr[0]) - circuit.barrier(qr) - circuit.measure(qr, cr) - circuits.append(circuit) - return circuits @@ -307,12 +261,6 @@ def pauli_reset_error_noise_models(): noise_model.add_quantum_error(error, 'reset', [1]) noise_models.append(noise_model) - # 25 % non-local Pauli error on qubit 1 for reset of qubit-0 - error = pauli_error([('X', 0.25), ('I', 0.75)]) - noise_model = NoiseModel() - noise_model.add_nonlocal_quantum_error(error, 'reset', [0], [1]) - noise_models.append(noise_model) - return noise_models @@ -328,9 +276,5 @@ def pauli_reset_error_counts(shots, hex_counts=True): counts = [3 * shots / 4, 0, shots / 4, 0] counts_lists.append(counts) - # 25 % non-local Pauli error on qubit 1 for reset of qubit-0 - counts = [3 * shots / 4, 0, shots / 4, 0] - counts_lists.append(counts) - # Convert to counts dict return [list2dict(i, hex_counts) for i in counts_lists]