def to_second_q_op(self) -> FermionicOp:
        """Creates the operator representing the Hamiltonian defined by these electronic integrals.

        This method uses ``to_spin`` internally to map the electronic integrals into the spin
        orbital basis.

        Returns:
            The :class:`~qiskit_nature.second_q.operators.FermionicOp` given by these
            electronic integrals.
        """
        spin_matrix = self.to_spin()
        register_length = len(spin_matrix)

        if not np.any(spin_matrix):
            return FermionicOp.zero(register_length)

        spin_matrix_iter = spin_matrix.flat
        # NOTE: we need to access `.coords` before `.next()` is called for the first time!
        coords = spin_matrix_iter.coords
        op_data = []
        for coeff in spin_matrix_iter:
            if coeff:
                op_data.append((self._calc_coeffs_with_ops(coords), coeff))
            coords = spin_matrix_iter.coords

        return FermionicOp(op_data, register_length=register_length, display_format="sparse")
Exemple #2
0
    def _build_fermionic_excitation_ops(
            self, excitations: Sequence) -> list[FermionicOp]:
        """Builds all possible excitation operators with the given number of excitations for the
        specified number of particles distributed in the number of orbitals.

        Args:
            excitations: the list of excitations.

        Returns:
            The list of excitation operators in the second quantized formalism.
        """
        operators = []

        for exc in excitations:
            label = ["I"] * self.num_spin_orbitals
            for occ in exc[0]:
                label[occ] = "+"
            for unocc in exc[1]:
                label[unocc] = "-"
            op = FermionicOp("".join(label), display_format="dense")
            op -= op.adjoint()
            # we need to account for an additional imaginary phase in the exponent (see also
            # `PauliTrotterEvolution.convert`)
            op *= 1j  # type: ignore
            operators.append(op)

        return operators
 def test_init_empty_str(self, pre_processing):
     """Test __init__ with empty string"""
     actual = FermionicOp(pre_processing(""),
                          register_length=3,
                          display_format="dense")
     desired = FermionicOp("III", display_format="dense")
     self.assertFermionEqual(actual, desired)
Exemple #4
0
    def test_mapping_for_single_op(self):
        """Test for single register operator."""
        with self.subTest("test +"):
            op = FermionicOp("+", display_format="dense")
            expected = PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)])
            self.assertEqual(ParityMapper().map(op), expected)

        with self.subTest("test -"):
            op = FermionicOp("-", display_format="dense")
            expected = PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)])
            self.assertEqual(ParityMapper().map(op), expected)

        with self.subTest("test N"):
            op = FermionicOp("N", display_format="dense")
            expected = PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)])
            self.assertEqual(ParityMapper().map(op), expected)

        with self.subTest("test E"):
            op = FermionicOp("E", display_format="dense")
            expected = PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)])
            self.assertEqual(ParityMapper().map(op), expected)

        with self.subTest("test I"):
            op = FermionicOp("I", display_format="dense")
            expected = PauliSumOp.from_list([("I", 1)])
            self.assertEqual(ParityMapper().map(op), expected)
 def test_init_sparse_label(self, labels, pre_processing):
     """Test __init__ with sparse label"""
     dense_label, sparse_label = labels
     fer_op = FermionicOp(pre_processing(sparse_label),
                          register_length=len(dense_label),
                          display_format="sparse")
     targ = FermionicOp(dense_label, display_format="sparse")
     self.assertFermionEqual(fer_op, targ)
 def test_init_multiple_digits(self):
     """Test __init__ for sparse label with multiple digits"""
     actual = FermionicOp([("-_2 +_10", 1 + 2j), ("-_12", 56 + 0j)],
                          register_length=13,
                          display_format="dense")
     desired = [
         ("II-IIIIIII+II", 1 + 2j),
         ("IIIIIIIIIIII-", 56),
     ]
     self.assertListEqual(actual.to_list(), desired)
 def test_terms(self):
     """Test terms"""
     op = FermionicOp([("+", 1.0), ("N", 2.0), ("-+", 3.0)],
                      display_format="dense")
     expected = {
         ((("+", 0), ), 1.0),
         ((("+", 0), ("-", 0)), 2.0),
         ((("-", 0), ("+", 1)), 3.0),
     }
     self.assertEqual(set(op.terms()), expected)
 def test_second_q_ops(self):
     """Test second_q_ops."""
     op = self.prop.second_q_ops()["AngularMomentum"]
     with open(
         self.get_resource_path("angular_momentum_op.json", "second_q/properties/resources"),
         "r",
         encoding="utf8",
     ) as file:
         expected = json.load(file)
         expected_op = FermionicOp(expected).simplify()
     self.assertSetEqual(frozenset(op.to_list("dense")), frozenset(expected_op.to_list("dense")))
    def test_equiv(self):
        """test equiv"""
        op1 = FermionicOp("+_0 -_1") + FermionicOp("+_1 -_0")
        op2 = FermionicOp("+_0 -_1")
        op3 = FermionicOp("+_0 -_1") + (1 + 1e-7) * FermionicOp("+_1 -_0")
        self.assertFalse(op1.equiv(op2))
        self.assertFalse(op1.equiv(op3))
        self.assertTrue(op1.equiv(op3, atol=1e-6))

        with self.assertRaisesRegex(TypeError, "type"):
            op1.equiv("a")
    def test_mul(self):
        """Test __mul__, and __rmul__"""
        with self.subTest("rightmul"):
            fer_op = FermionicOp("+-", display_format="dense") * 2
            targ = FermionicOp([("+-", 2)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("left mul"):
            fer_op = (2 + 1j) * FermionicOp([("+N", 3), ("E-", 1)],
                                            display_format="dense")
            targ = FermionicOp([("+N", (6 + 3j)), ("E-", (2 + 1j))],
                               display_format="dense")
            self.assertFermionEqual(fer_op, targ)
    def test_init_multiterm(self):
        """Test __init__ with multi terms"""
        with self.subTest("Test 1"):
            labels = [("N", 2), ("-", 3.14)]
            self.assertListEqual(
                FermionicOp(labels, display_format="dense").to_list(), labels)

        with self.subTest("Test 2"):
            labels = [("+-", 1), ("-+", -1)]
            op = FermionicOp([("+_0 -_1", 1.0), ("-_0 +_1", -1.0)],
                             register_length=2,
                             display_format="dense")
            self.assertListEqual(op.to_list(), labels)
    def test_add(self):
        """Test __add__"""
        fer_op = 3 * FermionicOp("+N", display_format="dense") + FermionicOp(
            "E-", display_format="dense")
        targ = FermionicOp([("+N", 3), ("E-", 1)], display_format="dense")
        self.assertFermionEqual(fer_op, targ)

        fer_op = sum(
            FermionicOp(label, display_format="dense")
            for label in ["NIII", "INII", "IINI", "IIIN"])
        targ = FermionicOp([("NIII", 1), ("INII", 1), ("IINI", 1),
                            ("IIIN", 1)],
                           display_format="dense")
        self.assertFermionEqual(fer_op, targ)
    def test_adjoint(self):
        """Test adjoint method"""
        with self.subTest("adjoint"):
            fer_op = ~FermionicOp([("+N", 3), ("N-", 1), ("--", 2 + 4j)],
                                  display_format="dense")
            targ = FermionicOp([("-N", 3), ("N+", 1), ("++", (-2 + 4j))],
                               display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("adjoint 2"):
            fer_op = FermionicOp([("+-", 1), ("II", 2j)],
                                 display_format="dense").adjoint()
            targ = FermionicOp([("-+", -1), ("II", -2j)],
                               display_format="dense")
            self.assertFermionEqual(fer_op, targ)
 def test_init_from_tuple_label(self):
     """Test __init__ for tuple"""
     desired = [((("-", 2), ("+", 10)), (1 + 2j)), ((("-", 12), ), 56)]
     # tuple
     actual = FermionicOp(
         [((("-", 2), ("+", 10)), 1 + 2j), ((("-", 12), ), 56)],
         register_length=13,
         display_format="dense",
     )
     self.assertEqual(actual._data, desired)
     # list
     actual = FermionicOp(
         [([("-", 2), ("+", 10)], 1 + 2j), ([("-", 12)], 56)],
         register_length=13,
         display_format="dense",
     )
     self.assertListEqual(actual._data, desired)
    def test_matmul_multi(self):
        """Test matrix multiplication"""
        with self.subTest("single matmul"):
            fer_op = FermionicOp("+-", display_format="dense") @ FermionicOp(
                "-I", display_format="dense")
            targ = FermionicOp([("N-", -1)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("multi matmul"):
            fer_op = FermionicOp("+-", display_format="dense") @ FermionicOp(
                "-I", display_format="dense")
            fer_op = (FermionicOp("+N", display_format="dense") +
                      FermionicOp("E-", display_format="dense")) @ (
                          FermionicOp("II", display_format="dense") +
                          FermionicOp("-+", display_format="dense"))
            targ = FermionicOp([("+N", 1), ("N+", 1), ("E-", 1), ("-E", -1)],
                               display_format="dense")
            self.assertFermionEqual(fer_op, targ)
Exemple #16
0
    def map(self, second_q_op: FermionicOp) -> PauliSumOp:
        if not isinstance(second_q_op, FermionicOp):
            raise TypeError("Type ", type(second_q_op), " not supported.")

        if second_q_op.display_format == "sparse":
            second_q_op = FermionicOp(second_q_op._to_dense_label_data(),
                                      display_format="dense")

        edge_list = _bksf_edge_list_fermionic_op(second_q_op)
        sparse_pauli = _convert_operator(second_q_op, edge_list)

        ## Simplify and sort the result
        sparse_pauli = sparse_pauli.simplify()
        indices = sparse_pauli.paulis.argsort()
        table = sparse_pauli.paulis[indices]
        coeffs = sparse_pauli.coeffs[indices]
        sorted_sparse_pauli = SparsePauliOp(table, coeffs)

        return PauliSumOp(sorted_sparse_pauli)
    def test_two_qubit_reduction(self):
        """Test mapping to qubit operator with two qubit reduction"""
        mapper = ParityMapper()
        qubit_conv = QubitConverter(mapper, two_qubit_reduction=True)

        with self.subTest(
                "Two qubit reduction ignored as no num particles given"):
            qubit_op = qubit_conv.convert(self.h2_op)
            self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY)
            self.assertIsNone(qubit_conv.num_particles)

        with self.subTest("Two qubit reduction, num particles given"):
            qubit_op = qubit_conv.convert(self.h2_op, self.num_particles)
            self.assertEqual(qubit_op,
                             TestQubitConverter.REF_H2_PARITY_2Q_REDUCED)
            self.assertEqual(qubit_conv.num_particles, self.num_particles)

        with self.subTest("convert_match()"):
            qubit_op = qubit_conv.convert_match(self.h2_op)
            self.assertEqual(qubit_op,
                             TestQubitConverter.REF_H2_PARITY_2Q_REDUCED)
            self.assertEqual(qubit_conv.num_particles, self.num_particles)

        with self.subTest("State is reset (Num particles lost)"):
            qubit_op = qubit_conv.convert(self.h2_op)
            self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY)
            self.assertIsNone(qubit_conv.num_particles)

        with self.subTest("Num particles given again"):
            qubit_op = qubit_conv.convert(self.h2_op, self.num_particles)
            self.assertEqual(qubit_op,
                             TestQubitConverter.REF_H2_PARITY_2Q_REDUCED)

        with self.subTest("Set for no two qubit reduction"):
            qubit_conv.two_qubit_reduction = False
            self.assertFalse(qubit_conv.two_qubit_reduction)
            qubit_op = qubit_conv.convert(self.h2_op)
            self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY)

        # Regression test against https://github.com/Qiskit/qiskit-nature/issues/271
        with self.subTest(
                "Two qubit reduction skipped when operator too small"):
            qubit_conv.two_qubit_reduction = True
            small_op = FermionicOp([("N_0", 1.0), ("E_1", 1.0)],
                                   register_length=2,
                                   display_format="sparse")
            expected_op = 1.0 * (I ^ I) - 0.5 * (I ^ Z) + 0.5 * (Z ^ Z)
            with contextlib.redirect_stderr(io.StringIO()) as out:
                qubit_op = qubit_conv.convert(small_op,
                                              num_particles=self.num_particles)
            self.assertEqual(qubit_op, expected_op)
            self.assertTrue(out.getvalue().strip().startswith(
                "The original qubit operator only contains 2 qubits! "
                "Skipping the requested two-qubit reduction!"))
Exemple #18
0
 def test_fermionic_op(self):
     """Test conversion to FermionicOp."""
     hermitian_part = np.array([[1, 2j], [-2j, 3]])
     antisymmetric_part = np.array([[0, 4j], [-4j, 0]])
     constant = 5.0
     quad_ham = QuadraticHamiltonian(hermitian_part, antisymmetric_part,
                                     constant)
     fermionic_op = quad_ham.to_fermionic_op()
     expected_terms = [
         ("NI", 1.0),
         ("IN", 3.0),
         ("+-", 2j),
         ("-+", 2j),
         ("++", 4j),
         ("--", 4j),
         ("II", 5.0),
     ]
     expected_op = FermionicOp(expected_terms)
     matrix = fermionic_op.to_matrix(sparse=False)
     expected_matrix = expected_op.to_matrix(sparse=False)
     np.testing.assert_allclose(matrix, expected_matrix)
    def test_label_display_mode(self, label, pre_processing):
        """test label_display_mode"""
        fer_op = FermionicOp(pre_processing(label), display_format="dense")

        fer_op.display_format = "sparse"
        self.assertListEqual(fer_op.to_list(), str2list(label))
        fer_op.display_format = "dense"
        self.assertNotEqual(fer_op.to_list(), str2list(label))
Exemple #20
0
    def second_q_ops(self) -> dict[str, FermionicOp]:
        """Returns the second quantized magnetization operator.

        Returns:
            A `dict` of `SecondQuantizedOp` objects.
        """
        op = FermionicOp(
            [
                (f"N_{o}", 0.5 if o < self._num_spin_orbitals // 2 else -0.5)
                for o in range(self._num_spin_orbitals)
            ],
            register_length=self._num_spin_orbitals,
            display_format="sparse",
        )

        return {self.name: op}
    def second_q_ops(self, display_format: str = "sparse") -> FermionicOp:
        """Return the Hamiltonian of the Fermi-Hubbard model in terms of `FermionicOp`.

        Args:
            display_format: If sparse, the label is represented sparsely during output.
                If dense, the label is represented densely during output. Defaults to "dense".

        Returns:
            FermionicOp: The Hamiltonian of the Fermi-Hubbard model.
        """
        kinetic_ham = []
        interaction_ham = []
        weighted_edge_list = self._lattice.weighted_edge_list
        register_length = 2 * self._lattice.num_nodes
        # kinetic terms
        for spin in range(2):
            for node_a, node_b, weight in weighted_edge_list:
                if node_a == node_b:
                    index = 2 * node_a + spin
                    kinetic_ham.append((f"N_{index}", weight))

                else:
                    if node_a < node_b:
                        index_left = 2 * node_a + spin
                        index_right = 2 * node_b + spin
                        hopping_parameter = weight
                    elif node_a > node_b:
                        index_left = 2 * node_b + spin
                        index_right = 2 * node_a + spin
                        hopping_parameter = np.conjugate(weight)
                    kinetic_ham.append(
                        (f"+_{index_left} -_{index_right}", hopping_parameter))
                    kinetic_ham.append((f"-_{index_left} +_{index_right}",
                                        -np.conjugate(hopping_parameter)))
        # on-site interaction terms
        for node in self._lattice.node_indexes:
            index_up = 2 * node
            index_down = 2 * node + 1
            interaction_ham.append(
                (f"N_{index_up} N_{index_down}", self._onsite_interaction))

        ham = kinetic_ham + interaction_ham

        return FermionicOp(ham,
                           register_length=register_length,
                           display_format=display_format)
Exemple #22
0
 def to_fermionic_op(self) -> FermionicOp:
     """Convert to FermionicOp."""
     terms: list[tuple[list[tuple[str, int]],
                       complex]] = [([], self.constant)]
     for i in range(self._num_modes):
         terms.append(([("+", i), ("-", i)], self.hermitian_part[i, i]))
         for j in range(i + 1, self._num_modes):
             terms.append(([("+", i), ("-", j)], self.hermitian_part[i, j]))
             terms.append(([("+", j), ("-", i)], self.hermitian_part[j, i]))
             terms.append(([("+", i),
                            ("+", j)], self.antisymmetric_part[i, j]))
             terms.append(
                 ([("-", j),
                   ("-", i)], self.antisymmetric_part[i, j].conjugate()))
     # TODO remove display_format="sparse" once it's no longer needed to suppress warning
     return FermionicOp(terms,
                        register_length=self._num_modes,
                        display_format="sparse")
Exemple #23
0
def _get_adjacency_matrix(fer_op: FermionicOp) -> np.ndarray:
    """Return an adjacency matrix specifying the edges in the BKSF graph for the
    operator `fer_op`.

    The graph is undirected, so we choose to return the edges in the upper triangle.
    (There are no self edges.) The lower triangle entries are all `False`.

    Args:
      fer_op: The Fermionic operator.

    Returns:
      numpy.ndarray(dtype=bool): edge_matrix the adjacency matrix.
    """
    n_modes = fer_op.register_length
    edge_matrix = np.zeros((n_modes, n_modes), dtype=bool)
    for term in fer_op.to_list():
        if _operator_coefficient(term) != 0:
            _add_edges_for_term(edge_matrix, _operator_string(term))
    return edge_matrix
    def test_pow(self):
        """Test __pow__"""
        with self.subTest("square trivial"):
            fer_op = FermionicOp([("+N", 3), ("E-", 1)],
                                 display_format="dense")**2
            targ = FermionicOp([("II", 0)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("square nontrivial"):
            fer_op = FermionicOp([("+N", 3), ("N-", 1)],
                                 display_format="dense")**2
            targ = FermionicOp([("+-", -3)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("3rd power"):
            fer_op = (3 * FermionicOp("IIII", display_format="dense"))**3
            targ = FermionicOp([("IIII", 27)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)

        with self.subTest("0th power"):
            fer_op = FermionicOp([("+N", 3), ("E-", 1)],
                                 display_format="dense")**0
            targ = FermionicOp([("II", 1)], display_format="dense")
            self.assertFermionEqual(fer_op, targ)
def _build_single_hopping_operator(
    excitation: Tuple[Tuple[int, ...], Tuple[int, ...]],
    num_spin_orbitals: int,
    qubit_converter: QubitConverter,
) -> Tuple[PauliSumOp, List[bool]]:
    label = ["I"] * num_spin_orbitals
    for occ in excitation[0]:
        label[occ] = "+"
    for unocc in excitation[1]:
        label[unocc] = "-"
    fer_op = FermionicOp(("".join(label), 4.0 ** len(excitation[0])), display_format="sparse")

    qubit_op: PauliSumOp = qubit_converter.convert_only(fer_op, qubit_converter.num_particles)
    z2_symmetries = qubit_converter.z2symmetries

    commutativities = []
    if not z2_symmetries.is_empty():
        for symmetry in z2_symmetries.symmetries:
            symmetry_op = PauliSumOp.from_list([(symmetry.to_label(), 1.0)])
            paulis = qubit_op.primitive.paulis
            len_paulis = len(paulis)
            commuting = len(paulis.commutes_with_all(symmetry_op.primitive.paulis)) == len_paulis
            anticommuting = (
                len(paulis.anticommutes_with_all(symmetry_op.primitive.paulis)) == len_paulis
            )

            if commuting != anticommuting:  # only one of them is True
                if commuting:
                    commutativities.append(True)
                elif anticommuting:
                    commutativities.append(False)
            else:
                raise QiskitNatureError(
                    f"Symmetry {symmetry.to_label()} neither commutes nor anti-commutes "
                    "with excitation operator."
                )

    return qubit_op, commutativities
 def test_init_invalid_label(self, label, register_length):
     """Test __init__ with invalid label"""
     with self.assertRaises(ValueError):
         FermionicOp(label,
                     register_length=register_length,
                     display_format="dense")
 def test_init(self, label, pre_processing):
     """Test __init__"""
     fer_op = FermionicOp(pre_processing(label), display_format="dense")
     self.assertListEqual(fer_op.to_list(), [(label, 1)])
     self.assertFermionEqual(eval(repr(fer_op)), fer_op)  # pylint: disable=eval-used
 def test_induced_norm(self):
     """Test induced norm."""
     op = 3 * FermionicOp("+") + 4j * FermionicOp("-")
     self.assertAlmostEqual(op.induced_norm(), 7.0)
     self.assertAlmostEqual(op.induced_norm(2), 5.0)
 def assertFermionEqual(self, first: FermionicOp, second: FermionicOp):
     """Fail if two FermionicOps are different.
     Note that this equality check is approximated since the true equality check is costly.
     """
     self.assertSetEqual(frozenset(first.to_list()),
                         frozenset(second.to_list(first.display_format)))
    def test_normal_ordered(self):
        """test normal_ordered method"""
        with self.subTest("Test for creation operator"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp("+")
            fer_op = orig.normal_ordered()
            targ = FermionicOp("+_0", display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test for annihilation operator"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp("-")
            fer_op = orig.normal_ordered()
            targ = FermionicOp("-_0", display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test for number operator"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp("N")
            fer_op = orig.normal_ordered()
            targ = FermionicOp("+_0 -_0", display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test for empty operator"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp("E")
            fer_op = orig.normal_ordered()
            targ = FermionicOp([("", 1), ("+_0 -_0", -1)],
                               display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test for multiple operators 1"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp("-+")
            fer_op = orig.normal_ordered()
            targ = FermionicOp([("-_0 +_1", 1)], display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test for multiple operators 2"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = 3 * FermionicOp("E+-")
            fer_op = orig.normal_ordered()
            targ = FermionicOp([("+_1 -_2", 3), ("+_0 -_0 +_1 -_2", -3)],
                               display_format="sparse")
            self.assertFermionEqual(fer_op, targ)
            self.assertEqual(orig.display_format, "sparse")

        with self.subTest("Test normal ordering simplifies"):
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=UserWarning)
                orig = FermionicOp([("-_1 +_2", 3), ("+_2 -_1", -3)],
                                   display_format="sparse")
            fer_op = orig.normal_ordered()
            expected = [((("-", 1), ("+", 2)), 6)]
            self.assertEqual(fer_op._data, expected)