Esempio n. 1
0
def integer_to_ref(n, nqubits):
    """Takes an integer pertaining to a biary number and returns the corresponding
    determinant occupation list (reference).

        Arguments
        ---------

        n : int
            The index.

        nqubits : int
            The number of qubits (will be length of list).

        Returns
        -------

        ref : list
            A list of 1's and 0's representing spin orbital occupations for a
            single Slater determinant.

    """
    qb = qforte.QubitBasis(n)
    ref = []
    for i in range(nqubits):
        if (qb.get_bit(i)):
            ref.append(1)
        else:
            ref.append(0)
    return ref
Esempio n. 2
0
    def fill_excited_dets(self):
        for _, sq_op in self._pool_obj:
            # 1. Identify the excitation operator
            # occ => i,j,k,...
            # vir => a,b,c,...
            # sq_op is 1.0(a^ b^ i j) - 1.0(j^ i^ b a)

            temp_idx = sq_op.terms()[0][2][-1]
            # TODO: This code assumes that the first N orbitals are occupied, and the others are virtual.
            # Use some other mechanism to identify the occupied orbitals, so we can use use PQE on excited
            # determinants.
            if temp_idx < int(
                    sum(self._ref) / 2):  # if temp_idx is an occupied idx
                sq_creators = sq_op.terms()[0][1]
                sq_annihilators = sq_op.terms()[0][2]
            else:
                sq_creators = sq_op.terms()[0][2]
                sq_annihilators = sq_op.terms()[0][1]

            # 2. Get the bit representation of the sq_ex_op acting on the reference.
            # We determine the projective condition for this amplitude by zero'ing this residual.

            # `destroyed` exists solely for error catching.
            destroyed = False

            excited_det = qforte.QubitBasis(self._nqb)
            for k, occ in enumerate(self._ref):
                excited_det.set_bit(k, occ)

            # loop over annihilators
            for p in reversed(sq_annihilators):
                if (excited_det.get_bit(p) == 0):
                    destroyed = True
                    break

                excited_det.set_bit(p, 0)

            # then over creators
            for p in reversed(sq_creators):
                if (excited_det.get_bit(p) == 1):
                    destroyed = True
                    break

                excited_det.set_bit(p, 1)

            if destroyed:
                raise ValueError(
                    "no ops should destroy reference, something went wrong!!")

            I = excited_det.add()

            qc_temp = qforte.Computer(self._nqb)
            qc_temp.apply_circuit(self._Uprep)
            qc_temp.apply_operator(sq_op.jw_transform())
            phase_factor = qc_temp.get_coeff_vec()[I]

            self._excited_dets.append((I, phase_factor))
Esempio n. 3
0
def get_op_from_basis_idx(ref, I):

    max_nbody = 100

    nqb = len(ref)
    nel = int(sum(ref))

    # TODO(Nick): incorparate more flexability into this
    na_el = int(nel / 2)
    nb_el = int(nel / 2)

    basis_I = qf.QubitBasis(I)

    nbody = 0
    pn = 0
    na_I = 0
    nb_I = 0
    holes = []  # i, j, k, ...
    particles = []  # a, b, c, ...
    parity = []

    # for ( p=0; p<nel; p++) {
    for p in range(nel):
        bit_val = int(basis_I.get_bit(p))
        nbody += (1 - bit_val)
        pn += bit_val
        if (p % 2 == 0):
            na_I += bit_val
        else:
            nb_I += bit_val

        if (bit_val - 1):
            holes.append(p)
            if (p % 2 == 0):
                parity.append(1)
            else:
                parity.append(-1)

    # for ( q=nel; q<nqb; q++)
    for q in range(nel, nqb):
        bit_val = int(basis_I.get_bit(q))
        pn += bit_val
        if (q % 2 == 0):
            na_I += bit_val
        else:
            nb_I += bit_val

        if (bit_val):
            particles.append(q)
            if (q % 2 == 0):
                parity.append(1)
            else:
                parity.append(-1)

    if (pn == nel and na_I == na_el and nb_I == nb_el):
        if (nbody != 0 and nbody <= max_nbody):

            total_parity = 1
            # for (const auto& z: parity)
            for z in parity:
                total_parity *= z

            if (total_parity == 1):
                # particles.insert(particles.end(), holes.begin(), holes.end());
                excitation = particles + holes
                dexcitation = list(reversed(excitation))
                # std::vector<> particles_adj (particles.rbegin(), particles.rend());
                sigma_I = [1.0, tuple(excitation)]
Esempio n. 4
0
    def build_sa_refs(self):
        """Builds a list of spin adapted references to be used in the MRSQK procedure.
        """

        if(self._fast==False):
            raise NotImplementedError('Only fast algorithm avalible to build spin adapted refs.')

        target_root = self._target_root
        self._pre_sa_ref_lst = []
        num_refs_per_config = []

        for ref in self._single_det_refs:
            if(ref not in self._pre_sa_ref_lst):
                if(open_shell(ref)):
                    temp = build_eq_dets(ref)
                    self._pre_sa_ref_lst = self._pre_sa_ref_lst + temp
                    num_refs_per_config.append(len(temp))
                else:
                    self._pre_sa_ref_lst.append(ref)
                    num_refs_per_config.append(1)

        h_mat = self.build_classical_CI_mats()

        evals, evecs = eig(h_mat)

        sorted_evals_idxs = sorted_largest_idxs(evals, use_real=True, rev=False)
        sorted_evals = np.zeros((len(evals)), dtype=float)
        sorted_evecs = np.zeros(np.shape(evecs), dtype=float)
        for n in range(len(evals)):
            old_idx = sorted_evals_idxs[n][1]
            sorted_evals[n]   = np.real(evals[old_idx])
            sorted_evecs[:,n] = np.real(evecs[:,old_idx])

        if(np.abs(sorted_evecs[:,0][0]) < 1.0e-6):
            print('Classical CI ground state likely of wrong symmetry, trying other roots!')
            max = len(sorted_evals)
            adjusted_root = 0
            Co_val = 0.0
            while (Co_val < 1.0e-6):
                adjusted_root += 1
                Co_val = np.abs(sorted_evecs[:,adjusted_root][0])

            target_root = adjusted_root
            print('Now using classical CI root: ', target_root)

        target_state = sorted_evecs[:,target_root]
        basis_coeff_lst = []
        norm_basis_coeff_lst = []
        det_lst = []
        coeff_idx = 0
        for num_refs in num_refs_per_config:
            start = coeff_idx
            end = coeff_idx + num_refs

            summ = 0.0
            for val in target_state[start:end]:
                summ += val * val
            temp = [x / np.sqrt(summ) for x in target_state[start:end]]
            norm_basis_coeff_lst.append(temp)

            basis_coeff_lst.append(target_state[start:end])
            det_lst.append(self._pre_sa_ref_lst[start:end])
            coeff_idx += num_refs

        print('\n\n   ==> Classical CI with spin adapted dets summary <==')
        print('-----------------------------------------------------------')
        print('\nList augmented to included all spin \nconfigurations for open shells.')
        print('\n  Coeff                    determinant  ')
        print('----------------------------------------')
        for i, det in enumerate(self._pre_sa_ref_lst):
            qf_det_idx = ref_to_basis_idx(det)
            basis = qforte.QubitBasis(qf_det_idx)
            if(target_state[i] > 0.0):
                print('   ', round(target_state[i], 4), '                ', basis.str(self._nqb))
            else:
                print('  ', round(target_state[i], 4), '                ', basis.str(self._nqb))

        basis_importnace_lst = []
        for basis_coeff in basis_coeff_lst:
            for coeff in basis_coeff:
                val = 0.0
                val += coeff*coeff
            basis_importnace_lst.append(val)

        sorted_basis_importnace_lst = sorted_largest_idxs(basis_importnace_lst, use_real=True, rev=True)

        print('\n\n        ==> Final MRSQK reference space summary <==')
        print('-----------------------------------------------------------')

        self._sa_ref_lst = []
        for i in range(self._d):
            print('\nRef ', i+1)
            print('---------------------------')
            old_idx = sorted_basis_importnace_lst[i][1]
            basis_vec = []
            for k in range( len(basis_coeff_lst[old_idx]) ):
                temp = ( norm_basis_coeff_lst[old_idx][k], det_lst[old_idx][k] )
                basis_vec.append( temp )

                qf_det_idx = ref_to_basis_idx(temp[1])
                basis = qforte.QubitBasis(qf_det_idx)
                if(temp[0] > 0.0):
                    print('   ', round(temp[0], 4), '     ', basis.str(self._nqb))
                else:
                    print('  ', round(temp[0], 4), '     ', basis.str(self._nqb))

            self._sa_ref_lst.append(basis_vec)
Esempio n. 5
0
    def build_refs(self):
        """Builds a list of single determinant references (non spin-adapted) to
        be used in the MRSQK procedure.
        """

        initial_ref_lst = []
        true_initial_ref_lst = []
        Nis_untruncated = self._ninitial_states

        # Adjust dimension of system in case matrix was ill conditioned.
        if(self._ninitial_states > len(self._srqk._eigenvalues)):
            print('\n', self._ninitial_states, ' initial states requested, but QK produced ',
                        len(self._srqk._eigenvalues), ' stable roots.\n Using ',
                        len(self._srqk._eigenvalues),
                        'intial states instead.')

            self._ninitial_states = len(self._srqk._eigenvalues)

        sorted_evals_idxs = sorted_largest_idxs(self._srqk._eigenvalues, use_real=True, rev=False)
        sorted_evals = np.zeros((self._ninitial_states), dtype=complex)
        sorted_evecs = np.zeros((Nis_untruncated, self._ninitial_states), dtype=complex)
        for n in range(self._ninitial_states):
            old_idx = sorted_evals_idxs[n][1]
            sorted_evals[n]   = self._srqk._eigenvalues[old_idx]
            sorted_evecs[:,n] = self._srqk._eigenvectors[:,old_idx]

        sorted_sq_mod_evecs = sorted_evecs * np.conjugate(sorted_evecs)

        basis_coeff_mat = np.array(self._srqk._omega_lst)
        Cprime = (np.conj(sorted_evecs.transpose())).dot(basis_coeff_mat)
        for n in range(self._ninitial_states):
            for i, val in enumerate(Cprime[n]):
                Cprime[n][i] *= np.conj(val)

        for n in range(self._ninitial_states):
            for i, val in enumerate(basis_coeff_mat[n]):
                basis_coeff_mat[n][i] *= np.conj(val)

        Cprime_sq_mod = (sorted_sq_mod_evecs.transpose()).dot(basis_coeff_mat)

        true_idx_lst = []
        idx_lst = []

        if(self._use_spin_adapted_refs):
            num_single_det_refs = 2*self._d
        else:
            num_single_det_refs = self._d

        if(self._target_root is not None):

            true_sorted_idxs = sorted_largest_idxs(Cprime[self._target_root,:])
            sorted_idxs = sorted_largest_idxs(Cprime_sq_mod[self._target_root,:])

            for n in range(num_single_det_refs):
                idx_lst.append( sorted_idxs[n][1] )
                true_idx_lst.append( true_sorted_idxs[n][1] )

        else:
            raise NotImplementedError("psudo state-avaraged selection approach not yet functional")

        print('\n\n      ==> Initial QK Determinat selection summary  <==')
        print('-----------------------------------------------------------')

        if(self._use_phase_based_selection):
            print('\nMost important determinats:\n')
            print('index                     determinant  ')
            print('----------------------------------------')
            for i, idx in enumerate(true_idx_lst):
                basis = qforte.QubitBasis(idx)
                print('  ', i+1, '                ', basis.str(self._nqb))

        else:
            print('\nMost important determinats:\n')
            print('index                     determinant  ')
            print('----------------------------------------')
            for i, idx in enumerate(idx_lst):
                basis = qforte.QubitBasis(idx)
                print('  ', i+1, '                ', basis.str(self._nqb))

        for idx in true_idx_lst:
            true_initial_ref_lst.append(integer_to_ref(idx, self._nqb))

        if(self._ref not in true_initial_ref_lst):
            print('\n***Adding initial referance determinant!***\n')
            for i in range(len(true_initial_ref_lst) - 1):
                true_initial_ref_lst[i+1] = true_initial_ref_lst[i]

            true_initial_ref_lst[0] = initial_ref

        for idx in idx_lst:
            initial_ref_lst.append(integer_to_ref(idx, self._nqb))

        if(self._ref not in initial_ref_lst):
            print('\n***Adding initial referance determinant!***\n')
            staggard_initial_ref_lst = [initial_ref]
            for i in range(len(initial_ref_lst) - 1):
                staggard_initial_ref_lst.append(initial_ref_lst[i].copy())

            initial_ref_lst[0] = initial_ref
            initial_ref_lst = staggard_initial_ref_lst.copy()

        if(self._use_phase_based_selection):
            self._single_det_refs = true_initial_ref_lst

        else:
            self._single_det_refs = initial_ref_lst
Esempio n. 6
0
    def build_sa_qk_mats(self):
        """Returns the QK effective hamiltonain and overlap matrices in a basis
        of spin adapted references.
        """

        num_tot_basis = len(self._sa_ref_lst) * self._nstates_per_ref

        h_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex)
        s_mat = np.zeros((num_tot_basis,num_tot_basis), dtype=complex)

        omega_lst = []
        Homega_lst = []

        for i, ref in enumerate(self._sa_ref_lst):
            for m in range(self._nstates_per_ref):
                Um = qforte.Circuit()
                phase1 = 1.0
                if(m>0):
                    fact = (0.0-1.0j) * m * self._mr_dt
                    expn_op1, phase1 = trotterize(self._qb_ham, factor=fact, trotter_number=self._trotter_number)
                    Um.add(expn_op1)

                QC = qforte.Computer(self._nqb)
                state_prep_lst = []
                for term in ref:
                    coeff = term[0]
                    det = term[1]
                    idx = ref_to_basis_idx(det)
                    state = qforte.QubitBasis(idx)
                    state_prep_lst.append( (state, coeff) )

                QC.set_state(state_prep_lst)
                QC.apply_circuit(Um)
                QC.apply_constant(phase1)
                omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex))

                QC.apply_operator(self._qb_ham)
                Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex))

        if(self._diagonalize_each_step):
            print('\n\n')
            print(f"{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}")
            print('-------------------------------------------------------------------------------')

        # TODO (opt): add this to previous loop
        for p in range(num_tot_basis):
            for q in range(p, num_tot_basis):
                h_mat[p][q] = np.vdot(omega_lst[p], Homega_lst[q])
                h_mat[q][p] = np.conj(h_mat[p][q])
                s_mat[p][q] = np.vdot(omega_lst[p], omega_lst[q])
                s_mat[q][p] = np.conj(s_mat[p][q])

            if (self._diagonalize_each_step):
                # TODO (cleanup): have this print to a separate file
                evals, evecs = canonical_geig_solve(s_mat[0:p+1, 0:p+1],
                                   h_mat[0:p+1, 0:p+1],
                                   print_mats=False,
                                   sort_ret_vals=True)

                scond = np.linalg.cond(s_mat[0:p+1, 0:p+1])
                cs_str = '{:.2e}'.format(scond)

                k = p+1
                self._n_classical_params = k
                if(k==1):
                    self._n_cnot = self._srqk._n_cnot
                else:
                    self._n_cnot = 2 * Um.get_num_cnots()
                self._n_pauli_trm_measures  = k * self._Nl + self._srqk._n_pauli_trm_measures
                self._n_pauli_trm_measures += k * (k-1) * self._Nl
                self._n_pauli_trm_measures += k * (k-1)

                print(f' {scond:7.2e}    {np.real(evals[self._target_root]):+15.9f}    {self._n_classical_params:8}        {self._n_cnot:10}        {self._n_pauli_trm_measures:12}')

        return s_mat, h_mat
Esempio n. 7
0
def ref_string(ref, nqb):
    temp = ref.copy()
    temp.reverse()
    ref_basis_idx = int("".join(str(x) for x in temp), 2)
    ref_basis = qforte.QubitBasis(ref_basis_idx)
    return ref_basis.str(nqb)
Esempio n. 8
0
    def get_op_from_basis_idx(self, I):

        max_nbody = len(self._nbody_counts)
        nqb = len(self._ref)
        nel = int(sum(self._ref))

        # TODO(Nick): incorparate more flexability into this
        na_el = int(nel / 2)
        nb_el = int(nel / 2)
        basis_I = qf.QubitBasis(I)

        nbody = 0
        pn = 0
        na_I = 0
        nb_I = 0
        holes = []  # i, j, k, ...
        particles = []  # a, b, c, ...
        parity = []

        for p in range(nel):
            bit_val = int(basis_I.get_bit(p))
            nbody += (1 - bit_val)
            pn += bit_val
            if (p % 2 == 0):
                na_I += bit_val
            else:
                nb_I += bit_val

            if (bit_val - 1):
                holes.append(p)
                if (p % 2 == 0):
                    parity.append(1)
                else:
                    parity.append(-1)

        for q in range(nel, nqb):
            bit_val = int(basis_I.get_bit(q))
            pn += bit_val
            if (q % 2 == 0):
                na_I += bit_val
            else:
                nb_I += bit_val

            if (bit_val):
                particles.append(q)
                if (q % 2 == 0):
                    parity.append(1)
                else:
                    parity.append(-1)

        if (pn == nel and na_I == na_el and nb_I == nb_el):
            if (nbody == 0):
                return qf.SQOperator()
            if (nbody != 0 and nbody <= max_nbody):

                total_parity = 1
                for z in parity:
                    total_parity *= z

                if (total_parity == 1):
                    K_temp = qf.SQOperator()
                    K_temp.add(+1.0, particles, holes)
                    K_temp.add(-1.0, holes[::-1], particles[::-1])
                    K_temp.simplify()

                    return K_temp

        return qf.SQOperator()
Esempio n. 9
0
    def add_from_basis_idx(self, I):

        max_nbody = len(self._nbody_counts)
        nqb = len(self._ref)
        nel = int(sum(self._ref))

        # TODO(Nick): incorparate more flexability into this
        na_el = int(nel / 2)
        nb_el = int(nel / 2)
        basis_I = qf.QubitBasis(I)

        nbody = 0
        pn = 0
        na_I = 0
        nb_I = 0
        holes = []  # i, j, k, ...
        particles = []  # a, b, c, ...
        parity = []

        # for ( p=0; p<nel; p++) {
        for p in range(nel):
            bit_val = int(basis_I.get_bit(p))
            nbody += (1 - bit_val)
            pn += bit_val
            if (p % 2 == 0):
                na_I += bit_val
            else:
                nb_I += bit_val

            if (bit_val - 1):
                holes.append(p)
                if (p % 2 == 0):
                    parity.append(1)
                else:
                    parity.append(-1)

        for q in range(nel, nqb):
            bit_val = int(basis_I.get_bit(q))
            pn += bit_val
            if (q % 2 == 0):
                na_I += bit_val
            else:
                nb_I += bit_val

            if (bit_val):
                particles.append(q)
                if (q % 2 == 0):
                    parity.append(1)
                else:
                    parity.append(-1)

        if (pn == nel and na_I == na_el and nb_I == nb_el):
            if (nbody != 0 and nbody <= max_nbody):

                total_parity = 1
                for z in parity:
                    total_parity *= z

                if (total_parity == 1):
                    K_temp = qf.SQOperator()
                    K_temp.add(+1.0, particles, holes)
                    K_temp.add(-1.0, holes[::-1], particles[::-1])
                    K_temp.simplify()
                    # this is potentially slow
                    self._Nm.insert(0, len(K_temp.jw_transform().terms()))
                    self._nbody_counts[nbody - 1] += 1