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