diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index e212e7aeab0..fced6b309aa 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -789,6 +789,10 @@ Here's a list of deprecations made this release. For a more detailed breakdown o

Documentation 📝

+* The functions in `qml.qchem.vibrational` are updated to include additional information about the + theory and input arguments. + [(#6918)](https://github.com/PennyLaneAI/pennylane/pull/6918) + * The usage examples for `qml.decomposition.DecompositionGraph` have been updated. [(#7692)](https://github.com/PennyLaneAI/pennylane/pull/7692) diff --git a/pennylane/labs/tests/vibrational/test_pes_generator.py b/pennylane/labs/tests/vibrational/test_pes_generator.py index c1522e1d469..6a06c23e5c3 100644 --- a/pennylane/labs/tests/vibrational/test_pes_generator.py +++ b/pennylane/labs/tests/vibrational/test_pes_generator.py @@ -27,7 +27,7 @@ h5py = pytest.importorskip("h5py") -# pylint: disable=too-many-arguments, protected-access, too-many-positional-arguments +# pylint: disable=too-many-arguments, protected-access, too-many-positional-arguments, unsubscriptable-object ref_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_ref_files") diff --git a/pennylane/qchem/vibrational/localize_modes.py b/pennylane/qchem/vibrational/localize_modes.py index 92aa94eead8..0da70f739b4 100644 --- a/pennylane/qchem/vibrational/localize_modes.py +++ b/pennylane/qchem/vibrational/localize_modes.py @@ -174,26 +174,38 @@ def _localize_modes(freqs, vecs): def localize_normal_modes(freqs, vecs, bins=[2600]): - """ - Localizes vibrational normal modes. + r"""Computes spatially localized vibrational normal modes. - The normal modes are localized by separating frequencies into specified ranges following the - procedure described in `J. Chem. Phys. 141, 104105 (2014) + The vibrational normal modes are localized using a localizing unitary following the procedure + described in `J. Chem. Phys. 141, 104105 (2014) `_. + Efficient-anharmonic-vibrational-spectroscopy-for?redirectedFrom=fulltext>`_. The localizing + unitary :math:`U` is defined in terms of the normal and local coordinates, :math:`q` and + :math:`\tilde{q}`, respectively as: + + .. math:: + + \tilde{q} = \sum_{j=1}^M U_{ij} q_j, + + where :math:`M` is the number of modes. The normal modes + can be separately localized, to prevent mixing between specific groups of normal modes, by + defining frequency ranges in ``bins``. For instance, ``bins = [2600]`` allows to separately + localize modes that have frequencies above and below :math:`2600` reciprocal centimetre (:math:`\text{cm}^{-1}`). + Similarly, ``bins = [1300, 2600]`` allows to separately localize modes in three groups that have + frequencies below :math:`1300`, between :math:`1300-2600` and above :math:`2600`. Args: - freqs (list[float]): normal mode frequencies in ``cm^-1`` - vecs (TensorLike[float]): displacement vectors for normal modes - bins (list[float]): List of upper bound frequencies in ``cm^-1`` for creating separation bins . - Default is ``[2600]`` which means having one bin for all frequencies between ``0`` and ``2600 cm^-1``. + freqs (TensorLike[float]): normal mode frequencies in reciprocal centimetre (:math:`\text{cm}^{-1}`). + vecs (TensorLike[float]): displacement vectors of the normal modes + bins (List[float]): grid of frequencies for grouping normal modes. + Default is ``[2600]``. Returns: tuple: A tuple containing the following: - - list[float] : localized frequencies - - TensorLike[float] : localized displacement vectors - - TensorLike[float] : localization matrix describing the relationship between - original and localized modes. + - TensorLike[float] : localized frequencies in reciprocal centimetre (:math:`\text{cm}^{-1}`). + - List[TensorLike[float]] : localized displacement vectors + - TensorLike[float] : localization matrix describing the relationship between the + original and the localized modes **Example** @@ -209,7 +221,7 @@ def localize_normal_modes(freqs, vecs, bins=[2600]): ... [-5.49709883e-17, 7.49851221e-08, -2.77912798e-02]]]) >>> freqs_loc, vecs_loc, uloc = qml.qchem.localize_normal_modes(freqs, vectors) >>> freqs_loc - array([1332.62008773, 2296.73455892, 2296.7346082 ]) + array([1332.62013257, 2296.73453455, 2296.73460655]) """ if not bins: diff --git a/pennylane/qchem/vibrational/taylor_ham.py b/pennylane/qchem/vibrational/taylor_ham.py index 88323055ea7..29c64161a1a 100644 --- a/pennylane/qchem/vibrational/taylor_ham.py +++ b/pennylane/qchem/vibrational/taylor_ham.py @@ -274,51 +274,79 @@ def _fit_threebody(threemode_op, max_deg, min_deg=3): def taylor_coeffs(pes, max_deg=4, min_deg=3): - r"""Compute fitted coefficients for Taylor vibrational Hamiltonian. + r"""Computes the coefficients of a Taylor vibrational Hamiltonian. - The coefficients are defined following Eq. 5 of `arXiv:1703.09313 - `_ as: - - .. math:: - - \Phi_{ijk} = \frac{k_{ijk}}{\sqrt{\omega_i \omega_j \omega_k}} - \quad \text{and} \quad - \Phi_{ijkl} = \frac{k_{ijkl}}{\sqrt{\omega_i \omega_j \omega_k \omega_l}}, - - where :math:`\Phi_{ijk}` and :math:`\Phi_{ijkl}` are the third- and fourth-order reduced force - constants, respectively, defined in terms of the third- and fourth-order partial derivatives - of the potential energy surface data. + The coefficients are computed from a multi-dimensional polynomial fit over potential energy data + computed along normal coordinates, with a polynomial specified by ``min_deg`` and ``max_deg``. Args: - pes (VibrationalPES): object containing the vibrational potential energy surface data - max_deg (int): maximum degree of Taylor form polynomial - min_deg (int): minimum degree of Taylor form polynomial + pes (VibrationalPES): the vibrational potential energy surface object + max_deg (int): maximum degree of the polynomial used to compute the coefficients + min_deg (int): minimum degree of the polynomial used to compute the coefficients Returns: - tuple(TensorLike[float]): the coefficients of the one-body, two-body and three-body terms + List(TensorLike[float]): the coefficients of the Taylor vibrational Hamiltonian **Example** - >>> pes_onemode = np.array([[0.309, 0.115, 0.038, 0.008, 0.000, 0.006, 0.020, 0.041, 0.070]]) - >>> pes_twomode = np.zeros((1, 1, 9, 9)) - >>> dipole_onemode = np.zeros((1, 9, 3)) - >>> gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02, - ... 4.33e-01, 7.20e-01, 4.33e-01, - ... 8.85e-02, 4.94e-03, 3.96e-05]) - >>> grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19]) - >>> pes_object = qml.qchem.VibrationalPES( - ... freqs=np.array([0.025]), - ... grid=grid, - ... uloc=np.array([[1.0]]), - ... gauss_weights=gauss_weights, - ... pes_data=[pes_onemode, pes_twomode], - ... dipole_data=[dipole_onemode], - ... localized=True, - ... dipole_level=1, - ... ) - >>> one, two = qml.qchem.taylor_coeffs(pes_object, 4, 2) - >>> print(one) - [[-0.00088528 -0.00361425 0.00068143]] + >>> freqs = np.array([0.0249722]) + >>> pes_onemode = np.array([[0.08477, 0.01437, 0.00000, 0.00937, 0.03414]]) + >>> pes_object = qml.qchem.VibrationalPES(freqs=freqs, pes_data=[pes_onemode]) + >>> coeffs = qml.qchem.taylor_coeffs(pes_object, 4, 2) + >>> print(coeffs) + [array([[-4.73959071e-05, -3.06785775e-03, 5.21798831e-04]])] + + .. details:: + :title: Theory + + A molecular potential energy surface can be defined as [Eq. 7 of + `J. Chem. Phys. 135, 134108 (2011) `_]: + + .. math:: + + V = V_0 + \sum_{i} F_i q_i + \sum_{i,j} F_{ij} q_i q_j + + \sum_{i,j,k} F_{ijk} q_i q_j q_k + \cdots, + + where :math:`q` is a normal coordinate and :math:`F` represents the derivatives of the + potential energy surface. + + This function computes these derivatives via Taylor expansion of the potential energy data + by performing a multi-dimensional polynomial fit. + + The potential energy surface along the normal coordinate can be defined as + + .. math:: + + V(q_1,\cdots,q_M) = V_0 + \sum_{i=1}^M V_1^{(i)}(q_i) + \sum_{i>j} + V_2^{(i,j)}(q_i,q_j) + \sum_{i>> freqs = np.array([0.01885397]) - >>> grid, weights = np.polynomial.hermite.hermgauss(9) - >>> pes_onebody = np.array([[0.05235573, 0.03093067, 0.01501878, 0.00420778, 0.0, - ... 0.00584504, 0.02881817, 0.08483433, 0.22025702]]) - >>> pes_twobody = None - >>> dipole_onebody = np.array([[[-1.92201700e-16, 1.45397041e-16, -1.40451549e-01], - ... [-1.51005108e-16, 9.53185441e-17, -1.03377032e-01], - ... [-1.22793018e-16, 7.22781963e-17, -6.92825934e-02], - ... [-1.96537436e-16, -5.86686504e-19, -3.52245369e-02], - ... [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], - ... [ 5.24758835e-17, -1.40650833e-16, 3.69955543e-02], - ... [-4.52407941e-17, 1.38406311e-16, 7.60888733e-02], - ... [-4.63820104e-16, 5.42928787e-17, 1.17726042e-01], - ... [ 1.19224372e-16, 9.12491386e-17, 1.64013197e-01]]]) - >>> vib_obj = qml.qchem.VibrationalPES( - ... freqs=freqs, - ... grid=grid, - ... gauss_weights=weights, - ... uloc=None, - ... pes_data=[pes_onebody, pes_twobody], - ... dipole_data=[dipole_onebody], - ... localized=False - ... ) - >>> x, y, z = qml.qchem.taylor_dipole_coeffs(vib_obj, 4, 2) - >>> print(z) - [array([[ 1.64124324e-03, 5.39120159e-03, -4.80053702e-05]])] + >>> freqs = np.array([0.0249722]) + >>> dipole_onemode = np.array([[[-1.24222060e-16, -6.29170686e-17, -7.04678188e-02], + ... [ 3.83941489e-16, -2.31579327e-18, -3.24444991e-02], + ... [ 1.67813138e-17, -5.63904474e-17, -5.60662627e-15], + ... [-7.37584781e-17, -5.51948189e-17, 2.96786374e-02], + ... [ 1.40526000e-16, -3.67126324e-17, 5.92006212e-02]]]) + >>> pes_object = qml.qchem.VibrationalPES(freqs=freqs, dipole_data=[dipole_onemode]) + >>> coeffs_x, coeffs_y, coeffs_z = qml.qchem.taylor_dipole_coeffs(pes_object, 4, 2) + >>> print(coeffs_z) + [array([[-1.54126823e-03, 8.17300533e-03, 3.94178001e-05]])] + + .. details:: + :title: Theory + + The dipole :math:`D` along each of the :math:`x, y,` and :math:`z` directions is defined as: + + .. math:: + + D(q_1,\cdots,q_M) = D_0 + \sum_{i=1}^M D_1^{(i)}(q_i) + \sum_{i>j} + D_2^{(i,j)}(q_i,q_j) + \sum_{ij} + V_2^{(i,j)}(q_i,q_j) + \sum_{i`_. + .. math:: + + V_0 &\equiv V(q_1=0,\cdots,q_M=0) \\ + V_1^{(i)}(q_i) &\equiv V(0,\cdots,0,q_i,0,\cdots,0) - V_0 \\ + V_2^{(i,j)}(q_i,q_j) &\equiv V(0,\cdots,q_i,\cdots,q_j,\cdots,0) - + V_1^{(i)}(q_i) - V_1^{(j)}(q_j) - V_0 \\ + \nonumber \vdots + + These terms are then used in a multi-dimensional polynomial fit to get :math:`n`-mode Taylor + coefficients. For instance, the one-mode Taylor coefficient :math:`\Phi` is related to the + one-mode potential energy surface data as: + + .. math:: + + V_1^{(j)}(q_j) \approx \Phi^{(2)}_j q_j^2 + \Phi^{(3)}_j q_j^3 + ... + + Similarly, the two-mode and three-mode Taylor coefficients are computed if the two-mode and + three-mode potential energy surface data, :math:`V_2^{(j, k)}(q_j, q_k)` and + :math:`V_3^{(j, k, l)}(q_j, q_k, q_l)`, are provided. + + This real-space form of the vibrational Hamiltonian can be represented in the bosonic basis by + using equations defined in Eqs. 6, 7 of `arXiv:1703.09313 `_: + + .. math:: + + \hat q_i = \frac{1}{\sqrt{2}}(b_i^\dagger + b_i), \quad + \hat p_i = \frac{1}{\sqrt{2}}(b_i^\dagger - b_i), + + where :math:`b^\dagger` and :math:`b` are bosonic creation and annihilation operators, + respectively. Args: - coeffs (list(float)): the coefficients of the Hamiltonian - freqs (list(float)): the harmonic frequencies in atomic units - is_local (bool): Flag whether the vibrational modes are localized. Default is ``True``. - uloc (list(list(float))): localization matrix indicating the relationship between original - and localized modes + coeffs (list(tensorlike(float))): the coefficients of a Taylor vibrational Hamiltonian + freqs (array(float)): the harmonic vibrational frequencies in atomic units + is_local (bool): Whether the vibrational modes are localized. Default is ``True``. + uloc (tensorlike(float)): normal mode localization matrix with shape ``(m, m)`` where + ``m = len(freqs)`` Returns: - pennylane.bose.BoseSentence: Taylor bosonic hamiltonian + pennylane.bose.BoseSentence: Taylor bosonic Hamiltonian **Example** - >>> one_mode = np.array([[-0.00088528, -0.00361425, 0.00068143]]) - >>> two_mode = np.array([[[0., 0., 0., 0., 0., 0.]]]) >>> freqs = np.array([0.025]) + >>> one_mode = np.array([[-0.00088528, -0.00361425, 0.00068143]]) >>> uloc = np.array([[1.0]]) - >>> ham = qml.qchem.taylor_bosonic(coeffs=[one_mode, two_mode], freqs=freqs, uloc=uloc) + >>> ham = qml.qchem.taylor_bosonic(coeffs=[one_mode], freqs=freqs, uloc=uloc) >>> print(ham) -0.0012778303419517393 * b⁺(0) b⁺(0) b⁺(0) + -0.0038334910258552178 * b⁺(0) b⁺(0) b(0) @@ -635,51 +741,102 @@ def taylor_bosonic(coeffs, freqs, is_local=True, uloc=None): def taylor_hamiltonian( pes, max_deg=4, min_deg=3, mapping="binary", n_states=2, wire_map=None, tol=1e-12 ): - """Return Taylor vibrational Hamiltonian. + r"""Returns Taylor vibrational Hamiltonian. + + The Taylor vibrational Hamiltonian is defined in terms of kinetic :math:`T` and potential + :math:`V` components as: + + .. math:: + + H = T + V. + + The kinetic term is defined in terms of momentum :math:`p` operator as + + .. math:: + + T = \sum_{i\geq j} K_{ij} p_i p_j, + + where the :math:`K` matrix is defined in terms of vibrational frequencies, :math:`\omega`, and + mode localization unitary matrix, :math:`U`, as: + + .. math:: + + K_{ij} = \sum_{k=1}^M \frac{\omega_k}{2} U_{ki} U_{kj}. + + The potential term is defined in terms of the normal coordinate operator :math:`q` as: + + .. math:: + + V(q_1,\cdots,q_M) = V_0 + \sum_{i=1}^M V_1^{(i)}(q_i) + \sum_{i>j} + V_2^{(i,j)}(q_i,q_j) + \sum_{i`_. - The Hamiltonian is then converted to a qubit operator with a selected ``mapping`` method. + Similarly, the two-mode and three-mode Taylor coefficients are computed if the two-mode and + three-mode potential energy surface data, :math:`V_2^{(j, k)}(q_j, q_k)` and + :math:`V_3^{(j, k, l)}(q_j, q_k, q_l)`, are provided. + + This real space form of the vibrational Hamiltonian can be represented in the bosonic basis by + using equations defined in Eqs. 6, 7 of `arXiv:1703.09313 `_: + + .. math:: + + \hat q_i = \frac{1}{\sqrt{2}}(b_i^\dagger + b_i), \quad + \hat p_i = \frac{1}{\sqrt{2}}(b_i^\dagger - b_i), + + where :math:`b^\dagger` and :math:`b` are bosonic creation and annihilation operators, + respectively. + + The bosonic Hamiltonian is then converted to a qubit operator with a selected ``mapping`` + method to obtain a linear combination as: + + .. math:: + + H = \sum_{i} c_i P_i, + + where :math:`P` is a tensor product of Pauli operators and :math:`c` is a constant. Args: pes (VibrationalPES): object containing the vibrational potential energy surface data - max_deg (int): maximum degree of Taylor form polynomial - min_deg (int): minimum degree of Taylor form polynomial + max_deg (int): maximum degree of the polynomial used to compute the coefficients + min_deg (int): minimum degree of the polynomial used to compute the coefficients mapping (str): Method used to map to qubit basis. Input values can be ``"binary"`` or ``"unary"``. Default is ``"binary"``. n_states(int): maximum number of allowed bosonic states wire_map (dict): A dictionary defining how to map the states of the Bose operator to qubit wires. If ``None``, integers used to label the bosonic states will be used as wire labels. Defaults to ``None``. - tol (float): tolerance for discarding the imaginary part of the coefficients + tol (float): tolerance for discarding the imaginary part of the coefficients during mapping Returns: Operator: the Taylor Hamiltonian **Example** - >>> pes_onemode = np.array([[0.309, 0.115, 0.038, 0.008, 0.000, 0.006, 0.020, 0.041, 0.070]]) - >>> pes_twomode = np.zeros((1, 1, 9, 9)) - >>> dipole_onemode = np.zeros((1, 9, 3)) - >>> gauss_weights = np.array([3.96e-05, 4.94e-03, 8.85e-02, - ... 4.33e-01, 7.20e-01, 4.33e-01, - ... 8.85e-02, 4.94e-03, 3.96e-05]) - >>> grid = np.array([-3.19, -2.27, -1.47, -0.72, 0.0, 0.72, 1.47, 2.27, 3.19]) - >>> pes_object = qml.qchem.VibrationalPES( - ... freqs=np.array([0.025]), - ... grid=grid, - ... uloc=np.array([[1.0]]), - ... gauss_weights=gauss_weights, - ... pes_data=[pes_onemode, pes_twomode], - ... dipole_data=[dipole_onemode], - ... localized=True, - ... dipole_level=1, - ... ) - >>> qml.qchem.taylor_hamiltonian(pes_object, 4, 2) - ( - -0.003833496032473659 * X(0) - + (0.0256479442871582+0j) * I(0) - + (-0.013079509779221888+0j) * Z(0) - ) + >>> freqs = np.array([0.0249722]) + >>> pes_onemode = np.array([[0.08477, 0.01437, 0.00000, 0.00937, 0.03414]]) + >>> pes_object = qml.qchem.VibrationalPES(freqs=freqs, pes_data=[pes_onemode], localized=False) + >>> ham = qml.qchem.taylor_hamiltonian(pes_object) + >>> print(ham) + 0.026123120450329353 * I(0) + -0.01325338030021957 * Z(0) + -0.0032539545260859464 * X(0) """ coeffs_arr = taylor_coeffs(pes, max_deg, min_deg) bose_op = taylor_bosonic(coeffs_arr, pes.freqs, is_local=pes.localized, uloc=pes.uloc) diff --git a/pennylane/qchem/vibrational/vibrational_class.py b/pennylane/qchem/vibrational/vibrational_class.py index f12358c7e83..9f1e2c0c5ad 100644 --- a/pennylane/qchem/vibrational/vibrational_class.py +++ b/pennylane/qchem/vibrational/vibrational_class.py @@ -11,8 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""This module contains functions and classes to generate a pes object. -This object stores all the necessary information to construct +"""This module contains functions and classes to generate data needed to construct a vibrational Hamiltonian for a given molecule.""" from dataclasses import dataclass @@ -26,52 +25,84 @@ @dataclass class VibrationalPES: - r"""Data class to save potential energy surface information computed along vibrational normal modes. + r"""Data class to store information needed to construct a vibrational Hamiltonian for a molecule. Args: - freqs (list[float]): normal-mode frequencies in atomic units - grid (list[float]): the sample points on the Gauss-Hermite quadrature grid - gauss_weights (list[float]): the weights on the Gauss-Hermite quadrature grid - uloc (TensorLike[float]): localization matrix indicating the relationship between original and localized modes - pes_data (list[TensorLike[float]]): tuple containing one-mode, two-mode and three-mode PES - dipole_data (list[TensorLike[float]]): tuple containing one-mode, two-mode and three-mode dipole - localized (bool): Flag that localization of modes was used to generate PES and dipole. Default is ``True``. - dipole_level (int): The level up to which dipole matrix elements are to be calculated. Input values can be - 1, 2, or 3 for upto one-mode dipole, two-mode dipole and three-mode dipole, respectively. Default - value is 1. + freqs (array[float]): normal-mode frequencies in atomic units + grid (array[float]): grid points to compute potential energy surface data. + Should be the sample points of the Gauss-Hermite quadrature. + gauss_weights (array[float]): weights associate with each point in ``grid``. + Should be the weights of the Gauss-Hermite quadrature. + uloc (TensorLike[float]): normal mode localization matrix with shape ``(m, m)`` where + ``m = len(freqs)`` + pes_data (list[TensorLike[float]]): list of one-mode, two-mode and three-mode potential + energy surface data, with shapes ``(m, l)``, ``(m, m, l, l)`` ``(m, m, m, l, l, l)``, + respectively, where ``m = len(freqs)`` and ``l > 0`` + dipole_data (list[TensorLike[float]]): list of one-mode, two-mode and three-mode dipole + moment data, with shapes ``(m, l, 3)``, ``(m, m, l, l, 3)`` ``(m, m, m, l, l, l, 3)``, + respectively, where ``m = len(freqs)`` and ``l > 0`` + localized (bool): Whether the potential energy surface data correspond to localized normal + modes. Default is ``True``. + dipole_level (int): The level up to which dipole moment data are to be calculated. Input + values can be ``1``, ``2``, or ``3`` for up to one-mode dipole, two-mode dipole and + three-mode dipole, respectively. Default value is ``1``. **Example** + This example shows how to construct the :class:`~.qchem.vibrational.VibrationalPES` object for a + linear diatomic molecule, e.g., :math:`H_2`, with only one vibrational normal mode. The one-mode + potential energy surface data is obtained by sampling ``9`` points along the normal mode, with + grid points and weights that correspond to a Gauss-Hermite quadrature. + >>> freqs = np.array([0.01885397]) >>> grid, weights = np.polynomial.hermite.hermgauss(9) - >>> pes_onebody = [[0.05235573, 0.03093067, 0.01501878, 0.00420778, 0.0, + >>> pes_onemode = [[0.05235573, 0.03093067, 0.01501878, 0.00420778, 0.0, ... 0.00584504, 0.02881817, 0.08483433, 0.22025702]] - >>> pes_twobody = None - >>> dipole_onebody = [[[-1.92201700e-16, 1.45397041e-16, -1.40451549e-01], - ... [-1.51005108e-16, 9.53185441e-17, -1.03377032e-01], - ... [-1.22793018e-16, 7.22781963e-17, -6.92825934e-02], - ... [-1.96537436e-16, -5.86686504e-19, -3.52245369e-02], - ... [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], - ... [ 5.24758835e-17, -1.40650833e-16, 3.69955543e-02], - ... [-4.52407941e-17, 1.38406311e-16, 7.60888733e-02], - ... [-4.63820104e-16, 5.42928787e-17, 1.17726042e-01], - ... [ 1.19224372e-16, 9.12491386e-17, 1.64013197e-01]]] - >>> vib_obj = qml.qchem.VibrationalPES(freqs=freqs, grid=grid, gauss_weights=weights, - ... uloc=None, pes_data=[pes_onebody, pes_twobody], - ... dipole_data=[dipole_onebody], localized=False) - >>> vib_obj.freqs + >>> vib_pes = qml.qchem.VibrationalPES(freqs=freqs, grid=grid, + ... gauss_weights=weights, pes_data=[pes_onemode]) + >>> vib_pes.freqs array([0.01885397]) + The following example shows how to construct the :class:`~.qchem.vibrational.VibrationalPES` + object for a nonlinear triatomic molecule, e.g., :math:`H_3^+`, with three vibrational + normal modes. We assume that the potential energy surface and dipole data are obtained by + sampling ``5`` points along the normal mode, with grid points and weights that correspond to a + Gauss-Hermite quadrature. + + >>> freqs = np.array([0.00978463, 0.00978489, 0.01663723]) + >>> grid, weights = np.polynomial.hermite.hermgauss(5) + >>> + >>> uloc = np.array([[-0.99098585, 0.13396657, 0.], + ... [-0.13396657, -0.99098585, 0.], + ... [ 0. , 0. , 1.]]) + >>> + >>> pes_onemode = np.random.rand(3, 5) + >>> pes_twomode = np.random.rand(3, 3, 5, 5) + >>> pes_threemode = np.random.rand(3, 3, 3, 5, 5, 5) + >>> + >>> dipole_onemode = np.random.rand(3, 5, 3) + >>> dipole_twomode = np.random.rand(3, 3, 5, 5, 3) + >>> dipole_threemode = np.random.rand(3, 3, 3, 5, 5, 5, 3) + >>> + >>> localized = True + >>> dipole_level = 3 + >>> + >>> vib_obj = qml.qchem.VibrationalPES(freqs=freqs, grid=grid, gauss_weights=weights, + ... uloc=uloc, pes_data=[pes_onemode, pes_twomode, pes_threemode], + ... dipole_data=[dipole_onemode, dipole_twomode, dipole_threemode], + ... localized=True, dipole_level=3) + >>> print(vib_obj.dipole_threemode.shape) + (3, 3, 3, 5, 5, 5, 3) """ def __init__( self, - freqs, - grid, - gauss_weights, - uloc, - pes_data, - dipole_data, + freqs=None, + grid=None, + gauss_weights=None, + uloc=None, + pes_data=None, + dipole_data=None, localized=True, dipole_level=1, ): @@ -79,10 +110,10 @@ def __init__( self.grid = grid self.gauss_weights = gauss_weights self.uloc = uloc - self.pes_onemode = pes_data[0] - self.pes_twomode = pes_data[1] - self.pes_threemode = pes_data[2] if len(pes_data) > 2 else None - self.dipole_onemode = dipole_data[0] + self.pes_onemode = pes_data[0] if pes_data else None + self.pes_twomode = pes_data[1] if pes_data and len(pes_data) > 1 else None + self.pes_threemode = pes_data[2] if pes_data and len(pes_data) > 2 else None + self.dipole_onemode = dipole_data[0] if dipole_data else None self.dipole_twomode = dipole_data[1] if dipole_level >= 2 else None self.dipole_threemode = dipole_data[2] if dipole_level >= 3 else None self.localized = localized @@ -165,9 +196,10 @@ def optimize_geometry(molecule, method="rhf"): r"""Computes the equilibrium geometry of a molecule. Args: - molecule (:func:`~pennylane.qchem.molecule.Molecule`): Molecule object - method (str): Electronic structure method that can be either restricted and unrestricted - Hartree-Fock, ``'rhf'`` and ``'uhf'``, respectively. Default is ``'rhf'``. + molecule (~qchem.molecule.Molecule): the molecule object + method (str): Electronic structure method used to perform geometry optimization. + Available options are ``"rhf"`` and ``"uhf"`` for restricted and unrestricted + Hartree-Fock, respectively. Default is ``"rhf"``. Returns: array[array[float]]: optimized atomic positions in Cartesian coordinates diff --git a/pennylane/qchem/vibrational/vscf.py b/pennylane/qchem/vibrational/vscf.py index 95dedbceaba..5a5f07f8d4e 100644 --- a/pennylane/qchem/vibrational/vscf.py +++ b/pennylane/qchem/vibrational/vscf.py @@ -429,7 +429,7 @@ def vscf_integrals(h_integrals, d_integrals=None, modals=None, cutoff=None, cuto The ``h_integral`` tensor must have one of these dimensions: - - 1-mode coupled integrals: `(n, m)` + - 1-mode coupled integrals: `(n, m, m)` - 2-mode coupled integrals: `(n, n, m, m, m, m)` - 3-mode coupled integrals: `(n, n, n, m, m, m, m, m, m)`