Source code for mindquantum.algorithm.nisq.chem.unitary_cc

# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.
# ============================================================================

# pylint: disable=duplicate-code

"""Unitary coupled-cluster ansatz."""

from mindquantum.core.circuit import Circuit, add_prefix
from mindquantum.core.operators import TimeEvolution

from .._ansatz import Ansatz
from .transform import Transform
from .uccsd0 import uccsd0_singlet_generator


def _check_int_list(input_list, name):
    """Check if input_list is a list of integers."""
    if not isinstance(input_list, list):
        raise ValueError(f"The input {str(name)} should be a list, but get {type(input_list)}.")
    for i in input_list:
        if not isinstance(i, int):
            raise ValueError(f"The indices of {str(name)} should be integer, but get {type(i)}.")


# pylint: disable=too-few-public-methods
[docs]class UCCAnsatz(Ansatz): r""" The unitary coupled-cluster ansatz for molecular simulations. .. math:: U(\vec{\theta}) = \prod_{j=1}^{N(N\ge1)}{\prod_{i=0}^{N_{j}}{\exp{(\theta_{i}\hat{\tau}_{i})}}} where :math:`\hat{\tau}` are anti-Hermitian operators. Note: Currently, the circuit is construncted using JW transformation. In addition, the reference state wave function (Hartree-Fock) will NOT be included. Args: n_qubits(int): Number of qubits (spin-orbitals). Default: None. n_electrons(int): Number of electrons (occupied spin-orbitals). Default: None. occ_orb(list): Indices of manually assigned occupied spatial orbitals, for ansatz construction only. Default: None. vir_orb(list): Indices of manually assigned virtual spatial orbitals, for ansatz construction only. Default: None. generalized(bool): Whether to use generalized excitations which do not distinguish occupied or virtual orbitals (UCCGSD). Default: False. trotter_step(int): The order of Trotterization step. Default: 1. Examples: >>> from mindquantum.algorithm.nisq import UCCAnsatz >>> ucc = UCCAnsatz(12, 4, occ_orb=[1], ... vir_orb=[2, 3], ... generalized=True, ... trotter_step=2) >>> circuit = ucc.circuit.remove_barrier() >>> len(circuit) 3624 >>> params_list = ucc.circuit.params_name >>> len(params_list) 48 >>> circuit[-10:] q5: ──●────RX(7π/2)───────H───────●────────────────────────────●───────H────── │ │ │ q7: ──X───────H────────RX(π/2)────X────RZ(-0.5*t_1_d0_d_17)────X────RX(7π/2)── """ # pylint: disable=too-many-arguments def __init__(self, n_qubits=None, n_electrons=None, occ_orb=None, vir_orb=None, generalized=False, trotter_step=1): """Initialize a UCCAnsatz object.""" if n_qubits is not None and not isinstance(n_qubits, int): raise ValueError(f"The number of qubits should be integer, but get {type(n_qubits)}.") if n_electrons is not None and not isinstance(n_electrons, int): raise ValueError(f"The number of electrons should be integer, but get {type(n_electrons)}.") if isinstance(n_electrons, int) and n_electrons > n_qubits: raise ValueError( "The number of electrons must be smaller than the number of qubits (spin-orbitals) in the ansatz!" ) if occ_orb is not None: _check_int_list(occ_orb, "occupied orbitals") if vir_orb is not None: _check_int_list(vir_orb, "virtual orbitals") if not isinstance(generalized, bool): raise ValueError(f"The parameter generalized should be bool, but get {type(generalized)}.") if not isinstance(trotter_step, int) or trotter_step < 1: raise ValueError("Trotter step must be a positive integer!") super().__init__("Unitary CC", n_qubits, n_qubits, n_electrons, occ_orb, vir_orb, generalized, trotter_step) # pylint: disable=arguments-differ,too-many-arguments def _implement(self, n_qubits, n_electrons, occ_orb=None, vir_orb=None, generalized=False, trotter_step=1): """Implement the UCC ansatz using uccsd0.""" ansatz_circuit = Circuit() for trotter_idx in range(trotter_step): uccsd0_fermion_op = uccsd0_singlet_generator(n_qubits, n_electrons, True, occ_orb, vir_orb, generalized) uccsd0_circuit = TimeEvolution(Transform(uccsd0_fermion_op).jordan_wigner().imag, 1).circuit # Modify parameter names uccsd0_circuit_modified = add_prefix(uccsd0_circuit, "t_" + str(trotter_idx)) ansatz_circuit += uccsd0_circuit_modified n_qubits_circuit = 0 if list(ansatz_circuit): n_qubits_circuit = ansatz_circuit.n_qubits # If the ansatz's n_qubits is not set by user, use n_qubits_circuit. if self.n_qubits is None: self.n_qubits = n_qubits_circuit if self.n_qubits < n_qubits_circuit: raise ValueError( f"The number of qubits in the ansatz circuit {n_qubits_circuit} is larger than the input" f" n_qubits {n_qubits}! Please check input parameters such as occ_orb, etc." ) self._circuit = ansatz_circuit