Beispiel #1
0
    def build_sparse_S_b(self, b):
        b_sparse = []
        idx_sparse = []
        for I, bI in enumerate(b):
            if (np.abs(bI) > self._b_thresh):
                idx_sparse.append(I)
                b_sparse.append(bI)
        Idim = len(idx_sparse)
        self._n_pauli_trm_measures += int(Idim * (Idim + 1) * 0.5)

        S = np.zeros((len(b_sparse), len(b_sparse)), dtype=complex)

        Ipsi_qc = qf.QuantumComputer(self._nqb)
        Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))
        CI = np.zeros(shape=(Idim, int(2**self._nqb)), dtype=complex)

        for i in range(Idim):
            S[i][
                i] = 1.0  # With Pauli strings, this is always the inner product
            Ii = idx_sparse[i]
            Ipsi_qc.apply_operator(self._sig.terms()[Ii][1])
            CI[i, :] = copy.deepcopy(Ipsi_qc.get_coeff_vec())
            for j in range(i):
                S[i][j] = S[j][i] = np.vdot(CI[i, :], CI[j, :])
            Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))

        return idx_sparse, np.real(S), np.real(b_sparse)
Beispiel #2
0
    def build_b(self):
        """
        Construct the vector b (eq. 5b) of Motta, with h[m] the full Hamiltonian.
        """

        b = np.zeros(self._NI, dtype=complex)

        denom = np.sqrt(1 - 2 * self._db * self._Ekb[-1])
        prefactor = -1.0j / denom

        self._n_pauli_trm_measures += self._Nl * self._NI

        Hpsi_qc = qf.QuantumComputer(self._nqb)
        Hpsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))
        Hpsi_qc.apply_operator(self._qb_ham)
        C_Hpsi_qc = copy.deepcopy(Hpsi_qc.get_coeff_vec())

        for I, (op_coefficient, operator) in enumerate(self._sig.terms()):
            Hpsi_qc.apply_operator(operator)
            exp_val = np.vdot(self._qc.get_coeff_vec(),
                              Hpsi_qc.get_coeff_vec())
            b[I] = prefactor * op_coefficient * exp_val
            Hpsi_qc.set_coeff_vec(C_Hpsi_qc)

        return np.real(b)
Beispiel #3
0
    def build_classical_CI_mats(self):
        """Builds a classical configuration interaction out of single determinants.
        """
        num_tot_basis = len(self._pre_sa_ref_lst)
        h_CI = np.zeros((num_tot_basis,num_tot_basis), dtype=complex)

        omega_lst = []
        Homega_lst = []

        for i, ref in enumerate(self._pre_sa_ref_lst):
            # NOTE: do NOT use Uprep here (is determinant specific).
            Un = qforte.QuantumCircuit()
            for j in range(self._nqb):
                if ref[j] == 1:
                    Un.add_gate(qforte.gate('X', j, j))

            QC = qforte.QuantumComputer(self._nqb)
            QC.apply_circuit(Un)
            omega_lst.append(np.asarray(QC.get_coeff_vec(), dtype=complex))

            Homega = np.zeros((2**self._nqb), dtype=complex)

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

            for j in range(len(omega_lst)):
                h_CI[i][j] = np.vdot(omega_lst[i], Homega_lst[j])
                h_CI[j][i] = np.conj(h_CI[i][j])

        return h_CI
Beispiel #4
0
    def perfect_experimental_avg(self, params):

        """
        calculates the exact experimental result of the operator the Experiment object was initialized with

        :param params: (list) the list of parameters for the state preparation ansatz.

        """

        if(self.prepare_each_time_==False):
            #1 initialize a quantum computer
            qc = qforte.QuantumComputer(self.n_qubits_)

            #2 build/update generator with params
            # self.generator_.set_parameters(params)

            #3 apply generator (once if prepare_each_time = False, N_sample times if)
            qc.apply_circuit(self.generator_)

            #4 measure operator
            n_terms = len(self.operator_.terms())
            term_sum = 0.0

            for k in range(n_terms):
                term_sum += self.operator_.terms()[k][0] * qc.perfect_measure_circuit(self.operator_.terms()[k][1]);

            return numpy.real(term_sum)

        elif(self.prepare_each_time_==True):
            raise Exception('No support yet for measurement with multiple state preparations')
Beispiel #5
0
    def test_build_from_openfermion(self):
        print('\n')
        trial_state = qforte.QuantumComputer(4)

        trial_prep = [None] * 5
        trial_prep[0] = qforte.gate('H', 0, 0)
        trial_prep[1] = qforte.gate('H', 1, 1)
        trial_prep[2] = qforte.gate('H', 2, 2)
        trial_prep[3] = qforte.gate('H', 3, 3)
        trial_prep[4] = qforte.gate('cX', 0, 1)

        trial_circ = qforte.QuantumCircuit()

        #prepare the circuit
        for gate in trial_prep:
            trial_circ.add_gate(gate)

        # use circuit to prepare trial state
        trial_state.apply_circuit(trial_circ)

        test_operator = QubitOperator('X2 Y1', 0.0 - 0.25j)
        test_operator += QubitOperator('Y2 Y1', 0.25)
        test_operator += QubitOperator('X2 X1', 0.25)
        test_operator += QubitOperator('Y2 X1', 0.0 + 0.25j)
        print(test_operator)

        qforte_operator = qforte.build_from_openfermion(test_operator)

        qforte.smart_print(qforte_operator)

        exp = trial_state.direct_op_exp_val(qforte_operator)
        print(exp)
        self.assertAlmostEqual(exp, 0.2499999999999999 + 0.0j)
Beispiel #6
0
    def build_orb_energies(self):
        self._orb_e = []

        print('\nBuilding single particle energies list:')
        print('---------------------------------------')
        qc = qforte.QuantumComputer(self._nqb)
        qc.apply_circuit(build_Uprep(self._ref, 'reference'))
        E0 = qc.direct_op_exp_val(self._qb_ham)

        for i in range(self._nqb):
            qc = qforte.QuantumComputer(self._nqb)
            qc.apply_circuit(build_Uprep(self._ref, 'reference'))
            qc.apply_gate(qforte.gate('X', i, i))
            Ei = qc.direct_op_exp_val(self._qb_ham)

            if (i < sum(self._ref)):
                ei = E0 - Ei
            else:
                ei = Ei - E0

            print(f'  {i:3}     {ei:+16.12f}')
            self._orb_e.append(ei)
Beispiel #7
0
    def run(self,
            beta=1.0,
            db=0.2,
            expansion_type='SD',
            sparseSb=True,
            b_thresh=1.0e-6,
            x_thresh=1.0e-10,
            do_lanczos=False,
            lanczos_gap=2):

        self._beta = beta
        self._db = db
        self._nbeta = int(beta / db) + 1
        self._sq_ham = self._sys.get_sq_hamiltonian()
        self._expansion_type = expansion_type
        self._sparseSb = sparseSb
        self._total_phase = 1.0 + 0.0j
        self._Uqite = qf.QuantumCircuit()
        self._b_thresh = b_thresh
        self._x_thresh = x_thresh

        self._n_classical_params = 0
        self._n_cnot = 0
        self._n_pauli_trm_measures = 0

        self._do_lanczos = do_lanczos
        self._lanczos_gap = lanczos_gap

        qc_ref = qf.QuantumComputer(self._nqb)
        qc_ref.apply_circuit(self._Uprep)
        self._Ekb = [np.real(qc_ref.direct_op_exp_val(self._qb_ham))]

        # Print options banner (should done for all algorithms).
        self.print_options_banner()

        # Build expansion pool.
        self.build_expansion_pool()

        # Do the imaginary time evolution.
        self.evolve()

        if (self._do_lanczos):
            self.do_qlanczos()

        # Print summary banner (should done for all algorithms).
        self.print_summary_banner()

        # verify that required attributes were defined
        # (should done for all algorithms).
        self.verify_run()
Beispiel #8
0
    def ref_state(self):
        """ Return a qforte.QuantumComputer instance which represents the Hartree-Fock state of the input molecule
        """
        hf_qc = qforte.QuantumComputer(self.molecule.n_qubits)
        hf_cir = qforte.QuantumCircuit()

        for i_a in self.occ_idx_alpha:
            X_ia = qforte.make_gate('X', i_a, i_a)
            hf_cir.add_gate(X_ia)

        for i_b in self.occ_idx_beta:
            X_ib = qforte.make_gate('X', i_b, i_b)
            hf_cir.add_gate(X_ib)

        hf_qc.apply_circuit(hf_cir)
        return hf_qc
Beispiel #9
0
    def build_S(self):
        """
        Construct the matrix S (eq. 5a) of Motta.
        """
        Idim = self._NI

        S = np.zeros((Idim, Idim), dtype=complex)

        Ipsi_qc = qf.QuantumComputer(self._nqb)
        Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))
        # CI[I][J] = (σ_I Ψ)_J
        CI = np.zeros(shape=(Idim, int(2**self._nqb)), dtype=complex)

        for i in range(Idim):
            S[i][
                i] = 1.0  # With Pauli strings, this is always the inner product
            Ipsi_qc.apply_operator(self._sig.terms()[i][1])
            CI[i, :] = copy.deepcopy(Ipsi_qc.get_coeff_vec())
            for j in range(i):
                S[i][j] = S[j][i] = np.vdot(CI[i, :], CI[j, :])
            Ipsi_qc.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))

        return np.real(S)
Beispiel #10
0
    def evolve(self):
        self._Uqite.add_circuit(self._Uprep)
        self._qc = qf.QuantumComputer(self._nqb)
        self._qc.apply_circuit(self._Uqite)

        if (self._do_lanczos):
            self._lanczos_vecs = []
            self._Hlanczos_vecs = []

            self._lanczos_vecs.append(copy.deepcopy(self._qc.get_coeff_vec()))

            qcSig_temp = qf.QuantumComputer(self._nqb)
            qcSig_temp.set_coeff_vec(copy.deepcopy(self._qc.get_coeff_vec()))
            qcSig_temp.apply_operator(self._qb_ham)
            self._Hlanczos_vecs.append(
                copy.deepcopy(qcSig_temp.get_coeff_vec()))

        print(
            f"{'beta':>7}{'E(beta)':>18}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}"
        )
        print(
            '-------------------------------------------------------------------------------'
        )
        print(
            f' {0.0:7.3f}    {self._Ekb[0]:+15.9f}    {self._n_classical_params:8}        {self._n_cnot:10}        {self._n_pauli_trm_measures:12}'
        )

        if (self._print_summary_file):
            f = open("summary.dat", "w+", buffering=1)
            f.write(
                f"#{'beta':>7}{'E(beta)':>18}{'N(params)':>14}{'N(CNOT)':>18}{'N(measure)':>20}\n"
            )
            f.write(
                '#-------------------------------------------------------------------------------\n'
            )
            f.write(
                f'  {0.0:7.3f}    {self._Ekb[0]:+15.9f}    {self._n_classical_params:8}        {self._n_cnot:10}        {self._n_pauli_trm_measures:12}\n'
            )

        for kb in range(1, self._nbeta):
            self.do_qite_step()
            if (self._do_lanczos):
                if (kb % self._lanczos_gap == 0):
                    self._lanczos_vecs.append(
                        copy.deepcopy(self._qc.get_coeff_vec()))

                    qcSig_temp = qf.QuantumComputer(self._nqb)
                    qcSig_temp.set_coeff_vec(
                        copy.deepcopy(self._qc.get_coeff_vec()))
                    qcSig_temp.apply_operator(self._qb_ham)
                    self._Hlanczos_vecs.append(
                        copy.deepcopy(qcSig_temp.get_coeff_vec()))

            print(
                f' {kb*self._db:7.3f}    {self._Ekb[kb]:+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'  {kb*self._db:7.3f}    {self._Ekb[kb]:+15.9f}    {self._n_classical_params:8}        {self._n_cnot:10}        {self._n_pauli_trm_measures:12}\n'
                )
        self._Egs = self._Ekb[-1]

        if (self._print_summary_file):
            f.close()
Beispiel #11
0
    def run(self,
            t = 1.0,
            nruns = 20,
            success_prob = 0.5,
            num_precise_bits = 4,
            return_phases=False):

        self._t = t
        self._nruns = nruns
        self._success_prob = success_prob
        self._num_precise_bits = num_precise_bits
        self._return_phases = return_phases
        self._Uqpe = qforte.QuantumCircuit()
        self._n_state_qubits = self._nqb
        eps = 1 - success_prob
        self._n_ancilla = num_precise_bits + int(np.log2(2 + (1.0/eps)))
        self._n_tot_qubits = self._n_state_qubits + self._n_ancilla
        self._abegin = self._n_state_qubits
        self._aend = self._n_tot_qubits - 1

        self._n_classical_params = 0
        self._n_pauli_trm_measures = nruns

        # Print options banner (should done for all algorithms).
        self.print_options_banner()

        ######### QPE ########

        # add hadamard circ to split ancilla register
        self._Uqpe.add_circuit(self.get_Uhad())

        # add initial state preparation circuit
        self._Uqpe.add_circuit(self._Uprep)

        # add controll e^-iHt circuit
        self._Uqpe.add_circuit(self.get_dynamics_circ())

        # add reverse QFT
        self._Uqpe.add_circuit(self.get_qft_circuit('reverse'))

        computer = qforte.QuantumComputer(self._n_tot_qubits)
        computer.apply_circuit(self._Uqpe)

        self._n_cnot = self._Uqpe.get_num_cnots()

        if(self._fast):
            z_readouts = computer.measure_z_readouts_fast(self._abegin, self._aend, self._nruns)
        else:
            Zcirc = self.get_z_circuit()
            z_readouts = computer.measure_readouts(Zcirc, self._nruns)

        self._phases = []
        for readout in z_readouts:
            val = 0.0
            i = 1
            for z in readout:
                val += z / (2**i)
                i += 1
            self._phases.append(val)

        # find final binary string of phase readouts:
        final_readout = []
        final_readout_aves = []
        for i in range(self._n_ancilla):
            iave = 0.0
            for readout in z_readouts:
                iave += readout[i]
            iave /= nruns
            final_readout_aves.append(iave)
            if (iave > (1.0/2)):
                final_readout.append(1)
            else:
                final_readout.append(0)

        self._final_phase = 0.0
        counter = 0
        for i, z in enumerate(final_readout):
            self._final_phase += z / (2**(i+1))

        Eqpe = -2 * np.pi * self._final_phase / t
        res = stats.mode(np.asarray(self._phases))
        self._mode_phase = res.mode[0]
        self._mode_energy = -2 * np.pi * self._mode_phase / t

        print('\n           ==> QPE readout averages <==')
        print('------------------------------------------------')
        for i, ave in enumerate(final_readout_aves):
            print('  bit ', i,  ': ', ave)
        print('\n  Final bit readout: ', final_readout)

        ######### QPE ########

        # set Egs
        self._Egs = Eqpe

        # set Umaxdepth
        self._Umaxdepth = self._Uqpe

        # Print summary banner (should done for all algorithms).
        self.print_summary_banner()

        # verify that required attributes were defined
        # (should done for all algorithms).
        self.verify_run()
Beispiel #12
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
Beispiel #13
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
Beispiel #14
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))
Beispiel #15
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