def setUp(self): self.fermion_term = FermionOperator('1^ 2^ 3 4', -3.17) self.fermion_operator = self.fermion_term + hermitian_conjugated( self.fermion_term) self.boson_term = BosonOperator('1^ 2^ 3 4', -3.17) self.boson_operator = self.boson_term + hermitian_conjugated( self.boson_term) self.quad_term = QuadOperator('q0 p0 q1 p0 p0', -3.17) self.quad_operator = self.quad_term + hermitian_conjugated( self.quad_term) self.qubit_operator = jordan_wigner(self.fermion_operator)
def test_ccr_onsite(self): c1 = FermionOperator(((1, 1),)) a1 = hermitian_conjugated(c1) self.assertTrue(normal_ordered(c1 * a1) == FermionOperator(()) - normal_ordered(a1 * c1)) self.assertTrue(jordan_wigner(c1 * a1) == QubitOperator(()) - jordan_wigner(a1 * c1))
def number_nonconserving_fop(rank: int, norb: int) -> FermionOperator: # TODO: Complete docstring. """Returns a FermionOperator Hamiltonian which... Args: rank: norb: """ hamil = FermionOperator() if rank >= 0: hamil += FermionOperator("", 6.0) if rank >= 2: for i in range(0, 2 * norb, 2): for j in range(0, 2 * norb, 2): opstring = str(i) + " " + str(j + 1) hamil += FermionOperator( opstring, (i + 1 + j * 2) * 0.1 - (i + 1 + 2 * (j + 1)) * 0.1j, ) opstring = str(i) + "^ " + str(j + 1) + "^ " hamil += FermionOperator(opstring, (i + 1 + j) * 0.1 + (i + 1 + j) * 0.1j) opstring = str(i + 1) + " " + str(j) hamil += FermionOperator(opstring, (i + 1 + j) * 0.1 - (i + 1 + j) * 0.1j) opstring = str(i + 1) + "^ " + str(j) + "^ " hamil += FermionOperator( opstring, (i + 1 + j * 2) * 0.1 + (i + 1 + 2 * (j + 1)) * 0.1j, ) return (hamil + hermitian_conjugated(hamil)) / 2.0
def fermion_generator(self) -> 'openfermion.FermionOperator': """The FermionOperator G such that the gate's unitary is exp(-i t G) with exponent t using the Jordan-Wigner transformation.""" half_generator = sum( (w * G for w, G in zip(self.weights, self.fermion_generator_components()) ), ops.FermionOperator()) return half_generator + hermitian_conjugated(half_generator)
def test_jordan_wigner_interaction_op_with_zero_term(self): test_op = FermionOperator('1^ 2^ 3 4') test_op += hermitian_conjugated(test_op) interaction_op = get_interaction_operator(test_op) interaction_op.constant = 0.0 retransformed_test_op = reverse_jordan_wigner( jordan_wigner(interaction_op))
def get_dressed_operator(dress_operator, operator2dress): """ Return a QubitOperator modified as U^HU Args: dress_operator: U operator2dress: H """ new_operator = hermitian_conjugated(dress_operator) * operator2dress new_operator = new_operator * dress_operator return new_operator
def test_jordan_wigner_twobody_interaction_op_allunique(self): test_op = FermionOperator('1^ 2^ 3 4') test_op += hermitian_conjugated(test_op) retransformed_test_op = reverse_jordan_wigner( jordan_wigner(get_interaction_operator(test_op))) self.assertTrue( normal_ordered(retransformed_test_op) == normal_ordered(test_op))
def s_matrix(operators, state): l = len(operators) s = numpy.empty((l, l), dtype=numpy.complex128) for i in range(l): for j in range(i + 1): o = hermitian_conjugated(operators[i]) o *= operators[j] e = expectation_value(o, state) s[i, j] = e if i != j: s[j, i] = e.conjugate() return s
def test_uccsd_anti_hermitian(self): """Test operators are anti-Hermitian independent of inputs""" test_orbitals = 4 single_amplitudes = randn(*(test_orbitals, ) * 2) double_amplitudes = randn(*(test_orbitals, ) * 4) generator = uccsd_generator(single_amplitudes, double_amplitudes) conj_generator = hermitian_conjugated(generator) self.assertEqual(generator, -1. * conj_generator)
def test_jordan_wigner_one_body(self): # Make sure it agrees with jordan_wigner(FermionTerm). for p in range(self.n_qubits): for q in range(self.n_qubits): # Get test qubit operator. test_operator = jordan_wigner_one_body(p, q) # Get correct qubit operator. fermion_term = FermionOperator(((p, 1), (q, 0))) correct_op = jordan_wigner(fermion_term) hermitian_conjugate = hermitian_conjugated(fermion_term) if not fermion_term == hermitian_conjugate: correct_op += jordan_wigner(hermitian_conjugate) self.assertTrue(test_operator == correct_op)
def get_qubit_excitation_generator(qubits_1, qubits_2): assert len(qubits_2) == len(qubits_1) term_1 = QubitOperator('') for qubit_1, qubit_2 in zip(qubits_1, qubits_2): term_1 *= (QubitOperator('X{}'.format(qubit_1), 0.5) + QubitOperator('Y{}'.format(qubit_1), 0.5j)) term_1 *= (QubitOperator('X{}'.format(qubit_2), 0.5) - QubitOperator('Y{}'.format(qubit_2), 0.5j)) term_2 = hermitian_conjugated(term_1) return term_1 - term_2
def test_qubitop_to_qiskitpauli(): """ Conversion of QubitOperator; accuracy test """ hop_term = FermionOperator(((2, 1), (0, 0))) term = hop_term + hermitian_conjugated(hop_term) pauli_term = jordan_wigner(term) qiskit_op = qubitop_to_qiskitpauli(pauli_term) ground_truth = WeightedPauliOperator([[0.5, Pauli.from_label("XZX")], [0.5, Pauli.from_label("YZY")]]) assert ground_truth == qiskit_op
def test_qubitop_to_paulisum(): """ Conversion of QubitOperator; accuracy test """ hop_term = FermionOperator(((2, 1), (0, 0))) term = hop_term + hermitian_conjugated(hop_term) pauli_term = jordan_wigner(term) forest_term = qubitop_to_pyquilpauli(pauli_term) ground_truth = PauliTerm("X", 0) * PauliTerm("Z", 1) * PauliTerm("X", 2) ground_truth += PauliTerm("Y", 0) * PauliTerm("Z", 1) * PauliTerm("Y", 2) ground_truth *= 0.5 assert ground_truth == forest_term
def add_singles(occ_idx, vir_idx): for i in occ_idx: for a in vir_idx: single = FermionOperator(((a, 1), (i, 0))) # 1. build Fermion anti-Hermitian operator single -= hermitian_conjugated(single) # 2. JW transformation to qubit operator jw_single = jordan_wigner(single) h_single_commutator = commutator(self.h_qubit_OF, jw_single) # 3. qforte.build_from_openfermion(OF_qubitop) qf_jw_single = qforte.build_from_openfermion(jw_single) qf_commutator = qforte.build_from_openfermion(h_single_commutator) self.fermion_ops.append((i,a)) self.jw_ops.append(qf_jw_single) self.jw_commutators.append(qf_commutator)
def test_uccsd_singlet_anti_hermitian(self): """Test that the singlet version is anti-Hermitian""" test_orbitals = 8 test_electrons = 4 packed_amplitude_size = uccsd_singlet_paramsize( test_orbitals, test_electrons) packed_amplitudes = randn(int(packed_amplitude_size)) generator = uccsd_singlet_generator(packed_amplitudes, test_orbitals, test_electrons) conj_generator = hermitian_conjugated(generator) self.assertEqual(generator, -1. * conj_generator)
def add_doubles(occ_idx_pairs, vir_idx_pairs): for ji in occ_idx_pairs: for ba in vir_idx_pairs: j, i = ji b, a = ba double = FermionOperator(F'{a}^ {b}^ {i} {j}') double -= hermitian_conjugated(double) jw_double = jordan_wigner(double) h_double_commutator = commutator(self.h_qubit_OF, jw_double) qf_jw_double = qforte.build_from_openfermion(jw_double) qf_commutator = qforte.build_from_openfermion( h_double_commutator) self.fermion_ops.append((j,i,b,a)) self.jw_ops.append(qf_jw_double) self.jw_commutators.append(qf_commutator)
def conv_anti(hamiltonian_list): """ str型にしたフェルミ演算子から 反エルミートな演算子を作る """ op = 0 * QubitOperator("") for i in range(len(hamiltonian_list)): fop = FermionOperator(hamiltonian_list[i][1]) anti_fop = fop - hermitian_conjugated(fop) anti_tmp = str(jordan_wigner(anti_fop)).replace("]", "") anti_list = [ x.strip().split("[") for x in anti_tmp.split("+") # anti_tmp.strip()ではなくx.strip()では? #if not anti_tmp.strip() == ""] if not x.strip() == "" ] # 1以上ではない? if len(anti_list) > 1: for j in range(len(anti_list)): qop = QubitOperator(anti_list[j][1]) op += qop return op
def test_jordan_wigner_two_body(self): # Make sure it agrees with jordan_wigner(FermionTerm). for p in range(self.n_qubits): for q in range(self.n_qubits): for r in range(self.n_qubits): for s in range(self.n_qubits): # Get test qubit operator. test_operator = jordan_wigner_two_body(p, q, r, s) # Get correct qubit operator. fermion_term = FermionOperator( ((p, 1), (q, 1), (r, 0), (s, 0))) correct_op = jordan_wigner(fermion_term) hermitian_conjugate = hermitian_conjugated( fermion_term) if not fermion_term == hermitian_conjugate: if p == r and q == s: pass else: correct_op += jordan_wigner( hermitian_conjugate) self.assertTrue(test_operator == correct_op, str(test_operator - correct_op))
def setUp(self): self.fermion_term = FermionOperator('1^ 2^ 3 4', -3.17) self.fermion_operator = self.fermion_term + hermitian_conjugated( self.fermion_term) self.qubit_operator = jordan_wigner(self.fermion_operator)
def test_jordan_wigner_twobody_interaction_op_reversal_symmetric(self): test_op = FermionOperator('1^ 2^ 2 1') test_op += hermitian_conjugated(test_op) self.assertTrue( jordan_wigner(test_op) == jordan_wigner( get_interaction_operator(test_op)))
def _hubbard(parity, x_dimension, y_dimension, tunneling, local_interaction, coulomb, chemical_potential=0., magnetic_field=0., periodic=True, spinless=False, particle_hole_symmetry=False): r"""Returns a generic Hubbard-style Hamiltonian, to be used by the bose_hubbard and fermi_hubbard wrapper functions. See the corresponding bose_hubbard and fermi_hubbard for descriptions of the various arguments. Args: parity (int): parity=-1 returns the output as an instance of the FermionOperator class. Alternatively, parity=1 returns the output as an instance of the BosonOperator class Returns: hubbard_model: An instance of the FermionOperator or BosonOperator class. """ tunneling = float(tunneling) local_interaction = float(local_interaction) coulomb = float(coulomb) chemical_potential = float(chemical_potential) magnetic_field = float(magnetic_field) if parity == -1: Op = FermionOperator elif parity == 1: Op = BosonOperator # Initialize operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = n_sites if not spinless: n_spin_orbitals *= 2 hubbard_model = Op.zero() # Select particle-hole symmetry if particle_hole_symmetry: coulomb_shift = Op((), 0.5) else: coulomb_shift = Op.zero() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential to the spinless case. The magnetic field # doesn't contribute. if spinless and chemical_potential: hubbard_model += number_operator(n_spin_orbitals, site, -chemical_potential, parity=parity) # no Pauli-Exclusion principle, spinless particles interact on-site if parity == 1: int_op = number_operator( n_sites, site, local_interaction, parity=1) \ * (number_operator(n_sites, site, parity=1) - Op.identity()) hubbard_model += int_op # With spin, add the chemical potential and magnetic field terms. elif not spinless: hubbard_model += number_operator(n_spin_orbitals, up_index(site), -chemical_potential - magnetic_field, parity=parity) hubbard_model += number_operator(n_spin_orbitals, down_index(site), -chemical_potential + magnetic_field, parity=parity) # Add local pair interaction terms. operator_1 = number_operator( n_spin_orbitals, up_index(site), parity=parity) \ - coulomb_shift operator_2 = number_operator( n_spin_orbitals, down_index(site), parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Index coupled orbitals. right_neighbor = site + 1 bottom_neighbor = site + x_dimension # Account for periodic boundaries. if periodic: if (x_dimension > 2) and ((site + 1) % x_dimension == 0): right_neighbor -= x_dimension if (y_dimension > 2) and (site + x_dimension + 1 > n_sites): bottom_neighbor -= x_dimension * y_dimension # Add transition to neighbor on right. if (right_neighbor) % x_dimension or (periodic and x_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator( n_spin_orbitals, site, 1.0, parity=parity) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, right_neighbor, 1.0, parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(right_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) # Add transition to neighbor below. if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator( n_spin_orbitals, site, parity=parity) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, bottom_neighbor, parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) return hubbard_model
def fermi_hubbard(x_dimension, y_dimension, tunneling, coulomb, chemical_potential=0., magnetic_field=0., periodic=True, spinless=False, particle_hole_symmetry=False): """Return symbolic representation of a Fermi-Hubbard Hamiltonian. The idea of this model is that some fermions move around on a grid and the energy of the model depends on where the fermions are. The Hamiltonians of this model live on a grid of dimensions `x_dimension` x `y_dimension`. The grid can have periodic boundary conditions or not. In the standard Fermi-Hubbard model (which we call the "spinful" model), there is room for an "up" fermion and a "down" fermion at each site on the grid. In this model, there are a total of `2N` spin-orbitals, where `N = x_dimension * y_dimension` is the number of sites. In the spinless model, there is only one spin-orbital per site for a total of `N`. The Hamiltonian for the spinful model has the form .. math:: \\begin{align} H = &- t \sum_{\langle i,j \\rangle} \sum_{\sigma} (a^\dagger_{i, \sigma} a_{j, \sigma} + a^\dagger_{j, \sigma} a_{i, \sigma}) + U \sum_{i} a^\dagger_{i, \\uparrow} a_{i, \\uparrow} a^\dagger_{j, \downarrow} a_{j, \downarrow} \\\\ &- \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma} - h \sum_i (a^\dagger_{i, \\uparrow} a_{i, \\uparrow} - a^\dagger_{i, \downarrow} a_{i, \downarrow}) \\end{align} where - The indices :math:`\langle i, j \\rangle` run over pairs :math:`i` and :math:`j` of sites that are connected to each other in the grid - :math:`\sigma \in \\{\\uparrow, \downarrow\\}` is the spin - :math:`t` is the tunneling amplitude - :math:`U` is the Coulomb potential - :math:`\mu` is the chemical potential - :math:`h` is the magnetic field One can also construct the Hamiltonian for the spinless model, which has the form .. math:: H = - t \sum_{k=1}^{N-1} (a_k^\dagger a_{k + 1} + a_{k+1}^\dagger a_k) + U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1} - \mu \sum_{k=1}^N a_k^\dagger a_k. Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. tunneling (float): The tunneling amplitude :math:`t`. coulomb (float): The attractive local interaction strength :math:`U`. chemical_potential (float, optional): The chemical potential :math:`\mu` at each site. Default value is 0. magnetic_field (float, optional): The magnetic field :math:`h` at each site. Default value is 0. Ignored for the spinless case. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. spinless (bool, optional): If True, return a spinless Fermi-Hubbard model. Default is False. particle_hole_symmetry (bool, optional): If False, the repulsion term corresponds to: .. math:: U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1} If True, the repulsion term is replaced by: .. math:: U \sum_{k=1}^{N-1} (a_k^\dagger a_k - \\frac12) (a_{k+1}^\dagger a_{k+1} - \\frac12) which is unchanged under a particle-hole transformation. Default is False Returns: hubbard_model: An instance of the FermionOperator class. """ tunneling = float(tunneling) coulomb = float(coulomb) chemical_potential = float(chemical_potential) magnetic_field = float(magnetic_field) # Initialize fermion operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = n_sites if not spinless: n_spin_orbitals *= 2 hubbard_model = FermionOperator.zero() # Select particle-hole symmetry if particle_hole_symmetry: coulomb_shift = FermionOperator((), 0.5) else: coulomb_shift = FermionOperator.zero() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential to the spinless case. The magnetic field # doesn't contribute. if spinless and chemical_potential: hubbard_model += number_operator(n_spin_orbitals, site, -chemical_potential) # With spin, add the chemical potential and magnetic field terms. elif not spinless: hubbard_model += number_operator( n_spin_orbitals, up_index(site), -chemical_potential - magnetic_field) hubbard_model += number_operator( n_spin_orbitals, down_index(site), -chemical_potential + magnetic_field) # Add local pair interaction terms. operator_1 = number_operator(n_spin_orbitals, up_index(site)) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, down_index(site)) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Index coupled orbitals. right_neighbor = site + 1 bottom_neighbor = site + x_dimension # Account for periodic boundaries. if periodic: if (x_dimension > 2) and ((site + 1) % x_dimension == 0): right_neighbor -= x_dimension if (y_dimension > 2) and (site + x_dimension + 1 > n_sites): bottom_neighbor -= x_dimension * y_dimension # Add transition to neighbor on right. if (right_neighbor) % x_dimension or (periodic and x_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator(n_spin_orbitals, site, 1.0) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, right_neighbor, 1.0) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) # Add transition to neighbor below. if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator(n_spin_orbitals, site) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, bottom_neighbor) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) return hubbard_model
def apply_constraints(operator, n_fermions): """Function to use linear programming to apply constraints. Args: operator(FermionOperator): FermionOperator with only 1- and 2-body terms that we wish to vectorize. n_fermions(int): The number of particles in the simulation. Returns: modified_operator(FermionOperator): The operator with reduced norm that has been modified with equality constraints. """ # Get constraint matrix. n_orbitals = count_qubits(operator) constraints = constraint_matrix(n_orbitals, n_fermions) n_constraints, n_terms = constraints.get_shape() # Get vectorized operator. vectorized_operator = operator_to_vector(operator) initial_bound = numpy.sum(numpy.absolute(vectorized_operator[1::])) ** 2 print('Initial bound on measurements is %f.' % initial_bound) # Get linear programming coefficient vector. n_variables = n_constraints + n_terms lp_vector = numpy.zeros(n_variables, float) lp_vector[-n_terms:] = 1. # Get linear programming constraint matrix. lp_constraint_matrix = scipy.sparse.dok_matrix((2 * n_terms, n_variables)) for (i, j), value in constraints.items(): if j: lp_constraint_matrix[j, i] = value lp_constraint_matrix[n_terms + j, i] = -value for i in range(n_terms): lp_constraint_matrix[i, n_constraints + i] = -1. lp_constraint_matrix[n_terms + i, n_constraints + i] = -1. # Get linear programming constraint vector. lp_constraint_vector = numpy.zeros(2 * n_terms, float) lp_constraint_vector[:n_terms] = vectorized_operator lp_constraint_vector[-n_terms:] = -vectorized_operator # Perform linear programming. print('Starting linear programming.') options = {'maxiter': int(1e6)} bound = n_constraints * [(None, None)] + n_terms * [(0, None)] solution = scipy.optimize.linprog(c=lp_vector, A_ub=lp_constraint_matrix.toarray(), b_ub=lp_constraint_vector, bounds=bound, options=options) # Analyze results. print(solution['message']) assert solution['success'] solution_vector = solution['x'] objective = solution['fun'] ** 2 print('Program terminated after %i iterations.' % solution['nit']) # Alternative bound. residuals = solution_vector[-n_terms:] alternative_bound = numpy.sum(numpy.absolute(residuals[1::])) ** 2 print('Bound implied by solution vector is %f.' % alternative_bound) # Make sure residuals are positive. for residual in residuals: assert residual > -1e-6 # Get bound on updated Hamiltonian. weights = solution_vector[:n_constraints] final_vectorized_operator = (vectorized_operator - constraints.transpose() * weights) final_bound = numpy.sum( numpy.absolute(final_vectorized_operator[1::])) ** 2 print('Actual bound determined is %f.' % final_bound) # Return modified operator. modified_operator = vector_to_operator( final_vectorized_operator, n_orbitals) return (modified_operator + hermitian_conjugated(modified_operator)) / 2.
def mean_field_dwave(x_dimension, y_dimension, tunneling, sc_gap, chemical_potential=0., periodic=True): r"""Return symbolic representation of a BCS mean-field d-wave Hamiltonian. The Hamiltonians of this model live on a grid of dimensions `x_dimension` x `y_dimension`. The grid can have periodic boundary conditions or not. Each site on the grid can have an "up" fermion and a "down" fermion. Therefore, there are a total of `2N` spin-orbitals, where `N = x_dimension * y_dimension` is the number of sites. The Hamiltonian for this model has the form .. math:: \begin{align} H = &- t \sum_{\langle i,j \rangle} \sum_\sigma (a^\dagger_{i, \sigma} a_{j, \sigma} + a^\dagger_{j, \sigma} a_{i, \sigma}) - \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma} \\ &- \sum_{\langle i,j \rangle} \Delta_{ij} (a^\dagger_{i, \uparrow} a^\dagger_{j, \downarrow} - a^\dagger_{i, \downarrow} a^\dagger_{j, \uparrow} + a_{j, \downarrow} a_{i, \uparrow} - a_{j, \uparrow} a_{i, \downarrow}) \end{align} where - The indices :math:`\langle i, j \rangle` run over pairs :math:`i` and :math:`j` of sites that are connected to each other in the grid - :math:`\sigma \in \{\uparrow, \downarrow\}` is the spin - :math:`t` is the tunneling amplitude - :math:`\Delta_{ij}` is equal to :math:`+\Delta/2` for horizontal edges and :math:`-\Delta/2` for vertical edges, where :math:`\Delta` is the superconducting gap. - :math:`\mu` is the chemical potential Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. tunneling (float): The tunneling amplitude :math:`t`. sc_gap (float): The superconducting gap :math:`\Delta` chemical_potential (float, optional): The chemical potential :math:`\mu` at each site. Default value is 0. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. Returns: mean_field_dwave_model: An instance of the FermionOperator class. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = 2 * n_sites mean_field_dwave_model = FermionOperator() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential mean_field_dwave_model += number_operator(n_spin_orbitals, up_index(site), -chemical_potential) mean_field_dwave_model += number_operator(n_spin_orbitals, down_index(site), -chemical_potential) # Index coupled orbitals. right_neighbor = site + 1 bottom_neighbor = site + x_dimension # Account for periodic boundaries. if periodic: if (x_dimension > 2) and ((site + 1) % x_dimension == 0): right_neighbor -= x_dimension if (y_dimension > 2) and (site + x_dimension + 1 > n_sites): bottom_neighbor -= x_dimension * y_dimension # Add transition to neighbor on right if (site + 1) % x_dimension or (periodic and x_dimension > 2): # Add spin-up hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down_index(site), 1), (down_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up_index(site), 1), (down_index(right_neighbor), 1)) pairing_term = FermionOperator(operators, sc_gap / 2.) operators = ((down_index(site), 1), (up_index(right_neighbor), 1)) pairing_term += FermionOperator(operators, -sc_gap / 2.) mean_field_dwave_model -= pairing_term mean_field_dwave_model -= hermitian_conjugated(pairing_term) # Add transition to neighbor below. if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2): # Add spin-up hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up_index(site), 1), (down_index(bottom_neighbor), 1)) pairing_term = FermionOperator(operators, -sc_gap / 2.) operators = ((down_index(site), 1), (up_index(bottom_neighbor), 1)) pairing_term += FermionOperator(operators, sc_gap / 2.) mean_field_dwave_model -= pairing_term mean_field_dwave_model -= hermitian_conjugated(pairing_term) # Return. return mean_field_dwave_model
from openfermion.transforms import jordan_wigner from openfermion.ops import FermionOperator from openfermion.utils import hermitian_conjugated from forestopenfermion import qubitop_to_pyquilpauli, pyquilpauli_to_qubitop from pyquil.quil import Program from pyquil.gates import X from pyquil.paulis import exponentiate from pyquil.api import QVMConnection hubbard_hamiltonian = FermionOperator() spatial_orbitals = 4 for i in range(spatial_orbitals): electron_hop_alpha = FermionOperator(((2*i, 1), (2*((i+1) % spatial_orbitals), 0))) electron_hop_beta = FermionOperator(((2*i+1, 1), ((2 * ((i+1) % spatial_orbitals) + 1), 0))) hubbard_hamiltonian += -1 * (electron_hop_alpha + hermitian_conjugated(electron_hop_alpha)) hubbard_hamiltonian += -1 * (electron_hop_beta + hermitian_conjugated(electron_hop_beta)) hubbard_hamiltonian += FermionOperator(((2*i, 1), (2*i, 0), (2*i+1, 1), (2*i+1, 0)), 4.0) hubbard_term_generator = jordan_wigner(hubbard_hamiltonian) pyquil_hubbard_generator = qubitop_to_pyquilpauli(hubbard_term_generator) localized_electrons_program = Program() localized_electrons_program.inst([X(0), X(1)]) pyquil_program = Program() for term in pyquil_hubbard_generator.terms: pyquil_program += exponentiate(0.1*term) print (localized_electrons_program + pyquil_program) qvm = QVMConnection() print(qvm.run(pyquil_program, [0], 10)) wf = qvm.wavefunction(pyquil_program)
def test_ucc_h2_singlet(self): geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered( get_fermion_operator(self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian) # Test UCCSD for accuracy against FCI using loaded t amplitudes. ucc_operator = uccsd_generator(self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps) hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) uccsd_sparse = jordan_wigner_sparse(ucc_operator) uccsd_state = scipy.sparse.linalg.expm_multiply(uccsd_sparse, hf_state) expected_uccsd_energy = expectation(self.hamiltonian_matrix, uccsd_state) self.assertAlmostEqual(expected_uccsd_energy, self.molecule.fci_energy, places=4) print("UCCSD ENERGY: {}".format(expected_uccsd_energy)) # Test CCSD singlet for precise match against FCI using loaded t # amplitudes packed_amplitudes = uccsd_singlet_get_packed_amplitudes( self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps, self.molecule.n_qubits, self.molecule.n_electrons) ccsd_operator = uccsd_singlet_generator(packed_amplitudes, self.molecule.n_qubits, self.molecule.n_electrons, anti_hermitian=False) ccsd_sparse_r = jordan_wigner_sparse(ccsd_operator) ccsd_sparse_l = jordan_wigner_sparse( -hermitian_conjugated(ccsd_operator)) ccsd_state_r = scipy.sparse.linalg.expm_multiply( ccsd_sparse_r, hf_state) ccsd_state_l = scipy.sparse.linalg.expm_multiply( ccsd_sparse_l, hf_state) expected_ccsd_energy = ccsd_state_l.conjugate().dot( self.hamiltonian_matrix.dot(ccsd_state_r)) self.assertAlmostEqual(expected_ccsd_energy, self.molecule.fci_energy)