Ejemplo n.º 1
0
    def do_qite_step(self):

        btot = self.build_b()
        A = qf.QubitOperator()

        if(self._sparseSb):
            sp_idxs, S, btot = self.build_sparse_S_b(btot)
        else:
            S = self.build_S()

        x = lstsq(S, btot)[0]
        x = np.real(x)


        # sing_vals = lstsq(S, btot)[3]
        # print(np.abs(sing_vals[-1] / sing_vals[0]))

        if(self._sparseSb):
            for I, spI in enumerate(sp_idxs):
                if np.abs(x[I]) > self._x_thresh:
                    A.add(-1.0j * self._db * x[I], self._sig.terms()[spI][1].terms()[0][1])
                    self._n_classical_params += 1

        else:
            for I, SigI in enumerate(self._sig.terms()):
                if np.abs(x[I]) > self._x_thresh:
                    A.add(-1.0j * self._db * x[I], SigI[1].terms()[0][1])
                    self._n_classical_params += 1

        if(self._verbose):
            print('\nbtot:\n ', btot)
            print('\n S:  \n')
            matprint(S)
            print('\n x:  \n')
            print(x)

        eiA_kb, phase1 = trotterize(A, trotter_number=self._trotter_number)
        self._total_phase *= phase1
        self._Uqite.add(eiA_kb)
        self._qc.apply_circuit(eiA_kb)
        self._Ekb.append(np.real(self._qc.direct_op_exp_val(self._qb_ham)))

        self._n_cnot += eiA_kb.get_num_cnots()

        if(self._verbose):
            qf.smart_print(self._qc)
Ejemplo n.º 2
0
    def ansatz_circuit(self, amplitudes=None):
        """ This function returns the Circuit object built
        from the appropriate amplitudes.

        Parameters
        ----------
        amplitudes : list
            A list of parameters that define the variational degrees of freedom in
            the state preparation circuit Uvqc. This is needed for the scipy minimizer.
        """
        temp_pool = qf.SQOpPool()
        tamps = self._tamps if amplitudes is None else amplitudes
        for tamp, top in zip(tamps, self._tops):
            temp_pool.add(tamp, self._pool_obj[top][1])

        A = temp_pool.get_qubit_operator('commuting_grp_lex')

        U, phase1 = trotterize(A, trotter_number=self._trotter_number)
        if phase1 != 1.0 + 0.0j:
            raise ValueError("Encountered phase change, phase not equal to (1.0 + 0.0i)")
        return U
Ejemplo n.º 3
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
Ejemplo n.º 4
0
    def build_qk_mats(self):
        num_tot_basis = len(self._single_det_refs) * 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)

        # TODO (opt): make numpy arrays.
        omega_lst = []
        Homega_lst = []

        for i, ref in enumerate(self._single_det_refs):
            for m in range(self._nstates_per_ref):
                # NOTE: do NOT use Uprep here (is determinant specific).
                Um = qforte.Circuit()
                for j in range(self._nqb):
                    if ref[j] == 1:
                        Um.add(qforte.gate('X', j, j))
                        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)
                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('-------------------------------------------------------------------------------')

        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
Ejemplo n.º 5
0
    def build_sa_qk_mats(self):
        # TODO (cleanup): imporve/update docs
        """Returns matrices P and Q with dimension
        (nstates_per_ref * len(ref_lst) X nstates_per_ref * len(ref_lst))
        based on the evolution of two unitary operators Um = exp(-i * m * dt * H)
        and Un = exp(-i * n * dt *H).

        This is done for all spin adapted refrerences |Phi_K> in ref_lst,
        with (Q) and without (P) measuring with respect to the operator H.
        Elements P_mn are given by <Phi_I| Um^dag Un | Phi_J>.
        Elements Q_mn are given by <Phi_I| Um^dag H Un | Phi_J>.
        This function builds P and Q in an efficient manor and gives the same result
        as M built from 'matrix_element', but is unphysical for a quantum computer.

            Arguments
            ---------

            ref_lst : list of lists
                A list containing all of the spin adapted references |Phi_K> to perfrom evolutions on.
                Is specifically a list of lists of pairs containing coefficient vales
                and a lists pertaning to single determinants.
                As an example,
                ref_lst = [ [ (1.0, [1,1,0,0]) ], [ (0.7071, [0,1,1,0]), (0.7071, [1,0,0,1]) ] ].

            nstates_per_ref : int
                The number of Krylov basis states to generate for each reference.

            dt_lst : list
                List of time steps to use for each reference (ususally the same for
                all references).

            H : QuantumOperator
                The operator to time evolove and measure with respect to
                (usually the Hamiltonain).

            nqubits : int
                The number of qubits

            trot_number : int
                The number of trotter steps (m) to perform when approximating the matrix
                exponentials (Um or Un). For the exponential of two non commuting terms
                e^(A + B), the approximate operator C(m) = (e^(A/m) * e^(B/m))^m is
                exact in the infinite m limit.

            Returns
            -------
            s_mat : ndarray
                A numpy array containing the elements P_mn

            h_mat : ndarray
                A numpy array containing the elements Q_mn

        """

        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):

                # TODO (cleanup): will need to consider gate count for this part.
                Um = qforte.QuantumCircuit()
                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_circuit(expn_op1)

                QC = qforte.QuantumComputer(self._nqb)
                state_prep_lst = []
                for term in ref:
                    coeff = term[0]
                    det = term[1]
                    idx = ref_to_basis_idx(det)
                    state = qforte.QuantumBasis(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
Ejemplo n.º 6
0
    def measure_gradient(self, params=None, use_entire_pool=False):
        """
        Parameters
        ----------
        HAm : QuantumOpPool
            The commutator to measure.

        Ucirc : QuantumCircuit
            The state preparation circuit.
        """

        if not self._fast:
            raise ValueError(
                "self._fast must be True for gradient measurement.")

        grads = np.zeros(len(self._tamps))

        if use_entire_pool:
            M = len(self._pool)
            pool_amps = np.zeros(M)
            for tamp, top in zip(self._tamps, self._tops):
                pool_amps[top] = tamp
        else:
            M = len(self._tamps)

        grads = np.zeros(M)

        if params is None:
            Utot = self.build_Uvqc()
        else:
            Utot = self.build_Uvqc(params)

        qc_psi = qforte.QuantumComputer(
            self._nqb
        )  # build | sig_N > according ADAPT-VQE analytical grad section
        qc_psi.apply_circuit(Utot)
        qc_sig = qforte.QuantumComputer(
            self._nqb
        )  # build | psi_N > according ADAPT-VQE analytical grad section
        psi_i = copy.deepcopy(qc_psi.get_coeff_vec())
        qc_sig.set_coeff_vec(copy.deepcopy(
            psi_i))  # not sure if copy is faster or reapplication of state
        qc_sig.apply_operator(self._qb_ham)

        mu = M - 1

        # find <sing_N | K_N | psi_N>
        if use_entire_pool:
            Kmu_prev = self._pool[mu][1].jw_transform()
            Kmu_prev.mult_coeffs(self._pool[mu][0])
        else:
            Kmu_prev = self._pool[self._tops[mu]][1].jw_transform()
            Kmu_prev.mult_coeffs(self._pool[self._tops[mu]][0])

        qc_psi.apply_operator(Kmu_prev)
        grads[mu] = 2.0 * np.real(
            np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec()))

        #reset Kmu_prev |psi_i> -> |psi_i>
        qc_psi.set_coeff_vec(copy.deepcopy(psi_i))

        for mu in reversed(range(M - 1)):

            # mu => N-1 => M-2
            # mu+1 => N => M-1
            # Kmu => KN-1
            # Kmu_prev => KN

            if use_entire_pool:
                tamp = pool_amps[mu + 1]
            elif params is None:
                tamp = self._tamps[mu + 1]
            else:
                tamp = params[mu + 1]

            if use_entire_pool:
                Kmu = self._pool[mu][1].jw_transform()
                Kmu.mult_coeffs(self._pool[mu][0])
            else:
                Kmu = self._pool[self._tops[mu]][1].jw_transform()
                Kmu.mult_coeffs(self._pool[self._tops[mu]][0])

            Umu, pmu = trotterize(Kmu_prev,
                                  factor=-tamp,
                                  trotter_number=self._trotter_number)

            if (pmu != 1.0 + 0.0j):
                raise ValueError(
                    "Encountered phase change, phase not equal to (1.0 + 0.0i)"
                )

            qc_sig.apply_circuit(Umu)
            qc_psi.apply_circuit(Umu)
            psi_i = copy.deepcopy(qc_psi.get_coeff_vec())

            qc_psi.apply_operator(Kmu)
            grads[mu] = 2.0 * np.real(
                np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec()))

            #reset Kmu |psi_i> -> |psi_i>
            qc_psi.set_coeff_vec(copy.deepcopy(psi_i))
            Kmu_prev = Kmu

        np.testing.assert_allclose(np.imag(grads),
                                   np.zeros_like(grads),
                                   atol=1e-7)

        return grads
Ejemplo n.º 7
0
    def run(self,
            spqe_thresh=1.0e-2,
            spqe_maxiter=20,
            dt=0.001,
            M_omega='inf',
            opt_thresh=1.0e-5,
            opt_maxiter=30,
            use_cumulative_thresh=True):

        if (self._state_prep_type != 'occupation_list'):
            raise ValueError(
                "SPQE implementation can only handle occupation_list Hartree-Fock reference."
            )

        self._spqe_thresh = spqe_thresh
        self._spqe_maxiter = spqe_maxiter
        self._dt = dt
        if (M_omega != 'inf'):
            self._M_omega = int(M_omega)
        else:
            self._M_omega = M_omega

        self._use_cumulative_thresh = use_cumulative_thresh
        self._opt_thresh = opt_thresh
        self._opt_maxiter = opt_maxiter

        self._nbody_counts = []
        self._n_classical_params_lst = []

        self._results = []
        self._energies = []
        self._grad_norms = []
        self._tops = []
        self._tamps = []
        self._converged = False
        self._res_vec_evals = 0
        self._res_m_evals = 0

        self._curr_energy = 0.0

        self._n_classical_params = 0
        self._n_cnot = 0
        self._n_cnot_lst = []
        self._n_pauli_trm_measures = 0
        self._n_pauli_trm_measures_lst = []

        self.print_options_banner()

        self._Nm = []
        self._pool_type = 'full'
        self._eiH, self._eiH_phase = trotterize(
            self._qb_ham,
            factor=self._dt * (0.0 + 1.0j),
            trotter_number=self._trotter_number)

        for occupation in self._ref:
            if occupation:
                self._nbody_counts.append(0)

        self._pool_obj = qf.SQOpPool()
        for I in range(2**self._nqb):
            self._pool_obj.add_term(0.0, self.get_op_from_basis_idx(I))

        self.build_orb_energies()
        spqe_iter = 0
        hit_maxiter = 0

        if (self._print_summary_file):
            f = open("summary.dat", "w+", buffering=1)
            f.write(
                f"#{'Iter(k)':>8}{'E(k)':>14}{'N(params)':>17}{'N(CNOT)':>18}{'N(measure)':>20}\n"
            )
            f.write(
                '#-------------------------------------------------------------------------------\n'
            )

        while not self._converged:

            print('\n\n -----> SPQE iteration ', spqe_iter, ' <-----\n')
            self.update_ansatz()

            if self._converged:
                break

            if (self._verbose):
                print('\ntoperators included from pool: \n', self._tops)
                print('\ntamplitudes for tops: \n', self._tamps)

            self.solve()

            if (self._verbose):
                print('\ntamplitudes for tops post solve: \n',
                      np.real(self._tamps))

            if (self._print_summary_file):
                f.write(
                    f'  {spqe_iter:7}    {self._energies[-1]:+15.9f}    {len(self._tamps):8}        {self._n_cnot_lst[-1]:10}        {sum(self._n_pauli_trm_measures_lst):12}\n'
                )
            spqe_iter += 1

            if spqe_iter > self._spqe_maxiter - 1:
                hit_maxiter = 1
                break

        if (self._print_summary_file):
            f.close()

        if hit_maxiter:
            self._Egs = self.get_final_energy(hit_max_spqe_iter=1)

        self._Egs = self.get_final_energy()

        print("\n\n")
        print("---> Final n-body excitation counts in SPQE ansatz <---")
        print("\n")
        print(f"{'Excitaion order':>20}{'Number of operators':>30}")
        print('---------------------------------------------------------')
        for l, nl in enumerate(self._nbody_counts):
            print(f"{l+1:12}              {nl:14}")

        print('\n\n')
        print(
            f"{'Iter(k)':>8}{'E(k)':>14}{'N(params)':>17}{'N(CNOT)':>18}{'N(measure)':>20}"
        )
        print(
            '-------------------------------------------------------------------------------'
        )

        for k, Ek in enumerate(self._energies):
            print(
                f' {k:7}    {Ek:+15.9f}    {self._n_classical_params_lst[k]:8}        {self._n_cnot_lst[k]:10}        {sum(self._n_pauli_trm_measures_lst[:k+1]):12}'
            )

        self._n_classical_params = len(self._tamps)
        self._n_cnot = self._n_cnot_lst[-1]
        self._n_pauli_trm_measures = sum(self._n_pauli_trm_measures_lst)

        self.print_summary_banner()
        self.verify_run()
Ejemplo n.º 8
0
    def get_residual_vector(self, trial_amps):
        if (self._pool_type == 'sa_SD'):
            raise ValueError(
                'Must use single term particle-hole nbody operators for residual calculation'
            )

        temp_pool = qforte.SQOpPool()
        for param, top in zip(trial_amps, self._tops):
            temp_pool.add_term(param, self._pool[top][1])

        A = temp_pool.get_quantum_operator('commuting_grp_lex')
        U, U_phase = trotterize(A, trotter_number=self._trotter_number)
        if U_phase != 1.0 + 0.0j:
            raise ValueError(
                "Encountered phase change, phase not equal to (1.0 + 0.0i)")

        qc_res = qforte.QuantumComputer(self._nqb)
        qc_res.apply_circuit(self._Uprep)
        qc_res.apply_circuit(U)
        qc_res.apply_operator(self._qb_ham)
        qc_res.apply_circuit(U.adjoint())

        coeffs = qc_res.get_coeff_vec()
        residuals = []

        for m in self._tops:

            # 1. Identify the excitation operator
            sq_op = self._pool[m][1]
            # 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][1][-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_ex_op = sq_op.terms()[0][1]
            else:
                sq_ex_op = sq_op.terms()[1][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.
            nbody = int(len(sq_ex_op) / 2)
            # `destroyed` exists solely for error catching.
            destroyed = False

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

            # loop over annihilators
            for p in reversed(range(nbody, 2 * nbody)):
                if (excited_det.get_bit(sq_ex_op[p]) == 0):
                    destroyed = True
                    break

                excited_det.set_bit(sq_ex_op[p], 0)

            # then over creators
            for p in reversed(range(0, nbody)):
                if (excited_det.get_bit(sq_ex_op[p]) == 1):
                    destroyed = True
                    break

                excited_det.set_bit(sq_ex_op[p], 1)

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

            I = excited_det.add()

            # 3. Compute the phase of the operator, relative to its determinant.
            qc_temp = qforte.QuantumComputer(self._nqb)
            qc_temp.apply_circuit(self._Uprep)
            qc_temp.apply_operator(sq_op.jw_transform())
            phase_factor = qc_temp.get_coeff_vec()[I]

            # 4. Get the residual element, after accounting for numerical noise.
            res_m = coeffs[I] * phase_factor
            if (np.imag(res_m) != 0.0):
                raise ValueError(
                    "residual has imaginary component, something went wrong!!")

            if (self._noise_factor > 1e-12):
                res_m = np.random.normal(np.real(res_m), self._noise_factor)

            residuals.append(res_m)

        return residuals
Ejemplo n.º 9
0
    def update_ansatz(self):
        self._n_pauli_measures_k = 0
        x0 = copy.deepcopy(self._tamps)
        init_gues_energy = self.energy_feval(x0)

        # do U^dag e^iH U |Phi_o> = |Phi_res>
        temp_pool = qf.SQOpPool()
        for param, top in zip(self._tamps, self._tops):
            temp_pool.add_term(param, self._pool[top][1])

        A = temp_pool.get_quantum_operator('commuting_grp_lex')
        U, U_phase = trotterize(A, trotter_number=self._trotter_number)
        if U_phase != 1.0 + 0.0j:
            raise ValueError(
                "Encountered phase change, phase not equal to (1.0 + 0.0i)")

        qc_res = qf.QuantumComputer(self._nqb)
        qc_res.apply_circuit(self._Uprep)
        qc_res.apply_circuit(U)
        qc_res.apply_circuit(self._eiH)
        qc_res.apply_circuit(U.adjoint())

        res_coeffs = qc_res.get_coeff_vec()
        lgrst_op_factor = 0.0

        # ned to sort the coeffs to psi_tilde
        temp_order_resids = []

        # build different res_sq list using M_omega
        if (self._M_omega != 'inf'):
            res_sq_tmp = [
                np.real(np.conj(res_coeffs[I]) * res_coeffs[I])
                for I in range(len(res_coeffs))
            ]

            # Nmu_lst => [ det1, det2, det3, ... det_M_omega]
            det_lst = np.random.choice(len(res_coeffs),
                                       self._M_omega,
                                       p=res_sq_tmp)

            print(f'|Co|dt^2 :       {np.amax(res_sq_tmp):12.14f}')
            print(
                f'mu_o :           {np.where(res_sq_tmp == np.amax(res_sq_tmp))[0][0]}'
            )

            No_idx = np.where(res_sq_tmp == np.amax(res_sq_tmp))[0][0]
            print(f'\nNo_idx   {No_idx:4}')

            No = np.count_nonzero(det_lst == No_idx)
            print(f'\nNo       {No:10}')

            res_sq = []
            Nmu_lst = []
            for mu in range(len(res_coeffs)):
                Nmu = np.count_nonzero(det_lst == mu)
                if (Nmu > 0):
                    print(
                        f'mu:    {mu:8}      Nmu      {Nmu:10}  r_mu: { Nmu / (self._M_omega):12.14f} '
                    )
                    Nmu_lst.append((Nmu, mu))
                res_sq.append((Nmu / (self._M_omega), mu))

            ## 1. sort
            Nmu_lst.sort()
            res_sq.sort()

            ## 2. set norm
            self._curr_res_sq_norm = 0.0
            for rmu_sq in res_sq[:-1]:
                self._curr_res_sq_norm += rmu_sq[0]

            self._curr_res_sq_norm /= (self._dt * self._dt)

            ## 3. print stuff
            print('  \n--> Begin selection opt with residual magnitudes:')
            print('  Initial guess energy:          ',
                  round(init_gues_energy, 10))
            print(
                f'  Norm of approximate res vec:  {np.sqrt(self._curr_res_sq_norm):14.12f}'
            )

            ## 4. check conv status (need up update function with if(M_omega != 'inf'))
            if (len(Nmu_lst) == 1):
                print('  SPQE converged with M_omega thresh!')
                self._converged = 1
                self._final_energy = self._energies[-1]
                self._final_result = self._results[-1]
            else:
                self._converged = 0

            ## 5. add new toperator
            if not self._converged:
                if self._verbose:
                    print('\n')
                    print('     op index (Imu)     Number of times measured')
                    print('  -----------------------------------------------')

                res_sq_sum = 0.0
                n_ops_added = 0
                for Nmu_tup in Nmu_lst[:-1]:
                    if (self._verbose):
                        print(
                            f"  {Nmu_tup[1]:10}                  {np.real(Nmu_tup[0]):14}"
                        )
                    n_ops_added += 1
                    if (Nmu_tup[1] not in self._tops):
                        self._tops.insert(0, Nmu_tup[1])
                        self._tamps.insert(0, 0.0)
                        self.add_op_from_basis_idx(Nmu_tup[1])

                self._n_classical_params_lst.append(len(self._tops))

        else:  # when M_omega == 'inf', proceed with standard SPQE
            res_sq = [(np.real(np.conj(res_coeffs[I]) * res_coeffs[I]), I)
                      for I in range(len(res_coeffs))]
            res_sq.sort()
            self._curr_res_sq_norm = 0.0
            for rmu_sq in res_sq[:-1]:
                self._curr_res_sq_norm += rmu_sq[0]

            self._curr_res_sq_norm /= (self._dt * self._dt)

            print(
                '  \n--> Begin selection opt with residual magnitudes |r_mu|:')
            print('  Initial guess energy: ', round(init_gues_energy, 10))
            print(
                f'  Norm of res vec:      {np.sqrt(self._curr_res_sq_norm):14.12f}'
            )

            self.conv_status()

            if not self._converged:
                if self._verbose:
                    print('\n')
                    print('     op index (Imu)           Residual Facotr')
                    print('  -----------------------------------------------')
                res_sq_sum = 0.0
                n_ops_added = 0

                # for the canonical SPQE batch addition from |r^2| as a importance criterion
                if (self._use_cumulative_thresh):
                    temp_ops = []
                    for rmu_sq in res_sq[:-1]:
                        res_sq_sum += (rmu_sq[0] / (self._dt * self._dt))
                        if res_sq_sum > (self._spqe_thresh *
                                         self._spqe_thresh):
                            if (self._verbose):
                                Ktemp = self.get_op_from_basis_idx(rmu_sq[1])
                                print(
                                    f"  {rmu_sq[1]:10}                  {np.real(rmu_sq[0])/(self._dt * self._dt):14.12f}   {Ktemp.str()}"
                                )
                            n_ops_added += 1
                            if (rmu_sq[1] not in self._tops):
                                temp_ops.append(rmu_sq[1])
                                self.add_op_from_basis_idx(rmu_sq[1])

                    ### consistant with op ordering inspired by traditional renormalization approaches ###
                    for temp_op in temp_ops[::-1]:
                        self._tops.insert(0, temp_op)
                        self._tamps.insert(0, 0.0)

                else:  # add individual ops
                    res_sq.reverse()
                    op_added = False
                    for rmu_sq in res_sq[1:]:
                        if (op_added):
                            break
                        print(
                            f"  {rmu_sq[1]:10}                  {np.real(rmu_sq[0])/(self._dt * self._dt):14.12f}"
                        )
                        if (rmu_sq[1] not in self._tops):
                            print('op added!')
                            self._tops.insert(0, rmu_sq[1])
                            self._tamps.insert(0, 0.0)
                            self.add_op_from_basis_idx(rmu_sq[1])
                            op_added = True

                self._n_classical_params_lst.append(len(self._tops))
Ejemplo n.º 10
0
    def get_residual_vector(self, trial_amps):
        temp_pool = qforte.SQOpPool()
        for param, top in zip(trial_amps, self._tops):
            temp_pool.add_term(param, self._pool[top][1])

        A = temp_pool.get_quantum_operator('commuting_grp_lex')
        U, U_phase = trotterize(A, trotter_number=self._trotter_number)
        if U_phase != 1.0 + 0.0j:
            raise ValueError(
                "Encountered phase change, phase not equal to (1.0 + 0.0i)")

        qc_res = qforte.QuantumComputer(self._nqb)
        qc_res.apply_circuit(self._Uprep)
        qc_res.apply_circuit(U)
        qc_res.apply_operator(self._qb_ham)
        qc_res.apply_circuit(U.adjoint())

        coeffs = qc_res.get_coeff_vec()
        residuals = []

        # each operator needs a score, so loop over toperators
        for m in self._tops:
            sq_op = self._pool[m][1]
            # 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][1][-1]
            if temp_idx < int(
                    sum(self._ref) / 2):  # if temp_idx is an occupid idx
                sq_sub_tamp, sq_sub_top = sq_op.terms()[0]
            else:
                sq_sub_tamp, sq_sub_top = sq_op.terms()[1]

            nbody = int(len(sq_sub_top) / 2)
            destroyed = False
            denom = 1.0

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

            # loop over anihilators
            for p in reversed(range(nbody, 2 * nbody)):
                if (basis_I.get_bit(sq_sub_top[p]) == 0):
                    destroyed = True
                    break

                basis_I.set_bit(sq_sub_top[p], 0)

            # then over creators
            for p in reversed(range(0, nbody)):
                if (basis_I.get_bit(sq_sub_top[p]) == 1):
                    destroyed = True
                    break

                basis_I.set_bit(sq_sub_top[p], 1)

            if not destroyed:

                I = basis_I.add()

                ## check for correct dets
                det_I = integer_to_ref(I, self._nqb)
                nel_I = sum(det_I)
                cor_spin_I = correct_spin(det_I, 0)

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

                res_m = coeffs[I] * sign_adjust  # * sq_sub_tamp
                if (np.imag(res_m) > 0.0):
                    raise ValueError(
                        "residual has imaginary component, someting went wrong!!"
                    )

                residuals.append(res_m)

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

        return residuals
Ejemplo n.º 11
0
    def build_qk_mats(self):
        """Returns matrices S and H needed for the MRSQK algorithm using the Trotterized
        form of the unitary operators U_n = exp(-i n dt H)

        The mathematical operations of this function are unphysical for a quantum
        computer, but efficient for a simulator.

        Returns
        -------
        s_mat : ndarray
            A numpy array containing the elements S_mn = <Phi | Um^dag Un | Phi>.
            _nstates by _nstates

        h_mat : ndarray
            A numpy array containing the elements H_mn = <Phi | Um^dag H Un | Phi>
            _nstates by _nstates
        """

        h_mat = np.zeros((self._nstates, self._nstates), dtype=complex)
        s_mat = np.zeros((self._nstates, self._nstates), dtype=complex)

        # Store these vectors for the aid of MRSQK
        self._omega_lst = []
        Homega_lst = []

        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(
                '-------------------------------------------------------------------------------'
            )

            if (self._print_summary_file):
                f = open("summary.dat", "w+", buffering=1)
                f.write(
                    f"#{'k(S)':>7}{'E(Npar)':>19}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}\n"
                )
                f.write(
                    '#-------------------------------------------------------------------------------\n'
                )

        for m in range(self._nstates):
            # Compute U_m = exp(-i m dt H)
            Um = qforte.QuantumCircuit()
            Um.add_circuit(self._Uprep)
            phase1 = 1.0

            if (m > 0):
                fact = (0.0 - 1.0j) * m * self._dt
                expn_op1, phase1 = trotterize(
                    self._qb_ham,
                    factor=fact,
                    trotter_number=self._trotter_number)
                Um.add_circuit(expn_op1)

            # Compute U_m |φ>
            QC = qforte.QuantumComputer(self._nqb)
            QC.apply_circuit(Um)
            QC.apply_constant(phase1)
            self._omega_lst.append(
                np.asarray(QC.get_coeff_vec(), dtype=complex))

            # Compute H U_m |φ>
            QC.apply_operator(self._qb_ham)
            Homega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex))

            # Compute S_mn = <φ| U_m^\dagger U_n |φ> and H_mn = <φ| U_m^\dagger H U_n |φ>
            for n in range(len(self._omega_lst)):
                h_mat[m][n] = np.vdot(self._omega_lst[m], Homega_lst[n])
                h_mat[n][m] = np.conj(h_mat[m][n])
                s_mat[m][n] = np.vdot(self._omega_lst[m], self._omega_lst[n])
                s_mat[n][m] = np.conj(s_mat[m][n])

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

                scond = np.linalg.cond(s_mat[0:k, 0:k])
                self._n_classical_params = k
                self._n_cnot = 2 * Um.get_num_cnots()
                self._n_pauli_trm_measures = k * self._Nl
                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}'
                )
                if (self._print_summary_file):
                    f.write(
                        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}\n'
                    )

        if (self._diagonalize_each_step and self._print_summary_file):
            f.close()

        self._n_classical_params = self._nstates
        self._n_cnot = 2 * Um.get_num_cnots()
        # diagonal terms of Hbar
        self._n_pauli_trm_measures = self._nstates * self._Nl
        # off-diagonal of Hbar (<X> and <Y> of Hadamard test)
        self._n_pauli_trm_measures += self._nstates * (self._nstates -
                                                       1) * self._Nl
        # off-diagonal of S (<X> and <Y> of Hadamard test)
        self._n_pauli_trm_measures += self._nstates * (self._nstates - 1)

        return s_mat, h_mat
Ejemplo n.º 12
0
    def measure_gradient(self, params=None):
        """ Returns the disentangled (factorized) UCC gradient, using a
        recursive approach.

        Parameters
        ----------
        params : list of floats
            The variational parameters which characterize _Uvqc.
        """

        if not self._fast:
            raise ValueError("self._fast must be True for gradient measurement.")

        M = len(self._tamps)

        grads = np.zeros(M)

        if params is None:
            Utot = self.build_Uvqc()
        else:
            Utot = self.build_Uvqc(params)

        qc_psi = qforte.Computer(self._nqb) # build | sig_N > according ADAPT-VQE analytical grad section
        qc_psi.apply_circuit(Utot)
        qc_sig = qforte.Computer(self._nqb) # build | psi_N > according ADAPT-VQE analytical grad section
        psi_i = copy.deepcopy(qc_psi.get_coeff_vec())
        qc_sig.set_coeff_vec(copy.deepcopy(psi_i)) # not sure if copy is faster or reapplication of state
        qc_sig.apply_operator(self._qb_ham)

        mu = M-1

        # find <sing_N | K_N | psi_N>
        Kmu_prev = self._pool_obj[self._tops[mu]][1].jw_transform()
        Kmu_prev.mult_coeffs(self._pool_obj[self._tops[mu]][0])

        qc_psi.apply_operator(Kmu_prev)
        grads[mu] = 2.0 * np.real(np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec()))

        #reset Kmu_prev |psi_i> -> |psi_i>
        qc_psi.set_coeff_vec(copy.deepcopy(psi_i))

        for mu in reversed(range(M-1)):

            # mu => N-1 => M-2
            # mu+1 => N => M-1
            # Kmu => KN-1
            # Kmu_prev => KN

            if params is None:
                tamp = self._tamps[mu+1]
            else:
                tamp = params[mu+1]

            Kmu = self._pool_obj[self._tops[mu]][1].jw_transform()
            Kmu.mult_coeffs(self._pool_obj[self._tops[mu]][0])

            Umu, pmu = trotterize(Kmu_prev, factor=-tamp, trotter_number=self._trotter_number)

            if (pmu != 1.0 + 0.0j):
                raise ValueError("Encountered phase change, phase not equal to (1.0 + 0.0i)")

            qc_sig.apply_circuit(Umu)
            qc_psi.apply_circuit(Umu)
            psi_i = copy.deepcopy(qc_psi.get_coeff_vec())

            qc_psi.apply_operator(Kmu)
            grads[mu] = 2.0 * np.real(np.vdot(qc_sig.get_coeff_vec(), qc_psi.get_coeff_vec()))

            #reset Kmu |psi_i> -> |psi_i>
            qc_psi.set_coeff_vec(copy.deepcopy(psi_i))
            Kmu_prev = Kmu

        np.testing.assert_allclose(np.imag(grads), np.zeros_like(grads), atol=1e-7)

        return grads