示例#1
0
 def test_init_raising_lowering_ops(self):
     """Test __init__ for +_i -_i pattern"""
     with self.subTest("one reg"):
         actual = SpinOp("+_0 -_0", spin=1, register_length=1)
         expected = SpinOp([("X_0^2", 1), ("Y_0^2", 1), ("Z_0", 1)],
                           spin=1,
                           register_length=1)
         self.assertSpinEqual(actual, expected)
     with self.subTest("two reg"):
         actual = SpinOp("+_1 -_1 +_0 -_0", spin=3 / 2, register_length=2)
         expected = SpinOp(
             [
                 ("X_0^2 X_1^2", 1),
                 ("X_0^2 Y_1^2", 1),
                 ("X_0^2 Z_1", 1),
                 ("Y_0^2 X_1^2", 1),
                 ("Y_0^2 Y_1^2", 1),
                 ("Y_0^2 Z_1", 1),
                 ("Z_0 X_1^2", 1),
                 ("Z_0 Y_1^2", 1),
                 ("Z_0 Z_1", 1),
             ],
             spin=3 / 2,
             register_length=2,
         )
         self.assertSpinEqual(actual, expected)
示例#2
0
 def test_init_multiple_digits(self):
     """Test __init__ for sparse label with multiple digits"""
     actual = SpinOp([("X_10^20", 1 + 2j), ("X_12^34", 56)],
                     Fraction(5, 2),
                     register_length=13)
     desired = [("X_10^20", 1 + 2j), ("X_12^34", 56)]
     self.assertListEqual(actual.to_list(), desired)
示例#3
0
 def test_init_label(self, label, pre_processing):
     """Test __init__"""
     spin = SpinOp(pre_processing(label), register_length=len(label) // 3)
     expected_label = " ".join(lb for lb in label.split() if lb[0] != "I")
     if not expected_label:
         expected_label = f"I_{len(label) // 3 - 1}"
     self.assertListEqual(spin.to_list(), [(expected_label, 1)])
     self.assertSpinEqual(eval(repr(spin)), spin)  # pylint: disable=eval-used
示例#4
0
    def test_add(self):
        """Test __add__"""
        with self.subTest("sum of heisenberg"):
            actual = self.heisenberg + self.heisenberg
            desired = SpinOp(
                (self.heisenberg_spin_array, 2 * self.heisenberg_coeffs),
                spin=1)
            self.assertSpinEqual(actual, desired)

        with self.subTest("raising operator"):
            plus = SpinOp("+", 3 / 2)
            x = SpinOp("X", 3 / 2)
            y = SpinOp("Y", 3 / 2)
            self.assertSpinEqual(x + 1j * y, plus)
示例#5
0
    def second_q_ops(self, display_format: Optional[str] = None) -> SpinOp:
        """Return the Hamiltonian of the Ising model in terms of `SpinOp`.

        Args:
            display_format: Not supported for Spin operators. If specified, it will be ignored.

        Returns:
            SpinOp: The Hamiltonian of the Ising model.
        """
        if display_format is not None:
            logger.warning(
                "Spin operators do not support display-format. Provided display-format "
                "parameter will be ignored."
            )
        ham = []
        weighted_edge_list = self._lattice.weighted_edge_list
        register_length = self._lattice.num_nodes
        # kinetic terms
        for node_a, node_b, weight in weighted_edge_list:
            if node_a == node_b:
                index = node_a
                ham.append((f"X_{index}", weight))

            else:
                index_left = node_a
                index_right = node_b
                coupling_parameter = weight
                ham.append((f"Z_{index_left} Z_{index_right}", coupling_parameter))

        return SpinOp(ham, spin=Fraction(1, 2), register_length=register_length)
示例#6
0
 def test_flatten_ladder_ops(self):
     """Test _flatten_ladder_ops"""
     actual = SpinOp._flatten_ladder_ops([("+-", 2j)])
     self.assertSetEqual(
         frozenset(actual),
         frozenset([("XX", 2j), ("XY", 2), ("YX", -2), ("YY", 2j)]),
     )
示例#7
0
    def test_adjoint(self):
        """Test adjoint method and dagger property"""
        with self.subTest("heisenberg adjoint"):
            actual = self.heisenberg.adjoint()
            desired = SpinOp((self.heisenberg_spin_array,
                              self.heisenberg_coeffs.conjugate().T),
                             spin=1)
            self.assertSpinEqual(actual, desired)

        with self.subTest("imag heisenberg adjoint"):
            actual = ~((3 + 2j) * self.heisenberg)
            desired = SpinOp(
                (self.heisenberg_spin_array,
                 ((3 + 2j) * self.heisenberg_coeffs).conjugate().T),
                spin=1,
            )
            self.assertSpinEqual(actual, desired)
示例#8
0
    def test_hermiticity(self):
        """test is_hermitian"""
        # deliberately define test operator with X and Y which creates duplicate terms in .to_list()
        # in case .adjoint() simplifies terms
        with self.subTest("operator hermitian"):
            test_op = SpinOp("+ZXY") + SpinOp("-ZXY")
            self.assertTrue(test_op.is_hermitian())

        with self.subTest("operator not hermitian"):
            test_op = SpinOp("+ZXY") - SpinOp("-ZXY")
            self.assertFalse(test_op.is_hermitian())
示例#9
0
 def test_init_heisenberg(self):
     """Test __init__ for Heisenberg model."""
     actual = SpinOp(
         [
             ("XX", -1),
             ("YY", -1),
             ("ZZ", -1),
             ("ZI", -0.3),
             ("IZ", -0.3),
         ],
         spin=1,
     )
     self.assertSpinEqual(actual, self.heisenberg)
    def map(self, second_q_op: SpinOp) -> PauliSumOp:
        """Map spins to qubits using the Logarithmic encoding.

        Args:
            second_q_op: Spins mapped to qubits.

        Returns:
            Qubit operators generated by the Logarithmic encoding
        """
        qubit_ops_list: List[PauliSumOp] = []

        # get logarithmic encoding of the general spin matrices.
        spinx, spiny, spinz, identity = self._logarithmic_encoding(
            second_q_op.spin)
        for idx, (_, coeff) in enumerate(second_q_op.to_list()):

            operatorlist: List[PauliSumOp] = []

            for n_x, n_y, n_z in zip(second_q_op.x[idx], second_q_op.y[idx],
                                     second_q_op.z[idx]):

                operator_on_spin_i: List[PauliSumOp] = []

                if n_x > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spinx] * int(n_x)))

                if n_y > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spiny] * int(n_y)))

                if n_z > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spinz] * int(n_z)))

                if operator_on_spin_i:
                    single_operator_on_spin_i = reduce(operator.matmul,
                                                       operator_on_spin_i)
                    operatorlist.append(single_operator_on_spin_i)

                else:
                    # If n_x=n_y=n_z=0, simply add the embedded Identity operator.
                    operatorlist.append(identity)

            # Now, we can tensor all operators in this list
            qubit_ops_list.append(coeff *
                                  reduce(operator.xor, reversed(operatorlist)))

        qubit_op = reduce(operator.add, qubit_ops_list)

        return qubit_op
示例#11
0
 def setUp(self):
     super().setUp()
     self.heisenberg_spin_array = np.array([
         [[1, 1], [0, 0], [0, 0], [0, 0], [0, 0]],
         [[0, 0], [1, 1], [0, 0], [0, 0], [0, 0]],
         [[0, 0], [0, 0], [1, 1], [1, 0], [0, 1]],
     ], )
     self.heisenberg_coeffs = np.array([-1, -1, -1, -0.3, -0.3])
     self.heisenberg = SpinOp(
         (self.heisenberg_spin_array, self.heisenberg_coeffs),
         spin=1,
     )
     self.zero_op = SpinOp(
         (np.array([[[0, 0]], [[0, 0]], [[0, 0]]]), np.array([0])),
         spin=1,
     )
     self.spin_1_matrix = {
         "I": np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]),
         "X": np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) / np.sqrt(2),
         "Y":
         np.array([[0, -1j, 0], [1j, 0, -1j], [0, 1j, 0]]) / np.sqrt(2),
         "Z": np.array([[1, 0, 0], [0, 0, 0], [0, 0, -1]]),
     }
示例#12
0
    def test_simplify(self):
        """Test simplify"""
        with self.subTest("trivial reduce"):
            actual = (self.heisenberg - self.heisenberg).simplify()
            self.assertListEqual(actual.to_list(), [("I_1", 0)])

        with self.subTest("nontrivial reduce"):
            test_op = SpinOp(
                (
                    np.array([[[0, 1], [0, 1]], [[0, 0], [0, 0]],
                              [[1, 0], [1, 0]]]),
                    np.array([1.5, 2.5]),
                ),
                spin=3 / 2,
            )
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0 X_1", 4)])

        with self.subTest("nontrivial reduce 2"):
            test_op = SpinOp(
                (
                    np.array([
                        [[0, 1], [0, 1], [1, 1]],
                        [[0, 0], [0, 0], [0, 0]],
                        [[1, 0], [1, 0], [0, 0]],
                    ]),
                    np.array([1.5, 2.5, 2]),
                ),
                spin=3 / 2,
            )
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0 X_1", 4),
                                                    ("X_0 X_1", 2)])

        with self.subTest("nontrivial reduce 3"):
            test_op = SpinOp([("+_0 -_0", 1)], register_length=4)
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0", 1), ("Y_0^2", 1),
                                                    ("X_0^2", 1)])
示例#13
0
class TestLinearMapper(QiskitNatureTestCase):
    """Test Linear Mapper"""

    spin_op1 = SpinOp([("Y_0^2", -0.432 + 1.32j)], 0.5, 1)
    ref_qubit_op1 = (-0.054 + 0.165j) * (I ^ I) + (0.054 - 0.165j) * (Z ^ Z)

    spin_op2 = SpinOp([("X_0 Z_0 I_1", -1.139 + 0.083j)], 0.5, 2)
    ref_qubit_op2 = (0.010375 + 0.142375j) * (I ^ I ^ Y ^ X) + (-0.010375 - 0.142375j) * (
        I ^ I ^ X ^ Y
    )

    spin_op3 = SpinOp([("X_0 Y_0^2 Z_0 X_1 Y_1 Y_2 Z_2", -0.18 + 1.204j)], 0.5, 3)
    ref_qubit_op3 = (
        (0.000587890625 + 8.7890625e-05j) * (Y ^ Y ^ I ^ Z ^ Y ^ X)
        + (0.000587890625 + 8.7890625e-05j) * (X ^ X ^ I ^ Z ^ Y ^ X)
        + (-0.000587890625 - 8.7890625e-05j) * (Y ^ Y ^ Z ^ I ^ Y ^ X)
        + (-0.000587890625 - 8.7890625e-05j) * (X ^ X ^ Z ^ I ^ Y ^ X)
        + (-0.000587890625 - 8.7890625e-05j) * (Y ^ Y ^ I ^ Z ^ X ^ Y)
        + (-0.000587890625 - 8.7890625e-05j) * (X ^ X ^ I ^ Z ^ X ^ Y)
        + (0.000587890625 + 8.7890625e-05j) * (Y ^ Y ^ Z ^ I ^ X ^ Y)
        + (0.000587890625 + 8.7890625e-05j) * (X ^ X ^ Z ^ I ^ X ^ Y)
    )

    spin_op4 = SpinOp([("I_0 Z_1", -0.875 - 0.075j)], 1.5, 2)
    ref_qubit_op4 = (
        (-0.65625 - 0.05625j) * (Z ^ I ^ I ^ I ^ I ^ I ^ I ^ I)
        + (-0.21875 - 0.01875j) * (I ^ Z ^ I ^ I ^ I ^ I ^ I ^ I)
        + (0.21875 + 0.01875j) * (I ^ I ^ Z ^ I ^ I ^ I ^ I ^ I)
        + (0.65625 + 0.05625j) * (I ^ I ^ I ^ Z ^ I ^ I ^ I ^ I)
    )

    spin_op5 = SpinOp([("X_0 I_1 I_2 I_3 I_4 I_5 I_6 I_7", 4 + 0j)], 0.5, 8) + SpinOp(
        [("I_0^2 I_2 I_3 I_4 I_5 I_6 I_7 I_8", 8 + 0j)], 0.5, 8
    )
    ref_qubit_op5 = (
        (8.0 + 0j) * (I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I)
        + (1.0 + 0j) * (I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ X ^ X)
        + (1.0 + 0j) * (I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ I ^ Y ^ Y)
    )

    @data(
        (spin_op1, ref_qubit_op1),
        (spin_op2, ref_qubit_op2),
        (spin_op3, ref_qubit_op3),
        (spin_op4, ref_qubit_op4),
        (spin_op5, ref_qubit_op5),
    )
    @unpack
    def test_mapping(self, spin_op, ref_qubit_op):
        """Test mapping to qubit operator"""
        mapper = LinearMapper()
        qubit_op = mapper.map(spin_op)
        self.assertEqual(qubit_op, ref_qubit_op)
示例#14
0
    def map(self, second_q_op: SpinOp) -> PauliSumOp:

        qubit_ops_list: List[PauliSumOp] = []

        # get linear encoding of the general spin matrices
        spinx, spiny, spinz, identity = self._linear_encoding(second_q_op.spin)

        for idx, (_, coeff) in enumerate(second_q_op.to_list()):

            operatorlist: List[PauliSumOp] = []

            for n_x, n_y, n_z in zip(second_q_op.x[idx], second_q_op.y[idx],
                                     second_q_op.z[idx]):

                operator_on_spin_i: List[PauliSumOp] = []

                if n_x > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spinx] * int(n_x)))

                if n_y > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spiny] * int(n_y)))

                if n_z > 0:
                    operator_on_spin_i.append(
                        reduce(operator.matmul, [spinz] * int(n_z)))

                if np.any([n_x, n_y, n_z]) > 0:
                    single_operator_on_spin_i = reduce(operator.matmul,
                                                       operator_on_spin_i)
                    operatorlist.append(single_operator_on_spin_i.reduce())

                else:
                    # If n_x=n_y=n_z=0, simply add the embedded Identity operator.
                    operatorlist.append(identity)

            # Now, we can tensor all operators in this list
            # NOTE: in Qiskit's opflow the `XOR` (i.e. `^`) operator does the tensor product
            qubit_ops_list.append(coeff *
                                  reduce(operator.xor, reversed(operatorlist)))

        qubit_op = reduce(operator.add, qubit_ops_list)

        return qubit_op
class TestLogarithmicMapper(QiskitNatureTestCase):
    """Test Logarithmic Mapper"""

    spin_op1 = SpinOp([("Y_0", -0.432 + 1.32j)], 0.5, 1)
    ref_qubit_op1 = (-0.216 + 0.66j) * (Y)

    spin_op2 = SpinOp([("X_0 Z_0 I_1", -1.139 + 0.083j)], 0.5, 2)
    ref_qubit_op2 = (0.02075 + 0.28475j) * (I ^ Y)

    spin_op3 = SpinOp([("X_0 Y_0^2 Z_0 X_1 Y_1 Y_2 Z_2", -0.18 + 1.204j)], 0.5,
                      3)
    ref_qubit_op3 = (-0.004703125 - 0.000703125j) * (X ^ Z ^ Y)

    spin_op4 = SpinOp([("I_0 Z_1", -0.875 - 0.075j)], 1.5, 2)
    ref_qubit_op4 = (-0.4375 - 0.0375j) * (I ^ Z ^ I ^ I) + (
        -0.875 - 0.075j) * (Z ^ I ^ I ^ I)

    spin_op5 = SpinOp(
        [("X_0 I_1 I_2 I_3 I_4 I_5 I_6 I_7", 4 + 0j)], 0.5, 8) + SpinOp(
            [("I_0^2 I_2 I_3 I_4 I_5 I_6 I_7 I_8", 8 + 0j)], 0.5, 8)

    ref_qubit_op5 = (8.0 + 0j) * (I ^ I ^ I ^ I ^ I ^ I ^ I ^ I) + (
        2.0 + 0j) * (I ^ I ^ I ^ I ^ I ^ I ^ I ^ X)

    spin_op6 = SpinOp([("Z_0", -0.875 - 0.075j)], 1, 1)
    ref_qubit_op6 = ((-0.4375 - 0.0375j) * (I ^ I) + (0.4375 + 0.0375j) *
                     (I ^ Z) + (-0.875 - 0.075j) * (Z ^ Z))

    ref_qubit_op7 = ((-0.4375 - 0.0375j) * (I ^ I) + (-0.4375 - 0.0375j) *
                     (I ^ Z) + (-0.875 - 0.075j) * (Z ^ I))

    @data(
        (spin_op1, ref_qubit_op1),
        (spin_op2, ref_qubit_op2),
        (spin_op3, ref_qubit_op3),
        (spin_op4, ref_qubit_op4),
        (spin_op5, ref_qubit_op5),
        (spin_op6, ref_qubit_op6, 2),
        (spin_op6, ref_qubit_op7, 2, False),
    )
    @unpack
    def test_mapping(self, spin_op, ref_qubit_op, padding=1, embed_upper=True):
        """Test mapping to qubit operator"""
        mapper = LogarithmicMapper(padding, embed_upper)
        qubit_op = mapper.map(spin_op)
        self.assertEqual(qubit_op, ref_qubit_op)
    def _logarithmic_encoding(
        self, spin: Union[Fraction, int]
    ) -> Tuple[PauliSumOp, PauliSumOp, PauliSumOp, PauliSumOp]:
        """The logarithmic encoding.

        Args:
            spin: Positive half-integer (integer or half-odd-integer) that represents spin.

        Returns:
            A tuple containing four PauliSumOp.
        """
        spin_op_encoding: List[PauliSumOp] = []
        dspin = int(2 * spin + 1)
        num_qubits = int(np.ceil(np.log2(dspin)))

        # Get the spin matrices
        spin_matrices = [
            SpinOp(symbol, spin=spin).to_matrix() for symbol in "XYZ"
        ]
        # Append the identity
        spin_matrices.append(np.eye(dspin))

        # Embed the spin matrices in a larger matrix of size 2**num_qubits x 2**num_qubits
        embedded_spin_matrices = [
            self._embed_matrix(matrix, num_qubits) for matrix in spin_matrices
        ]

        # Generate operators from these embedded spin matrices
        embedded_operators = [
            Operator(matrix) for matrix in embedded_spin_matrices
        ]
        for op in embedded_operators:
            op = SparsePauliOp.from_operator(op)
            op.chop()
            spin_op_encoding.append(PauliSumOp(1.0 * op))

        return tuple(spin_op_encoding)  # type: ignore
示例#17
0
 def test_init_dense_label(self, labels, pre_processing):
     """Test __init__ for dense label"""
     dense_label, sparse_label = labels
     actual = SpinOp(pre_processing(dense_label))
     desired = SpinOp([(sparse_label, 1)], register_length=len(dense_label))
     self.assertSpinEqual(actual, desired)
示例#18
0
    def _linear_encoding(self, spin: Union[Fraction,
                                           float]) -> List[PauliSumOp]:
        """
        Generates a 'linear_encoding' of the spin S operators 'X', 'Y', 'Z' and 'identity'
        to qubit operators (linear combinations of pauli strings).
        In this 'linear_encoding' each individual spin S system is represented via
        2S+1 qubits and the state |s> is mapped to the state |00...010..00>, where the s-th qubit is
        in state 1.

        Returns:
            The 4-element list of transformed spin S 'X', 'Y', 'Z' and 'identity' operators.
            I.e. spin_op_encoding[0]` corresponds to the linear combination of pauli strings needed
            to represent the embedded 'X' operator
        """

        spin_op_encoding: List[PauliSumOp] = []
        dspin = int(2 * spin + 1)
        nqubits = dspin

        # quick functions to generate a pauli with X / Y / Z at location `i`
        pauli_id = Pauli('I' * nqubits)

        def pauli_x(i):
            return Pauli('I' * i + 'X' + 'I' * (nqubits - i - 1))

        def pauli_y(i):
            return Pauli('I' * i + 'Y' + 'I' * (nqubits - i - 1))

        def pauli_z(i):
            return Pauli('I' * i + 'Z' + 'I' * (nqubits - i - 1))

        # 1. build the non-diagonal X operator
        x_summands = []
        for i, coeff in enumerate(
                np.diag(SpinOp("X", spin=spin).to_matrix(), 1)):
            x_summands.append(
                PauliSumOp(coeff / 2. *
                           SparsePauliOp(pauli_x(i).dot(pauli_x(i + 1))) +
                           coeff / 2. *
                           SparsePauliOp(pauli_y(i).dot(pauli_y(i + 1)))))
        spin_op_encoding.append(reduce(operator.add, x_summands))

        # 2. build the non-diagonal Y operator
        y_summands = []
        for i, coeff in enumerate(
                np.diag(SpinOp("Y", spin=spin).to_matrix(), 1)):
            y_summands.append(
                PauliSumOp(-1j * coeff / 2. *
                           SparsePauliOp(pauli_x(i).dot(pauli_y(i + 1))) +
                           1j * coeff / 2. *
                           SparsePauliOp(pauli_y(i).dot(pauli_x(i + 1)))))
        spin_op_encoding.append(reduce(operator.add, y_summands))

        # 3. build the diagonal Z
        z_summands = []
        for i, coeff in enumerate(np.diag(SpinOp("Z", spin=spin).to_matrix())):
            # get the first upper diagonal of coeff.
            z_summands.append(
                PauliSumOp(coeff / 2. * SparsePauliOp(pauli_z(i)) +
                           coeff / 2. * SparsePauliOp(pauli_id)))

        z_operator = reduce(operator.add, z_summands)
        spin_op_encoding.append(z_operator)

        # 4. add the identity operator
        spin_op_encoding.append(PauliSumOp(1. * SparsePauliOp(pauli_id)))

        # return the lookup table for the transformed XYZI operators
        return spin_op_encoding
示例#19
0
 def test_init_invalid_label(self, label):
     """Test __init__ for invalid label"""
     with self.assertRaises(ValueError):
         SpinOp(label)
示例#20
0
    def test_init_pm_label(self):
        """Test __init__ with plus and minus label"""
        with self.subTest("plus"):
            plus = SpinOp([("+_0", 2)], register_length=1)
            desired = SpinOp([("X_0", 2), ("Y_0", 2j)], register_length=1)
            self.assertSpinEqual(plus, desired)

        with self.subTest("dense plus"):
            plus = SpinOp([("+", 2)])
            desired = SpinOp([("X_0", 2), ("Y_0", 2j)], register_length=1)
            self.assertSpinEqual(plus, desired)

        with self.subTest("minus"):
            minus = SpinOp([("-_0", 2)], register_length=1)
            desired = SpinOp([("X_0", 2), ("Y_0", -2j)], register_length=1)
            self.assertSpinEqual(minus, desired)

        with self.subTest("minus"):
            minus = SpinOp([("-", 2)])
            desired = SpinOp([("X_0", 2), ("Y_0", -2j)], register_length=1)
            self.assertSpinEqual(minus, desired)

        with self.subTest("plus tensor minus"):
            plus_tensor_minus = SpinOp([("+_0 -_1", 3)], register_length=2)
            desired = SpinOp(
                [("X_0 X_1", 3), ("Y_0 X_1", 3j), ("X_0 Y_1", -3j),
                 ("Y_0 Y_1", 3)],
                register_length=2,
            )
            self.assertSpinEqual(plus_tensor_minus, desired)

        with self.subTest("dense plus tensor minus"):
            plus_tensor_minus = SpinOp([("+-", 3)])
            desired = SpinOp(
                [("X_0 X_1", 3), ("Y_0 X_1", 3j), ("X_0 Y_1", -3j),
                 ("Y_0 Y_1", 3)],
                register_length=2,
            )
            self.assertSpinEqual(plus_tensor_minus, desired)
示例#21
0
 def assertSpinEqual(first: SpinOp, second: SpinOp):
     """Fail if two SpinOps have different matrix representations."""
     np.testing.assert_array_almost_equal(first.to_matrix(),
                                          second.to_matrix())
示例#22
0
 def test_neg(self):
     """Test __neg__"""
     actual = -self.heisenberg
     desired = SpinOp((self.heisenberg_spin_array, -self.heisenberg_coeffs),
                      spin=1)
     self.assertSpinEqual(actual, desired)
示例#23
0
class TestSpinOp(QiskitNatureTestCase):
    """SpinOp tests."""
    def setUp(self):
        super().setUp()
        self.heisenberg_spin_array = np.array([
            [[1, 1], [0, 0], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [1, 1], [0, 0], [0, 0], [0, 0]],
            [[0, 0], [0, 0], [1, 1], [1, 0], [0, 1]],
        ], )
        self.heisenberg_coeffs = np.array([-1, -1, -1, -0.3, -0.3])
        self.heisenberg = SpinOp(
            (self.heisenberg_spin_array, self.heisenberg_coeffs),
            spin=1,
        )
        self.zero_op = SpinOp(
            (np.array([[[0, 0]], [[0, 0]], [[0, 0]]]), np.array([0])),
            spin=1,
        )
        self.spin_1_matrix = {
            "I": np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]),
            "X": np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) / np.sqrt(2),
            "Y":
            np.array([[0, -1j, 0], [1j, 0, -1j], [0, 1j, 0]]) / np.sqrt(2),
            "Z": np.array([[1, 0, 0], [0, 0, 0], [0, 0, -1]]),
        }

    @staticmethod
    def assertSpinEqual(first: SpinOp, second: SpinOp):
        """Fail if two SpinOps have different matrix representations."""
        np.testing.assert_array_almost_equal(first.to_matrix(),
                                             second.to_matrix())

    @data(*product(
        (*sparse_labels(1), *sparse_labels(2), *sparse_labels(3)),
        (str2str, str2tuple, str2list),
    ))
    @unpack
    def test_init_label(self, label, pre_processing):
        """Test __init__"""
        spin = SpinOp(pre_processing(label), register_length=len(label) // 3)
        expected_label = " ".join(lb for lb in label.split() if lb[0] != "I")
        if not expected_label:
            expected_label = f"I_{len(label) // 3 - 1}"
        self.assertListEqual(spin.to_list(), [(expected_label, 1)])
        self.assertSpinEqual(eval(repr(spin)), spin)  # pylint: disable=eval-used

    @data(*product(
        (
            *zip(dense_labels(1), sparse_labels(1)),
            *zip(dense_labels(2), sparse_labels(2)),
            *zip(dense_labels(3), sparse_labels(3)),
        ),
        (str2str, str2tuple, str2list),
    ))
    @unpack
    def test_init_dense_label(self, labels, pre_processing):
        """Test __init__ for dense label"""
        dense_label, sparse_label = labels
        actual = SpinOp(pre_processing(dense_label))
        desired = SpinOp([(sparse_label, 1)], register_length=len(dense_label))
        self.assertSpinEqual(actual, desired)

    def test_init_pm_label(self):
        """Test __init__ with plus and minus label"""
        with self.subTest("plus"):
            plus = SpinOp([("+_0", 2)], register_length=1)
            desired = SpinOp([("X_0", 2), ("Y_0", 2j)], register_length=1)
            self.assertSpinEqual(plus, desired)

        with self.subTest("dense plus"):
            plus = SpinOp([("+", 2)])
            desired = SpinOp([("X_0", 2), ("Y_0", 2j)], register_length=1)
            self.assertSpinEqual(plus, desired)

        with self.subTest("minus"):
            minus = SpinOp([("-_0", 2)], register_length=1)
            desired = SpinOp([("X_0", 2), ("Y_0", -2j)], register_length=1)
            self.assertSpinEqual(minus, desired)

        with self.subTest("minus"):
            minus = SpinOp([("-", 2)])
            desired = SpinOp([("X_0", 2), ("Y_0", -2j)], register_length=1)
            self.assertSpinEqual(minus, desired)

        with self.subTest("plus tensor minus"):
            plus_tensor_minus = SpinOp([("+_0 -_1", 3)], register_length=2)
            desired = SpinOp(
                [("X_0 X_1", 3), ("Y_0 X_1", 3j), ("X_0 Y_1", -3j),
                 ("Y_0 Y_1", 3)],
                register_length=2,
            )
            self.assertSpinEqual(plus_tensor_minus, desired)

        with self.subTest("dense plus tensor minus"):
            plus_tensor_minus = SpinOp([("+-", 3)])
            desired = SpinOp(
                [("X_0 X_1", 3), ("Y_0 X_1", 3j), ("X_0 Y_1", -3j),
                 ("Y_0 Y_1", 3)],
                register_length=2,
            )
            self.assertSpinEqual(plus_tensor_minus, desired)

    def test_init_heisenberg(self):
        """Test __init__ for Heisenberg model."""
        actual = SpinOp(
            [
                ("XX", -1),
                ("YY", -1),
                ("ZZ", -1),
                ("ZI", -0.3),
                ("IZ", -0.3),
            ],
            spin=1,
        )
        self.assertSpinEqual(actual, self.heisenberg)

    def test_init_multiple_digits(self):
        """Test __init__ for sparse label with multiple digits"""
        actual = SpinOp([("X_10^20", 1 + 2j), ("X_12^34", 56)],
                        Fraction(5, 2),
                        register_length=13)
        desired = [("X_10^20", 1 + 2j), ("X_12^34", 56)]
        self.assertListEqual(actual.to_list(), desired)

    @data("IJX", "Z_0 X_0", "Z_0 +_0", "+_0 X_0")
    def test_init_invalid_label(self, label):
        """Test __init__ for invalid label"""
        with self.assertRaises(ValueError):
            SpinOp(label)

    def test_init_raising_lowering_ops(self):
        """Test __init__ for +_i -_i pattern"""
        with self.subTest("one reg"):
            actual = SpinOp("+_0 -_0", spin=1, register_length=1)
            expected = SpinOp([("X_0^2", 1), ("Y_0^2", 1), ("Z_0", 1)],
                              spin=1,
                              register_length=1)
            self.assertSpinEqual(actual, expected)
        with self.subTest("two reg"):
            actual = SpinOp("+_1 -_1 +_0 -_0", spin=3 / 2, register_length=2)
            expected = SpinOp(
                [
                    ("X_0^2 X_1^2", 1),
                    ("X_0^2 Y_1^2", 1),
                    ("X_0^2 Z_1", 1),
                    ("Y_0^2 X_1^2", 1),
                    ("Y_0^2 Y_1^2", 1),
                    ("Y_0^2 Z_1", 1),
                    ("Z_0 X_1^2", 1),
                    ("Z_0 Y_1^2", 1),
                    ("Z_0 Z_1", 1),
                ],
                spin=3 / 2,
                register_length=2,
            )
            self.assertSpinEqual(actual, expected)

    def test_neg(self):
        """Test __neg__"""
        actual = -self.heisenberg
        desired = SpinOp((self.heisenberg_spin_array, -self.heisenberg_coeffs),
                         spin=1)
        self.assertSpinEqual(actual, desired)

    def test_mul(self):
        """Test __mul__, and __rmul__"""
        actual = self.heisenberg * 2
        desired = SpinOp(
            (self.heisenberg_spin_array, 2 * self.heisenberg_coeffs), spin=1)
        self.assertSpinEqual(actual, desired)

    def test_div(self):
        """Test __truediv__"""
        actual = self.heisenberg / 3
        desired = SpinOp(
            (self.heisenberg_spin_array, self.heisenberg_coeffs / 3), spin=1)
        self.assertSpinEqual(actual, desired)

    def test_add(self):
        """Test __add__"""
        with self.subTest("sum of heisenberg"):
            actual = self.heisenberg + self.heisenberg
            desired = SpinOp(
                (self.heisenberg_spin_array, 2 * self.heisenberg_coeffs),
                spin=1)
            self.assertSpinEqual(actual, desired)

        with self.subTest("raising operator"):
            plus = SpinOp("+", 3 / 2)
            x = SpinOp("X", 3 / 2)
            y = SpinOp("Y", 3 / 2)
            self.assertSpinEqual(x + 1j * y, plus)

    def test_sub(self):
        """Test __sub__"""
        actual = self.heisenberg - self.heisenberg
        self.assertSpinEqual(actual, self.zero_op)

    def test_adjoint(self):
        """Test adjoint method"""
        with self.subTest("heisenberg adjoint"):
            actual = self.heisenberg.adjoint()
            desired = SpinOp(
                (self.heisenberg_spin_array,
                 self.heisenberg_coeffs.conjugate().T),
                spin=1,
            )
            self.assertSpinEqual(actual, desired)

        with self.subTest("imag heisenberg adjoint"):
            actual = ~((3 + 2j) * self.heisenberg)
            desired = SpinOp(
                (
                    self.heisenberg_spin_array,
                    ((3 + 2j) * self.heisenberg_coeffs).conjugate().T,
                ),
                spin=1,
            )
            self.assertSpinEqual(actual, desired)

        # TODO: implement adjoint for same register operators.
        # with self.sub Test("adjoint same register op"):
        #     actual = SpinOp("X_0 Y_0 Z_0").adjoint()

        #     print(actual.to_matrix())
        #     print(SpinOp("X_0 Y_0 Z_0").to_matrix().T.conjugate())

    def test_reduce(self):
        """Test reduce"""
        with self.assertWarns(DeprecationWarning):
            actual = (self.heisenberg - self.heisenberg).reduce()
            self.assertListEqual(actual.to_list(), [("I_1", 0)])

    def test_simplify(self):
        """Test simplify"""
        with self.subTest("trivial reduce"):
            actual = (self.heisenberg - self.heisenberg).simplify()
            self.assertListEqual(actual.to_list(), [("I_1", 0)])

        with self.subTest("nontrivial reduce"):
            test_op = SpinOp(
                (
                    np.array([[[0, 1], [0, 1]], [[0, 0], [0, 0]],
                              [[1, 0], [1, 0]]]),
                    np.array([1.5, 2.5]),
                ),
                spin=3 / 2,
            )
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0 X_1", 4)])

        with self.subTest("nontrivial reduce 2"):
            test_op = SpinOp(
                (
                    np.array([
                        [[0, 1], [0, 1], [1, 1]],
                        [[0, 0], [0, 0], [0, 0]],
                        [[1, 0], [1, 0], [0, 0]],
                    ]),
                    np.array([1.5, 2.5, 2]),
                ),
                spin=3 / 2,
            )
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0 X_1", 4),
                                                    ("X_0 X_1", 2)])

        with self.subTest("nontrivial reduce 3"):
            test_op = SpinOp([("+_0 -_0", 1)], register_length=4)
            actual = test_op.simplify()
            self.assertListEqual(actual.to_list(), [("Z_0", 1), ("Y_0^2", 1),
                                                    ("X_0^2", 1)])

    @data(*dense_labels(1))
    def test_to_matrix_single_qutrit(self, label):
        """Test to_matrix for single qutrit op"""
        actual = SpinOp(label, 1).to_matrix()
        np.testing.assert_array_almost_equal(actual, self.spin_1_matrix[label])

    @data(*product(dense_labels(1), dense_labels(1)))
    @unpack
    def test_to_matrix_sum_single_qutrit(self, label1, label2):
        """Test to_matrix for sum qutrit op"""
        actual = (SpinOp(label1, 1) + SpinOp(label2, 1)).to_matrix()
        np.testing.assert_array_almost_equal(
            actual, self.spin_1_matrix[label1] + self.spin_1_matrix[label2])

    @data(*dense_labels(2))
    def test_to_matrix_two_qutrit(self, label):
        """Test to_matrix for two qutrit op"""
        actual = SpinOp(label, 1).to_matrix()
        desired = np.kron(self.spin_1_matrix[label[0]],
                          self.spin_1_matrix[label[1]])
        np.testing.assert_array_almost_equal(actual, desired)

    @data(*dense_labels(1), *dense_labels(2), *dense_labels(3))
    def test_consistency_with_pauli(self, label):
        """Test consistency with pauli"""
        actual = SpinOp(label).to_matrix()
        desired = Pauli(label).to_matrix() / (2**(len(label) -
                                                  label.count("I")))
        np.testing.assert_array_almost_equal(actual, desired)

    def test_flatten_ladder_ops(self):
        """Test _flatten_ladder_ops"""
        actual = SpinOp._flatten_ladder_ops([("+-", 2j)])
        self.assertSetEqual(
            frozenset(actual),
            frozenset([("XX", 2j), ("XY", 2), ("YX", -2), ("YY", 2j)]),
        )

    def test_hermiticity(self):
        """test is_hermitian"""
        # deliberately define test operator with X and Y which creates duplicate terms in .to_list()
        # in case .adjoint() simplifies terms
        with self.subTest("operator hermitian"):
            test_op = SpinOp("+ZXY") + SpinOp("-ZXY")
            self.assertTrue(test_op.is_hermitian())

        with self.subTest("operator not hermitian"):
            test_op = SpinOp("+ZXY") - SpinOp("-ZXY")
            self.assertFalse(test_op.is_hermitian())
示例#24
0
 def test_mul(self):
     """Test __mul__, and __rmul__"""
     actual = self.heisenberg * 2
     desired = SpinOp(
         (self.heisenberg_spin_array, 2 * self.heisenberg_coeffs), spin=1)
     self.assertSpinEqual(actual, desired)
示例#25
0
 def test_div(self):
     """Test __truediv__"""
     actual = self.heisenberg / 3
     desired = SpinOp(
         (self.heisenberg_spin_array, self.heisenberg_coeffs / 3), spin=1)
     self.assertSpinEqual(actual, desired)
示例#26
0
 def test_to_matrix_single_qutrit(self, label):
     """Test to_matrix for single qutrit op"""
     actual = SpinOp(label, 1).to_matrix()
     np.testing.assert_array_almost_equal(actual, self.spin_1_matrix[label])
示例#27
0
 def test_consistency_with_pauli(self, label):
     """Test consistency with pauli"""
     actual = SpinOp(label).to_matrix()
     desired = Pauli(label).to_matrix() / (2**(len(label) -
                                               label.count("I")))
     np.testing.assert_array_almost_equal(actual, desired)
示例#28
0
 def test_to_matrix_two_qutrit(self, label):
     """Test to_matrix for two qutrit op"""
     actual = SpinOp(label, 1).to_matrix()
     desired = np.kron(self.spin_1_matrix[label[0]],
                       self.spin_1_matrix[label[1]])
     np.testing.assert_array_almost_equal(actual, desired)
示例#29
0
 def test_to_matrix_sum_single_qutrit(self, label1, label2):
     """Test to_matrix for sum qutrit op"""
     actual = (SpinOp(label1, 1) + SpinOp(label2, 1)).to_matrix()
     np.testing.assert_array_almost_equal(
         actual, self.spin_1_matrix[label1] + self.spin_1_matrix[label2])