def test_expand_three(self, tol):
        """Test that a 3 qubit gate correctly expands to 4 qubits."""
        dev = plf.NumpyWavefunctionDevice(wires=4)

        # test applied to wire 0,1,2
        res = dev.expand(U_toffoli, [0, 1, 2])
        expected = np.kron(U_toffoli, I)
        self.assertAllAlmostEqual(res, expected, delta=tol)

        # test applied to wire 1,2,3
        res = dev.expand(U_toffoli, [1, 2, 3])
        expected = np.kron(I, U_toffoli)
        self.assertAllAlmostEqual(res, expected, delta=tol)

        # test applied to wire 0,2,3
        res = dev.expand(U_toffoli, [0, 2, 3])
        expected = (np.kron(SWAP, np.kron(I, I)) @ np.kron(I, U_toffoli)
                    @ np.kron(SWAP, np.kron(I, I)))
        self.assertAllAlmostEqual(res, expected, delta=tol)

        # test applied to wire 0,1,3
        res = dev.expand(U_toffoli, [0, 1, 3])
        expected = (np.kron(np.kron(I, I), SWAP) @ np.kron(U_toffoli, I)
                    @ np.kron(np.kron(I, I), SWAP))
        self.assertAllAlmostEqual(res, expected, delta=tol)

        # test applied to wire 3, 1, 2
        res = dev.expand(U_toffoli, [3, 1, 2])
        # change the control qubit on the Toffoli gate
        rows = np.array([0, 4, 1, 5, 2, 6, 3, 7])
        expected = np.kron(I, U_toffoli[:, rows][rows])
        self.assertAllAlmostEqual(res, expected, delta=tol)

        # test applied to wire 3, 0, 2
        res = dev.expand(U_toffoli, [3, 0, 2])
        # change the control qubit on the Toffoli gate
        rows = np.array([0, 4, 1, 5, 2, 6, 3, 7])
        expected = (np.kron(SWAP, np.kron(I, I)) @ np.kron(
            I, U_toffoli[:, rows][rows]) @ np.kron(SWAP, np.kron(I, I)))
        self.assertAllAlmostEqual(res, expected, delta=tol)
Beispiel #2
0
# ~~~~~~~
# Next, we create a quantum device with 4 qubits.

dev = qml.device("default.qubit", wires=n_wires, analytic=True, shots=1)

##############################################################################
# We also require a quantum node which will apply the operators according to the
# angle parameters, and return the expectation value of the observable
# :math:`\sigma_z^{j}\sigma_z^{k}` to be used in each term of the objective function later on. The
# argument ``edge`` specifies the chosen edge term in the objective function, :math:`(j,k)`.
# Once optimized, the same quantum node can be used for sampling an approximately optimal bitstring
# if executed with the ``edge`` keyword set to ``None``. Additionally, we specify the number of layers
# (repeated applications of :math:`U_BU_C`) using the keyword ``n_layers``.

pauli_z = [[1, 0], [0, -1]]
pauli_z_2 = np.kron(pauli_z, pauli_z)


@qml.qnode(dev)
def circuit(gammas, betas, edge=None, n_layers=1):
    # apply Hadamards to get the n qubit |+> state
    for wire in range(n_wires):
        qml.Hadamard(wires=wire)
    # p instances of unitary operators
    for i in range(n_layers):
        U_C(gammas[i])
        U_B(betas[i])
    if edge is None:
        # measurement phase
        return qml.sample(comp_basis_measurement(range(n_wires)))
    # during the optimization phase we are evaluating a term
Beispiel #3
0
# Since the specific problem considered in this tutorial has a small size, we can also
# solve it in a classical way and then compare the results with our quantum solution.
#

##############################################################################
# Classical algorithm
# ^^^^^^^^^^^^^^^^^^^
# To solve the problem in a classical way, we use the explicit matrix representation in
# terms of numerical NumPy arrays.

Id = np.identity(2)
Z = np.array([[1, 0], [0, -1]])
X = np.array([[0, 1], [1, 0]])

A_0 = np.identity(8)
A_1 = np.kron(np.kron(X, Z), Id)
A_2 = np.kron(np.kron(X, Id), Id)

A_num = c[0] * A_0 + c[1] * A_1 + c[2] * A_2
b = np.ones(8) / np.sqrt(8)

##############################################################################
# We can print the explicit values of :math:`A` and :math:`b`:

print("A = \n", A_num)
print("b = \n", b)

##############################################################################
# The solution can be computed via a matrix inversion:

A_inv = np.linalg.inv(A_num)
Beispiel #4
0
#     qml.PauliZ(wires=wires[0])
#     qml.RZ(-z/2, wires=wires[0])
#
#
# def U(z, wires):
#     oper_A(wires=[wires[0], wires[1]])
#     oper_B(wires=[wires[2], wires[3]])
#     oper_C(z, wires=[wires[1], wires[2], wires[3]])
#     oper_D(z, wires=wires)
#     oper_A(wires=[wires[0], wires[1]])

# Construction of U
# Basis states
s0 = np.asanyarray([[1, 0]])
s1 = np.asanyarray([[0, 1]])
s00 = np.kron(s0, s0)
s01 = np.kron(s0, s1)
s10 = np.kron(s1, s0)
s11 = np.kron(s1, s1)

# Bell states
sp = (s01 + s10) / np.sqrt(2)  # (|01> + |10>)/√2
sm = (s01 - s10) / np.sqrt(2)  # (|01> - |10>)/√2

o0000 = s00.transpose() * s00
o1111 = s11.transpose() * s11
opp = sp.transpose() * sp
omm = sm.transpose() * sm


def U0_(t, wires):
Beispiel #5
0
rotated_probs = circuit_qwc(weights)
print(rotated_probs)

##############################################################################
# We're not quite there yet; we have only calculated the probabilities of the variational circuit
# rotated into the shared eigenbasis---the :math:`|\langle \phi_n |\psi\rangle|^2`. To recover the
# *expectation values* of the two QWC observables from the probabilities, recall that we need one
# final piece of information: their eigenvalues :math:`\lambda_{A, n}` and :math:`\lambda_{B, n}`.
#
# We know that the single-qubit Pauli operators each have eigenvalues :math:`(1, -1)`, while the identity
# operator has eigenvalues :math:`(1, 1)`. We can make use of ``np.kron`` to quickly
# generate the eigenvalues of the full Pauli terms, making sure that the order
# of the eigenvalues in the Kronecker product corresponds to the tensor product.

eigenvalues_XYI = np.kron(np.kron([1, -1], [1, -1]), [1, 1])
eigenvalues_XIZ = np.kron(np.kron([1, -1], [1, 1]), [1, -1])

# Taking the linear combination of the eigenvalues and the probabilities
print("Expectation value of XYI = ", np.dot(eigenvalues_XYI, rotated_probs))
print("Expectation value of XIZ = ", np.dot(eigenvalues_XIZ, rotated_probs))

##############################################################################
# Compare this to the result when we used two circuit evaluations. We have successfully used a
# single circuit evaluation to recover both expectation values!
#
# Luckily, PennyLane automatically performs this QWC grouping under the hood. We simply
# return the two QWC Pauli terms from the QNode:


@qml.qnode(dev)
# To perform "doubly stochastic" gradient descent, we simply apply the stochastic
# gradient descent approach from above, but in addition also uniformly sample
# a subset of the terms for the Hamiltonian expectation at each optimization step.
# This inserts another element of stochasticity into the system---all the while
# convergence continues to be guaranteed!
#
# Let's create a QNode that randomly samples a single term from the above
# Hamiltonian as the observable to be measured.

I = np.identity(2)
X = np.array([[0, 1], [1, 0]])
Y = np.array([[0, -1j], [1j, 0]])
Z = np.array([[1, 0], [0, -1]])

terms = np.array([
    2 * np.kron(I, X), 4 * np.kron(I, Z), -np.kron(X, X), 5 * np.kron(Y, Y),
    2 * np.kron(Z, X)
])


@qml.qnode(dev_stochastic)
def circuit(params, n=None):
    StronglyEntanglingLayers(weights=params, wires=[0, 1])
    idx = np.random.choice(np.arange(5), size=n, replace=False)
    A = np.sum(terms[idx], axis=0)
    return expval(qml.Hermitian(A, wires=[0, 1]))


def loss(params):
    return 4 + (5 / 1) * circuit(params, n=1)
    def test_hermitian(self, device, tol, skip_if):
        """Test that a tensor product involving qml.Hermitian works correctly"""
        n_wires = 3
        dev = device(n_wires)

        if dev.shots is None:
            pytest.skip("Device is in analytic mode, cannot test sampling.")

        if "Hermitian" not in dev.observables:
            pytest.skip(
                "Skipped because device does not support the Hermitian observable."
            )

        skip_if(dev, {"supports_tensor_observables": False})

        theta = 0.432
        phi = 0.123
        varphi = -0.543

        A_ = 0.1 * np.array([
            [-6, 2 + 1j, -3, -5 + 2j],
            [2 - 1j, 0, 2 - 1j, -5 + 4j],
            [-3, 2 + 1j, 0, -4 + 3j],
            [-5 - 2j, -5 - 4j, -4 - 3j, -6],
        ])

        @qml.qnode(dev)
        def circuit():
            qml.RX(theta, wires=[0])
            qml.RX(phi, wires=[1])
            qml.RX(varphi, wires=[2])
            qml.CNOT(wires=[0, 1])
            qml.CNOT(wires=[1, 2])
            return qml.sample(
                qml.PauliZ(wires=[0]) @ qml.Hermitian(A_, wires=[1, 2]))

        res = circuit()

        # res should only contain the eigenvalues of
        # the hermitian matrix tensor product Z
        Z = np.diag([1, -1])
        eigvals = np.linalg.eigvalsh(np.kron(Z, A_))
        assert np.allclose(sorted(np.unique(res)),
                           sorted(eigvals),
                           atol=tol(False))

        mean = np.mean(res)
        expected = (0.1 * 0.5 *
                    (-6 * np.cos(theta) *
                     (np.cos(varphi) + 1) - 2 * np.sin(varphi) *
                     (np.cos(theta) + np.sin(phi) - 2 * np.cos(phi)) +
                     3 * np.cos(varphi) * np.sin(phi) + np.sin(phi)))
        assert np.allclose(mean, expected, atol=tol(False))

        var = np.var(res)
        expected = (
            0.01 *
            (1057 - np.cos(2 * phi) + 12 *
             (27 + np.cos(2 * phi)) * np.cos(varphi) -
             2 * np.cos(2 * varphi) * np.sin(phi) *
             (16 * np.cos(phi) + 21 * np.sin(phi)) + 16 * np.sin(2 * phi) - 8 *
             (-17 + np.cos(2 * phi) + 2 * np.sin(2 * phi)) * np.sin(varphi) -
             8 * np.cos(2 * theta) *
             (3 + 3 * np.cos(varphi) + np.sin(varphi))**2 - 24 * np.cos(phi) *
             (np.cos(phi) + 2 * np.sin(phi)) * np.sin(2 * varphi) -
             8 * np.cos(theta) *
             (4 * np.cos(phi) *
              (4 + 8 * np.cos(varphi) + np.cos(2 * varphi) -
               (1 + 6 * np.cos(varphi)) * np.sin(varphi)) + np.sin(phi) *
              (15 + 8 * np.cos(varphi) - 11 * np.cos(2 * varphi) +
               42 * np.sin(varphi) + 3 * np.sin(2 * varphi)))) / 16)
        assert np.allclose(var, expected, atol=tol(False))
Beispiel #8
0
# ~~~~~~~
# Next, we create a quantum device with 4 qubits.

dev = qml.device("default.qubit", wires=n_wires, shots=1)

##############################################################################
# We also require a quantum node which will apply the operators according to the
# angle parameters, and return the expectation value of the observable
# :math:`\sigma_z^{j}\sigma_z^{k}` to be used in each term of the objective function later on. The
# argument ``edge`` specifies the chosen edge term in the objective function, :math:`(j,k)`.
# Once optimized, the same quantum node can be used for sampling an approximately optimal bitstring
# if executed with the ``edge`` keyword set to ``None``. Additionally, we specify the number of layers
# (repeated applications of :math:`U_BU_C`) using the keyword ``n_layers``.

pauli_z = [[1, 0], [0, -1]]
pauli_z_2 = np.kron(pauli_z, pauli_z, requires_grad=False)


@qml.qnode(dev)
def circuit(gammas, betas, edge=None, n_layers=1):
    # apply Hadamards to get the n qubit |+> state
    for wire in range(n_wires):
        qml.Hadamard(wires=wire)
    # p instances of unitary operators
    for i in range(n_layers):
        U_C(gammas[i])
        U_B(betas[i])
    if edge is None:
        # measurement phase
        return qml.sample(comp_basis_measurement(range(n_wires)))
    # during the optimization phase we are evaluating a term
Beispiel #9
0
    def test_hermitian(self, theta, phi, varphi, monkeypatch, tol):
        """Test that a tensor product involving qml.Hermitian works correctly"""
        dev = qml.device("expt.tensornet", wires=3)
        dev.reset()
        dev.apply("RX", wires=[0], par=[theta])
        dev.apply("RX", wires=[1], par=[phi])
        dev.apply("RX", wires=[2], par=[varphi])
        dev.apply("CNOT", wires=[0, 1], par=[])
        dev.apply("CNOT", wires=[1, 2], par=[])

        A = np.array(
            [
                [-6, 2 + 1j, -3, -5 + 2j],
                [2 - 1j, 0, 2 - 1j, -5 + 4j],
                [-3, 2 + 1j, 0, -4 + 3j],
                [-5 - 2j, -5 - 4j, -4 - 3j, -6],
            ]
        )

        with monkeypatch.context() as m:
            m.setattr("numpy.random.choice", lambda x, y, p: (x, p))
            s1, p = dev.sample(["PauliZ", "Hermitian"], [[0], [1, 2]], [[], [A]])

        # s1 should only contain the eigenvalues of
        # the hermitian matrix tensor product Z
        Z = np.diag([1, -1])
        eigvals = np.linalg.eigvalsh(np.kron(Z, A))
        assert set(np.round(s1, 8)).issubset(set(np.round(eigvals, 8)))

        mean = s1 @ p
        expected = 0.5 * (
            -6 * np.cos(theta) * (np.cos(varphi) + 1)
            - 2 * np.sin(varphi) * (np.cos(theta) + np.sin(phi) - 2 * np.cos(phi))
            + 3 * np.cos(varphi) * np.sin(phi)
            + np.sin(phi)
        )
        assert np.allclose(mean, expected, atol=tol, rtol=0)

        var = (s1 ** 2) @ p - (s1 @ p).real ** 2
        expected = (
            1057
            - np.cos(2 * phi)
            + 12 * (27 + np.cos(2 * phi)) * np.cos(varphi)
            - 2 * np.cos(2 * varphi) * np.sin(phi) * (16 * np.cos(phi) + 21 * np.sin(phi))
            + 16 * np.sin(2 * phi)
            - 8 * (-17 + np.cos(2 * phi) + 2 * np.sin(2 * phi)) * np.sin(varphi)
            - 8 * np.cos(2 * theta) * (3 + 3 * np.cos(varphi) + np.sin(varphi)) ** 2
            - 24 * np.cos(phi) * (np.cos(phi) + 2 * np.sin(phi)) * np.sin(2 * varphi)
            - 8
            * np.cos(theta)
            * (
                4
                * np.cos(phi)
                * (
                    4
                    + 8 * np.cos(varphi)
                    + np.cos(2 * varphi)
                    - (1 + 6 * np.cos(varphi)) * np.sin(varphi)
                )
                + np.sin(phi)
                * (
                    15
                    + 8 * np.cos(varphi)
                    - 11 * np.cos(2 * varphi)
                    + 42 * np.sin(varphi)
                    + 3 * np.sin(2 * varphi)
                )
            )
        ) / 16
        assert np.allclose(var, expected, atol=tol, rtol=0)
    def test_ev(self):
        """Test that expectation values are calculated correctly"""
        self.logTestName()
        self.dev.reset()

        # loop through all supported observables
        for name, fn in self.dev._expectation_map.items():
            print(name)
            log.debug("\tTesting %s observable...", name)

            # start in the state |00>
            self.dev._state = np.array([1, 0, 1, 1]) / np.sqrt(3)

            # get the equivalent pennylane operation class
            op = qml.expval.__getattribute__(name)

            if op.par_domain == "A":
                # the parameter is an array
                p = [H]
            else:
                # the parameter is a float
                p = [0.432423, -0.12312, 0.324][:op.num_params]

            if callable(fn):
                # if the default.qubit is an operation accepting parameters,
                # initialise it using the parameters generated above.
                O = fn(*p)
            else:
                # otherwise, the operation is simply an array.
                O = fn

            print("op.num_wires=" + str(op.num_wires))

            # calculate the expected output
            if op.num_wires == 1 or op.num_wires == 0:
                expected_out = self.dev._state.conj() @ np.kron(
                    O, I) @ self.dev._state
            elif op.num_wires == 2:
                expected_out = self.dev._state.conj() @ O @ self.dev._state
            else:
                raise NotImplementedError(
                    "Test for operations with num_wires=" + op.num_wires +
                    " not implemented.")

            res = self.dev.ev(O, wires=[0])

            # verify the device is now in the expected state
            self.assertAllAlmostEqual(res, expected_out, delta=self.tol)

            # text exception raised if matrix is not 2x2 or 4x4
            with self.assertRaisesRegex(
                    ValueError,
                    "Only one and two-qubit expectation is supported."):
                self.dev.ev(U_toffoli, [0])

            # text warning raised if matrix is complex
            with self.assertLogs(level="WARNING") as l:
                self.dev.ev(H + 1j, [0])
                self.assertEqual(len(l.output), 1)
                self.assertEqual(len(l.records), 1)
                self.assertIn("Nonvanishing imaginary part", l.output[0])
Beispiel #11
0
        qml.grad(circuit_dq)(params), tol)


@pytest.mark.parametrize(
    "returns",
    [
        qml.PauliZ(0),
        qml.PauliX(2),
        qml.PauliZ(0) @ qml.PauliY(3),
        qml.Hadamard(2),
        qml.Hadamard(3) @ qml.PauliZ(2),
        # qml.Projector([0, 1], wires=[0, 2]) @ qml.Hadamard(3)
        # qml.Projector([0, 0], wires=[2, 0])
        qml.PauliX(0) @ qml.PauliY(3),
        qml.PauliY(0) @ qml.PauliY(2) @ qml.PauliY(3),
        qml.Hermitian(np.kron(qml.PauliY.compute_matrix(),
                              qml.PauliZ.compute_matrix()),
                      wires=[3, 2]),
        qml.Hermitian(np.array([[0, 1], [1, 0]], requires_grad=False),
                      wires=0),
        qml.Hermitian(np.array([[0, 1], [1, 0]], requires_grad=False), wires=0)
        @ qml.PauliZ(2),
    ],
)
def test_integration(returns):
    """Integration tests that compare to default.qubit for a large circuit containing parametrized
    operations"""
    dev_def = qml.device("default.qubit", wires=range(4))
    dev_lightning = qml.device("lightning.qubit", wires=range(4))

    def circuit(params):
        circuit_ansatz(params, wires=range(4))
Beispiel #12
0
def Generator(theta1, theta2, theta3):
    G = theta1.item() * np.kron(X, I) - \
        theta2 * np.kron(Z, X) + \
        theta3 * np.kron(I, X)
    return G
Beispiel #13
0
    def test_apply(self, gate, apply_unitary, shots, qvm, compiler):
        """Test the application of gates"""
        dev = plf.QVMDevice(device="3q-qvm",
                            shots=shots,
                            parametric_compilation=False)

        try:
            # get the equivalent pennylane operation class
            op = getattr(qml.ops, gate)
        except AttributeError:
            # get the equivalent pennylane-forest operation class
            op = getattr(plf, gate)

        # the list of wires to apply the operation to
        w = list(range(op.num_wires))

        if op.par_domain == "A":
            # the parameter is an array
            if gate == "QubitUnitary":
                p = np.array(U)
                w = [0]
                state = apply_unitary(U, 3)
            elif gate == "BasisState":
                p = np.array([1, 1, 1])
                state = np.array([0, 0, 0, 0, 0, 0, 0, 1])
                w = list(range(dev.num_wires))

            with qml.tape.QuantumTape() as tape:
                op(p, wires=w)
                obs = qml.expval(qml.PauliZ(0))
        else:
            p = [0.432_423, 2, 0.324][:op.num_params]
            fn = test_operation_map[gate]
            if callable(fn):
                # if the default.qubit is an operation accepting parameters,
                # initialise it using the parameters generated above.
                O = fn(*p)
            else:
                # otherwise, the operation is simply an array.
                O = fn

            # calculate the expected output
            state = apply_unitary(O, 3)
            # Creating the tape using a parametrized operation
            if p:
                with qml.tape.QuantumTape() as tape:
                    op(*p, wires=w)
                    obs = qml.expval(qml.PauliZ(0))

            # Creating the tape using an operation that take no parameters
            else:
                with qml.tape.QuantumTape() as tape:
                    op(wires=w)
                    obs = qml.expval(qml.PauliZ(0))

        dev.apply(tape.operations, rotations=tape.diagonalizing_gates)

        dev._samples = dev.generate_samples()

        res = dev.expval(obs.obs)
        expected = np.vdot(state, np.kron(np.kron(Z, I), I) @ state)

        # verify the device is now in the expected state
        # Note we have increased the tolerance here, since we are only
        # performing 1024 shots.
        self.assertAllAlmostEqual(res, expected, delta=3 / np.sqrt(shots))
    def test_expand_three(self):
        """Test that a 3 qubit gate correctly expands to 4 qubits."""
        self.logTestName()

        dev = DefaultQubit(wires=4)

        # test applied to wire 0,1,2
        res = dev.expand(U_toffoli, [0, 1, 2])
        expected = np.kron(U_toffoli, I)
        self.assertAllAlmostEqual(res, expected, delta=self.tol)

        # test applied to wire 1,2,3
        res = dev.expand(U_toffoli, [1, 2, 3])
        expected = np.kron(I, U_toffoli)
        self.assertAllAlmostEqual(res, expected, delta=self.tol)

        # test applied to wire 0,2,3
        res = dev.expand(U_toffoli, [0, 2, 3])
        expected = np.kron(U_swap, np.kron(I, I)) @ np.kron(I, U_toffoli) @ np.kron(U_swap, np.kron(I, I))
        self.assertAllAlmostEqual(res, expected, delta=self.tol)

        # test applied to wire 0,1,3
        res = dev.expand(U_toffoli, [0, 1, 3])
        expected = np.kron(np.kron(I, I), U_swap) @ np.kron(U_toffoli, I) @ np.kron(np.kron(I, I), U_swap)
        self.assertAllAlmostEqual(res, expected, delta=self.tol)

        # test applied to wire 3, 1, 2
        res = dev.expand(U_toffoli, [3, 1, 2])
        # change the control qubit on the Toffoli gate
        rows = np.array([0, 4, 1, 5, 2, 6, 3, 7])
        expected = np.kron(I, U_toffoli[:, rows][rows])
        self.assertAllAlmostEqual(res, expected, delta=self.tol)

        # test applied to wire 3, 0, 2
        res = dev.expand(U_toffoli, [3, 0, 2])
        # change the control qubit on the Toffoli gate
        rows = np.array([0, 4, 1, 5, 2, 6, 3, 7])
        expected = np.kron(U_swap, np.kron(I, I)) @ np.kron(I, U_toffoli[:, rows][rows]) @ np.kron(U_swap, np.kron(I, I))
        self.assertAllAlmostEqual(res, expected, delta=self.tol)