Exemple #1
0
    def get_ansatz_circuit(self, param_list, provide_op_list=None):
        """ Return a qforte.QuantumCircuit object parametrized by input param_list
        
        Parameters
        ----------
        param_list: list
            list of parameters [param_1,..., param_n]
            to prepare the circuit 'exp(param_n*A_n)...exp(param_1*A_1)'
        
        Returns
        -------
        param_circuit: instance of qforte.QuantumCircuit class
            the circuit to be applied on the reference state to get wavefunction ansatz. 
        
        """
        if provide_op_list == None:
            op_list = self.ansatz_ops.copy()
        else:
            op_list = provide_op_list

        param_circuit = qforte.QuantumCircuit()
        for i in range(len(param_list)):
            param_i = param_list[i]
            op = op_list[i]
            # exp_op is a circuit object
            exp_op = qforte.QuantumCircuit()

            for coeff, term in op.terms():
                factor = coeff * param_i
                #'exponentiate_single_term' function returns a tuple (exponential(Cir), 1.0)
                exp_term = exponentiate_single_term(factor, term)[0]
                exp_op.add_circuit(exp_term)

            param_circuit.add_circuit(exp_op)
        return param_circuit
Exemple #2
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)
Exemple #3
0
def build_from_openfermion(OF_qubitops, time_evo_factor = 1.0):

    """
    builds a QuantumOperator instance in
    qforte from a openfermion QubitOperator instance

    :param OF_qubitops: (QubitOperator) the QubitOperator instance from openfermion.
    """

    qforte_ops = qforte.QuantumOperator()

    #for term, coeff in sorted(OF_qubitops.terms.items()):
    for term, coeff in OF_qubitops.terms.items():
        circ_term = qforte.QuantumCircuit()
        #Exclude zero terms
        if np.isclose(coeff, 0.0):
            continue
        for factor in term:
            index, action = factor
            # Read the string name for actions(gates)
            action_string = OF_qubitops.action_strings[OF_qubitops.actions.index(action)]

            #Make qforte gates and add to circuit
            gate_this = qforte.gate(action_string, index, index)
            circ_term.add_gate(gate_this)

        #Add this term to operator
        qforte_ops.add_term(coeff*time_evo_factor, circ_term)

    return qforte_ops
Exemple #4
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
Exemple #5
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()
Exemple #6
0
def build_circuit(Inputstr):

    circ = qforte.QuantumCircuit()
    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_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1])))
        else:
            if 'R' in inputgate[0]:
                circ.add_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[1]), float(inputgate[2])))
            else:
                circ.add_gate(qforte.gate(inputgate[0], int(inputgate[1]), int(inputgate[2])))

    return circ
Exemple #7
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
Exemple #8
0
    def build_expansion_pool(self):
        print('\n==> Building expansion pool <==')
        self._sig = qf.QuantumOpPool()

        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_quantum_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 alph, rho in sig_temp.terms():
                nygates = 0
                temp_rho = qf.QuantumCircuit()
                for gate in rho.gates():
                    temp_rho.add_gate(
                        qf.gate(gate.gate_id(), gate.target(), gate.control()))
                    if (gate.gate_id() == "Y"):
                        nygates += 1

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

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

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

        self._NI = len(self._sig.terms())
Exemple #9
0
def trotterize(operator, factor=1.0, trotter_number=1, trotter_order=1):

    """
    returns a circuit equivilant to an exponentiated QuantumOperator

    :param operator: (QuantumOperator) 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 troterization approximation, can be 1 or 2
    """

    total_phase = 1.0
    troterized_operator = qforte.QuantumCircuit()

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


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

        ho_op = qforte.QuantumOperator()

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

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

    return (troterized_operator, total_phase)
Exemple #10
0
    def get_qft_circuit(self, direct):
        """Generates a circuit for Quantum Fourier Transformation with no swaping
        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 : QuantumCircuit
                A circuit representing the Quantum Fourier Transform.
        """

        qft_circ = qforte.QuantumCircuit()
        lens = self._aend - self._abegin + 1
        for j in range(lens):
            qft_circ.add_gate(qforte.gate('H', j+self._abegin, j+self._abegin))
            for k in range(2, lens+1-j):
                phase = 2.0*np.pi/(2**k)
                qft_circ.add_gate(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"')

        return qft_circ
Exemple #11
0
def organizer_to_circuit(op_organizer):
    """Builds a QuantumCircuit from a operator orgainizer.

    Parameters
    ----------
    op_organizer : list
        An object to organize what the coefficient and Pauli operators in terms
        of the QuantumOperator 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 QuantumOperator.
    """
    operator = qforte.QuantumOperator()
    for coeff, word in op_organizer:
        circ = qforte.QuantumCircuit()
        for letter in word:
            circ.add_gate(qforte.gate(letter[0], letter[1], letter[1]))

        operator.add_term(coeff, circ)

    return operator
Exemple #12
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 : QuantumCircuit
                A circuit of consecutive Hadamard gates.
        """
        Uhad = qforte.QuantumCircuit()
        for j in range(self._abegin, self._aend + 1):
            Uhad.add_gate(qforte.gate('H', j, j))

        return Uhad
Exemple #13
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.QuantumCircuit()
    lens = nb - na + 1
    for j in range(lens):
        qft_circ.add_gate(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_gate(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_gate(qforte.gate('SWAP', i + na, lens - 1 - i + na))
    else:
        for i in range(int((lens - 1) / 2)):
            qft_circ.add_gate(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
Exemple #14
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 : QuantumCircuit
                A circuit representing the the Z gates to be measured.
        """

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

        return Z_circ
Exemple #15
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 equivilant to an exponentiated QuantumOperator 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: (QuantumOperator) 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 troterization approximation, can be 1 or 2
    """

    total_phase = 1.0
    troterized_operator = qforte.QuantumCircuit()

    if (trotter_number == 1) and (trotter_order == 1):
        #loop over terms in operator
        if(Use_open_cRz):
            for term in operator.terms():
                term_generator, phase = qforte.exponentiate_single_term(factor*term[0],term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=True)
                for gate in term_generator.gates():
                    troterized_operator.add_gate(gate)
                total_phase *= phase
        else:
            for term in operator.terms():
                term_generator, phase = qforte.exponentiate_single_term(factor*term[0],term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx)
                for gate in term_generator.gates():
                    troterized_operator.add_gate(gate)
                total_phase *= phase

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

        if(Use_open_cRz):
            for trot_term in ho_op.terms():
                term_generator, phase = qforte.exponentiate_single_term(trot_term[0],trot_term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx, Use_open_cRz=True)
                for gate in term_generator.gates():
                    troterized_operator.add_gate(gate)
                total_phase *= phase
        else:
            for trot_term in ho_op.terms():
                term_generator, phase = qforte.exponentiate_single_term(trot_term[0],trot_term[1], Use_cRz=True, ancilla_idx=ancilla_qubit_idx)
                for gate in term_generator.gates():
                    troterized_operator.add_gate(gate)
                total_phase *= phase

    return (troterized_operator, total_phase)
Exemple #16
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
            ---------

            ref : list
                The the reference state |Phi_o>.

            dt : float
                The real time step value (delta t).

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

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

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

            nqubits : int
                The number of qubits

            A : QuantumOperator
                The overal operator to measure with respect to (optional).

            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
            -------
            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.QuantumCircuit()
        temp_op1 = qforte.QuantumOperator()
        # 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_term(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(gate)

        Ub = qforte.QuantumCircuit()

        temp_op2 = qforte.QuantumOperator()
        for t in self._qb_ham.terms():
            c, op = t
            phase = -1.0j * m * self._dt * c
            temp_op2.add_term(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(gate)

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

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

            cir.add_circuit(Uk)

            cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx))
            cir.add_circuit(Ub)
            cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx))

            X_op = qforte.QuantumOperator()
            x_circ = qforte.QuantumCircuit()
            Y_op = qforte.QuantumOperator()
            y_circ = qforte.QuantumCircuit()

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

            X_op.add_term(1.0, x_circ)
            Y_op.add_term(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.QuantumCircuit()
                for gate in V_l.gates():
                    gate_str = gate.gate_id()
                    target = gate.target()
                    control_gate_str = 'c' + gate_str
                    cV_l.add_gate(
                        qforte.gate(control_gate_str, target, ancilla_idx))

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

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

                cir.add_circuit(Uk)
                cir.add_circuit(cV_l)

                cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx))
                cir.add_circuit(Ub)
                cir.add_gate(qforte.gate('X', ancilla_idx, ancilla_idx))

                X_op = qforte.QuantumOperator()
                x_circ = qforte.QuantumCircuit()
                Y_op = qforte.QuantumOperator()
                y_circ = qforte.QuantumCircuit()

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

                X_op.add_term(1.0, x_circ)
                Y_op.add_term(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
Exemple #17
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
Exemple #18
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()
Exemple #19
0
    def get_dynamics_circ(self):
        """Generates a circuit for controlled dynamics operations used in phase
        estimation. It approximates the exponentiated hermetina operator H as e^-iHt.

            Arguments
            ---------

            H : QuantumOperator
                The hermetian operaotr whos dynamics and eigenstates are of interest,
                ususally the Hamiltonian.

            trotter_num : int
                The trotter number (m) to use for the decompostion. Exponentiation
                is exact in the m --> infinity limit.

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

            n_ancilla : int
                The number of anciall qubit used for the phase estimation.
                Determintes the total number of time steps.

            t : float
                The total evolution time.

            Returns
            -------

            Udyn : QuantumCircuit
                A circuit approximating controlled application of e^-iHt.
        """
        Udyn = qforte.QuantumCircuit()
        ancilla_idx = self._abegin
        total_phase = 1.0
        for n in range(self._n_ancilla):
            tn = 2 ** n
            temp_op = qforte.QuantumOperator()
            scaler_terms = []
            for h in self._qb_ham.terms():
                c, op = h
                phase = -1.0j * self._t * c #* tn
                temp_op.add_term(phase, op)
                gates = op.gates()
                if op.size() == 0:
                    scaler_terms.append(c * self._t)


            expn_op, phase1 = trotterize_w_cRz(temp_op,
                                               ancilla_idx,
                                               trotter_number=self._trotter_number)

            # Rotation for the scaler Hamiltonian term
            Udyn.add_gate(qforte.gate('R', ancilla_idx, ancilla_idx,  -1.0 * np.sum(scaler_terms) * float(tn)))

            # NOTE: Approach uses 2^ancilla_idx blocks of the time evolution circuit
            for i in range(tn):
                for gate in expn_op.gates():
                    Udyn.add_gate(gate)

            ancilla_idx += 1

        return Udyn
Exemple #20
0
def exponentiate_single_term(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: float
        an imaginary coefficient that multiplies the Pauli string
    :param term: QuantumCircuit
        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-16:
        print("exp factor: ", coefficient)
        raise ValueError(
            'exponentiate_single_term() called with a real coefficient')

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

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

    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_gate(qforte.gate('H', target, control))
            to_original.add_gate(qforte.gate('H', target, control))
        elif (id == 'Y'):
            to_z.add_gate(qforte.gate('Rzy', target, control))
            to_original.add_gate(qforte.gate('Rzy', target, control))
        elif (id == 'I'):
            continue

        if (prev_target is not None):
            cX_circ.add_gate(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_circuit(to_z)
    exponential.add_circuit(cX_circ)

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

    exponential.add_gate(z_rot)

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

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

    return (exponential, 1.0)
Exemple #21
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