# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019, 2020, 2021, 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Integration Tests for AerStatevector
"""

import unittest
from math import pi
import numpy as np
import logging
from itertools import permutations
from ddt import ddt, data
from numpy.testing import assert_allclose

from qiskit import transpile
from qiskit.exceptions import QiskitError
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.providers.basicaer import QasmSimulatorPy
from qiskit.quantum_info.random import random_unitary, random_statevector, random_pauli
from qiskit.quantum_info.states import Statevector
from qiskit.circuit.library import QuantumVolume
from qiskit.quantum_info import Kraus
from qiskit.quantum_info.operators.operator import Operator
from qiskit.quantum_info.operators.symplectic import Pauli, SparsePauliOp
from qiskit.quantum_info.operators.predicates import matrix_equal
from qiskit.visualization.state_visualization import numbers_to_latex_terms, state_to_latex
from qiskit.circuit.library import QFT, HGate

from test.terra import common
from qiskit_aer import AerSimulator
from qiskit_aer.aererror import AerError
from qiskit_aer.noise import pauli_error, QuantumError
from qiskit_aer.quantum_info.states import AerStatevector
from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT


logger = logging.getLogger(__name__)


@ddt
class TestAerStatevector(common.QiskitAerTestCase):
    """AerState tests"""

    def test_qv(self):
        """Test generation of Aer's StateVector with QV"""
        circ = QuantumVolume(5, seed=1111)
        state = AerStatevector(circ)
        expected = Statevector(circ)

        for e, s in zip(expected, state):
            self.assertAlmostEqual(e, s)

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

        state = AerStatevector(circ, seed_simulator=1)

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

        self.assertNotEqual(counts0, counts1)

        state = AerStatevector(circ, seed_simulator=10)

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

        self.assertNotEqual(counts2, counts3)

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

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

        state = AerStatevector(circ, seed_simulator=1)

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

        self.assertNotEqual(counts0, counts1)

        state = AerStatevector(circ, seed_simulator=1)

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

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

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

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

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

        self.assertEqual("statevector", state1.metadata()["method"])
        self.assertEqual("CPU", state1.metadata()["device"])

        state2 = AerStatevector(circ, method="matrix_product_state")
        self.assertEqual("matrix_product_state", state2.metadata()["method"])
        self.assertEqual("CPU", state2.metadata()["device"])

        self.assertEqual(state1, state2)

    def test_GHZ(self):
        """Test each method can process ghz"""
        ghz = QuantumCircuit(4)
        ghz.h(0)
        ghz.cx(0, 1)
        ghz.cx(1, 2)
        ghz.cx(2, 3)

        for method in ["statevector", "matrix_product_state"]:
            sv = AerStatevector(ghz, method=method)
            counts = sv.sample_counts(shots=1024)
            self.assertEqual(2, len(counts))
            self.assertTrue("0000" in counts)
            self.assertTrue("1111" in counts)

    def test_QFT(self):
        """Test each method can process qft"""
        qft = QuantumCircuit(4)
        qft.h(range(4))
        qft.compose(QFT(4), inplace=True)

        for method in ["statevector", "matrix_product_state"]:
            sv = AerStatevector(qft, method=method)
            counts = sv.sample_counts(shots=1024)
            self.assertEqual(1, len(counts))
            self.assertTrue("0000" in counts)

    def test_single_qubit_QV(self):
        """Test single qubit QuantumVolume"""
        state = AerStatevector(QuantumVolume(1))
        counts = state.sample_counts(shots=1024)
        self.assertEqual(1, len(counts))
        self.assertTrue("0" in counts)

    def test_evolve(self):
        """Test method and device properties"""
        circ1 = QuantumVolume(5, seed=1111)
        circ2 = circ1.compose(circ1)
        circ3 = circ2.compose(circ1)

        state1 = AerStatevector(circ1)
        state2 = AerStatevector(circ2)
        state3 = AerStatevector(circ3)

        self.assertEqual(state1.evolve(circ1), state2)
        self.assertEqual(state1.evolve(circ1).evolve(circ1), state3)

    def test_decompose(self):
        """Test basic gates can be decomposed correctly"""
        circ = QuantumCircuit(3)
        circ.h(0)
        circ.x(1)
        circ.ry(np.pi / 2, 2)
        state1 = AerStatevector(circ)

    def test_ry(self):
        # Test ry
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.x(1)
        circuit.ry(np.pi / 2, 2)
        psi = AerStatevector.from_instruction(circuit)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        self.assertEqual(target, psi)

    def test_u(self):
        # Test u
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.h(1)
        circuit.u(0.1, 0.1, 0.1, 0)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_cu(self):
        # Test cu
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.h(1)
        circuit.cu(0.1, 0.1, 0.1, 0.1, 0, 1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_h(self):
        # Test h
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.h(1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_x(self):
        # Test x
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.x(1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_cx(self):
        # Test cx
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.cx(0, 1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_y(self):
        # Test y
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.y(1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_cy(self):
        # Test cy
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.cy(0, 1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_z(self):
        # Test z
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.z(0)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_cz(self):
        # Test cz
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.cz(0, 1)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_unitary(self):
        # Test unitary
        circuit = QuantumCircuit(3)
        mat = random_unitary(8).data
        circuit.unitary(mat, range(3))
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_diagonal(self):
        # Test diagonal
        circuit = QuantumCircuit(3)
        circuit.h(range(3))
        diagonal = [1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0]
        circuit.diagonal(diagonal, list(range(3)))
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_kraus(self):
        """Test kraus"""

        def same_1qubit_statevectors_up_to_global_phase(sv1, sv2):
            if not isinstance(sv1, np.ndarray):
                sv1 = sv1.data
            if not isinstance(sv2, np.ndarray):
                sv2 = sv2.data
            return np.isclose(np.cross(sv1, sv2), 0)

        circuit = QuantumCircuit(1)
        circuit.h(0)
        circuit.y(0)
        p_error = 0.5
        error = pauli_error([("Y", p_error), ("I", 1 - p_error)])
        circuit.append(Kraus(error).to_instruction(), [0])

        circuit_no_noise = QuantumCircuit(1)
        circuit_no_noise.h(0)
        circuit_no_noise.y(0)
        target0 = AerStatevector.from_label("0").evolve(Operator(circuit_no_noise))
        circuit_no_noise.y(0)
        target1 = AerStatevector.from_label("0").evolve(Operator(circuit_no_noise))

        for i in range(100):
            psi = AerStatevector(circuit, seed_simulator=i)
            self.assertTrue(
                same_1qubit_statevectors_up_to_global_phase(psi, target0)
                or same_1qubit_statevectors_up_to_global_phase(psi, target1)
            )

    def test_deepcopy(self):
        """Test deep copy"""
        import copy

        circ1 = QuantumVolume(5, seed=1111)

        state1 = AerStatevector(circ1)
        state2 = copy.deepcopy(state1)

        for pa1, pa2 in zip(state1, state2):
            self.assertAlmostEqual(pa1, pa2)

        self.assertNotEqual(id(state1._data), id(state2._data))

    def test_initialize_with_ndarray(self):
        """Test ndarray initialization"""
        circ = QuantumVolume(5, seed=1111)
        expected = Statevector(circ)
        state = AerStatevector(expected.data)

        for e, s in zip(expected, state):
            self.assertAlmostEqual(e, s)

    def test_initialize_with_non_contiguous_ndarray(self):
        """Test ndarray initialization"""

        for n_qubits in [2, 4, 8, 16]:
            u = random_unitary(n_qubits, seed=1111).data
            vec = u[0, :]
            state = AerStatevector(vec)

            self.assertTrue(np.allclose(state, vec, rtol=RTOL_DEFAULT, atol=ATOL_DEFAULT))

    def test_initialize_with_terra_statevector(self):
        """Test Statevector initialization"""
        circ = QuantumVolume(5, seed=1111)
        expected = Statevector(circ)
        state = AerStatevector(expected)

        for e, s in zip(expected, state):
            self.assertAlmostEqual(e, s)

    def test_initialize_with_aer_statevector(self):
        """Test AerStatevector initialization"""
        circ = QuantumVolume(5, seed=1111)
        expected = AerStatevector(circ)
        state = AerStatevector(expected)

        for e, s in zip(expected, state):
            self.assertAlmostEqual(e, s)

    ####                                          ####
    ###   Copy from test_statevector.py in terra   ###
    ####                                          ####
    @classmethod
    def rand_vec(cls, n, normalize=False):
        """Return complex vector or statevector"""
        seed = np.random.randint(0, np.iinfo(np.int32).max)
        logger.debug("rand_vec default_rng seeded with seed=%s", seed)
        rng = np.random.default_rng(seed)

        vec = rng.random(n) + 1j * rng.random(n)
        if normalize:
            vec /= np.sqrt(np.dot(vec, np.conj(vec)))
        return vec

    def test_init_array_qubit(self):
        """Test subsystem initialization from N-qubit array."""
        # Test automatic inference of qubit subsystems
        vec = self.rand_vec(8)
        for dims in [None, 8]:
            state = AerStatevector(vec, dims=dims)
            assert_allclose(state.data, vec)
            self.assertEqual(state.dim, 8)
            self.assertEqual(state.dims(), (2, 2, 2))
            self.assertEqual(state.num_qubits, 3)

    def test_init_circuit(self):
        """Test initialization from circuit."""
        circuit = QuantumCircuit(3)
        circuit.x(0)
        state = AerStatevector(circuit)

        self.assertEqual(state.dim, 8)
        self.assertEqual(state.dims(), (2, 2, 2))
        self.assertTrue(all(state.data == np.array([0, 1, 0, 0, 0, 0, 0, 0], dtype=complex)))
        self.assertEqual(state.num_qubits, 3)

    def test_init_array_except(self):
        """Test initialization exception from array."""
        vec = self.rand_vec(4)
        self.assertRaises(QiskitError, AerStatevector, vec, dims=[4, 2])
        self.assertRaises(QiskitError, AerStatevector, vec, dims=[2, 4])
        self.assertRaises(QiskitError, AerStatevector, vec, dims=5)

    def test_init_statevector(self):
        """Test initialization from AerStatevector."""
        vec1 = AerStatevector(self.rand_vec(4))
        vec2 = AerStatevector(vec1)
        self.assertEqual(vec1, vec2)

    def test_from_circuit(self):
        """Test initialization from a circuit."""
        # random unitaries
        u0 = random_unitary(2).data
        u1 = random_unitary(2).data
        # add to circuit
        qr = QuantumRegister(2)
        circ = QuantumCircuit(qr)
        circ.unitary(u0, [qr[0]])
        circ.unitary(u1, [qr[1]])
        target = AerStatevector(np.kron(u1, u0).dot([1, 0, 0, 0]))
        vec = AerStatevector.from_instruction(circ)
        self.assertEqual(vec, target)

        # Test tensor product of 1-qubit gates
        circuit = QuantumCircuit(3)
        circuit.h(0)
        circuit.x(1)
        circuit.ry(np.pi / 2, 2)
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

        # Test decomposition of Controlled-Phase gate
        lam = np.pi / 4
        circuit = QuantumCircuit(2)
        circuit.h(0)
        circuit.h(1)
        circuit.cp(lam, 0, 1)
        target = AerStatevector.from_label("00").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

        # Test decomposition of controlled-H gate
        circuit = QuantumCircuit(2)
        circ.x(0)
        circuit.ch(0, 1)
        target = AerStatevector.from_label("00").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

        # Test custom controlled gate
        qc = QuantumCircuit(2)
        qc.x(0)
        qc.h(1)
        gate = qc.to_gate()
        gate_ctrl = gate.control()

        circuit = QuantumCircuit(3)
        circuit.x(0)
        circuit.append(gate_ctrl, range(3))
        target = AerStatevector.from_label("000").evolve(Operator(circuit))
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

        # Test initialize instruction
        target = AerStatevector([1, 0, 0, 1j]) / np.sqrt(2)
        circuit = QuantumCircuit(2)
        circuit.initialize(target.data, [0, 1])
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

        # Test reset instruction
        target = AerStatevector([1, 0])
        circuit = QuantumCircuit(1)
        circuit.h(0)
        circuit.reset(0)
        psi = AerStatevector.from_instruction(circuit)
        self.assertEqual(psi, target)

    def test_from_instruction(self):
        """Test initialization from an instruction."""
        target = np.dot(HGate().to_matrix(), [1, 0])
        vec = AerStatevector.from_instruction(HGate()).data
        global_phase_equivalent = matrix_equal(vec, target, ignore_phase=True)
        self.assertTrue(global_phase_equivalent)

    def test_from_label(self):
        """Test initialization from a label"""
        x_p = AerStatevector(np.array([1, 1]) / np.sqrt(2))
        x_m = AerStatevector(np.array([1, -1]) / np.sqrt(2))
        y_p = AerStatevector(np.array([1, 1j]) / np.sqrt(2))
        y_m = AerStatevector(np.array([1, -1j]) / np.sqrt(2))
        z_p = AerStatevector(np.array([1, 0]))
        z_m = AerStatevector(np.array([0, 1]))

        label = "01"
        target = z_p.tensor(z_m)
        self.assertEqual(target, AerStatevector.from_label(label))

        label = "+-"
        target = x_p.tensor(x_m)
        self.assertEqual(target, AerStatevector.from_label(label))

        label = "rl"
        target = y_p.tensor(y_m)
        self.assertEqual(target, AerStatevector.from_label(label))

    def test_equal(self):
        """Test __eq__ method"""
        for _ in range(10):
            vec = self.rand_vec(4)
            self.assertEqual(AerStatevector(vec), AerStatevector(vec.tolist()))

    def test_getitem(self):
        """Test __getitem__ method"""
        for _ in range(10):
            vec = self.rand_vec(4)
            state = AerStatevector(vec)
            for i in range(4):
                self.assertEqual(state[i], vec[i])
                self.assertEqual(state[format(i, "b")], vec[i])

    def test_getitem_except(self):
        """Test __getitem__ method raises exceptions."""
        for i in range(1, 4):
            state = AerStatevector(self.rand_vec(2**i))
            self.assertRaises(QiskitError, state.__getitem__, 2**i)
            self.assertRaises(QiskitError, state.__getitem__, -1)

    def test_copy(self):
        """Test AerStatevector copy method"""
        for _ in range(5):
            vec = self.rand_vec(4)
            orig = AerStatevector(vec)
            cpy = orig.copy()
            cpy._data[0] += 1.0
            self.assertFalse(cpy == orig)

    def test_is_valid(self):
        """Test is_valid method."""
        state = AerStatevector([1, 1])
        self.assertFalse(state.is_valid())
        for _ in range(10):
            state = AerStatevector(self.rand_vec(4, normalize=True))
            self.assertTrue(state.is_valid())

    def test_to_operator(self):
        """Test to_operator method for returning projector."""
        for _ in range(10):
            vec = self.rand_vec(4)
            target = Operator(np.outer(vec, np.conj(vec)))
            op = AerStatevector(vec).to_operator()
            self.assertEqual(op, target)

    def test_evolve(self):
        """Test _evolve method."""
        for _ in range(10):
            op = random_unitary(4)
            vec = self.rand_vec(4)
            target = AerStatevector(np.dot(op.data, vec))
            evolved = AerStatevector(vec).evolve(op)
            self.assertEqual(target, evolved)

    def test_evolve_subsystem(self):
        """Test subsystem _evolve method."""
        # Test evolving single-qubit of 3-qubit system
        for _ in range(5):
            vec = self.rand_vec(8)
            state = AerStatevector(vec)
            op0 = random_unitary(2)
            op1 = random_unitary(2)
            op2 = random_unitary(2)

            # Test evolve on 1-qubit
            op = op0
            op_full = Operator(np.eye(4)).tensor(op)
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[0]), target)

            # Evolve on qubit 1
            op_full = Operator(np.eye(2)).tensor(op).tensor(np.eye(2))
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[1]), target)

            # Evolve on qubit 2
            op_full = op.tensor(np.eye(4))
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[2]), target)

            # Test evolve on 2-qubits
            op = op1.tensor(op0)

            # Evolve on qubits [0, 2]
            op_full = op1.tensor(np.eye(2)).tensor(op0)
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[0, 2]), target)

            # Evolve on qubits [2, 0]
            op_full = op0.tensor(np.eye(2)).tensor(op1)
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[2, 0]), target)

            # Test evolve on 3-qubits
            op = op2.tensor(op1).tensor(op0)

            # Evolve on qubits [0, 1, 2]
            op_full = op
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[0, 1, 2]), target)

            # Evolve on qubits [2, 1, 0]
            op_full = op0.tensor(op1).tensor(op2)
            target = AerStatevector(np.dot(op_full.data, vec))
            self.assertEqual(state.evolve(op, qargs=[2, 1, 0]), target)

    def test_evolve_global_phase(self):
        """Test evolve circuit with global phase."""
        state_i = AerStatevector([1, 0])
        qr = QuantumRegister(2)
        phase = np.pi / 4
        circ = QuantumCircuit(qr, global_phase=phase)
        circ.x(0)
        state_f = state_i.evolve(circ, qargs=[0])
        target = AerStatevector([0, 1]) * np.exp(1j * phase)

        self.assertEqual(state_f, target)

    def test_conjugate(self):
        """Test conjugate method."""
        for _ in range(10):
            vec = self.rand_vec(4)
            target = AerStatevector(np.conj(vec))
            state = AerStatevector(vec).conjugate()
            self.assertEqual(state, target)

    def test_expand(self):
        """Test expand method."""
        for _ in range(10):
            vec0 = self.rand_vec(2)
            vec1 = self.rand_vec(4)
            target = np.kron(vec1, vec0)
            state = AerStatevector(vec0).expand(AerStatevector(vec1))
            self.assertEqual(state.dim, 8)
            self.assertEqual(state.dims(), (2, 2, 2))
            assert_allclose(state.data, target)

    def test_tensor(self):
        """Test tensor method."""
        for _ in range(10):
            vec0 = self.rand_vec(2)
            vec1 = self.rand_vec(4)
            target = np.kron(vec0, vec1)
            state = AerStatevector(vec0).tensor(AerStatevector(vec1))
            self.assertEqual(state.dim, 8)
            self.assertEqual(state.dims(), (2, 2, 2))
            assert_allclose(state.data, target)

    def test_inner(self):
        """Test inner method."""
        for _ in range(10):
            vec0 = AerStatevector(self.rand_vec(4))
            vec1 = AerStatevector(self.rand_vec(4))
            target = np.vdot(vec0.data, vec1.data)
            result = vec0.inner(vec1)
            self.assertAlmostEqual(result, target)

    def test_add(self):
        """Test add method."""
        for _ in range(10):
            vec0 = self.rand_vec(4)
            vec1 = self.rand_vec(4)
            state0 = AerStatevector(vec0)
            state1 = AerStatevector(vec1)
            self.assertEqual(state0 + state1, AerStatevector(vec0 + vec1))

    def test_add_except(self):
        """Test add method raises exceptions."""
        state1 = AerStatevector(self.rand_vec(2))
        state2 = AerStatevector(self.rand_vec(4))
        self.assertRaises(QiskitError, state1.__add__, state2)

    def test_subtract(self):
        """Test subtract method."""
        for _ in range(10):
            vec0 = self.rand_vec(4)
            vec1 = self.rand_vec(4)
            state0 = AerStatevector(vec0)
            state1 = AerStatevector(vec1)
            self.assertEqual(state0 - state1, AerStatevector(vec0 - vec1))

    def test_multiply(self):
        """Test multiply method."""
        for _ in range(10):
            vec = self.rand_vec(4)
            state = AerStatevector(vec)
            val = np.random.rand() + 1j * np.random.rand()
            self.assertEqual(val * state, AerStatevector(val * state))

    def test_negate(self):
        """Test negate method"""
        for _ in range(10):
            vec = self.rand_vec(4)
            state = AerStatevector(vec)
            self.assertEqual(-state, AerStatevector(-1 * vec))

    def test_equiv(self):
        """Test equiv method"""
        vec = np.array([1, 0, 0, -1j]) / np.sqrt(2)
        phase = np.exp(-1j * np.pi / 4)
        statevec = AerStatevector(vec)
        self.assertTrue(statevec.equiv(phase * vec))
        self.assertTrue(statevec.equiv(AerStatevector(phase * vec)))
        self.assertFalse(statevec.equiv(2 * vec))

    def test_equiv_on_circuit(self):
        """Test the equiv method on different types of input."""
        statevec = AerStatevector([1, 0])

        qc = QuantumCircuit(1)
        self.assertTrue(statevec.equiv(qc))
        qc.x(0)
        self.assertFalse(statevec.equiv(qc))

    def test_to_dict(self):
        """Test to_dict method"""

        with self.subTest(msg="dims = (2, 3)"):
            with self.assertRaises(Exception):
                vec = AerStatevector(np.arange(1, 7), dims=(2, 3))

        with self.subTest(msg="dims = (11, )"):
            with self.assertRaises(Exception):
                vec = AerStatevector(np.arange(1, 12), dims=(11,))

        with self.subTest(msg="dims = (2, 11)"):
            with self.assertRaises(Exception):
                vec = AerStatevector(np.arange(1, 23), dims=(2, 11))

    def test_probabilities_product(self):
        """Test probabilities method for product state"""

        state = AerStatevector.from_label("+0")

        # 2-qubit qargs
        with self.subTest(msg="P(None)"):
            probs = state.probabilities()
            target = np.array([0.5, 0, 0.5, 0])
            self.assertTrue(np.allclose(probs, target))

        with self.subTest(msg="P([0, 1])"):
            probs = state.probabilities([0, 1])
            target = np.array([0.5, 0, 0.5, 0])
            self.assertTrue(np.allclose(probs, target))

        with self.subTest(msg="P([1, 0]"):
            probs = state.probabilities([1, 0])
            target = np.array([0.5, 0.5, 0, 0])
            self.assertTrue(np.allclose(probs, target))

        # 1-qubit qargs
        with self.subTest(msg="P([0])"):
            probs = state.probabilities([0])
            target = np.array([1, 0])
            self.assertTrue(np.allclose(probs, target))

        with self.subTest(msg="P([1])"):
            probs = state.probabilities([1])
            target = np.array([0.5, 0.5])
            self.assertTrue(np.allclose(probs, target))

    def test_probabilities_ghz(self):
        """Test probabilities method for GHZ state"""

        state = (AerStatevector.from_label("000") + AerStatevector.from_label("111")) / np.sqrt(2)

        # 3-qubit qargs
        target = np.array([0.5, 0, 0, 0, 0, 0, 0, 0.5])
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

        # 2-qubit qargs
        target = np.array([0.5, 0, 0, 0.5])
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

        # 1-qubit qargs
        target = np.array([0.5, 0.5])
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

    def test_probabilities_w(self):
        """Test probabilities method with W state"""

        state = (
            AerStatevector.from_label("001")
            + AerStatevector.from_label("010")
            + AerStatevector.from_label("100")
        ) / np.sqrt(3)

        # 3-qubit qargs
        target = np.array([0, 1 / 3, 1 / 3, 0, 1 / 3, 0, 0, 0])
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

        # 2-qubit qargs
        target = np.array([1 / 3, 1 / 3, 1 / 3, 0])
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

        # 1-qubit qargs
        target = np.array([2 / 3, 1 / 3])
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities(qargs)
                self.assertTrue(np.allclose(probs, target))

    def test_probabilities_dict_product(self):
        """Test probabilities_dict method for product state"""

        state = AerStatevector.from_label("+0")

        # 2-qubit qargs
        with self.subTest(msg="P(None)"):
            probs = state.probabilities_dict()
            target = {"00": 0.5, "10": 0.5}
            self.assertDictAlmostEqual(probs, target)

        with self.subTest(msg="P([0, 1])"):
            probs = state.probabilities_dict([0, 1])
            target = {"00": 0.5, "10": 0.5}
            self.assertDictAlmostEqual(probs, target)

        with self.subTest(msg="P([1, 0]"):
            probs = state.probabilities_dict([1, 0])
            target = {"00": 0.5, "01": 0.5}
            self.assertDictAlmostEqual(probs, target)

        # 1-qubit qargs
        with self.subTest(msg="P([0])"):
            probs = state.probabilities_dict([0])
            target = {"0": 1}
            self.assertDictAlmostEqual(probs, target)

        with self.subTest(msg="P([1])"):
            probs = state.probabilities_dict([1])
            target = {"0": 0.5, "1": 0.5}
            self.assertDictAlmostEqual(probs, target)

    def test_probabilities_dict_ghz(self):
        """Test probabilities_dict method for GHZ state"""

        state = (AerStatevector.from_label("000") + AerStatevector.from_label("111")) / np.sqrt(2)

        # 3-qubit qargs
        target = {"000": 0.5, "111": 0.5}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

        # 2-qubit qargs
        target = {"00": 0.5, "11": 0.5}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

        # 1-qubit qargs
        target = {"0": 0.5, "1": 0.5}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

    def test_probabilities_dict_w(self):
        """Test probabilities_dict method with W state"""

        state = (
            AerStatevector.from_label("001")
            + AerStatevector.from_label("010")
            + AerStatevector.from_label("100")
        ) / np.sqrt(3)

        # 3-qubit qargs
        target = np.array([0, 1 / 3, 1 / 3, 0, 1 / 3, 0, 0, 0])
        target = {"001": 1 / 3, "010": 1 / 3, "100": 1 / 3}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

        # 2-qubit qargs
        target = {"00": 1 / 3, "01": 1 / 3, "10": 1 / 3}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

        # 1-qubit qargs
        target = {"0": 2 / 3, "1": 1 / 3}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"P({qargs})"):
                probs = state.probabilities_dict(qargs)
                self.assertDictAlmostEqual(probs, target)

    def test_sample_counts_ghz(self):
        """Test sample_counts method for GHZ state"""

        shots = 2000
        threshold = 0.02 * shots
        state = (AerStatevector.from_label("000") + AerStatevector.from_label("111")) / np.sqrt(2)
        state.seed(100)

        # 3-qubit qargs
        target = {"000": shots / 2, "111": shots / 2}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"counts (qargs={qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

        # 2-qubit qargs
        target = {"00": shots / 2, "11": shots / 2}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"counts (qargs={qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

        # 1-qubit qargs
        target = {"0": shots / 2, "1": shots / 2}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"counts (qargs={qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

    def test_sample_counts_w(self):
        """Test sample_counts method for W state"""
        shots = 6000
        threshold = 0.02 * shots
        state = (
            AerStatevector.from_label("001")
            + AerStatevector.from_label("010")
            + AerStatevector.from_label("100")
        ) / np.sqrt(3)
        state.seed(100)

        target = {"001": shots / 3, "010": shots / 3, "100": shots / 3}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

        # 2-qubit qargs
        target = {"00": shots / 3, "01": shots / 3, "10": shots / 3}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"P({qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

        # 1-qubit qargs
        target = {"0": 2 * shots / 3, "1": shots / 3}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"P({qargs})"):
                counts = state.sample_counts(shots, qargs=qargs)
                self.assertDictAlmostEqual(counts, target, threshold)

    def test_sample_memory_ghz(self):
        """Test sample_memory method for GHZ state"""

        shots = 5000
        state = (AerStatevector.from_label("000") + AerStatevector.from_label("111")) / np.sqrt(2)
        state.seed(100)

        # 3-qubit qargs
        target = {"000": shots / 2, "111": shots / 2}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

        # 2-qubit qargs
        target = {"00": shots / 2, "11": shots / 2}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

        # 1-qubit qargs
        target = {"0": shots / 2, "1": shots / 2}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

    def test_sample_memory_w(self):
        """Test sample_memory method for W state"""
        shots = 3000
        state = (
            AerStatevector.from_label("001")
            + AerStatevector.from_label("010")
            + AerStatevector.from_label("100")
        ) / np.sqrt(3)
        state.seed(100)

        target = {"001": shots / 3, "010": shots / 3, "100": shots / 3}
        for qargs in [[0, 1, 2], [2, 1, 0], [1, 2, 0], [1, 0, 2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

        # 2-qubit qargs
        target = {"00": shots / 3, "01": shots / 3, "10": shots / 3}
        for qargs in [[0, 1], [2, 1], [1, 2], [1, 2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

        # 1-qubit qargs
        target = {"0": 2 * shots / 3, "1": shots / 3}
        for qargs in [[0], [1], [2]]:
            with self.subTest(msg=f"memory (qargs={qargs})"):
                memory = state.sample_memory(shots, qargs=qargs)
                self.assertEqual(len(memory), shots)
                self.assertEqual(set(memory), set(target))

    def test_reset_2qubit(self):
        """Test reset method for 2-qubit state"""

        state = AerStatevector(np.array([1, 0, 0, 1]) / np.sqrt(2))
        state.seed(100)

        with self.subTest(msg="reset"):
            psi = state.copy()
            value = psi.reset()
            target = AerStatevector(np.array([1, 0, 0, 0]))
            self.assertEqual(value, target)

        with self.subTest(msg="reset"):
            psi = state.copy()
            value = psi.reset([0, 1])
            target = AerStatevector(np.array([1, 0, 0, 0]))
            self.assertEqual(value, target)

        with self.subTest(msg="reset [0]"):
            psi = state.copy()
            value = psi.reset([0])
            targets = [
                AerStatevector(np.array([1, 0, 0, 0])),
                AerStatevector(np.array([0, 0, 1, 0])),
            ]
            self.assertIn(value, targets)

        with self.subTest(msg="reset [0]"):
            psi = state.copy()
            value = psi.reset([1])
            targets = [
                AerStatevector(np.array([1, 0, 0, 0])),
                AerStatevector(np.array([0, 1, 0, 0])),
            ]
            self.assertIn(value, targets)

    def test_measure_2qubit(self):
        """Test measure method for 2-qubit state"""

        state = AerStatevector.from_label("+0")
        seed = 200
        shots = 100

        with self.subTest(msg="measure"):
            for i in range(shots):
                psi = state.copy()
                psi.seed(seed + i)
                outcome, value = psi.measure()
                self.assertIn(outcome, ["00", "10"])
                if outcome == "00":
                    target = AerStatevector.from_label("00")
                    self.assertEqual(value, target)
                else:
                    target = AerStatevector.from_label("10")
                    self.assertEqual(value, target)

        with self.subTest(msg="measure [0, 1]"):
            for i in range(shots):
                psi = state.copy()
                outcome, value = psi.measure([0, 1])
                self.assertIn(outcome, ["00", "10"])
                if outcome == "00":
                    target = AerStatevector.from_label("00")
                    self.assertEqual(value, target)
                else:
                    target = AerStatevector.from_label("10")
                    self.assertEqual(value, target)

        with self.subTest(msg="measure [1, 0]"):
            for i in range(shots):
                psi = state.copy()
                outcome, value = psi.measure([1, 0])
                self.assertIn(outcome, ["00", "01"])
                if outcome == "00":
                    target = AerStatevector.from_label("00")
                    self.assertEqual(value, target)
                else:
                    target = AerStatevector.from_label("10")
                    self.assertEqual(value, target)

        with self.subTest(msg="measure [0]"):
            for i in range(shots):
                psi = state.copy()
                outcome, value = psi.measure([0])
                self.assertEqual(outcome, "0")
                target = AerStatevector(np.array([1, 0, 1, 0]) / np.sqrt(2))
                self.assertEqual(value, target)

        with self.subTest(msg="measure [1]"):
            for i in range(shots):
                psi = state.copy()
                outcome, value = psi.measure([1])
                self.assertIn(outcome, ["0", "1"])
                if outcome == "0":
                    target = AerStatevector.from_label("00")
                    self.assertEqual(value, target)
                else:
                    target = AerStatevector.from_label("10")
                    self.assertEqual(value, target)

    def test_from_int(self):
        """Test from_int method"""

        with self.subTest(msg="from_int(0, 4)"):
            target = AerStatevector([1, 0, 0, 0])
            value = AerStatevector.from_int(0, 4)
            self.assertEqual(target, value)

        with self.subTest(msg="from_int(3, 4)"):
            target = AerStatevector([0, 0, 0, 1])
            value = AerStatevector.from_int(3, 4)
            self.assertEqual(target, value)

    def test_expval(self):
        """Test expectation_value method"""

        psi = AerStatevector([1, 0, 0, 1]) / np.sqrt(2)
        for label, target in [
            ("II", 1),
            ("XX", 1),
            ("YY", -1),
            ("ZZ", 1),
            ("IX", 0),
            ("YZ", 0),
            ("ZX", 0),
            ("YI", 0),
        ]:
            with self.subTest(msg=f"<{label}>"):
                op = Pauli(label)
                expval = psi.expectation_value(op)
                self.assertAlmostEqual(expval, target)

        psi = AerStatevector([np.sqrt(2), 0, 0, 0, 0, 0, 0, 1 + 1j]) / 2
        for label, target in [
            ("XXX", np.sqrt(2) / 2),
            ("YYY", -np.sqrt(2) / 2),
            ("ZZZ", 0),
            ("XYZ", 0),
            ("YIY", 0),
        ]:
            with self.subTest(msg=f"<{label}>"):
                op = Pauli(label)
                expval = psi.expectation_value(op)
                self.assertAlmostEqual(expval, target)

        labels = ["XXX", "IXI", "YYY", "III"]
        coeffs = [3.0, 5.5, -1j, 23]
        spp_op = SparsePauliOp.from_list(list(zip(labels, coeffs)))
        expval = psi.expectation_value(spp_op)
        target = 25.121320343559642 + 0.7071067811865476j
        self.assertAlmostEqual(expval, target)

    @data(
        "II",
        "IX",
        "IY",
        "IZ",
        "XI",
        "XX",
        "XY",
        "XZ",
        "YI",
        "YX",
        "YY",
        "YZ",
        "ZI",
        "ZX",
        "ZY",
        "ZZ",
        "-II",
        "-IX",
        "-IY",
        "-IZ",
        "-XI",
        "-XX",
        "-XY",
        "-XZ",
        "-YI",
        "-YX",
        "-YY",
        "-YZ",
        "-ZI",
        "-ZX",
        "-ZY",
        "-ZZ",
        "iII",
        "iIX",
        "iIY",
        "iIZ",
        "iXI",
        "iXX",
        "iXY",
        "iXZ",
        "iYI",
        "iYX",
        "iYY",
        "iYZ",
        "iZI",
        "iZX",
        "iZY",
        "iZZ",
        "-iII",
        "-iIX",
        "-iIY",
        "-iIZ",
        "-iXI",
        "-iXX",
        "-iXY",
        "-iXZ",
        "-iYI",
        "-iYX",
        "-iYY",
        "-iYZ",
        "-iZI",
        "-iZX",
        "-iZY",
        "-iZZ",
    )
    def test_expval_pauli(self, pauli):
        """Test expectation_value method for Pauli op"""
        seed = 1020
        op = Pauli(pauli)
        state = random_statevector(2**op.num_qubits, seed=seed)
        state = AerStatevector(state.data)
        target = state.expectation_value(op.to_matrix())
        expval = state.expectation_value(op)
        self.assertAlmostEqual(expval, target)

    @data([0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1])
    def test_expval_pauli_qargs(self, qubits):
        """Test expectation_value method for Pauli op"""
        seed = 1020
        op = random_pauli(2, seed=seed)
        state = random_statevector(2**3, seed=seed)
        state = AerStatevector(state.data)
        target = state.expectation_value(op.to_matrix(), qubits)
        expval = state.expectation_value(op, qubits)
        self.assertAlmostEqual(expval, target)

    @data(*(qargs for i in range(4) for qargs in permutations(range(4), r=i + 1)))
    def test_probabilities_qargs(self, qargs):
        """Test probabilities method with qargs"""
        # Get initial state
        nq = 4
        nc = len(qargs)
        state_circ = QuantumCircuit(nq, nc)
        for i in range(nq):
            state_circ.ry((i + 1) * np.pi / (nq + 1), i)

        # Get probabilities
        state = AerStatevector(state_circ)
        probs = state.probabilities(qargs)

        # Estimate target probs from simulator measurement
        sim = QasmSimulatorPy()
        shots = 5000
        seed = 100
        circ = transpile(state_circ, sim)
        circ.measure(qargs, range(nc))
        result = sim.run(circ, shots=shots, seed_simulator=seed).result()
        target = np.zeros(2**nc, dtype=float)
        for i, p in result.get_counts(0).int_outcomes().items():
            target[i] = p / shots
        # Compare
        delta = np.linalg.norm(probs - target)
        self.assertLess(delta, 0.05)

    def test_global_phase(self):
        """Test global phase is handled correctly when evolving statevector."""

        qc = QuantumCircuit(1)
        qc.rz(0.5, 0)
        qc2 = transpile(qc, basis_gates=["p"])
        sv = AerStatevector.from_instruction(qc2)
        expected = np.array([0.96891242 - 0.24740396j, 0])
        self.assertEqual(float(qc2.global_phase), 2 * np.pi - 0.25)
        self.assertEqual(sv, AerStatevector(expected))

    def test_reverse_qargs(self):
        """Test reverse_qargs method"""
        circ1 = QFT(5)
        circ2 = circ1.reverse_bits()

        state1 = AerStatevector.from_instruction(circ1)
        state2 = AerStatevector.from_instruction(circ2)
        self.assertEqual(state1.reverse_qargs(), state2)

    def test_drawings(self):
        """Test draw method"""
        qc1 = QFT(5)
        sv = AerStatevector.from_instruction(qc1)
        with self.subTest(msg="str(statevector)"):
            str(sv)
        for drawtype in ["repr", "text", "latex", "latex_source", "qsphere", "hinton", "bloch"]:
            with self.subTest(msg=f"draw('{drawtype}')"):
                sv.draw(drawtype)
        with self.subTest(msg=" draw('latex', convention='vector')"):
            sv.draw("latex", convention="vector")

    def test_state_to_latex_for_large_statevector(self):
        """Test conversion of large dense state vector"""
        sv = AerStatevector(np.ones((2**15, 1)))
        latex_representation = state_to_latex(sv)
        self.assertEqual(
            latex_representation,
            " |000000000000000\\rangle+ |000000000000001\\rangle+ |000000000000010\\rangle+"
            " |000000000000011\\rangle+ |000000000000100\\rangle+ |000000000000101\\rangle +"
            " \\ldots + |111111111111011\\rangle+ |111111111111100\\rangle+"
            " |111111111111101\\rangle+ |111111111111110\\rangle+ |111111111111111\\rangle",
        )

    def test_state_to_latex_for_large_sparse_statevector(self):
        """Test conversion of large sparse state vector"""
        sv = AerStatevector(np.eye(2**15, 1))
        latex_representation = state_to_latex(sv)
        self.assertEqual(latex_representation, " |000000000000000\\rangle")

    def test_number_to_latex_terms(self):
        """Test conversions of complex numbers to latex terms"""

        cases = [
            ([1 - 8e-17, 0], ["", None]),
            ([0, -1], [None, "-"]),
            ([0, 1], [None, ""]),
            ([0, 1j], [None, "i"]),
            ([-1, 1], ["-", "+"]),
            ([0, 1j], [None, "i"]),
            ([-1, 1j], ["-", "+i"]),
            ([1e-16 + 1j], ["i"]),
            ([-1 + 1e-16 * 1j], ["-"]),
            ([-1, -1 - 1j], ["-", "+(-1 - i)"]),
            ([np.sqrt(2) / 2, np.sqrt(2) / 2], ["\\frac{\\sqrt{2}}{2}", "+\\frac{\\sqrt{2}}{2}"]),
            ([1 + np.sqrt(2)], ["(1 + \\sqrt{2})"]),
        ]
        with self.assertWarns(DeprecationWarning):
            for numbers, latex_terms in cases:
                terms = numbers_to_latex_terms(numbers, 15)
                self.assertListEqual(terms, latex_terms)

    def test_statevector_draw_latex_regression(self):
        """Test numerical rounding errors are not printed"""
        sv = AerStatevector(np.array([1 - 8e-17, 8.32667268e-17j]))
        latex_string = sv.draw(output="latex_source")
        self.assertTrue(latex_string.startswith(" |0\\rangle"))
        self.assertNotIn("|1\\rangle", latex_string)

    def test_statevector_len(self):
        """Test state vector length"""
        empty_vector = []
        with self.assertRaises(Exception):
            empty_sv = AerStatevector([])


if __name__ == "__main__":
    unittest.main()
