Beispiel #1
0
    def _build_vibration_excitation_ops(self, excitations: Sequence) -> list[VibrationalOp]:
        """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 = []

        sum_modes = sum(self.num_modals)

        for exc in excitations:
            label = ["I"] * sum_modes
            for occ in exc[0]:
                label[occ] = "+"
            for unocc in exc[1]:
                label[unocc] = "-"
            op = VibrationalOp("".join(label), len(self.num_modals), self.num_modals)
            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
Beispiel #2
0
class TestUVCC(QiskitNatureTestCase):
    """Tests for the UVCC Ansatz."""

    @unpack
    @data(
        ("s", [2], [VibrationalOp([("+-", 1j), ("-+", -1j)], 1, 2)]),
        (
            "s",
            [2, 2],
            [
                VibrationalOp([("+-II", 1j), ("-+II", -1j)], 2, 2),
                VibrationalOp([("II+-", 1j), ("II-+", -1j)], 2, 2),
            ],
        ),
    )
    def test_ucc_ansatz(self, excitations, num_modals, expect):
        """Tests the UVCC Ansatz."""
        converter = QubitConverter(DirectMapper())

        ansatz = UVCC(qubit_converter=converter, num_modals=num_modals, excitations=excitations)

        assert_ucc_like_ansatz(self, ansatz, num_modals, expect)

    def test_transpile_no_parameters(self):
        """Test transpilation without parameters"""

        qubit_converter = QubitConverter(mapper=DirectMapper())

        ansatz = UVCC(qubit_converter=qubit_converter, num_modals=[2], excitations="s")
        ansatz = transpile(ansatz, optimization_level=3)
        self.assertEqual(ansatz.num_qubits, 2)
    def to_second_q_op(self) -> VibrationalOp:
        """Creates the operator representing the Hamiltonian defined by these vibrational integrals.

        Returns:
            The :class:`~qiskit_nature.second_q.operators.VibrationalOp` given by these
            vibrational integrals.

        Raises:
            QiskitNatureError: if no basis has been set yet.
        """
        try:
            matrix = self.to_basis()
        except QiskitNatureError as exc:
            raise QiskitNatureError() from exc

        num_modals_per_mode = self.basis._num_modals_per_mode
        num_modes = len(num_modals_per_mode)

        nonzero = np.nonzero(matrix)

        if not np.any(np.asarray(nonzero)):
            return VibrationalOp.zero(num_modes, num_modals_per_mode)

        labels = []

        for coeff, indices in zip(matrix[nonzero], zip(*nonzero)):
            # the indices need to be grouped into triplets of the form: (mode, modal_1, modal_2)
            grouped_indices = [
                tuple(int(j) for j in indices[i : i + 3]) for i in range(0, len(indices), 3)
            ]
            # the index groups need to processed in sorted order to produce a valid label
            coeff_label = self._create_label_for_coeff(sorted(grouped_indices))
            labels.append((coeff_label, coeff))

        return VibrationalOp(labels, num_modes, num_modals_per_mode)
Beispiel #4
0
 def test_simplify(self):
     """Test simplify"""
     test_op = (1j * VibrationalOp("+-", 2, 1) +
                1j * VibrationalOp("+-", 2, 1) -
                1j * VibrationalOp("-+", 2, 1) -
                1j * VibrationalOp("-+", 2, 1))
     expected = [("+-", 2j), ("-+", -2j)]
     self.assertEqual(test_op.simplify().to_list(), expected)
Beispiel #5
0
 def test_equiv(self):
     """test equiv"""
     op1 = VibrationalOp("+-", 2, 1) + VibrationalOp("-+", 2, 1)
     op2 = VibrationalOp("+-", 2, 1)
     op3 = VibrationalOp("+-", 2,
                         1) + (1 + 1e-7) * VibrationalOp("-+", 2, 1)
     self.assertFalse(op1.equiv(op2))
     self.assertFalse(op1.equiv(op3))
     self.assertTrue(op1.equiv(op3, atol=1e-6))
Beispiel #6
0
    def test_uvcc_vscf(self):
        """uvcc vscf test"""

        co2_2modes_2modals_2body = [
            [
                [[[0, 0, 0]], 320.8467332810141],
                [[[0, 1, 1]], 1760.878530705873],
                [[[1, 0, 0]], 342.8218290247543],
                [[[1, 1, 1]], 1032.396323618631],
            ],
            [
                [[[0, 0, 0], [1, 0, 0]], -57.34003649795117],
                [[[0, 0, 1], [1, 0, 0]], -56.33205925807966],
                [[[0, 1, 0], [1, 0, 0]], -56.33205925807966],
                [[[0, 1, 1], [1, 0, 0]], -60.13032761856809],
                [[[0, 0, 0], [1, 0, 1]], -65.09576309934431],
                [[[0, 0, 1], [1, 0, 1]], -62.2363839133389],
                [[[0, 1, 0], [1, 0, 1]], -62.2363839133389],
                [[[0, 1, 1], [1, 0, 1]], -121.5533969109279],
                [[[0, 0, 0], [1, 1, 0]], -65.09576309934431],
                [[[0, 0, 1], [1, 1, 0]], -62.2363839133389],
                [[[0, 1, 0], [1, 1, 0]], -62.2363839133389],
                [[[0, 1, 1], [1, 1, 0]], -121.5533969109279],
                [[[0, 0, 0], [1, 1, 1]], -170.744837386338],
                [[[0, 0, 1], [1, 1, 1]], -167.7433236025723],
                [[[0, 1, 0], [1, 1, 1]], -167.7433236025723],
                [[[0, 1, 1], [1, 1, 1]], -179.0536532281924],
            ],
        ]
        num_modes = 2
        num_modals = [2, 2]

        vibrational_op_labels = _create_labels(co2_2modes_2modals_2body)
        vibr_op = VibrationalOp(vibrational_op_labels, num_modes, num_modals)

        converter = QubitConverter(DirectMapper())

        qubit_op = converter.convert_match(vibr_op)

        init_state = VSCF(num_modals)

        uvcc_ansatz = UVCC(converter, num_modals, "sd", initial_state=init_state)

        q_instance = QuantumInstance(
            BasicAer.get_backend("statevector_simulator"),
            seed_transpiler=90,
            seed_simulator=12,
        )
        optimizer = COBYLA(maxiter=1000)

        algo = VQE(uvcc_ansatz, optimizer=optimizer, quantum_instance=q_instance)
        vqe_result = algo.compute_minimum_eigenvalue(qubit_op)

        energy = vqe_result.optimal_value

        self.assertAlmostEqual(energy, self.reference_energy, places=4)
Beispiel #7
0
    def test_init_pm_label(self):
        """Test __init__ with plus and minus label"""
        with self.subTest("minus plus"):
            result = VibrationalOp([("+_0*0 -_0*1", 2)], 1, 2)
            desired = [("+-", (2 + 0j))]
            self.assertEqual(result.to_list(), desired)

        with self.subTest("plus minus"):
            result = VibrationalOp([("-_0*0 +_0*1", 2)], 1, 2)
            desired = [("-+", (2 + 0j))]
            self.assertEqual(result.to_list(), desired)

        with self.subTest("plus minus minus plus"):
            result = VibrationalOp([("+_0*0 -_0*1 -_1*0 +_1*1", 3)], 2, 2)
            desired = [("+--+", (3 + 0j))]

            # Note: the order of list is irrelevant.
            self.assertSetEqual(frozenset(result.to_list()),
                                frozenset(desired))
Beispiel #8
0
def _build_single_hopping_operator(
    excitation: Tuple[Tuple[int, ...], Tuple[int, ...]],
    num_modals: List[int],
    qubit_converter: QubitConverter,
) -> PauliSumOp:
    sum_modes = sum(num_modals)

    label = ["I"] * sum_modes
    for occ in excitation[0]:
        label[occ] = "+"
    for unocc in excitation[1]:
        label[unocc] = "-"
    vibrational_op = VibrationalOp("".join(label), len(num_modals), num_modals)
    qubit_op: PauliSumOp = qubit_converter.convert_match(vibrational_op)

    return qubit_op
Beispiel #9
0
    def test_hermiticity(self):
        """test is_hermitian"""
        with self.subTest("operator hermitian"):
            # deliberately define test operator with duplicate terms in case .adjoint() simplifies terms
            test_op = (1j * VibrationalOp("+-", 2, 1) +
                       1j * VibrationalOp("+-", 2, 1) -
                       1j * VibrationalOp("-+", 2, 1) -
                       1j * VibrationalOp("-+", 2, 1))
            self.assertTrue(test_op.is_hermitian())

        with self.subTest("operator not hermitian"):
            test_op = (1j * VibrationalOp("+-", 2, 1) +
                       1j * VibrationalOp("+-", 2, 1) -
                       1j * VibrationalOp("-+", 2, 1))
            self.assertFalse(test_op.is_hermitian())

        with self.subTest("test passing atol"):
            test_op = 1j * VibrationalOp(
                "+-", 2, 1) - (1 + 1e-7) * 1j * VibrationalOp("-+", 2, 1)
            self.assertFalse(test_op.is_hermitian())
            self.assertFalse(test_op.is_hermitian(atol=1e-8))
            self.assertTrue(test_op.is_hermitian(atol=1e-6))
Beispiel #10
0
 def setUp(self):
     super().setUp()
     self.labels = [("+_1*0 -_1*1", 1215.375),
                    ("+_2*0 -_2*1 +_3*0 -_3*0", -6.385)]
     self.labels_double = [
         ("+_1*0 -_1*1", 2 * 1215.375),
         ("+_2*0 -_2*1 +_3*0 -_3*0", -2 * 6.385),
     ]
     self.labels_divided_3 = [
         ("+_1*0 -_1*1", 1215.375 / 3),
         ("+_2*0 -_2*1 +_3*0 -_3*0", -6.385 / 3),
     ]
     self.labels_neg = [
         ("+_1*0 -_1*1", -1215.375),
         ("+_2*0 -_2*1 +_3*0 -_3*0", 6.385),
     ]
     self.vibr_spin_op = VibrationalOp(self.labels, 4, 2)
    def _get_mode_op(self, mode: int) -> VibrationalOp:
        """Constructs an operator to evaluate which modal of a given mode is occupied.

        Args:
            mode: the mode index.

        Returns:
            The operator to evaluate which modal of the given mode is occupied.
        """
        num_modals_per_mode = self.basis._num_modals_per_mode

        labels: list[tuple[str, complex]] = []

        for modal in range(num_modals_per_mode[mode]):
            labels.append((f"+_{mode}*{modal} -_{mode}*{modal}", 1.0))

        return VibrationalOp(labels, len(num_modals_per_mode),
                             num_modals_per_mode)
Beispiel #12
0
    def __init__(
        self,
        num_modals: list[int],
        qubit_converter: QubitConverter | None = None,
    ) -> None:
        """
        Args:
            num_modals: Is a list defining the number of modals per mode. E.g. for a 3 modes system
                with 4 modals per mode num_modals = [4,4,4]
            qubit_converter: a QubitConverter instance. This argument is currently being ignored
                             because only a single use-case is supported at the time of release:
                             that of the :class:`DirectMapper`. However, for future-compatibility of
                             this functions signature, the argument has already been inserted.
        """
        # get the bitstring encoding initial state
        bitstr = vscf_bitstring(num_modals)

        # encode the bitstring in a `VibrationalOp`
        label = ["+" if bit else "I" for bit in bitstr]
        bitstr_op = VibrationalOp("".join(label), num_modes=len(num_modals), num_modals=num_modals)

        # map the `VibrationalOp` to a qubit operator
        if qubit_converter is not None:
            logger.warning(
                "The only supported `QubitConverter` is one with a `DirectMapper` as the mapper "
                "instance. However you specified %s as an input, which will be ignored until more "
                "variants will be supported.",
                str(qubit_converter),
            )
        qubit_converter = QubitConverter(DirectMapper())
        qubit_op: PauliSumOp = qubit_converter.convert_match(bitstr_op)

        # construct the circuit
        qr = QuantumRegister(qubit_op.num_qubits, "q")
        super().__init__(qr, name="VSCF")

        # add gates in the right positions
        for i, bit in enumerate(qubit_op.primitive.paulis.x[0]):
            if bit:
                self.x(i)
Beispiel #13
0
 def test_add(self):
     """Test __add__"""
     actual = (self.vibr_spin_op + self.vibr_spin_op).simplify()
     desired = VibrationalOp(self.labels_double, 4, 2)
     self.assertSpinEqual(actual, desired)
Beispiel #14
0
 def test_div(self):
     """Test __truediv__"""
     actual = self.vibr_spin_op / 3
     desired = VibrationalOp(self.labels_divided_3, 4, 2)
     self.assertSpinEqual(actual, desired)
Beispiel #15
0
 def test_neg(self):
     """Test __neg__"""
     actual = -self.vibr_spin_op
     desired = VibrationalOp(self.labels_neg, 4, 2)
     self.assertSpinEqual(actual, desired)
Beispiel #16
0
 def test_init_invalid_label(self, label):
     """Test __init__ for invalid label"""
     with self.assertRaises(ValueError):
         VibrationalOp(label, 1, 1)
Beispiel #17
0
 def test_mul(self):
     """Test __mul__, and __rmul__"""
     actual = self.vibr_spin_op * 2
     desired = VibrationalOp(self.labels_double, 4, 2)
     self.assertSpinEqual(actual, desired)
#
# 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.
"""Some expected VibrationalOps."""

from qiskit_nature.second_q.operators import VibrationalOp

_truncation_order_1_op = VibrationalOp(
    [
        ("NIIIIIII", 1268.0676746875001),
        ("INIIIIII", 3813.8767834375008),
        ("IINIIIII", 705.8633818750001),
        ("II+-IIII", -46.025705898886045),
        ("II-+IIII", -46.025705898886045),
        ("IIINIIII", 2120.1145593750007),
        ("IIIINIII", 238.31540750000005),
        ("IIIIINII", 728.9613775000003),
        ("IIIIIINI", 238.31540750000005),
        ("IIIIIIIN", 728.9613775000003),
    ],
    num_modes=4,
    num_modals=2,
)

_truncation_order_2_op = VibrationalOp(
    [
        ("NIIIIIII", 1268.0676746875001),
        ("INIIIIII", 3813.8767834375008),
        ("IINIIIII", 705.8633818750001),
        ("II+-IIII", -46.025705898886045),
        ("II-+IIII", -46.025705898886045),