Exemplo n.º 1
0
 def test_hermitian(self, hbar):
     """Test output is hermitian"""
     H, _ = kerr(self.kappa, hbar=hbar)
     assert is_hermitian(H)
     assert is_hermitian(get_quad_operator(H))
Exemplo n.º 2
0
 def test_quad_operator_nonhermitian(self):
     op = QuadOperator('q0 p1 q1')
     self.assertFalse(is_hermitian(op))
Exemplo n.º 3
0
 def test_qubit_operator_identity(self):
     op = QubitOperator(())
     self.assertTrue(is_hermitian(op))
Exemplo n.º 4
0
 def test_boson_operator_zero(self):
     op = BosonOperator()
     self.assertTrue(is_hermitian(op))
Exemplo n.º 5
0
 def test_boson_operator_nonhermitian(self):
     op = BosonOperator('0^ 1 2^ 3')
     self.assertFalse(is_hermitian(op))
Exemplo n.º 6
0
def get_diagonal_coulomb_hamiltonian(fermion_operator,
                                     n_qubits=None,
                                     ignore_incompatible_terms=False):
    r"""Convert a FermionOperator to a DiagonalCoulombHamiltonian.

    Args:
        fermion_operator(FermionOperator): The operator to convert.
        n_qubits(int): Optionally specify the total number of qubits in the
            system
        ignore_incompatible_terms(bool): This flag determines the behavior
            of this method when it encounters terms that are not represented
            by the DiagonalCoulombHamiltonian class, namely, terms that are
            not quadratic and not quartic of the form
            a^\dagger_p a_p a^\dagger_q a_q. If set to True, this method will
            simply ignore those terms. If False, then this method will raise
            an error if it encounters such a term. The default setting is False.
    """
    if not isinstance(fermion_operator, FermionOperator):
        raise TypeError('Input must be a FermionOperator.')

    if n_qubits is None:
        n_qubits = count_qubits(fermion_operator)
    if n_qubits < count_qubits(fermion_operator):
        raise ValueError('Invalid number of qubits specified.')

    fermion_operator = normal_ordered(fermion_operator)
    constant = 0.
    one_body = numpy.zeros((n_qubits, n_qubits), complex)
    two_body = numpy.zeros((n_qubits, n_qubits), float)

    for term, coefficient in fermion_operator.terms.items():
        # Ignore this term if the coefficient is zero
        if abs(coefficient) < EQ_TOLERANCE:
            continue

        if len(term) == 0:
            constant = coefficient
        else:
            actions = [operator[1] for operator in term]
            if actions == [1, 0]:
                p, q = [operator[0] for operator in term]
                one_body[p, q] = coefficient
            elif actions == [1, 1, 0, 0]:
                p, q, r, s = [operator[0] for operator in term]
                if p == r and q == s:
                    if abs(numpy.imag(coefficient)) > EQ_TOLERANCE:
                        raise ValueError(
                            'FermionOperator does not map to '
                            'DiagonalCoulombHamiltonian (not Hermitian).')
                    coefficient = numpy.real(coefficient)
                    two_body[p, q] = -.5 * coefficient
                    two_body[q, p] = -.5 * coefficient
                elif not ignore_incompatible_terms:
                    raise ValueError('FermionOperator does not map to '
                                     'DiagonalCoulombHamiltonian '
                                     '(contains terms with indices '
                                     '{}).'.format((p, q, r, s)))
            elif not ignore_incompatible_terms:
                raise ValueError('FermionOperator does not map to '
                                 'DiagonalCoulombHamiltonian (contains terms '
                                 'with action {}.'.format(tuple(actions)))

    # Check that the operator is Hermitian
    if not is_hermitian(one_body):
        raise ValueError(
            'FermionOperator does not map to DiagonalCoulombHamiltonian '
            '(not Hermitian).')

    return DiagonalCoulombHamiltonian(one_body, two_body, constant)
Exemplo n.º 7
0
 def test_sparse_matrix_Y(self):
     term = QubitOperator(((0, 'Y'), ))
     sparse_operator = get_sparse_operator(term)
     self.assertEqual(list(sparse_operator.data), [1j, -1j])
     self.assertEqual(list(sparse_operator.indices), [1, 0])
     self.assertTrue(is_hermitian(sparse_operator))
 def test_sparse_matrix_and_numpy_array_zero(self):
     op = numpy.zeros((4, 4))
     self.assertTrue(is_hermitian(op))
     op = csc_matrix(op)
     self.assertTrue(is_hermitian(op))
 def test_sparse_matrix_and_numpy_array_identity(self):
     op = numpy.eye(4)
     self.assertTrue(is_hermitian(op))
     op = csc_matrix(op)
     self.assertTrue(is_hermitian(op))
Exemplo n.º 10
0
def get_quadratic_hamiltonian(fermion_operator,
                              chemical_potential=0.,
                              n_qubits=None):
    """Convert a quadratic fermionic operator to QuadraticHamiltonian.

    This function should only be called on fermionic operators which
    consist of only a_p^\dagger a_q, a_p^\dagger a_q^\dagger, and a_p a_q
    terms.

    Returns:
       quadratic_hamiltonian: An instance of the QuadraticHamiltonian class.

    Raises:
        TypeError: Input must be a FermionOperator.
        TypeError: FermionOperator does not map to QuadraticHamiltonian.

    Warning:
        Even assuming that each creation or annihilation operator appears
        at most a constant number of times in the original operator, the
        runtime of this method is exponential in the number of qubits.
    """
    if not isinstance(fermion_operator, FermionOperator):
        raise TypeError('Input must be a FermionOperator.')

    if n_qubits is None:
        n_qubits = count_qubits(fermion_operator)
    if n_qubits < count_qubits(fermion_operator):
        raise ValueError('Invalid number of qubits specified.')

    # Normal order the terms and initialize.
    fermion_operator = normal_ordered(fermion_operator)
    constant = 0.
    combined_hermitian_part = numpy.zeros((n_qubits, n_qubits), complex)
    antisymmetric_part = numpy.zeros((n_qubits, n_qubits), complex)

    # Loop through terms and assign to matrix.
    for term in fermion_operator.terms:
        coefficient = fermion_operator.terms[term]
        # Ignore this term if the coefficient is zero
        if abs(coefficient) < EQ_TOLERANCE:
            continue

        if len(term) == 0:
            # Constant term
            constant = coefficient
        elif len(term) == 2:
            ladder_type = [operator[1] for operator in term]
            p, q = [operator[0] for operator in term]

            if ladder_type == [1, 0]:
                combined_hermitian_part[p, q] = coefficient
            elif ladder_type == [1, 1]:
                # Need to check that the corresponding [0, 0] term is present
                conjugate_term = ((p, 0), (q, 0))
                if conjugate_term not in fermion_operator.terms:
                    raise QuadraticHamiltonianError(
                        'FermionOperator does not map '
                        'to QuadraticHamiltonian (not Hermitian).')
                else:
                    matching_coefficient = -fermion_operator.terms[
                        conjugate_term].conjugate()
                    discrepancy = abs(coefficient - matching_coefficient)
                    if discrepancy > EQ_TOLERANCE:
                        raise QuadraticHamiltonianError(
                            'FermionOperator does not map '
                            'to QuadraticHamiltonian (not Hermitian).')
                antisymmetric_part[p, q] += .5 * coefficient
                antisymmetric_part[q, p] -= .5 * coefficient
            else:
                # ladder_type == [0, 0]
                # Need to check that the corresponding [1, 1] term is present
                conjugate_term = ((p, 1), (q, 1))
                if conjugate_term not in fermion_operator.terms:
                    raise QuadraticHamiltonianError(
                        'FermionOperator does not map '
                        'to QuadraticHamiltonian (not Hermitian).')
                else:
                    matching_coefficient = -fermion_operator.terms[
                        conjugate_term].conjugate()
                    discrepancy = abs(coefficient - matching_coefficient)
                    if discrepancy > EQ_TOLERANCE:
                        raise QuadraticHamiltonianError(
                            'FermionOperator does not map '
                            'to QuadraticHamiltonian (not Hermitian).')
                antisymmetric_part[p, q] -= .5 * coefficient.conjugate()
                antisymmetric_part[q, p] += .5 * coefficient.conjugate()
        else:
            # Operator contains non-quadratic terms
            raise QuadraticHamiltonianError('FermionOperator does not map '
                                            'to QuadraticHamiltonian '
                                            '(contains non-quadratic terms).')

    # Compute Hermitian part
    hermitian_part = (combined_hermitian_part +
                      chemical_potential * numpy.eye(n_qubits))

    # Check that the operator is Hermitian
    if not is_hermitian(hermitian_part):
        raise QuadraticHamiltonianError(
            'FermionOperator does not map '
            'to QuadraticHamiltonian (not Hermitian).')

    # Form QuadraticHamiltonian and return.
    discrepancy = numpy.max(numpy.abs(antisymmetric_part))
    if discrepancy < EQ_TOLERANCE:
        # Hamiltonian conserves particle number
        quadratic_hamiltonian = QuadraticHamiltonian(
            hermitian_part,
            constant=constant,
            chemical_potential=chemical_potential)
    else:
        # Hamiltonian does not conserve particle number
        quadratic_hamiltonian = QuadraticHamiltonian(hermitian_part,
                                                     antisymmetric_part,
                                                     constant,
                                                     chemical_potential)

    return quadratic_hamiltonian
Exemplo n.º 11
0
 def test_qubit_operator_hermitian(self):
     op = QubitOperator('X0 Y2 Z5', 1. + 2.j)
     op += QubitOperator('X0 Y2 Z5', 1. - 2.j)
     self.assertTrue(is_hermitian(op))
Exemplo n.º 12
0
    def __init__(self, operator, t=1, mode='local', hbar=None):
        super().__init__([t, operator])

        try:
            # pylint: disable=protected-access
            self.hbar = _Engine._current_context.hbar
        except AttributeError:
            if hbar is None:
                raise ValueError(
                    "Either specify the hbar keyword argument, "
                    "or use this operator inside an engine context.")
            else:
                self.hbar = hbar

        if not is_hermitian(operator):
            raise ValueError("Hamiltonian must be Hermitian.")

        if mode == 'local':
            quad_operator = prune_unused_indices(operator)
        elif mode == 'global':
            quad_operator = operator

        if isinstance(quad_operator, BosonOperator):
            quad_operator = get_quad_operator(quad_operator, hbar=self.hbar)

        A, d = quadratic_coefficients(quad_operator)

        if mode == 'local':
            self.ns = A.shape[0] // 2
        elif mode == 'global':
            # pylint: disable=protected-access
            self.ns = _Engine._current_context.num_subsystems
            if A.shape[0] < 2 * self.ns:
                # expand the quadratic coefficient matrix to take
                # into account the extra modes
                A_n = A.shape[0] // 2
                tmp = np.zeros([2 * self.ns, 2 * self.ns])

                tmp[:A_n, :A_n] = A[:A_n, :A_n]
                tmp[:A_n, self.ns:self.ns + A_n] = A[:A_n, A_n:]
                tmp[self.ns:self.ns + A_n, :A_n] = A[A_n:, :A_n]
                tmp[self.ns:self.ns + A_n, self.ns:self.ns + A_n] = A[A_n:,
                                                                      A_n:]

                A = tmp

        self.S = expm(sympmat(self.ns) @ A * t)

        self.disp = False
        if not np.all(d == 0.):
            self.disp = True
            if np.all(A == 0.):
                self.d = d * t
            else:
                if np.linalg.cond(A) >= 1 / sys.float_info.epsilon:
                    # the matrix is singular, add a small epsilon
                    eps = 1e-9
                    epsI = eps * np.identity(2 * self.ns)
                    s = inv(A + epsI) @ d
                    tmp = (np.identity(2*self.ns) \
                        - expm(sympmat(self.ns) @ (A+epsI) * t).T) @ s / eps
                else:
                    s = inv(A) @ d
                    tmp = s - self.S.T @ s

                self.d = np.zeros([2 * self.ns])
                self.d[self.ns:] = tmp[:self.ns]
                self.d[:self.ns] = tmp[self.ns:]
Exemplo n.º 13
0
 def test_hermitian(self):
     """Test output is hermitian"""
     H, _ = zdisplacement(self.p)
     self.assertTrue(is_hermitian(H))
     self.assertTrue(is_hermitian(get_boson_operator(H, hbar=self.hbar)))
Exemplo n.º 14
0
 def test_hermitian(self, hbar):
     """Test output is hermitian"""
     H, _ = xdisplacement(self.x)
     assert is_hermitian(H)
     assert is_hermitian(get_boson_operator(H, hbar=hbar))
Exemplo n.º 15
0
 def test_hermitian(self):
     """Test output is hermitian"""
     self.logTestName()
     H, _ = xdisplacement(self.x)
     self.assertTrue(is_hermitian(H))
     self.assertTrue(is_hermitian(get_boson_operator(H, hbar=self.hbar)))
Exemplo n.º 16
0
 def test_sparse_matrix_and_numpy_array_nonhermitian(self):
     op = numpy.arange(16).reshape((4, 4))
     self.assertFalse(is_hermitian(op))
     op = csc_matrix(op)
     self.assertFalse(is_hermitian(op))
Exemplo n.º 17
0
def get_quadratic_hamiltonian(fermion_operator,
                              chemical_potential=0.,
                              n_qubits=None,
                              ignore_incompatible_terms=False):
    r"""Convert a quadratic fermionic operator to QuadraticHamiltonian.

    Args:
        fermion_operator(FermionOperator): The operator to convert.
        chemical_potential(float): A chemical potential to include in
            the returned operator
        n_qubits(int): Optionally specify the total number of qubits in the
            system
        ignore_incompatible_terms(bool): This flag determines the behavior
            of this method when it encounters terms that are not quadratic
            that is, terms that are not of the form a^\dagger_p a_q.
            If set to True, this method will simply ignore those terms.
            If False, then this method will raise an error if it encounters
            such a term. The default setting is False.

    Returns:
       quadratic_hamiltonian: An instance of the QuadraticHamiltonian class.

    Raises:
        TypeError: Input must be a FermionOperator.
        TypeError: FermionOperator does not map to QuadraticHamiltonian.

    Warning:
        Even assuming that each creation or annihilation operator appears
        at most a constant number of times in the original operator, the
        runtime of this method is exponential in the number of qubits.
    """
    if not isinstance(fermion_operator, FermionOperator):
        raise TypeError('Input must be a FermionOperator.')

    if n_qubits is None:
        n_qubits = count_qubits(fermion_operator)
    if n_qubits < count_qubits(fermion_operator):
        raise ValueError('Invalid number of qubits specified.')

    # Normal order the terms and initialize.
    fermion_operator = normal_ordered(fermion_operator)
    constant = 0.
    combined_hermitian_part = numpy.zeros((n_qubits, n_qubits), complex)
    antisymmetric_part = numpy.zeros((n_qubits, n_qubits), complex)

    # Loop through terms and assign to matrix.
    for term in fermion_operator.terms:
        coefficient = fermion_operator.terms[term]
        # Ignore this term if the coefficient is zero
        if abs(coefficient) < EQ_TOLERANCE:
            continue

        if len(term) == 0:
            # Constant term
            constant = coefficient
        elif len(term) == 2:
            ladder_type = [operator[1] for operator in term]
            p, q = [operator[0] for operator in term]

            if ladder_type == [1, 0]:
                combined_hermitian_part[p, q] = coefficient
            elif ladder_type == [1, 1]:
                # Need to check that the corresponding [0, 0] term is present
                conjugate_term = ((p, 0), (q, 0))
                if conjugate_term not in fermion_operator.terms:
                    raise QuadraticHamiltonianError(
                        'FermionOperator does not map '
                        'to QuadraticHamiltonian (not Hermitian).')
                else:
                    matching_coefficient = -fermion_operator.terms[
                        conjugate_term].conjugate()
                    discrepancy = abs(coefficient - matching_coefficient)
                    if discrepancy > EQ_TOLERANCE:
                        raise QuadraticHamiltonianError(
                            'FermionOperator does not map '
                            'to QuadraticHamiltonian (not Hermitian).')
                antisymmetric_part[p, q] += .5 * coefficient
                antisymmetric_part[q, p] -= .5 * coefficient
            else:
                # ladder_type == [0, 0]
                # Need to check that the corresponding [1, 1] term is present
                conjugate_term = ((p, 1), (q, 1))
                if conjugate_term not in fermion_operator.terms:
                    raise QuadraticHamiltonianError(
                        'FermionOperator does not map '
                        'to QuadraticHamiltonian (not Hermitian).')
                else:
                    matching_coefficient = -fermion_operator.terms[
                        conjugate_term].conjugate()
                    discrepancy = abs(coefficient - matching_coefficient)
                    if discrepancy > EQ_TOLERANCE:
                        raise QuadraticHamiltonianError(
                            'FermionOperator does not map '
                            'to QuadraticHamiltonian (not Hermitian).')
                antisymmetric_part[p, q] -= .5 * coefficient.conjugate()
                antisymmetric_part[q, p] += .5 * coefficient.conjugate()
        elif not ignore_incompatible_terms:
            # Operator contains non-quadratic terms
            raise QuadraticHamiltonianError('FermionOperator does not map '
                                            'to QuadraticHamiltonian '
                                            '(contains non-quadratic terms).')

    # Compute Hermitian part
    hermitian_part = (combined_hermitian_part +
                      chemical_potential * numpy.eye(n_qubits))

    # Check that the operator is Hermitian
    if not is_hermitian(hermitian_part):
        raise QuadraticHamiltonianError(
            'FermionOperator does not map '
            'to QuadraticHamiltonian (not Hermitian).')

    # Form QuadraticHamiltonian and return.
    discrepancy = numpy.max(numpy.abs(antisymmetric_part))
    if discrepancy < EQ_TOLERANCE:
        # Hamiltonian conserves particle number
        quadratic_hamiltonian = QuadraticHamiltonian(
            hermitian_part,
            constant=constant,
            chemical_potential=chemical_potential)
    else:
        # Hamiltonian does not conserve particle number
        quadratic_hamiltonian = QuadraticHamiltonian(hermitian_part,
                                                     antisymmetric_part,
                                                     constant,
                                                     chemical_potential)

    return quadratic_hamiltonian
Exemplo n.º 18
0
    def test_plane_wave_period_cutoff(self):
        # TODO: After figuring out the correct formula for period cutoff for
        #     dual basis, change period_cutoff to default, and change
        #     h_1 to also accept period_cutoff for real integration test.
        
        '''
        Key: Spatial dimension.
        Value: List of geometries.
        '''
        geometry_sets = {
                    2: [[('H', (0., 0.)), ('H', (0.8, 0.))], 
                        [('H', (0.1, 0.))]], 
                    3: [[('H', (0., 0., 0.)), ('H', (0.8, 0., 0.))], 
                        [('H', (0.1, 0., 0.))]]
                  }
        
        # [[spatial dimension, fieldline dimension]]
        dims = [[2, 2], [2, 3], [3, 3]]
        spinless_set = [True, False]
        scale = 8 * 1.
        
        for dim in dims:
            for geometry in geometry_sets[dim[0]]:
                for spinless in spinless_set:
                    grid = Grid(dimensions=dim[0], scale=scale, length=2)
                    period_cutoff = grid.volume_scale() ** (1. / grid.dimensions)

                    h_1 = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True, include_constant=False,
                                                 e_cutoff=None, fieldlines=dim[1])
                    jw_1 = jordan_wigner(h_1)
                    spectrum_1 = eigenspectrum(jw_1)

                    h_2 = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True, include_constant=False, 
                                                 e_cutoff=None, non_periodic=True, period_cutoff=period_cutoff,
                                                 fieldlines=dim[1])
                    jw_2 = jordan_wigner(h_2)
                    spectrum_2 = eigenspectrum(jw_2)

                    max_diff = numpy.amax(numpy.absolute(spectrum_1 - spectrum_2))

                    # Checks if non-periodic and periodic cases are different.
                    self.assertGreater(max_diff, 0.)


                    # TODO: This is only for code coverage. Remove after having real
                    #     integration test.
                    momentum_hamiltonian = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=True,
                                                                  include_constant=False, e_cutoff=None, 
                                                                  non_periodic=True, period_cutoff=period_cutoff,
                                                                  fieldlines=dim[1])

                    position_hamiltonian = plane_wave_hamiltonian(grid, geometry, spinless=True, plane_wave=False, 
                                                                  include_constant=False, e_cutoff=None, 
                                                                  non_periodic=True, period_cutoff=period_cutoff,
                                                                  fieldlines=dim[1])

                    # Confirm they are Hermitian
                    momentum_hamiltonian_operator = (
                        get_sparse_operator(momentum_hamiltonian))
                    self.assertTrue(is_hermitian(momentum_hamiltonian_operator))

                    position_hamiltonian_operator = (
                        get_sparse_operator(position_hamiltonian))
                    self.assertTrue(is_hermitian(position_hamiltonian_operator))

                    # Diagonalize.
                    jw_momentum = jordan_wigner(momentum_hamiltonian)
                    jw_position = jordan_wigner(position_hamiltonian)
                    momentum_spectrum = eigenspectrum(jw_momentum)
                    position_spectrum = eigenspectrum(jw_position)

                    # Confirm spectra are the same.
                    difference = numpy.amax(
                        numpy.absolute(momentum_spectrum - position_spectrum))
                    self.assertAlmostEqual(difference, 0.)
Exemplo n.º 19
0
def jw_get_ground_states_by_particle_number(sparse_operator,
                                            particle_number,
                                            sparse=True,
                                            num_eigs=3):
    """For a Jordan-Wigner encoded Hermitian operator, compute the lowest
    eigenvalue and eigenstates at a particular particle number. The operator
    must conserve particle number.

    Args:
        sparse_operator(sparse): A Jordan-Wigner encoded sparse operator.
        particle_number(int): The particle number at which to compute
            ground states.
        sparse(boolean, optional): Whether to use sparse eigensolver.
            Default is True.
        num_eigs(int, optional): The number of eigenvalues to request from the
            sparse eigensolver. Needs to be at least as large as the degeneracy
            of the ground energy in order to obtain all ground states.
            Only used if `sparse=True`. Default is 3.

    Returns:
        ground_energy(float): The lowest eigenvalue of sparse_operator within
            the eigenspace of the number operator corresponding to
            particle_number.

        ground_states(list[ndarray]): A list of the corresponding eigenstates.

    Warning:
        The running time of this method is exponential in the number of qubits.
    """
    # Check if operator is Hermitian
    if not is_hermitian(sparse_operator):
        raise ValueError('sparse_operator must be Hermitian.')

    n_qubits = int(numpy.log2(sparse_operator.shape[0]))

    # Check if operator conserves particle number
    sparse_num_op = jordan_wigner_sparse(number_operator(n_qubits))
    com = commutator(sparse_num_op, sparse_operator)
    if com.nnz:
        maxval = max(map(abs, com.data))
        if maxval > EQ_TOLERANCE:
            raise ValueError('sparse_operator must conserve particle number.')

    # Get the operator restricted to the subspace of the desired
    # particle number
    restricted_operator = jw_number_restrict_operator(sparse_operator,
                                                      particle_number,
                                                      n_qubits)

    if sparse and num_eigs >= restricted_operator.shape[0] - 1:
        # Restricted operator too small for sparse eigensolver
        sparse = False

    # Compute eigenvalues and eigenvectors
    if sparse:
        eigvals, eigvecs = scipy.sparse.linalg.eigsh(restricted_operator,
                                                     k=num_eigs,
                                                     which='SA')
        if abs(max(eigvals) - min(eigvals)) < EQ_TOLERANCE:
            warnings.warn(
                'The lowest {} eigenvalues are degenerate. '
                'There may be more ground states; increase '
                'num_eigs or set sparse=False to get '
                'them.'.format(num_eigs), RuntimeWarning)
    else:
        dense_restricted_operator = restricted_operator.toarray()
        eigvals, eigvecs = numpy.linalg.eigh(dense_restricted_operator)

    # Get the ground energy
    if sparse:
        ground_energy = sorted(eigvals)[0]
    else:
        # No need to sort in the case of dense eigenvalue computation
        ground_energy = eigvals[0]

    # Get the indices of eigenvectors corresponding to the ground energy
    ground_state_indices = numpy.where(
        abs(eigvals - ground_energy) < EQ_TOLERANCE)

    ground_states = list()

    for i in ground_state_indices[0]:
        restricted_ground_state = eigvecs[:, i]
        # Expand this ground state to the whole vector space
        number_indices = jw_number_indices(particle_number, n_qubits)
        expanded_ground_state = scipy.sparse.csc_matrix(
            (restricted_ground_state.flatten(),
             (number_indices, [0] * len(number_indices))),
            shape=(2**n_qubits, 1))
        # Add the expanded ground state to the list
        ground_states.append(expanded_ground_state)

    return ground_energy, ground_states
Exemplo n.º 20
0
def build_hamiltonian(ops: Union[FermionOperator, hamiltonian.Hamiltonian],
                      norb: int = 0,
                      conserve_number: bool = True,
                      e_0: complex = 0. + 0.j) -> 'hamiltonian.Hamiltonian':
    """Build a Hamiltonian object for the fqe

    Args:
        ops (FermionOperator, hamiltonian.Hamiltonian) - input operator as \
            FermionOperator.  If a Hamiltonian is passed as an argument, \
            this function returns as is.

        norb (int) - the number of orbitals in the system

        conserve_number (bool) - whether the operator conserves the number

        e_0 (complex) - the scalar part of the operator

    Returns:
        (hamiltonian.Hamiltonian) - General Hamiltonian that is created from ops
    """
    if isinstance(ops, hamiltonian.Hamiltonian):
        return ops

    if isinstance(ops, tuple):
        validate_tuple(ops)

        return general_hamiltonian.General(ops, e_0=e_0)

    if not isinstance(ops, FermionOperator):
        raise TypeError('Expected FermionOperator' \
                        ' but received {}.'.format(type(ops)))

    assert is_hermitian(ops)

    out: Any
    if len(ops.terms) <= 2:
        out = sparse_hamiltonian.SparseHamiltonian(ops, e_0=e_0)

    else:
        if not conserve_number:
            ops = transform_to_spin_broken(ops)

        ops = normal_ordered(ops)

        ops_rank, e_0 = split_openfermion_tensor(ops)  # type: ignore

        if norb == 0:
            for term in ops_rank.values():
                ablk, bblk = largest_operator_index(term)
                norb = max(norb, ablk // 2 + 1, bblk // 2 + 1)
        else:
            norb = norb

        ops_mat = {}
        maxrank = 0
        for rank, term in ops_rank.items():
            index = rank // 2 - 1
            ops_mat[index] = fermionops_tomatrix(term, norb)
            maxrank = max(index, maxrank)

        if len(ops_mat) == 1 and (0 in ops_mat):
            out = process_rank2_matrix(ops_mat[0], norb=norb, e_0=e_0)
        elif len(ops_mat) == 1 and \
            (1 in ops_mat) and \
            check_diagonal_coulomb(ops_mat[1]):
            out = diagonal_coulomb.DiagonalCoulomb(ops_mat[1], e_0=e_0)

        else:
            dtypes = [xx.dtype for xx in ops_mat.values()]
            dtypes = numpy.unique(dtypes)
            if len(dtypes) != 1:
                raise TypeError(
                    "Non-unique coefficient types for input operator")

            for i in range(maxrank + 1):
                if i not in ops_mat:
                    mat_dim = tuple([2 * norb for _ in range((i + 1) * 2)])
                    ops_mat[i] = numpy.zeros(mat_dim, dtype=dtypes[0])

            ops_mat2 = []
            for i in range(maxrank + 1):
                ops_mat2.append(ops_mat[i])

            out = general_hamiltonian.General(tuple(ops_mat2), e_0=e_0)

    out._conserve_number = conserve_number
    return out
Exemplo n.º 21
0
    def test_exceptions(self):
        with self.assertRaises(TypeError):
            _ = is_hermitian('a')

        with self.assertRaises(TypeError):
            _ = hermitian_conjugated(1)
Exemplo n.º 22
0
 def test_hermitian(self):
     """Test output is hermitian"""
     self.logTestName()
     H, _ = rotation(self.phi, hbar=self.hbar)
     self.assertTrue(is_hermitian(H))
     self.assertTrue(is_hermitian(get_quad_operator(H)))
Exemplo n.º 23
0
 def test_boson_operator_identity(self):
     op = BosonOperator(())
     self.assertTrue(is_hermitian(op))
Exemplo n.º 24
0
 def test_hermitian(self):
     """Test output is hermitian"""
     self.logTestName()
     H, _ = displacement(self.alpha, hbar=self.hbar)
     self.assertTrue(is_hermitian(H))
     self.assertTrue(is_hermitian(get_quad_operator(H)))
Exemplo n.º 25
0
 def test_boson_operator_hermitian(self):
     op = BosonOperator('0^ 1 2^ 3')
     op += BosonOperator('3^ 2 1^ 0')
     self.assertTrue(is_hermitian(op))
Exemplo n.º 26
0
 def test_hermitian(self):
     """Test output is hermitian"""
     self.logTestName()
     self.assertTrue(is_hermitian(self.H))
     self.assertTrue(
         is_hermitian(get_boson_operator(self.H, hbar=self.hbar)))
Exemplo n.º 27
0
 def test_qubit_operator_zero(self):
     op = QubitOperator()
     self.assertTrue(is_hermitian(op))
Exemplo n.º 28
0
 def test_hermitian(self):
     """Test output is hermitian"""
     self.logTestName()
     self.assertTrue(is_hermitian(self.H))
     self.assertTrue(is_hermitian(get_quad_operator(self.H)))
Exemplo n.º 29
0
 def test_qubit_operator_nonhermitian(self):
     op = QubitOperator('X0 Y2 Z5', 1. + 2.j)
     self.assertFalse(is_hermitian(op))
Exemplo n.º 30
0
 def test_hermitian(self, hbar):
     """Test output is hermitian"""
     assert is_hermitian(self.H)
     assert is_hermitian(get_boson_operator(self.H, hbar=hbar))