예제 #1
0
    def test_lih_ops(self):
        """Check the value of the operators on LiH
        """
        norb = 6
        nalpha = 2
        nbeta = 2
        nele = nalpha + nbeta

        _, _, lih_ground = build_lih_data.build_lih_data('energy')

        wfn = Wavefunction([[nele, nalpha - nbeta, norb]])
        wfn.set_wfn(strategy='from_data',
                    raw_data={(nele, nalpha - nbeta): lih_ground})

        operator = S2Operator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 0. + 0.j)
        operator = SzOperator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 0. + 0.j)
        operator = TimeReversalOp()
        self.assertAlmostEqual(wfn.expectationValue(operator), 1. + 0.j)
        operator = NumberOperator()
        self.assertAlmostEqual(wfn.expectationValue(operator), 4. + 0.j)
        self.assertAlmostEqual(wfn.expectationValue(operator, wfn), 4. + 0.j)
예제 #2
0
    def test_lih_energy(self):
        """Checking total energy with LiH
        """
        eref = -8.877719570384043
        norb = 6
        nalpha = 2
        nbeta = 2
        nele = nalpha + nbeta
        h1e, h2e, lih_ground = build_lih_data.build_lih_data('energy')

        elec_hamil = general_hamiltonian.General((h1e, h2e))
        wfn = Wavefunction([[nele, nalpha - nbeta, norb]])
        wfn.set_wfn(strategy='from_data',
                    raw_data={(nele, nalpha - nbeta): lih_ground})

        ecalc = wfn.expectationValue(elec_hamil)
        self.assertAlmostEqual(eref, ecalc, places=8)
예제 #3
0
    def test_hartree_fock_init(self):
        h1e, h2e, _ = build_lih_data('energy')
        elec_hamil = get_restricted_hamiltonian((h1e, h2e))
        norb = 6
        nalpha = 2
        nbeta = 2
        wfn = Wavefunction([[nalpha + nbeta, nalpha - nbeta, norb]])
        wfn.print_wfn()
        wfn.set_wfn(strategy='hartree-fock')
        wfn.print_wfn()
        self.assertEqual(wfn.expectationValue(elec_hamil), -8.857341498221992)
        hf_wf = numpy.zeros((int(binom(norb, 2)), int(binom(norb, 2))))
        hf_wf[0, 0] = 1.
        self.assertTrue(numpy.allclose(wfn.get_coeff((4, 0)), hf_wf))

        wfn = Wavefunction([[nalpha + nbeta, nalpha - nbeta, norb],
                            [nalpha + nbeta, 2, norb]])
        self.assertRaises(ValueError, wfn.set_wfn, strategy='hartree-fock')
예제 #4
0
    def adapt_vqe(self,
                  initial_wf: Wavefunction,
                  opt_method: str = 'L-BFGS-B',
                  opt_options=None,
                  v_reconstruct: bool = True,
                  num_ops_add: int = 1):
        """
        Run ADAPT-VQE using

        Args:
            initial_wf: Initial wavefunction at the start of the calculation
            opt_method: scipy optimizer to use
            opt_options: options  for scipy optimizer
            v_reconstruct: use valdemoro reconstruction
            num_ops_add: add this many operators from the pool to the
                         wavefunction
        """
        if opt_options is None:
            opt_options = {}
        operator_pool = []
        operator_pool_fqe: List[ABCHamiltonian] = []
        existing_parameters: List[float] = []
        self.gradients = []
        self.energies = [initial_wf.expectationValue(self.k2_fop)]
        iteration = 0
        while iteration < self.iter_max:
            # get current wavefunction
            wf = copy.deepcopy(initial_wf)
            for fqe_op, coeff in zip(operator_pool_fqe, existing_parameters):
                wf = wf.time_evolve(coeff, fqe_op)

            # calculate rdms for grad
            _, tpdm = wf.sector((self.nele, self.sz)).get_openfermion_rdms()
            if v_reconstruct:
                d3 = 6 * valdemaro_reconstruction(tpdm / 2, self.nele)
            else:
                d3 = wf.sector((self.nele, self.sz)).get_three_pdm()

            # get ACSE Residual and 2-RDM gradient
            acse_residual = two_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm, d3)
            one_body_residual = one_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm)

            # calculate grad of each operator in the pool
            pool_grad = []
            for operator in self.operator_pool.op_pool:
                grad_val = 0
                for op_term, coeff in operator.terms.items():
                    idx = [xx[0] for xx in op_term]
                    if len(idx) == 4:
                        grad_val += acse_residual[tuple(idx)] * coeff
                    elif len(idx) == 2:
                        grad_val += one_body_residual[tuple(idx)] * coeff
                pool_grad.append(grad_val)

            max_grad_terms_idx = \
                np.argsort(np.abs(pool_grad))[::-1][:num_ops_add]

            pool_terms = [
                self.operator_pool.op_pool[i] for i in max_grad_terms_idx
            ]
            operator_pool.extend(pool_terms)
            fqe_ops: List[ABCHamiltonian] = []
            for f_op in pool_terms:
                fqe_ops.append(
                    build_hamiltonian(1j * f_op,
                                      self.sdim,
                                      conserve_number=True))
            operator_pool_fqe.extend(fqe_ops)
            existing_parameters.extend([0] * len(fqe_ops))

            new_parameters, current_e = self.optimize_param(
                operator_pool_fqe,
                existing_parameters,
                initial_wf,
                opt_method,
                opt_options=opt_options)

            existing_parameters = new_parameters.tolist()
            if self.verbose:
                print(iteration, current_e, max(np.abs(pool_grad)))
            self.energies.append(current_e)
            self.gradients.append(pool_grad)
            if max(np.abs(pool_grad)) < self.stopping_eps or np.abs(
                    self.energies[-2] - self.energies[-1]) < self.delta_e_eps:
                break
            iteration += 1
예제 #5
0
    def vbc(self,
            initial_wf: Wavefunction,
            opt_method: str = 'L-BFGS-B',
            opt_options=None,
            num_opt_var=None,
            v_reconstruct=False,
            generator_decomp=None,
            generator_rank=None):
        """The variational Brillouin condition method

        Solve for the 2-body residual and then variationally determine
        the step size.  This exact simulation cannot be implemented without
        Trotterization.  A proxy for the approximate evolution is the update_
        rank pameter which limites the rank of the residual.

        Args:
            initial_wf: initial wavefunction
            opt_method: scipy optimizer name
            num_opt_var: Number of optimization variables to consider
            v_reconstruct: use valdemoro reconstruction of 3-RDM to calculate
                           the residual
            generator_decomp: None, takagi, or svd
            generator_rank: number of generator terms to take
        """
        if opt_options is None:
            opt_options = {}
        self.num_opt_var = num_opt_var
        nso = 2 * self.sdim
        operator_pool: List[Union[ABCHamiltonian, SumOfSquaresOperator]] = []
        operator_pool_fqe: List[
            Union[ABCHamiltonian, SumOfSquaresOperator]] = []
        existing_parameters: List[float] = []
        self.energies = []
        self.energies = [initial_wf.expectationValue(self.k2_fop)]
        self.residuals = []
        iteration = 0
        while iteration < self.iter_max:
            # get current wavefunction
            wf = copy.deepcopy(initial_wf)
            for op, coeff in zip(operator_pool_fqe, existing_parameters):
                if np.isclose(coeff, 0):
                    continue
                if isinstance(op, ABCHamiltonian):
                    wf = wf.time_evolve(coeff, op)
                elif isinstance(op, SumOfSquaresOperator):
                    for v, cc in zip(op.basis_rotation, op.charge_charge):
                        wf = evolve_fqe_givens_unrestricted(wf, v.conj().T)
                        wf = evolve_fqe_charge_charge_unrestricted(
                            wf, coeff * cc)
                        wf = evolve_fqe_givens_unrestricted(wf, v)
                    wf = evolve_fqe_givens_unrestricted(wf,
                                                        op.one_body_rotation)
                else:
                    raise ValueError("Can't evolve operator type {}".format(
                        type(op)))

            # calculate rdms for grad
            _, tpdm = wf.sector((self.nele, self.sz)).get_openfermion_rdms()
            if v_reconstruct:
                d3 = 6 * valdemaro_reconstruction_functional(
                    tpdm / 2, self.nele)
            else:
                d3 = wf.sector((self.nele, self.sz)).get_three_pdm()

            # get ACSE Residual and 2-RDM gradient
            acse_residual = two_rdo_commutator_symm(
                self.reduced_ham.two_body_tensor, tpdm, d3)

            if generator_decomp is None:
                fop = get_fermion_op(acse_residual)
            elif generator_decomp is 'svd':
                new_residual = np.zeros_like(acse_residual)
                for p, q, r, s in product(range(nso), repeat=4):
                    new_residual[p, q, r, s] = (acse_residual[p, q, r, s] -
                                                acse_residual[s, r, q, p]) / 2

                fop = self.get_svd_tensor_decomp(new_residual, generator_rank)
            elif generator_decomp is 'takagi':
                fop = self.get_takagi_tensor_decomp(acse_residual,
                                                    generator_rank)
            else:
                raise ValueError(
                    "Generator decomp must be None, svd, or takagi")

            operator_pool.extend([fop])
            fqe_ops: List[Union[ABCHamiltonian, SumOfSquaresOperator]] = []
            if isinstance(fop, ABCHamiltonian):
                fqe_ops.append(fop)
            elif isinstance(fop, SumOfSquaresOperator):
                fqe_ops.append(fop)
            else:
                fqe_ops.append(
                    build_hamiltonian(1j * fop, self.sdim,
                                      conserve_number=True))

            operator_pool_fqe.extend(fqe_ops)
            existing_parameters.extend([0])

            if self.num_opt_var is not None:
                if len(operator_pool_fqe) < self.num_opt_var:
                    pool_to_op = operator_pool_fqe
                    params_to_op = existing_parameters
                    current_wf = copy.deepcopy(initial_wf)
                else:
                    pool_to_op = operator_pool_fqe[-self.num_opt_var:]
                    params_to_op = existing_parameters[-self.num_opt_var:]
                    current_wf = copy.deepcopy(initial_wf)
                    for fqe_op, coeff in zip(
                            operator_pool_fqe[:-self.num_opt_var],
                            existing_parameters[:-self.num_opt_var]):
                        current_wf = current_wf.time_evolve(coeff, fqe_op)
                    temp_cwf = copy.deepcopy(current_wf)
                    for fqe_op, coeff in zip(pool_to_op, params_to_op):
                        if np.isclose(coeff, 0):
                            continue
                        temp_cwf = temp_cwf.time_evolve(coeff, fqe_op)

                new_parameters, current_e = self.optimize_param(
                    pool_to_op,
                    params_to_op,
                    current_wf,
                    opt_method,
                    opt_options=opt_options)

                if len(operator_pool_fqe) < self.num_opt_var:
                    existing_parameters = new_parameters.tolist()
                else:
                    existing_parameters[-self.num_opt_var:] = \
                        new_parameters.tolist()
            else:
                new_parameters, current_e = self.optimize_param(
                    operator_pool_fqe,
                    existing_parameters,
                    initial_wf,
                    opt_method,
                    opt_options=opt_options)
                existing_parameters = new_parameters.tolist()

            if self.verbose:
                print(iteration, current_e, np.max(np.abs(acse_residual)),
                      len(existing_parameters))
            self.energies.append(current_e)
            self.residuals.append(acse_residual)
            if np.max(np.abs(acse_residual)) < self.stopping_eps or np.abs(
                    self.energies[-2] - self.energies[-1]) < self.delta_e_eps:
                break
            iteration += 1