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 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).isclose( normal_ordered(test_op)))
def test_ccr_onsite(self): c1 = FermionOperator(((1, 1), )) a1 = hermitian_conjugated(c1) self.assertTrue( normal_ordered( c1 * a1).isclose(FermionOperator(()) - normal_ordered(a1 * c1))) self.assertTrue( jordan_wigner( c1 * a1).isclose(QubitOperator(()) - jordan_wigner(a1 * c1)))
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 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.isclose(hermitian_conjugate): correct_op += jordan_wigner(hermitian_conjugate) self.assertTrue(test_operator.isclose(correct_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.isclose(hermitian_conjugate): if p == r and q == s: pass else: correct_op += jordan_wigner( hermitian_conjugate) self.assertTrue(test_operator.isclose(correct_op), str(test_operator - correct_op))
def fermi_hubbard(x_dimension, y_dimension, tunneling, coulomb, chemical_potential=None, magnetic_field=None, periodic=True, spinless=False): """Return symbolic representation of a Fermi-Hubbard Hamiltonian. Args: x_dimension: An integer giving the number of sites in width. y_dimension: An integer giving the number of sites in height. tunneling: A float giving the tunneling amplitude. coulomb: A float giving the attractive local interaction strength. chemical_potential: An optional float giving the potential of each site. Default value is None. magnetic_field: An optional float giving a magnetic field at each site. Default value is None. periodic: If True, add periodic boundary conditions. spinless: An optional Boolean. If False, each site has spin up orbitals and spin down orbitals. If True, return a spinless Fermi-Hubbard model. verbose: An optional Boolean. If True, print all second quantized terms. Returns: hubbard_model: An instance of the FermionOperator class. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension if spinless: n_spin_orbitals = n_sites else: n_spin_orbitals = 2 * n_sites hubbard_model = FermionOperator((), 0.0) # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential and magnetic field terms. if chemical_potential and spinless: x_index = site % x_dimension y_index = (site - 1) // x_dimension sign = (-1.)**(x_index + y_index) coefficient = sign * chemical_potential hubbard_model += number_operator(n_spin_orbitals, site, coefficient) if chemical_potential and not spinless: coefficient = -1. * chemical_potential hubbard_model += number_operator(n_spin_orbitals, up(site), coefficient) hubbard_model += number_operator(n_spin_orbitals, down(site), coefficient) if magnetic_field and not spinless: coefficient = magnetic_field hubbard_model += number_operator(n_spin_orbitals, up(site), -coefficient) hubbard_model += number_operator(n_spin_orbitals, down(site), coefficient) # Add local pair interaction terms. if not spinless: operators = ((up(site), 1), (up(site), 0), (down(site), 1), (down(site), 0)) hubbard_model += FermionOperator(operators, coulomb) # 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): if spinless: # Add Coulomb term. operators = ((site, 1), (site, 0), (right_neighbor, 1), (right_neighbor, 0)) hubbard_model += FermionOperator(operators, coulomb) # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) else: # Add hopping term. operators = ((up(site), 1), (up(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down(site), 1), (down(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. operators = ((site, 1), (site, 0), (bottom_neighbor, 1), (bottom_neighbor, 0)) hubbard_model += FermionOperator(operators, coulomb) # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) else: # Add hopping term. operators = ((up(site), 1), (up(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down(site), 1), (down(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) # Return. return hubbard_model
def fermi_hubbard(x_dimension, y_dimension, tunneling, coulomb, chemical_potential=None, magnetic_field=None, 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 (a^\dagger_{i, \\uparrow} a_{i, \\uparrow} + a^\dagger_{i, \downarrow} a_{i, \downarrow}) - 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} + h \sum_{k=1}^N (-1)^k a_k^\dagger a_k - \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 None. magnetic_field (float, optional): The magnetic field :math:`h` at each site. Default value is None. 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. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension if spinless: n_spin_orbitals = n_sites else: n_spin_orbitals = 2 * n_sites hubbard_model = FermionOperator((), 0.0) # select particle-hole symmetry if particle_hole_symmetry: coulomb_shift = FermionOperator((), 0.5) else: coulomb_shift = FermionOperator((), 0.0) # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential and magnetic field terms. if chemical_potential and spinless: coefficient = -1. * chemical_potential hubbard_model += number_operator( n_spin_orbitals, site, coefficient) if magnetic_field and spinless: sign = (-1.) ** (site) coefficient = sign * magnetic_field hubbard_model += number_operator( n_spin_orbitals, site, coefficient) if chemical_potential and not spinless: coefficient = -1. * chemical_potential hubbard_model += number_operator( n_spin_orbitals, up_index(site), coefficient) hubbard_model += number_operator( n_spin_orbitals, down_index(site), coefficient) if magnetic_field and not spinless: coefficient = magnetic_field hubbard_model += number_operator( n_spin_orbitals, up_index(site), -coefficient) hubbard_model += number_operator( n_spin_orbitals, down_index(site), coefficient) # Add local pair interaction terms. if not spinless: operator_1 = number_operator( n_spin_orbitals, up_index(site), 1.0) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, down_index(site), 1.0) - 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 (site + 1) % 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)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) 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, 1.0) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, bottom_neighbor, 1.0) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) 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. return hubbard_model
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)
from openfermion.ops import FermionOperator, hermitian_conjugated from openfermion.transforms import jordan_wigner, bravyi_kitaev from openfermion.utils import eigenspectrum # Initialize an operator. fermion_operator = FermionOperator('2^ 0', 3.17) fermion_operator += hermitian_conjugated(fermion_operator) print(fermion_operator) # Transform to qubits under the Jordan-Wigner transformation and print its spectrum. jw_operator = jordan_wigner(fermion_operator) jw_spectrum = eigenspectrum(jw_operator) print(jw_operator) print(jw_spectrum) # Transform to qubits under the Bravyi-Kitaev transformation and print its spectrum. bk_operator = bravyi_kitaev(fermion_operator) bk_spectrum = eigenspectrum(bk_operator) print(bk_operator) print(bk_spectrum)
def mean_field_dwave(x_dimension, y_dimension, tunneling, sc_gap, chemical_potential=0., periodic=True): """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
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).isclose( jordan_wigner(get_interaction_operator(test_op))))
def meanfield_dwave(x_dimension, y_dimension, tunneling, sc_gap, periodic=True, verbose=False): """Return symbolic representation of a BCS mean-field d-wave Hamiltonian. Args: x_dimension: An integer giving the number of sites in width. y_dimension: An integer giving the number of sites in height. tunneling: A float giving the tunneling amplitude. sc_gap: A float giving the magnitude of the superconducting gap. periodic: If True, add periodic boundary conditions. verbose: An optional Boolean. If True, print all second quantized terms. Returns: meanfield_dwave_model: An instance of the FermionOperator class. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = 2 * n_sites meanfield_dwave_model = FermionOperator() # Loop through sites and add terms. for site in range(n_sites): # 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(site), 1.), (up(right_neighbor), 0.)) hopping_term = FermionOperator(operators, -tunneling) meanfield_dwave_model += hopping_term meanfield_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down(site), 1.), (down(right_neighbor), 0.)) hopping_term = FermionOperator(operators, -tunneling) meanfield_dwave_model += hopping_term meanfield_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up(site), 1.), (down(right_neighbor), 1.)) pairing_term = FermionOperator(operators, sc_gap / 2.) operators = ((down(site), 1.), (up(right_neighbor), 1.)) pairing_term += FermionOperator(operators, -sc_gap / 2.) meanfield_dwave_model -= pairing_term meanfield_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(site), 1.), (up(bottom_neighbor), 0.)) hopping_term = FermionOperator(operators, -tunneling) meanfield_dwave_model += hopping_term meanfield_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down(site), 1.), (down(bottom_neighbor), 0.)) hopping_term = FermionOperator(operators, -tunneling) meanfield_dwave_model += hopping_term meanfield_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up(site), 1.), (down(bottom_neighbor), 1.)) pairing_term = FermionOperator(operators, -sc_gap / 2.) operators = ((down(site), 1.), (up(bottom_neighbor), 1.)) pairing_term += FermionOperator(operators, sc_gap / 2.) meanfield_dwave_model -= pairing_term meanfield_dwave_model -= hermitian_conjugated(pairing_term) # Return. return meanfield_dwave_model
def apply_constraints(operator, n_fermions, use_scipy=True): """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. use_scipy(bool): Whether to use scipy (True) or cvxopt (False). 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.') if use_scipy: 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']) else: # Convert to CVXOpt sparse matrix. from cvxopt import matrix, solvers, spmatrix lp_vector = matrix(lp_vector) lp_constraint_matrix = lp_constraint_matrix.tocoo() lp_constraint_matrix = spmatrix(lp_constraint_matrix.data, lp_constraint_matrix.row.tolist(), lp_constraint_matrix.col.tolist()) lp_constraint_vector = matrix(lp_constraint_vector) # Run linear programming. solution = solvers.lp(c=lp_vector, G=lp_constraint_matrix, h=lp_constraint_vector, solver='glpk') # Analyze results. print(solution['status']) solution_vector = numpy.array(solution['x']).transpose()[0] # 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.