Example #1
0
def create_TFIM(n: int, h: float, J: float):
    """Creates a 1D Transverse Field Ising Model hamiltonian with
    open boundary conditions, i.e., no interaction between the
    first and last spin sites.

    n: int
        Number of lattice sites

    h: float
        Strength of magnetic field

    j: float
        Interaction strength 
    """

    TFIM = System()
    TFIM.hamiltonian = qf.QubitOperator()

    circuit = [(-h, f"Z_{i}") for i in range(n)]
    circuit += [(-J, f"X_{i} X_{i+1}") for i in range(n-1)]

    for coeff, op_str in circuit:
        TFIM.hamiltonian.add(coeff, qf.build_circuit(op_str))

    TFIM.hf_reference = [0] * n

    return TFIM
Example #2
0
def build_operator(Inputstr):

    ops = qforte.QubitOperator()
    sepstr = Inputstr.split(';')
    for i in range(len(sepstr)):
        inputterm = sepstr[i].split(',')
        ops.add(complex(inputterm[0]), qforte.build_circuit(inputterm[1]))

    return ops
Example #3
0
    def do_qite_step(self):

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

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

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


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

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

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

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

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

        self._n_cnot += eiA_kb.get_num_cnots()

        if(self._verbose):
            qf.smart_print(self._qc)
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)
Example #5
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())
Example #6
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
Example #7
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
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)
Example #9
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