def test_apply(self, gate, apply_unitary, tol):
        """Test the application of gates to a state"""
        dev = plf.NumpyWavefunctionDevice(wires=3)

        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))

        obs = qml.expval(qml.PauliZ(0))
        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))

            circuit_graph = qml.CircuitGraph([op(p, wires=w)] + [obs], {},
                                             dev.wires)
        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 circuit graph using a parametrized operation
            if p:
                circuit_graph = qml.CircuitGraph([op(*p, wires=w)] + [obs], {},
                                                 dev.wires)
            # Creating the circuit graph using an operation that take no parameters
            else:
                circuit_graph = qml.CircuitGraph([op(wires=w)] + [obs], {},
                                                 dev.wires)

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

        res = dev.expval(obs)

        # verify the device is now in the expected state
        self.assertAllAlmostEqual(dev._state, state, delta=tol)
    def test_sample_values(self, tol):
        """Tests if the samples returned by sample have
        the correct values
        """
        dev = plf.NumpyWavefunctionDevice(wires=1, shots=10)

        phi = 1.5708

        circuit_operations = [qml.RX(phi, wires=[0])]

        O = qml.sample(qml.PauliZ(0))

        observables = [O]
        circuit_graph = qml.CircuitGraph(circuit_operations + observables, {},
                                         dev.wires)

        # test correct variance for <Z> of a rotated state
        dev.apply(circuit_graph.operations,
                  rotations=circuit_graph.diagonalizing_gates)

        dev._samples = dev.generate_samples()
        s1 = dev.sample(O)

        # s1 should only contain 1 and -1
        self.assertAllAlmostEqual(s1**2, 1, delta=tol)
    def test_var_hermitian(self, tol):
        """Tests for variance calculation using an arbitrary Hermitian observable"""
        dev = plf.NumpyWavefunctionDevice(wires=2)

        phi = 0.543
        theta = 0.6543

        H = np.array([[4, -1 + 6j], [-1 - 6j, 2]])

        circuit_operations = [qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0])]

        O = qml.var(qml.Hermitian(H, wires=[0]))

        observables = [O]
        circuit_graph = qml.CircuitGraph(circuit_operations + observables, {},
                                         dev.wires)

        # test correct variance for <Z> of a rotated state
        dev.apply(circuit_graph.operations,
                  rotations=circuit_graph.diagonalizing_gates)

        var = dev.var(O)

        # test correct variance for <H> of a rotated state
        expected = 0.5 * (2 * np.sin(2 * theta) * np.cos(phi)**2 +
                          24 * np.sin(phi) * np.cos(phi) *
                          (np.sin(theta) - np.cos(theta)) +
                          35 * np.cos(2 * phi) + 39)

        self.assertAlmostEqual(var, expected, delta=tol)
예제 #4
0
    def test_multi_mode_hermitian_expectation(self, theta, phi, varphi, tol):
        """Test that arbitrary multi-mode Hermitian expectation values are correct"""
        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],
        ])

        dev = DefaultQubitTF(wires=2)
        queue = [
            qml.RY(theta, wires=0),
            qml.RY(phi, wires=1),
            qml.CNOT(wires=[0, 1])
        ]
        observables = [qml.Hermitian(A, wires=[0, 1])]

        for i in range(len(observables)):
            observables[i].return_type = qml.operation.Expectation

        res = dev.execute(
            qml.CircuitGraph(queue + observables, {}, Wires([0, 1])))

        # below is the analytic expectation value for this circuit with arbitrary
        # Hermitian observable A
        expected = 0.5 * (6 * np.cos(theta) * np.sin(phi) - np.sin(theta) *
                          (8 * np.sin(phi) + 7 * np.cos(phi) + 3) -
                          2 * np.sin(phi) - 6 * np.cos(phi) - 6)

        assert np.allclose(res, expected, atol=tol, rtol=0)
예제 #5
0
    def test_hermitian_expectation(self, theta, phi, varphi, tol):
        """Test that arbitrary Hermitian expectation values are correct"""
        dev = DefaultQubitTF(wires=2)
        queue = [
            qml.RY(theta, wires=0),
            qml.RY(phi, wires=1),
            qml.CNOT(wires=[0, 1])
        ]
        observables = [qml.Hermitian(A, wires=[i]) for i in range(2)]

        for i in range(len(observables)):
            observables[i].return_type = qml.operation.Expectation

        res = dev.execute(
            qml.CircuitGraph(queue + observables, {}, Wires([0, 1])))

        a = A[0, 0]
        re_b = A[0, 1].real
        d = A[1, 1]
        ev1 = ((a - d) * np.cos(theta) +
               2 * re_b * np.sin(theta) * np.sin(phi) + a + d) / 2
        ev2 = ((a - d) * np.cos(theta) * np.cos(phi) + 2 * re_b * np.sin(phi) +
               a + d) / 2
        expected = np.array([ev1, ev2])

        assert np.allclose(res, expected, atol=tol, rtol=0)
    def test_single_wire_expectation(self, gate, obs, expected, theta, phi, varphi, tol):
        """Test that identity expectation value (i.e. the trace) is 1"""
        dev = DefaultQubitTF(wires=2)
        queue = [gate(theta, wires=0), gate(phi, wires=1), qml.CNOT(wires=[0, 1])]
        observables = [obs(wires=[i]) for i in range(2)]

        for i in range(len(observables)):
            observables[i].return_type = qml.operation.Expectation

        res = dev.execute(qml.CircuitGraph(queue + observables, {}, Wires([0, 1, 2])))
        assert np.allclose(res, expected(theta, phi), atol=tol, rtol=0)
    def test_var(self, theta, phi, varphi, tol):
        """Tests for variance calculation"""
        dev = DefaultQubitTF(wires=1)
        # test correct variance for <Z> of a rotated state

        queue = [qml.RX(phi, wires=0), qml.RY(theta, wires=0)]
        observables = [qml.PauliZ(wires=[0])]

        for i in range(len(observables)):
            observables[i].return_type = qml.operation.Variance

        res = dev.execute(qml.CircuitGraph(queue + observables, {}, Wires([0])))
        expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta) ** 2 * np.cos(2 * phi))
        assert np.allclose(res, expected, atol=tol, rtol=0)
    def test_sample_values_hermitian_multi_qubit(self, tol):
        """Tests if the samples of a multi-qubit Hermitian observable returned by sample have
        the correct values
        """
        shots = 1_000_000
        dev = plf.NumpyWavefunctionDevice(wires=2, shots=shots)
        theta = 0.543

        A = np.array([
            [1, 2j, 1 - 2j, 0.5j],
            [-2j, 0, 3 + 4j, 1],
            [1 + 2j, 3 - 4j, 0.75, 1.5 - 2j],
            [-0.5j, 1, 1.5 + 2j, -1],
        ])

        circuit_operations = [
            qml.RX(theta, wires=[0]),
            qml.RY(2 * theta, wires=[1]),
            qml.CNOT(wires=[0, 1]),
        ]

        O = qml.sample(qml.Hermitian(A, wires=[0, 1]))

        observables = [O]
        circuit_graph = qml.CircuitGraph(circuit_operations + observables, {},
                                         dev.wires)

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

        dev._samples = dev.generate_samples()

        s1 = dev.sample(O)

        # s1 should only contain the eigenvalues of
        # the hermitian matrix
        eigvals = np.linalg.eigvalsh(A)
        assert np.allclose(sorted(list(set(s1))),
                           sorted(eigvals),
                           atol=tol,
                           rtol=0)

        # make sure the mean matches the analytic mean
        expected = (88 * np.sin(theta) + 24 * np.sin(2 * theta) -
                    40 * np.sin(3 * theta) + 5 * np.cos(theta) -
                    6 * np.cos(2 * theta) + 27 * np.cos(3 * theta) + 6) / 32
        assert np.allclose(np.mean(s1), expected, atol=0.1, rtol=0)
예제 #9
0
    def graph(self):
        """Returns a directed acyclic graph representation of the recorded
        quantum circuit:

        >>> tape.graph
        <pennylane.circuit_graph.CircuitGraph object at 0x7fcc0433a690>

        Note that the circuit graph is only constructed once, on first call to this property,
        and cached for future use.

        Returns:
            .CircuitGraph: the circuit graph object
        """
        if self._graph is None:
            self._graph = qml.CircuitGraph(self.operations, self.observables,
                                           self.wires, self._par_info,
                                           self.trainable_params)

        return self._graph
    def test_sample_values_hermitian(self, tol):
        """Tests if the samples of a Hermitian observable returned by sample have
        the correct values
        """
        dev = plf.NumpyWavefunctionDevice(wires=1, shots=1_000_000)
        theta = 0.543

        A = np.array([[1, 2j], [-2j, 0]])

        circuit_operations = [qml.RX(theta, wires=[0])]

        O = qml.sample(qml.Hermitian(A, wires=[0]))

        observables = [O]
        circuit_graph = qml.CircuitGraph(circuit_operations + observables, {},
                                         dev.wires)

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

        dev._samples = dev.generate_samples()

        s1 = dev.sample(O)

        # s1 should only contain the eigenvalues of
        # the hermitian matrix
        eigvals = np.linalg.eigvalsh(A)
        assert np.allclose(sorted(list(set(s1))),
                           sorted(eigvals),
                           atol=tol,
                           rtol=0)

        # the analytic mean is 2*sin(theta)+0.5*cos(theta)+0.5
        assert np.allclose(np.mean(s1),
                           2 * np.sin(theta) + 0.5 * np.cos(theta) + 0.5,
                           atol=0.1,
                           rtol=0)

        # the analytic variance is 0.25*(sin(theta)-4*cos(theta))^2
        assert np.allclose(np.var(s1),
                           0.25 * (np.sin(theta) - 4 * np.cos(theta))**2,
                           atol=0.1,
                           rtol=0)
예제 #11
0
    def test_var_hermitian(self, theta, phi, varphi, tol):
        """Tests for variance calculation using an arbitrary Hermitian observable"""
        dev = DefaultQubitTF(wires=2)

        # test correct variance for <H> of a rotated state
        H = np.array([[4, -1 + 6j], [-1 - 6j, 2]])
        queue = [qml.RX(phi, wires=0), qml.RY(theta, wires=0)]
        observables = [qml.Hermitian(H, wires=[0])]

        for i in range(len(observables)):
            observables[i].return_type = qml.operation.Variance

        res = dev.execute(qml.CircuitGraph(queue + observables, {}))
        expected = 0.5 * (2 * np.sin(2 * theta) * np.cos(phi)**2 +
                          24 * np.sin(phi) * np.cos(phi) *
                          (np.sin(theta) - np.cos(theta)) +
                          35 * np.cos(2 * phi) + 39)

        assert np.allclose(res, expected, atol=tol, rtol=0)
예제 #12
0
    def test_var(self, tol, qvm):
        """Tests for variance calculation"""
        dev = plf.WavefunctionDevice(wires=2)

        phi = 0.543
        theta = 0.6543

        circuit_operations = [qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0])]

        O = qml.var(qml.PauliZ(wires=[0]))

        observables = [O]
        circuit_graph = qml.CircuitGraph(circuit_operations + observables, {})

        # test correct variance for <Z> of a rotated state
        dev.apply(circuit_graph.operations,
                  rotations=circuit_graph.diagonalizing_gates)

        var = dev.var(qml.PauliZ(0))
        expected = 0.25 * (3 - np.cos(2 * theta) -
                           2 * np.cos(theta)**2 * np.cos(2 * phi))

        self.assertAlmostEqual(var, expected, delta=tol)
예제 #13
0
    def metric_tensor(self,
                      args,
                      kwargs=None,
                      *,
                      diag_approx=False,
                      only_construct=False):
        """Evaluate the value of the metric tensor.

        Args:
            args (tuple[Any]): positional (differentiable) arguments
            kwargs (dict[str, Any]): auxiliary arguments
            diag_approx (bool): iff True, use the diagonal approximation
            only_construct (bool): Iff True, construct the circuits used for computing
                the metric tensor but do not execute them, and return None.

        Returns:
            array[float]: metric tensor
        """
        # pylint:disable=too-many-branches
        kwargs = kwargs or {}
        kwargs = self._default_args(kwargs)

        if self.circuit is None or self.mutable:
            # construct the circuit
            self._construct(args, kwargs)

        if self._metric_tensor_subcircuits is None:
            self._construct_metric_tensor(diag_approx=diag_approx)

        if only_construct:
            return None

        # temporarily store the parameter values in the Variable class
        self._set_variables(args, kwargs)

        tensor = np.zeros([self.num_variables, self.num_variables])

        # execute constructed metric tensor subcircuits
        for params, circuit in self._metric_tensor_subcircuits.items():
            self.device.reset()

            s = np.array(circuit["scale"])
            V = circuit["eigenbasis_matrix"]

            if not diag_approx:
                # block diagonal approximation

                unitary_op = qml.QubitUnitary(V,
                                              wires=list(range(
                                                  self.num_wires)),
                                              do_queue=False)

                if isinstance(self.device, qml.QubitDevice):
                    ops = circuit["queue"] + [unitary_op
                                              ] + [qml.expval(qml.PauliZ(0))]
                    circuit_graph = qml.CircuitGraph(ops, self.variable_deps)
                    self.device.execute(circuit_graph)
                else:
                    self.device.execute(
                        circuit["queue"] + [unitary_op],
                        [
                            qml.expval(qml.PauliZ(wire))
                            for wire in list(range(self.device.num_wires))
                        ],
                    )

                probs = list(self.device.probability())

                first_order_ev = np.zeros([len(params)])
                second_order_ev = np.zeros([len(params), len(params)])

                for idx, ev in circuit["Ki_expectations"]:
                    first_order_ev[idx] = ev @ probs

                for idx, ev in circuit["KiKj_expectations"]:
                    # idx is a 2-tuple (i, j), representing
                    # generators K_i, K_j
                    second_order_ev[idx] = ev @ probs

                    # since K_i and K_j are assumed to commute,
                    # <psi|K_j K_i|psi> = <psi|K_i K_j|psi>,
                    # and thus the matrix of second-order expectations
                    # is symmetric
                    second_order_ev[idx[1], idx[0]] = second_order_ev[idx]

                g = np.zeros([len(params), len(params)])

                for i, j in itertools.product(range(len(params)), repeat=2):
                    g[i, j] = (s[i] * s[j] *
                               (second_order_ev[i, j] -
                                first_order_ev[i] * first_order_ev[j]))

                row = np.array(params).reshape(-1, 1)
                col = np.array(params).reshape(1, -1)
                circuit["result"] = np.diag(g)
                tensor[row, col] = g

            else:
                # diagonal approximation
                if isinstance(self.device, qml.QubitDevice):
                    circuit_graph = qml.CircuitGraph(
                        circuit["queue"] + circuit["observable"],
                        self.variable_deps)
                    variances = self.device.execute(circuit_graph)
                else:
                    variances = self.device.execute(circuit["queue"],
                                                    circuit["observable"])

                circuit["result"] = s**2 * variances
                tensor[np.array(params), np.array(params)] = circuit["result"]

        return tensor