Esempio n. 1
0
 def test_wrong_shape(self, hamiltonian):
     """Tests that an exception is raised if the Hamiltonian does not have
     the correct shape"""
     with pytest.raises(
         ValueError, match="The Hamiltonian should have shape",
     ):
         pu.decompose_hamiltonian(hamiltonian)
Esempio n. 2
0
    def test_decomposition(self, hamiltonian):
        """Tests that decompose_hamiltonian successfully decomposes Hamiltonians into a
        linear combination of Pauli matrices"""
        decomposed_coeff, decomposed_obs = pu.decompose_hamiltonian(hamiltonian)

        linear_comb = sum([decomposed_coeff[i] * o.matrix for i, o in enumerate(decomposed_obs)])
        assert np.allclose(hamiltonian, linear_comb)
Esempio n. 3
0
    def test_observable_types(self, hamiltonian, hide_identity):
        """Tests that the Hamiltonian decomposes into a linear combination of tensors,
        the identity matrix, and Pauli matrices."""
        allowed_obs = (Tensor, Identity, PauliX, PauliY, PauliZ)

        decomposed_coeff, decomposed_obs = pu.decompose_hamiltonian(hamiltonian, hide_identity)
        assert all([isinstance(o, allowed_obs) for o in decomposed_obs])
Esempio n. 4
0
    def test_result_length(self, hamiltonian):
        """Tests that tensors are composed of a number of terms equal to the number
        of qubits."""
        decomposed_coeff, decomposed_obs = pu.decompose_hamiltonian(hamiltonian)
        n = int(np.log2(len(hamiltonian)))

        tensors = filter(lambda obs: isinstance(obs, Tensor), decomposed_obs)
        assert all(len(tensor.obs) == n for tensor in tensors)
Esempio n. 5
0
    def test_hide_identity_true(self):
        """Tests that there are no Identity observables in the tensor products
        when hide_identity=True"""
        H = np.array(np.diag([0, 0, 0, 1]))
        coeff, obs_list = pu.decompose_hamiltonian(H, hide_identity=True)
        tensors = filter(lambda obs: isinstance(obs, Tensor), obs_list)

        for tensor in tensors:
            all_identities = all(isinstance(o, Identity) for o in tensor.obs)
            no_identities = not any(isinstance(o, Identity) for o in tensor.obs)
            assert all_identities or no_identities
Esempio n. 6
0
 def test_decomp(self, H):
     """Tests that decompose_hamiltonian successfully decomposes Hamiltonians into Pauli matrices"""
     N = int(np.log2(len(H)))
     coeff, combs = pu.decompose_hamiltonian(H)
     result = np.zeros((2**N, 2**N))
     matrices = []
     for term in combs:
         base = [qml.Identity] * N
         if term.num_wires == N:
             matrices.append(term.matrix)
         else:
             base[term.wires[0]] = term.__class__
             matrices.append(
                 functools.reduce(operator.matmul,
                                  [t(i)
                                   for i, t in enumerate(base)]).matrix)
     for term in zip(coeff, matrices):
         result = result + term[0] * term[1]
     assert (H == result).all
Esempio n. 7
0
 def test_not_hermitian(self):
     """Tests that an exception is raised if the Hamiltonian is not Hermitian, i.e.
     equal to its own conjugate transpose"""
     with pytest.raises(ValueError,
                        match="The Hamiltonian is not Hermitian"):
         pu.decompose_hamiltonian(np.array([[1, 2], [3, 4]]))
def find_excited_states(H):
    """
    Fill in the missing parts between the # QHACK # markers below. Implement
    a variational method that can find the three lowest energies of the provided
    Hamiltonian.

    Args:
        H (qml.Hamiltonian): The input Hamiltonian

    Returns:
        The lowest three eigenenergies of the Hamiltonian as a comma-separated string,
        sorted from smallest to largest.
    """

    energies = np.zeros(3)

    # QHACK #
    
    w1 = range(len(H.wires))
    w2 = range(len(H.wires))
    dev = qml.device('default.qubit', wires=H.wires)
    dev1 = qml.device('default.qubit', wires=w1)
    dev2 = qml.device('default.qubit', wires=w2)

    def variational_ansatz0(params, wires):
        params = params[1:]
        n_qubits = len(wires)
        n_rotations = len(params)

        if n_rotations > 1:
            n_layers = n_rotations // n_qubits
            n_extra_rots = n_rotations - n_layers * n_qubits

            for layer_idx in range(n_layers):
                layer_params = params[layer_idx * n_qubits : layer_idx * n_qubits + n_qubits, :]
                qml.broadcast(qml.Rot, wires, pattern="single", parameters=layer_params)
                qml.broadcast(qml.CNOT, wires, pattern="ring")

            extra_params = params[-n_extra_rots:, :]
            extra_wires = wires[: n_qubits - 1 - n_extra_rots : -1]
            qml.broadcast(qml.Rot, extra_wires, pattern="single", parameters=extra_params)
        else:
            qml.Rot(*params[0], wires=wires[0])

    def variational_ansatz1(params, wires):
        rot_params = params[0]
        params = params[1:]

        n_qubits = len(wires)
        n_rotations = len(params)

        qml.PauliX(wires=0)
        qml.RY(rot_params[1], wires=2)

        if n_rotations > 1:
            n_layers = n_rotations // n_qubits
            n_extra_rots = n_rotations - n_layers * n_qubits

            for layer_idx in range(n_layers):
                layer_params = params[layer_idx * n_qubits : layer_idx * n_qubits + n_qubits, :]
                qml.broadcast(qml.Rot, wires, pattern="single", parameters=layer_params)
                qml.broadcast(qml.CNOT, wires, pattern="ring")

            extra_params = params[-n_extra_rots:, :]
            extra_wires = wires[: n_qubits - 1 - n_extra_rots : -1]
            qml.broadcast(qml.Rot, extra_wires, pattern="single", parameters=extra_params)
        else:
            qml.Rot(*params[0], wires=wires[0])

    @qml.qnode(dev1)
    def get_state(params, wires=w2):
        n_qubits = len(wires)
        n_rotations = len(params)

        if n_rotations > 1:
            n_layers = n_rotations // n_qubits
            n_extra_rots = n_rotations - n_layers * n_qubits

            for layer_idx in range(n_layers):
                layer_params = params[layer_idx * n_qubits : layer_idx * n_qubits + n_qubits, :]
                qml.broadcast(qml.Rot, wires, pattern="single", parameters=layer_params)
                qml.broadcast(qml.CNOT, wires, pattern="ring")

            extra_params = params[-n_extra_rots:, :]
            extra_wires = wires[: n_qubits - 1 - n_extra_rots : -1]
            qml.broadcast(qml.Rot, extra_wires, pattern="single", parameters=extra_params)
        else:
            qml.Rot(*params[0], wires=wires[0])
        return qml.state()


    num_qubits = len(H.wires)
    num_param_sets = (2 ** num_qubits) - 1

    energy = 0

    cost_fn = qml.ExpvalCost(variational_ansatz0, H, dev)
    opt = qml.AdamOptimizer(stepsize=0.1)
    params = np.random.normal(0, np.pi, (num_param_sets+1, 3))

    conv_tol = 1e-08
    max_iterations = 400

    for n in range(max_iterations):
        params, prev_energy = opt.step_and_cost(cost_fn, params)
        energy = cost_fn(params)
        conv = np.abs(energy - prev_energy)

        if conv <= conv_tol:
            break

    energies[0] = energy   

    state_1 = get_state(params)
    print(state_1)
    
    additional_term = np.outer(state_1, state_1) * 3
    coeffs, ops = decompose_hamiltonian(additional_term)

    new_coeffs = (H.coeffs)
    for c in coeffs:
        new_coeffs.append(c)
    new_ops = (H.ops)
    for o in ops:
        new_ops.append(o)
    H1 = qml.Hamiltonian(new_coeffs, new_ops)

    energy1 = 0

    cost_fn_1 = qml.ExpvalCost(variational_ansatz, H1, dev)
    params1 = np.random.normal(0, np.pi, (num_param_sets, 3))

    for n in range(max_iterations):
        params1, prev_energy = opt.step_and_cost(cost_fn_1, params1)
        energy1 = cost_fn_1(params1)
        conv = np.abs(energy1 - prev_energy)

        if conv <= conv_tol:
            break

    energies[1] = energy1   

    # QHACK #

    return ",".join([str(E) for E in energies])
Esempio n. 9
0
    def qubit_operator_string(self, observable):
        """Serializes a PennyLane observable to a string compatible with the
        openfermion.QubitOperator class.

        This method decomposes an observable into a sum of Pauli terms and
        identities, if needed.

        **Example**

        >>> dev = QeQiskitDevice(wires=2)
        >>> obs = qml.PauliZ(0)
        >>> op_str = dev.qubit_operator_string(obs)
        >>> print(op_str)
        1 [Z0]
        >>> obs = qml.Hadamard(0)
        >>> op_str = dev.qubit_operator_string(obs)
        >>> print(op_str)
        0.7071067811865475 [X0] + 0.7071067811865475 [Z0]

        Args:
            observable (pennylane.operation.Observable): the observable to serialize

        Returns:
            str: the ``openfermion.QubitOperator`` string representation
        """
        accepted_obs = {"PauliX", "PauliY", "PauliZ", "Identity"}

        if isinstance(observable, Tensor):
            need_decomposition = any(o.name not in accepted_obs
                                     for o in observable.obs)
        else:
            need_decomposition = observable.name not in accepted_obs

        if need_decomposition:
            original_observable = observable

            # Decompose the matrix of the observable
            # This removes information about the wire labels used and
            # consecutive integer wires are used
            coeffs, obs_list = decompose_hamiltonian(
                matrix(original_observable))

            for idx in range(len(obs_list)):
                obs = obs_list[idx]

                if not isinstance(obs, Tensor):
                    # Convert terms to Tensor such that _terms_to_qubit_operator
                    # can be used
                    obs_list[idx] = Tensor(obs)

                # Need to use the custom wire labels of the original observable
                original_wires = original_observable.wires.tolist()
                for o, mapped_w in zip(obs_list[idx].obs, original_wires):
                    o._wires = Wires(mapped_w)

        else:
            if not isinstance(observable, Tensor):
                # If decomposition is not needed and is not a Tensor, we need
                # to convert the single observable
                observable = Tensor(observable)

            coeffs = [1]
            obs_list = [observable]

        # Use consecutive integers as default wire_map
        wire_map = {v: idx for idx, v in enumerate(self.wires)}
        return _terms_to_qubit_operator_string(coeffs,
                                               obs_list,
                                               wires=wire_map)