def Fredkin(i, j, k):
    """
    builds a circuit to simulate a three-qubit Fredkin gate
    (Controled-SWAP, CSWAP gate).

    :param i: control qubit 1
    :param j: swap qubit 1
    :param k: swap qubit 2
    """

    C12 = qforte.gate('cX', j, i)
    C32 = qforte.gate('cX', j, k)
    CV23 = qforte.gate('cV', k, j)
    CV13 = qforte.gate('cV', k, i)

    F_circ = qforte.Circuit()
    F_circ.add(C32)
    F_circ.add(CV23)
    F_circ.add(CV13)
    F_circ.add(C12)
    F_circ.add(CV23.adjoint())
    F_circ.add(C32)
    F_circ.add(C12)

    return F_circ
示例#2
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.Circuit()
            for j in range(self._nqb):
                if ref[j] == 1:
                    Un.add(qforte.gate('X', j, j))

            QC = qforte.Computer(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
示例#3
0
def build_Uprep(ref, state_prep_type):
    Uprep = qforte.Circuit()
    if state_prep_type == 'occupation_list':
        for j in range(len(ref)):
            if ref[j] == 1:
                Uprep.add(qforte.gate('X', j, j))
    else:
        raise ValueError(
            "Only 'occupation_list' supported as state preparation type")

    return Uprep
示例#4
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._expansion_type = expansion_type
        self._sparseSb = sparseSb
        self._total_phase = 1.0 + 0.0j
        self._Uqite = qf.Circuit()
        self._b_thresh = b_thresh
        self._x_thresh = x_thresh

        self._n_classical_params = 0
        self._n_cnot = self._Uprep.get_num_cnots()
        self._n_pauli_trm_measures = 0

        self._do_lanczos = do_lanczos
        self._lanczos_gap = lanczos_gap

        qc_ref = qf.Computer(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()
def trotterize(operator, factor=1.0, trotter_number=1, trotter_order=1):
    """
    returns a circuit equivalent to an exponentiated QubitOperator

    :param operator: (QubitOperator) the operator or state preparation ansatz
    (represented as a sum of pauli terms) to be exponentiated

    :param trotter_number: (int) for an operator A with terms A_i, the trotter_number
    is the exponent (N) for to product of single term
    exponentals e^A ~ ( Product_i(e^(A_i/N)) )^N

    :param trotter_number: (int) the order of the trotterization approximation, can be 1 or 2
    """

    total_phase = 1.0
    trotterized_operator = qforte.Circuit()

    if (trotter_number == 1) and (trotter_order == 1):
        #loop over terms in operator
        for term in operator.terms():
            term_generator, phase = qforte.exponentiate_pauli_string(
                factor * term[0], term[1])
            for gate in term_generator.gates():
                trotterized_operator.add(gate)
            total_phase *= phase

    else:
        if (trotter_order > 1):
            raise NotImplementedError(
                "Higher order trotterization is not yet implemented.")

        ho_op = qforte.QubitOperator()

        for k in range(1, trotter_number + 1):
            for term in operator.terms():
                ho_op.add(factor * term[0] / float(trotter_number), term[1])

        for trot_term in ho_op.terms():
            term_generator, phase = qforte.exponentiate_pauli_string(
                trot_term[0], trot_term[1])
            for gate in term_generator.gates():
                troterized_operator.add(gate)
            total_phase *= phase

    return (trotterized_operator, total_phase)
示例#6
0
def build_circuit(Inputstr):

    circ = qforte.Circuit()
    sepstr = Inputstr.split()  #Separate string to a list by space

    for i in range(len(sepstr)):
        inputgate = sepstr[i].split('_')
        if len(inputgate) == 2:
            circ.add(
                qforte.gate(inputgate[0], int(inputgate[1]),
                            int(inputgate[1])))
        else:
            if 'R' in inputgate[0]:
                circ.add(
                    qforte.gate(inputgate[0], int(inputgate[1]),
                                int(inputgate[1]), float(inputgate[2])))
            else:
                circ.add(
                    qforte.gate(inputgate[0], int(inputgate[1]),
                                int(inputgate[2])))

    return circ
示例#7
0
    def build_expansion_pool(self):
        print('\n==> Building expansion pool <==')
        self._sig = qf.QubitOpPool()

        if(self._expansion_type == 'complete_qubit'):
            if (self._nqb > 6):
                raise ValueError('Using complete qubits expansion will result in a very large number of terms!')
            self._sig.fill_pool("complete_qubit", self._ref)

        elif(self._expansion_type == 'cqoy'):
            self._sig.fill_pool("cqoy", self._ref)

        elif(self._expansion_type in {'SD', 'GSD', 'SDT', 'SDTQ', 'SDTQP', 'SDTQPH'}):
            P = qf.SQOpPool()
            P.set_orb_spaces(self._ref)
            P.fill_pool(self._expansion_type)
            sig_temp = P.get_qubit_operator("commuting_grp_lex", False)

            # Filter the generated operators, so that only those with an odd number of Y gates are allowed.
            # See section "Real Hamiltonians and states" in the SI of Motta for theoretical justification.
            # Briefly, this method solves Ax=b, but all b elements with an odd number of Y gates are imaginary and
            # thus vanish. This method will not be correct for non-real Hamiltonians or states.
            for _, rho in sig_temp.terms():
                nygates = 0
                temp_rho = qf.Circuit()
                for gate in rho.gates():
                    temp_rho.add(qf.gate(gate.gate_id(), gate.target(), gate.control()))
                    if (gate.gate_id() == "Y"):
                        nygates += 1

                if (nygates % 2 == 1):
                    rho_op = qf.QubitOperator()
                    rho_op.add(1.0, temp_rho)
                    self._sig.add(1.0, rho_op)

        else:
            raise ValueError('Invalid expansion type specified.')

        self._NI = len(self._sig.terms())
示例#8
0
    def get_dynamics_circ(self):
        """Generates controlled unitaries. Ancilla qubit n controls a Trotter
        approximation to exp(-iHt*2^n).

        Returns
        -------
        U : Circuit
            A circuit approximating controlled application of e^-iHt.
        """
        U = qforte.Circuit()
        ancilla_idx = self._abegin

        temp_op = qforte.QubitOperator()
        scalar_terms = []
        for scalar, operator in self._qb_ham.terms():
            phase = -1.0j * scalar * self._t
            if operator.size() == 0:
                scalar_terms.append(scalar * self._t)
            else:
                # Strangely, the code seems to work even if this line is outside the else clause.
                # TODO: Figure out how.
                temp_op.add(phase, operator)

        for n in range(self._n_ancilla):
            tn = 2 ** n
            expn_op, _ = trotterize_w_cRz(temp_op, ancilla_idx,
                                               trotter_number=self._trotter_number)

            # Rotation for the scalar Hamiltonian term
            U.add(qforte.gate('R', ancilla_idx, ancilla_idx, -1.0 * np.sum(scalar_terms) * float(tn)))

            for i in range(tn):
                U.add_circuit(expn_op)

            ancilla_idx += 1

        return U
示例#9
0
    def get_qft_circuit(self, direct):
        """Generates a circuit for Quantum Fourier Transformation with no swapping
        of bit positions.

            Arguments
            ---------

            self._abegin : int
                The index of the begin qubit.

            self._aend : int
                The index of the end qubit.

            direct : string
                The direction of the Fourier Transform can be 'forward' or 'reverse.'

            Returns
            -------

            qft_circ : Circuit
                A circuit representing the Quantum Fourier Transform.
        """

        qft_circ = qforte.Circuit()
        lens = self._aend - self._abegin + 1
        for j in range(lens):
            qft_circ.add(qforte.gate('H', j+self._abegin))
            for k in range(2, lens+1-j):
                phase = 2.0*np.pi/(2**k)
                qft_circ.add(qforte.gate('cR', j+self._abegin, j+k-1+self._abegin, phase))

        if direct == 'forward':
            return qft_circ
        elif direct == 'reverse':
            return qft_circ.adjoint()
        else:
            raise ValueError('QFT directions can only be "forward" or "reverse"')
示例#10
0
def qft_circuit(na, nb, direct):
    """
    generates a circuit for Quantum Fourier Transformation

    :param na: (int) the begin qubit
    :param nb: (int) the end qubit

    :param direct: (string) the direction of the Fourier Transform
    can be 'forward' or 'reverse'
    """

    # Build qft circuit
    qft_circ = qforte.Circuit()
    lens = nb - na + 1
    for j in range(lens):
        qft_circ.add(qforte.gate('H', j + na, j + na))
        for k in range(2, lens + 1 - j):
            phase = 2.0 * numpy.pi / (2**k)
            qft_circ.add(qforte.gate('cR', j + na, j + k - 1 + na, phase))

    # Build reversing circuit
    if lens % 2 == 0:
        for i in range(int(lens / 2)):
            qft_circ.add(qforte.gate('SWAP', i + na, lens - 1 - i + na))
    else:
        for i in range(int((lens - 1) / 2)):
            qft_circ.add(qforte.gate('SWAP', i + na, lens - 1 - i + na))

    if direct == 'forward':
        return qft_circ
    elif direct == 'reverse':
        return qft_circ.adjoint()
    else:
        raise ValueError('QFT directions can only be "forward" or "reverse"')

    return qft_circ
def Toffoli(i, j, k):
    """
    builds a circuit to simulate a three-qubit Toffoli gate
    (Control-Control-NOT, CCNOT gate).

    :param i: control qubit 1
    :param j: control qubit 2
    :param k: target qubit
    """

    T1 = qforte.gate('T', i, i)
    T2 = qforte.gate('T', j, j)
    T3 = qforte.gate('T', k, k)
    C12 = qforte.gate('cX', j, i)
    C13 = qforte.gate('cX', k, i)
    C23 = qforte.gate('cX', k, j)
    H3 = qforte.gate('H', k, k)

    T_circ = qforte.Circuit()
    T_circ.add(H3)
    T_circ.add(C23)
    T_circ.add(T3.adjoint())
    T_circ.add(C13)
    T_circ.add(T3)
    T_circ.add(C23)
    T_circ.add(T3.adjoint())
    T_circ.add(C13)
    T_circ.add(T2)
    T_circ.add(T3)
    T_circ.add(C12)
    T_circ.add(H3)
    T_circ.add(T1)
    T_circ.add(T2.adjoint())
    T_circ.add(C12)

    return T_circ
示例#12
0
    def get_z_circuit(self):
        """Generates a circuit of Z gates for each quibit in the ancilla register.

            Arguments
            ---------

            self._abegin : int
                The index of the begin qubit.

            self._aend : int
                The index of the end qubit.

            Returns
            -------

            z_circ : Circuit
                A circuit representing the the Z gates to be measured.
        """

        Z_circ = qforte.Circuit()
        for j in range(self._abegin, self._aend + 1):
            Z_circ.add(qforte.gate('Z', j))

        return Z_circ
示例#13
0
    def get_Uhad(self):
        """Generates a circuit which to puts all of the ancilla regester in
        superpostion.

            Arguments
            ---------

            self._abegin : int
                The index of the begin qubit.

            self._aend : int
                The index of the end qubit.

            Returns
            -------

            qft_circ : Circuit
                A circuit of consecutive Hadamard gates.
        """
        Uhad = qforte.Circuit()
        for j in range(self._abegin, self._aend + 1):
            Uhad.add(qforte.gate('H', j))

        return Uhad
示例#14
0
def organizer_to_circuit(op_organizer):
    """Builds a Circuit from a operator orgainizer.

    Parameters
    ----------
    op_organizer : list
        An object to organize what the coefficient and Pauli operators in terms
        of the QubitOperator will be.

        The orginzer is of the form
        [[coeff_a, [ ("X", i), ("Z", j),  ("Y", k), ...  ] ], [...] ...]
        where X, Y, Z are strings that indicate Pauli opterators;
        i, j, k index qubits and coeff_a indicates the coefficient for the ath
        term in the QubitOperator.
    """
    operator = qforte.QubitOperator()
    for coeff, word in op_organizer:
        circ = qforte.Circuit()
        for letter in word:
            circ.add(qforte.gate(letter[0], letter[1], letter[1]))

        operator.add(coeff, circ)

    return operator
示例#15
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
示例#16
0
    def build_qk_mats(self):
        """Returns matrices S and H needed for the QK 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.Circuit()
            Um.add(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(expn_op1)

            # Compute U_m |φ>
            QC = qforte.Computer(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
示例#17
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
示例#18
0
    def matrix_element(self, m, n, use_op=False):
        """Returns a single matrix element M_mn based on the evolution of
        two unitary operators Um = exp(-i * m * dt * H) and Un = exp(-i * n * dt * H)
        on a reference state |Phi_o>, (optionally) with respect to an operator A.
        Specifically, M_mn is given by <Phi_o| Um^dag Un | Phi_o> or
        (optionally if A is specified) <Phi_o| Um^dag A Un | Phi_o>.

        Arguments
        ---------

        m : int
            The number of time steps for the Um evolution.

        n : int
            The number of time steps for the Un evolution.

        Returns
        -------
        value : complex
            The outcome of measuring <X> and <Y> to determine <2*sigma_+>,
            ultimately the value of the matrix elemet.

        """
        value = 0.0
        ancilla_idx = self._nqb
        Uk = qforte.Circuit()
        temp_op1 = qforte.QubitOperator()
        # TODO (opt): move to C side.
        for t in self._qb_ham.terms():
            c, op = t
            phase = -1.0j * n * self._dt * c
            temp_op1.add(phase, op)

        expn_op1, phase1 = trotterize_w_cRz(
            temp_op1, ancilla_idx, trotter_number=self._trotter_number)

        for gate in expn_op1.gates():
            Uk.add(gate)

        Ub = qforte.Circuit()

        temp_op2 = qforte.QubitOperator()
        for t in self._qb_ham.terms():
            c, op = t
            phase = -1.0j * m * self._dt * c
            temp_op2.add(phase, op)

        expn_op2, phase2 = trotterize_w_cRz(
            temp_op2,
            ancilla_idx,
            trotter_number=self._trotter_number,
            Use_open_cRz=False)

        for gate in expn_op2.gates():
            Ub.add(gate)

        if not use_op:
            # TODO (opt): use Uprep
            cir = qforte.Circuit()
            for j in range(self._nqb):
                if self._ref[j] == 1:
                    cir.add(qforte.gate('X', j, j))

            cir.add(qforte.gate('H', ancilla_idx, ancilla_idx))

            cir.add(Uk)

            cir.add(qforte.gate('X', ancilla_idx, ancilla_idx))
            cir.add(Ub)
            cir.add(qforte.gate('X', ancilla_idx, ancilla_idx))

            X_op = qforte.QubitOperator()
            x_circ = qforte.Circuit()
            Y_op = qforte.QubitOperator()
            y_circ = qforte.Circuit()

            x_circ.add(qforte.gate('X', ancilla_idx, ancilla_idx))
            y_circ.add(qforte.gate('Y', ancilla_idx, ancilla_idx))

            X_op.add(1.0, x_circ)
            Y_op.add(1.0, y_circ)

            X_exp = qforte.Experiment(self._nqb + 1, cir, X_op, 100)
            Y_exp = qforte.Experiment(self._nqb + 1, cir, Y_op, 100)

            params = [1.0]
            x_value = X_exp.perfect_experimental_avg(params)
            y_value = Y_exp.perfect_experimental_avg(params)

            value = (x_value + 1.0j * y_value) * phase1 * np.conj(phase2)

        else:
            value = 0.0
            for t in self._qb_ham.terms():
                c, V_l = t

                # TODO (opt):
                cV_l = qforte.Circuit()
                for gate in V_l.gates():
                    gate_str = gate.gate_id()
                    target = gate.target()
                    control_gate_str = 'c' + gate_str
                    cV_l.add(qforte.gate(control_gate_str, target,
                                         ancilla_idx))

                cir = qforte.Circuit()
                # TODO (opt): use Uprep
                for j in range(self._nqb):
                    if self._ref[j] == 1:
                        cir.add(qforte.gate('X', j, j))

                cir.add(qforte.gate('H', ancilla_idx, ancilla_idx))

                cir.add(Uk)
                cir.add(cV_l)

                cir.add(qforte.gate('X', ancilla_idx, ancilla_idx))
                cir.add(Ub)
                cir.add(qforte.gate('X', ancilla_idx, ancilla_idx))

                X_op = qforte.QubitOperator()
                x_circ = qforte.Circuit()
                Y_op = qforte.QubitOperator()
                y_circ = qforte.Circuit()

                x_circ.add(qforte.gate('X', ancilla_idx, ancilla_idx))
                y_circ.add(qforte.gate('Y', ancilla_idx, ancilla_idx))

                X_op.add(1.0, x_circ)
                Y_op.add(1.0, y_circ)

                X_exp = qforte.Experiment(self._nqb + 1, cir, X_op, 100)
                Y_exp = qforte.Experiment(self._nqb + 1, cir, Y_op, 100)

                # TODO (cleanup): Remove params as required arg (Nick)
                params = [1.0]
                x_value = X_exp.perfect_experimental_avg(params)
                y_value = Y_exp.perfect_experimental_avg(params)

                element = (x_value + 1.0j * y_value) * phase1 * np.conj(phase2)
                value += c * element

        return value
示例#19
0
def trotterize_w_cRz(operator,
                     ancilla_qubit_idx,
                     factor=1.0,
                     Use_open_cRz=False,
                     trotter_number=1,
                     trotter_order=1):
    """
    Returns a circuit equivalent to an exponentiated QubitOperator in which each term
    in the trotterization exp(-i * theta_k ) only acts on the register if the ancilla
    qubit is in the |1> state.

    :param operator: (QubitOperator) the operator or state preparation ansatz
    (represented as a sum of pauli terms) to be exponentiated

    :param ancilla_qubit_idx: (int) the index of the ancilla qubit

    :param Use_open_cRz: (bool) uses an open controlled Rz gate in exponentiation
    (see Fig. 11 on page 185 of Nielson and Chung's
    "Quantum Computation and Quantum Informatoin 10th Aniversary Ed.")

    :param trotter_number: (int) for an operator A with terms A_i, the trotter_number
    is the exponent (N) for to product of single term
    exponentals e^A ~ ( Product_i(e^(A_i/N)) )^N

    :param trotter_order: (int) the order of the trotterization approximation, can be 1 or 2
    """

    total_phase = 1.0
    trotterized_operator = qforte.Circuit()

    if (trotter_number == 1) and (trotter_order == 1):
        for term in operator.terms():
            term_generator, phase = qforte.exponentiate_pauli_string(
                factor * term[0],
                term[1],
                Use_cRz=True,
                ancilla_idx=ancilla_qubit_idx,
                Use_open_cRz=Use_open_cRz)
            for gate in term_generator.gates():
                trotterized_operator.add(gate)
            total_phase *= phase

    else:
        if (trotter_order > 1):
            raise NotImplementedError(
                "Higher order trotterization is not yet implemented.")
        ho_op = qforte.QubitOperator()
        for k in range(1, trotter_number + 1):
            k = float(k)
            for term in operator.terms():
                ho_op.add(factor * term[0] / float(trotter_number), term[1])

        for trot_term in ho_op.terms():
            term_generator, phase = qforte.exponentiate_pauli_string(
                trot_term[0],
                trot_term[1],
                Use_cRz=True,
                ancilla_idx=ancilla_qubit_idx,
                Use_open_cRz=Use_open_cRz)
            for gate in term_generator.gates():
                trotterized_operator.add(gate)
            total_phase *= phase

    return (trotterized_operator, total_phase)
示例#20
0
    def run(self,
            guess_energy: float,
            t = 1.0,
            nruns = 20,
            success_prob = 0.5,
            num_precise_bits = 4):
        """
        guess_energy : A guess for the eigenvalue of the eigenspace with which |0>^(n)
            has greatest overlap. You should be confident the ground state is within
        t : A scaling parameter that controls the precision of the computation. You should
            confident that the eigenvalue of interest is within +/- t of the guess energy.
        """

        # float: evolution times
        self._t = t
        # int: number of times to sample the eigenvalue distribution
        self._nruns = nruns
        self._success_prob = success_prob
        self._num_precise_bits = num_precise_bits
        self._Uqpe = qforte.Circuit()
        # int: The number of qubits needed to represent the state.
        self._n_state_qubits = self._nqb
        eps = 1 - success_prob
        # int: The number of ancilla qubits used to hold eigenvalue information
        self._n_ancilla = num_precise_bits + int(np.log2(2 + (1.0/eps)))
        # int: The total number of qubits needed in the circuit
        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

        self._guess_energy = guess_energy
        self._guess_periods = round(self._t * guess_energy / (-2 * np.pi))

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

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

        # Apply Hadamard gates to all ancilla qubits
        self._Uqpe.add(self.get_Uhad())

        # Prepare the trial state on the non-ancilla qubits
        self._Uqpe.add(self._Uprep)

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

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

        computer = qforte.Computer(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 = sum(z / (2**i) for i, z in enumerate(readout, start=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 = sum(readout[i] for readout in z_readouts) / nruns
            final_readout_aves.append(iave)
            final_readout.append(1 if iave > 0.5 else 0)

        self._final_phase = sum(z / (2**i) for i, z in enumerate(final_readout, start=1))

        E_u = -2 * np.pi * (self._final_phase + self._guess_periods - 1) / t
        E_l = -2 * np.pi * (self._final_phase + self._guess_periods - 0) / t
        E_qpe = E_l if abs(E_l - guess_energy) < abs(E_u - guess_energy) else E_u

        res = stats.mode(np.asarray(self._phases))
        self._mode_phase = res.mode[0]
        E_u = -2 * np.pi * (self._mode_phase + self._guess_periods - 1) / t
        E_l = -2 * np.pi * (self._mode_phase + self._guess_periods - 0) / t
        self._mode_energy = E_l if abs(E_l - guess_energy) < abs(E_u - guess_energy) else E_u

        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 = E_qpe

        # 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()
示例#21
0
def exponentiate_pauli_string(coefficient,
                              term,
                              Use_cRz=False,
                              ancilla_idx=None,
                              Use_open_cRz=False):
    """
    returns the exponential of an string of Pauli operators multiplied by an imaginary coefficient

        exp(coefficient * term)

    Parameters
    ----------
    :param coefficient: complex
        an imaginary coefficient that multiplies the Pauli string
    :param term: Circuit
        a Pauli string to be exponentiated
    """
    # This function assumes that the factor is imaginary. The following tests for it.
    if np.abs(np.real(coefficient)) > 1.0e-14:
        raise ValueError(
            f'exponentiate_pauli_string() called with a real coefficient {coefficient}'
        )

    # If the Pauli string has no terms this is just a phase factor times the identity circuit
    if term.size() == 0:
        return (qforte.Circuit(), np.exp(coefficient))

    exponential = qforte.Circuit()
    to_z = qforte.Circuit()
    to_original = qforte.Circuit()
    cX_circ = qforte.Circuit()

    prev_target = None
    max_target = None

    for gate in term.gates():
        id = gate.gate_id()
        target = gate.target()
        control = gate.control()

        if (id == 'X'):
            to_z.add(qforte.gate('H', target, control))
            to_original.add(qforte.gate('H', target, control))
        elif (id == 'Y'):
            to_z.add(qforte.gate('Rzy', target, control))
            to_original.add(qforte.gate('Rzy', target, control))
        elif (id == 'I'):
            continue

        if (prev_target is not None):
            cX_circ.add(qforte.gate('cX', target, prev_target))

        prev_target = target
        max_target = target

    # Gate that actually contains the parameterization for the term
    # TODO(Nick): investigate real/imaginary usage of 'factor' in below expression

    if (Use_cRz):
        z_rot = qforte.gate('cRz', max_target, ancilla_idx,
                            -2.0 * np.imag(coefficient))
    else:
        z_rot = qforte.gate('Rz', max_target, max_target,
                            -2.0 * np.imag(coefficient))

    # Assemble the actual exponential
    exponential.add(to_z)
    exponential.add(cX_circ)

    if (Use_open_cRz):
        exponential.add(qforte.gate('X', ancilla_idx, ancilla_idx))

    exponential.add(z_rot)

    if (Use_open_cRz):
        exponential.add(qforte.gate('X', ancilla_idx, ancilla_idx))

    adj_cX_circ = cX_circ.adjoint()
    exponential.add(adj_cX_circ)
    adj_to_z = to_z.adjoint()
    exponential.add(adj_to_z)

    return (exponential, 1.0)