Exemplo n.º 1
0
def test_tape_mode_detection():
    """Test that the function `tape_mode_active` returns True
    only if tape mode is activated."""
    qml.disable_tape()
    assert not qml.tape_mode_active()
    qml.enable_tape()
    assert qml.tape_mode_active()
    def test_metric_tensor_tape_mode(self):
        """Test that the metric tensor can be calculated in tape mode, and that it is equal to a
        metric tensor calculated in non-tape mode."""
        if not qml.tape_mode_active():
            pytest.skip("This test is only intended for tape mode")

        dev = qml.device("default.qubit", wires=2)
        p = np.array([1., 1., 1.])

        def ansatz(params, **kwargs):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(params[2], wires=1)

        h = qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)])
        qnodes = qml.ExpvalCost(ansatz, h, dev)
        mt = qml.metric_tensor(qnodes)(p)
        assert qml.tape_mode_active()  # Check that tape mode is still active

        qml.disable_tape()

        @qml.qnode(dev)
        def circuit(params):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(params[2], wires=1)
            return qml.expval(qml.PauliZ(0))

        mt2 = circuit.metric_tensor([p])
        assert np.allclose(mt, mt2)
Exemplo n.º 3
0
    def test_backprop_gradients(self, mocker):
        """Test if KerasLayer is compatible with the backprop diff method."""
        if not qml.tape_mode_active():
            pytest.skip("This functionality is only supported in tape mode.")

        dev = qml.device("default.qubit.tf", wires=2)

        @qml.qnode(dev, interface="tf", diff_method="backprop")
        def f(inputs, weights):
            qml.templates.AngleEmbedding(inputs, wires=range(2))
            qml.templates.StronglyEntanglingLayers(weights, wires=range(2))
            return [qml.expval(qml.PauliZ(i)) for i in range(2)]

        weight_shapes = {"weights": (3, 2, 3)}

        qlayer = qml.qnn.KerasLayer(f, weight_shapes, output_dim=2)

        inputs = tf.ones((4, 2))

        with tf.GradientTape() as tape:
            out = tf.reduce_sum(qlayer(inputs))

        spy = mocker.spy(qml.tape.tapes.QubitParamShiftTape, "jacobian")

        grad = tape.gradient(out, qlayer.trainable_weights)
        assert grad is not None
        spy.assert_not_called()
    def test_grad_tf(self, qnodes, skip_if_no_tf_support, parallel, interface):
        """Test correct gradient of the QNodeCollection using
        the tf interface"""
        if parallel and qml.tape_mode_active():
            pytest.skip(
                "There appears to be a race condition when constructing TF tapes in parallel"
            )

        qnode1, qnode2 = qnodes

        # calculate the gradient of the collection using tf
        params = Variable([0.5643, -0.45])
        qc = qml.QNodeCollection([qnode1, qnode2])

        with tf.GradientTape() as tape:
            tape.watch(params)

            if parallel:
                with pytest.warns(UserWarning):
                    cost = sum(qc(params, parallel=parallel))
            else:
                cost = sum(qc(params, parallel=parallel))

            # the gradient will be None
            res = tape.gradient(cost, params).numpy()

        # calculate the gradient of the QNodes individually using tf
        params = Variable([0.5643, -0.45])

        with tf.GradientTape() as tape:
            tape.watch(params)
            cost = sum(qnode1(params) + qnode2(params))
            expected = tape.gradient(cost, params).numpy()

        assert np.all(res == expected)
Exemplo n.º 5
0
    def _evaluate_qnode(self, x):
        """Evaluates the QNode for a single input datapoint.

        Args:
            x (tensor): the datapoint

        Returns:
            tensor: output datapoint
        """
        if qml.tape_mode_active():
            return self._evaluate_qnode_tape_mode(x)

        qnode = self.qnode

        for arg in self.sig:
            if arg is not self.input_arg:  # Non-input arguments must always be positional
                w = self.qnode_weights[arg]

                qnode = functools.partial(qnode, w)
            else:
                if self.input_is_default:  # The input argument can be positional or keyword
                    qnode = functools.partial(qnode, **{self.input_arg: x})
                else:
                    qnode = functools.partial(qnode, x)
        return qnode().type(x.dtype)
Exemplo n.º 6
0
def _preprocess(weights, initial_layer_weights, wires):
    """Validate and pre-process inputs as follows:

    * Check the shapes of the two weights tensors.

    Args:
        weights (tensor_like): trainable parameters of the template
        initial_layer_weights (tensor_like): weight tensor for the initial rotation block, shape ``(M,)``
        wires (Wires): wires that template acts on

    Returns:
        int: number of times that the ansatz is repeated
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(weights)
        repeat = shape[0]

        if len(shape) > 1:
            if shape[1] != len(wires) - 1:
                raise ValueError(
                    f"Weights tensor must have second dimension of length {len(wires) - 1}; got {shape[1]}"
                )

            if shape[2] != 2:
                raise ValueError(
                    f"Weights tensor must have third dimension of length 2; got {shape[2]}"
                )

        shape2 = qml.math.shape(initial_layer_weights)
        if shape2 != (len(wires), ):
            raise ValueError(
                f"Initial layer weights must be of shape {(len(wires),)}; got {shape2}"
            )

    else:
        repeat = check_number_of_layers([weights])

        expected_shape_initial = (len(wires), )
        check_shape(
            initial_layer_weights,
            expected_shape_initial,
            msg="Initial layer weights must be of shape {}; got {}"
            "".format(expected_shape_initial,
                      get_shape(initial_layer_weights)),
        )

        if len(wires) in [0, 1]:
            expected_shape_weights = (0, )
        else:
            expected_shape_weights = (repeat, len(wires) - 1, 2)

        check_shape(
            weights,
            expected_shape_weights,
            msg="Weights tensor must be of shape {}; got {}"
            "".format(expected_shape_weights, get_shape(weights)),
        )
    return repeat
Exemplo n.º 7
0
    def test_exception_wrong_dim(self):
        """Verifies that exception is raised if the
        number of dimensions of features is incorrect."""
        if not qml.tape_mode_active():
            pytest.skip("This validation is only performed in tape mode")

        dev = qml.device("default.qubit", wires=4)
        initial_layer = np.random.randn(2)

        @qml.qnode(dev)
        def circuit(initial_layer, weights):
            SimplifiedTwoDesign(initial_layer, weights, wires=range(2))
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError,
                           match="Weights tensor must have second dimension"):
            weights = np.random.randn(2, 2, 2)
            circuit(initial_layer, weights)

        with pytest.raises(ValueError,
                           match="Weights tensor must have third dimension"):
            weights = np.random.randn(2, 1, 3)
            circuit(initial_layer, weights)

        with pytest.raises(ValueError,
                           match="Initial layer weights must be of shape"):
            initial_layer = np.random.randn(3)
            weights = np.random.randn(2, 1, 2)
            circuit(initial_layer, weights)
Exemplo n.º 8
0
    def test_optimize_grad(self):
        """Test that the gradient of ExpvalCost is accessible and correct when using observable
        optimization and the autograd interface."""
        if not qml.tape_mode_active():
            pytest.skip("This test is only intended for tape mode")

        dev = qml.device("default.qubit", wires=4)
        hamiltonian = big_hamiltonian

        cost = qml.ExpvalCost(qml.templates.StronglyEntanglingLayers,
                              hamiltonian,
                              dev,
                              optimize=True)
        cost2 = qml.ExpvalCost(qml.templates.StronglyEntanglingLayers,
                               hamiltonian,
                               dev,
                               optimize=False)

        w = qml.init.strong_ent_layers_uniform(2, 4, seed=1967)

        dc = qml.grad(cost)(w)
        exec_opt = dev.num_executions
        dev._num_executions = 0

        dc2 = qml.grad(cost2)(w)
        exec_no_opt = dev.num_executions

        assert exec_no_opt > exec_opt
        assert np.allclose(dc, big_hamiltonian_grad)
        assert np.allclose(dc2, big_hamiltonian_grad)
Exemplo n.º 9
0
    def test_qnode_collection_integration(self):
        """Test that a PassthruQNode using default.qubit.jax works with QNodeCollections."""
        dev = qml.device("default.qubit.jax", wires=2)

        def ansatz(weights, **kwargs):
            qml.RX(weights[0], wires=0)
            qml.RY(weights[1], wires=1)
            qml.CNOT(wires=[0, 1])

        obs_list = [
            qml.PauliX(0) @ qml.PauliY(1),
            qml.PauliZ(0),
            qml.PauliZ(0) @ qml.PauliZ(1)
        ]
        qnodes = qml.map(ansatz, obs_list, dev, interface="jax")
        if not qml.tape_mode_active():
            assert qnodes.interface == "jax"

        weights = jnp.array([0.1, 0.2])

        def cost(weights):
            return jnp.sum(jnp.array(qnodes(weights)))

        grad = jax.grad(cost)(weights)
        assert grad.shape == weights.shape
Exemplo n.º 10
0
def _preprocess(weight, wires):
    """Validate and pre-process inputs as follows:

    * Check the shape of the weights tensor.
    * Check that there are at least 2 wires.

    Args:
        weight (tensor_like): trainable parameters of the template
        wires (Wires): wires that template acts on
    """
    if len(wires) < 2:
        raise ValueError("expected at least two wires; got {}".format(
            len(wires)))

    if qml.tape_mode_active():

        shape = qml.math.shape(weight)
        if shape != ():
            raise ValueError(
                f"Weight must be a scalar tensor {()}; got shape {shape}.")

    else:
        expected_shape = ()
        check_shape(
            weight,
            expected_shape,
            msg="Weight must be a scalar; got shape {}".format(
                expected_shape, get_shape(weight)),
        )
Exemplo n.º 11
0
def _preprocess(weights):
    """Validate and pre-process inputs as follows:

    * Check that the weights tensor is 2-dimensional.

    Args:
        weights (tensor_like): trainable parameters of the template

    Returns:
        int: number of times that the ansatz is repeated
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(weights)

        if len(shape) != 2:
            raise ValueError(
                f"Weights tensor must be 2-dimensional; got shape {shape}")

        repeat = shape[0]

    else:
        repeat = check_number_of_layers([weights])
        n_rots = get_shape(weights)[1]

        expected_shape = (repeat, n_rots)
        check_shape(
            weights,
            expected_shape,
            msg="'weights' must be of shape {}; got {}"
            "".format(expected_shape, get_shape(weights)),
        )

    return repeat
Exemplo n.º 12
0
    def test_optimize_grad_torch(self, torch_support):
        """Test that the gradient of ExpvalCost is accessible and correct when using observable
        optimization and the Torch interface."""
        if not qml.tape_mode_active():
            pytest.skip("This test is only intended for tape mode")
        if not torch_support:
            pytest.skip("This test requires Torch")

        dev = qml.device("default.qubit", wires=4)
        hamiltonian = big_hamiltonian

        cost = qml.ExpvalCost(
            qml.templates.StronglyEntanglingLayers,
            hamiltonian,
            dev,
            optimize=True,
            interface="torch",
        )

        w = torch.tensor(qml.init.strong_ent_layers_uniform(2, 4, seed=1967),
                         requires_grad=True)

        res = cost(w)
        res.backward()
        dc = w.grad.detach().numpy()

        assert np.allclose(dc, big_hamiltonian_grad)
    def test_exception_wrong_dim(self):
        """Verifies that exception is raised if the
        number of dimensions of features is incorrect."""
        if not qml.tape_mode_active():
            pytest.skip("This validation is only performed in tape mode")

        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev)
        def circuit(state_vector):
            MottonenStatePreparation(state_vector, wires=range(2))
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError,
                           match="State vector must be a one-dimensional"):
            state_vector = np.array([[0, 1]])
            circuit(state_vector)

        with pytest.raises(ValueError, match="State vector must be of length"):
            state_vector = np.array([0, 1])
            circuit(state_vector)

        with pytest.raises(ValueError,
                           match="State vector has to be of length"):
            state_vector = np.array([0, 2, 0, 0])
            circuit(state_vector)
Exemplo n.º 14
0
    def test_optimize_grad_tf(self, tf_support):
        """Test that the gradient of ExpvalCost is accessible and correct when using observable
        optimization and the TensorFlow interface."""
        if not qml.tape_mode_active():
            pytest.skip("This test is only intended for tape mode")
        if not tf_support:
            pytest.skip("This test requires TensorFlow")

        dev = qml.device("default.qubit", wires=4)
        hamiltonian = big_hamiltonian

        cost = qml.ExpvalCost(qml.templates.StronglyEntanglingLayers,
                              hamiltonian,
                              dev,
                              optimize=True,
                              interface="tf")

        w = tf.Variable(qml.init.strong_ent_layers_uniform(2, 4, seed=1967))

        with tf.GradientTape() as tape:
            res = cost(w)

        dc = tape.gradient(res, w).numpy()

        assert np.allclose(dc, big_hamiltonian_grad)
Exemplo n.º 15
0
    def test_exception_wrong_dim(self):
        """Verifies that exception is raised if the
        number of dimensions of features is incorrect."""
        if not qml.tape_mode_active():
            pytest.skip("This validation is only performed in tape mode")

        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev)
        def circuit(weights):
            StronglyEntanglingLayers(weights, wires=range(2))
            return qml.expval(qml.PauliZ(0))

        with pytest.raises(ValueError,
                           match="Weights tensor must have second dimension"):
            weights = np.random.randn(2, 1, 3)
            circuit(weights)

        with pytest.raises(ValueError,
                           match="Weights tensor must have third dimension"):
            weights = np.random.randn(2, 2, 1)
            circuit(weights)

        with pytest.raises(ValueError,
                           match="Weights tensor must be 3-dimensional"):
            weights = np.random.randn(2, 2, 3, 1)
            circuit(weights)
Exemplo n.º 16
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
        """
        if self._multiple_devices:
            warnings.warn(
                "ExpvalCost was instantiated with multiple devices. Only the first device "
                "will be used to evaluate the metric tensor.")

        if qml.tape_mode_active():
            return self._qnode_for_metric_tensor_in_tape_mode.metric_tensor(
                args=args,
                kwargs=kwargs,
                diag_approx=diag_approx,
                only_construct=only_construct)

        # all the qnodes share the same ansatz so we select the first
        return self.qnodes.qnodes[0].metric_tensor(
            args=args,
            kwargs=kwargs,
            diag_approx=diag_approx,
            only_construct=only_construct)
Exemplo n.º 17
0
    def test_direct_qnode_integration(self):
        """Test that a regular QNode renders correctly."""
        dev = qml.device("default.qubit", wires=2)

        @qml.qnode(dev)
        def qfunc(a, w):
            qml.Hadamard(0)
            qml.CRX(a, wires=[0, 1])
            qml.Rot(w[0], w[1], w[2], wires=[1])
            qml.CRX(-a, wires=[0, 1])

            return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

        res = qfunc(2.3, [1.2, 3.2, 0.7])

        assert qfunc.draw() == (
            " 0: ──H──╭C────────────────────────────╭C─────────╭┤ ⟨Z ⊗ Z⟩ \n"
            + " 1: ─────╰RX(2.3)──Rot(1.2, 3.2, 0.7)──╰RX(-2.3)──╰┤ ⟨Z ⊗ Z⟩ \n"
        )

        assert qfunc.draw(charset="ascii") == (
            " 0: --H--+C----------------------------+C---------+| <Z @ Z> \n"
            + " 1: -----+RX(2.3)--Rot(1.2, 3.2, 0.7)--+RX(-2.3)--+| <Z @ Z> \n"
        )

        if not qml.tape_mode_active():
            assert qfunc.draw(show_variable_names=True) == (
                " 0: ──H──╭C─────────────────────────────╭C─────────╭┤ ⟨Z ⊗ Z⟩ \n"
                + " 1: ─────╰RX(a)──Rot(w[0], w[1], w[2])──╰RX(-1*a)──╰┤ ⟨Z ⊗ Z⟩ \n"
            )
Exemplo n.º 18
0
    def call(self, inputs):
        """Evaluates the QNode on input data using the initialized weights.

        Args:
            inputs (tensor): data to be processed

        Returns:
            tensor: output data
        """
        outputs = []
        for x in inputs:  # iterate over batch

            if qml.tape_mode_active():
                res = self._evaluate_qnode_tape_mode(x)
                outputs.append(res)
            else:
                # The QNode can require some passed arguments to be positional and others to be
                # keyword. The following loops through input arguments in order and uses
                # functools.partial to bind the argument to the QNode.
                qnode = self.qnode

                for arg in self.sig:
                    if arg is not self.input_arg:  # Non-input arguments must always be positional
                        w = self.qnode_weights[arg]
                        qnode = functools.partial(qnode, w)
                outputs.append(self.quanv(x))

        return tf.stack(outputs)
Exemplo n.º 19
0
def _preprocess(weights, wires):
    """Validate and pre-process inputs as follows:

    * Check the shape of the weights tensor.

    Args:
        weights (tensor_like): trainable parameters of the template
        wires (Wires): wires that template acts on
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(weights)
        if shape != (4**len(wires) - 1, ):
            raise ValueError(
                f"Weights tensor must be of shape {(4 ** len(wires) - 1,)}; got {shape}."
            )

    else:
        expected_shape = (4**len(wires) - 1, )
        check_shape(
            weights,
            expected_shape,
            msg="Weights tensor must be of shape {}; got {}."
            "".format(expected_shape, get_shape(weights)),
        )
Exemplo n.º 20
0
def _preprocess(features, wires):
    """Validate and pre-process inputs as follows:

    * Check that the features tensor is one-dimensional.
    * Check that the first dimension of the features tensor
      has length :math:`n`, where :math:`n` is the number of qubits.
    * Check that the entries of the features tensor are zeros and ones.

    Args:
        features (tensor_like): input features to pre-process
        wires (Wires): wires that template acts on

    Returns:
        array: numpy array representation of the features tensor
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(features)

        if len(shape) != 1:
            raise ValueError(
                f"Features must be one-dimensional; got shape {shape}.")

        n_features = shape[0]
        if n_features != len(wires):
            raise ValueError(
                f"Features must be of length {len(wires)}; got length {n_features}."
            )

        features = list(qml.math.toarray(features))

        if set(features) != {0, 1}:
            raise ValueError(
                f"Basis state must only consist of 0s and 1s; got {features}")

        return features

    # non-tape mode
    check_type(
        features,
        [Iterable],
        msg="Features must be iterable; got type {}".format(type(features)),
    )

    expected_shape = (len(wires), )
    check_shape(
        features,
        expected_shape,
        msg="Features must be of shape {}; got {}"
        "".format(expected_shape, get_shape(features)),
    )

    if any([b not in [0, 1] for b in features]):
        raise ValueError(
            "Basis state must only consist of 0s and 1s; got {}".format(
                features))

    return features
Exemplo n.º 21
0
def _preprocess(parameters, pattern, wires):
    """Validate and pre-process inputs as follows:

    * Check that pattern is recognised, or use default pattern if None.
    * Check the dimension of the parameters
    * Create wire sequence of the pattern.

    Args:
        parameters (tensor_like): trainable parameters of the template
        pattern (str): specifies the wire pattern
        wires (Wires): wires that template acts on

    Returns:
        wire_sequence, parameters: preprocessed pattern and parameters
    """

    if isinstance(pattern, str):
        _wires = wires
        if pattern not in OPTIONS:
            raise ValueError(f"did not recognize pattern {pattern}".format())
    else:
        # turn custom pattern into list of Wires objects
        _wires = [Wires(w) for w in pattern]
        # set "pattern" to "custom", indicating that custom settings have to be used
        pattern = "custom"

    # check that there are enough parameters for pattern
    if parameters is not None:

        if qml.tape_mode_active():
            shape = qml.math.shape(parameters)

            # expand dimension so that parameter sets for each unitary can be unpacked
            if len(shape) == 1:
                parameters = qml.math.expand_dims(parameters, 1)

        else:
            shape = get_shape(parameters)

            # expand dimension so that parameter sets for each unitary can be unpacked
            if len(shape) == 1:
                parameters = [[p] for p in parameters]

        # specific error message for ring edge case of 2 wires
        if (pattern == "ring") and (len(wires) == 2) and (shape[0] != 1):
            raise ValueError(
                "the ring pattern with 2 wires is an exception and only applies one unitary"
            )
        num_params = PATTERN_TO_NUM_PARAMS[pattern](_wires)
        if shape[0] != num_params:
            raise ValueError(
                "Parameters must contain entries for {} unitaries; got {} entries".format(
                    num_params, shape[0]
                )
            )

    wire_sequence = PATTERN_TO_WIRES[pattern](_wires)
    return wire_sequence, parameters
Exemplo n.º 22
0
def _preprocess(weights, wires, init_state):
    """Validate and pre-process inputs as follows:

    * Check that the weights tensor has the correct shape.
    * Extract a wire list for the subroutines of this template.
    * Cast initial state to a numpy array.

    Args:
        weights (tensor_like): trainable parameters of the template
        wires (Wires): wires that template acts on
        init_state (tensor_like): shape ``(len(wires),)`` tensor

    Returns:
        int, list[Wires], array: number of times that the ansatz is repeated, wires pattern,
            and preprocessed initial state
    """
    if len(wires) < 2:
        raise ValueError(
            "This template requires the number of qubits to be greater than one;"
            "got a wire sequence with {} elements".format(len(wires)))

    if qml.tape_mode_active():

        shape = qml.math.shape(weights)

        if len(shape) != 3:
            raise ValueError(
                f"Weights tensor must be 3-dimensional; got shape {shape}")

        if shape[1] != len(wires) - 1:
            raise ValueError(
                f"Weights tensor must have second dimension of length {len(wires) - 1}; got {shape[1]}"
            )

        if shape[2] != 2:
            raise ValueError(
                f"Weights tensor must have third dimension of length 2; got {shape[2]}"
            )

        repeat = shape[0]

    else:
        repeat = get_shape(weights)[0]

        expected_shape = (repeat, len(wires) - 1, 2)
        check_shape(
            weights,
            expected_shape,
            msg="Weights tensor must be of shape {}; got {}".format(
                expected_shape, get_shape(weights)),
        )

    nm_wires = [wires.subset([l, l + 1]) for l in range(0, len(wires) - 1, 2)]
    nm_wires += [wires.subset([l, l + 1]) for l in range(1, len(wires) - 1, 2)]
    # we can extract the numpy representation here
    # since init_state can never be differentiable
    init_state = qml.math.toarray(init_state)
    return repeat, nm_wires, init_state
Exemplo n.º 23
0
    def test_qubit_circuit_with_variable_names(
        self, parameterized_qubit_qnode, drawn_parameterized_qubit_circuit_with_variable_names
    ):
        """Test that a parametrized qubit circuit renders correctly with variable names."""
        if qml.tape_mode_active():
            pytest.skip("show_variable_names not supported in tape mode")

        output = parameterized_qubit_qnode.draw(show_variable_names=True)
        assert output == drawn_parameterized_qubit_circuit_with_variable_names
Exemplo n.º 24
0
    def __init__(
        self,
        ansatz,
        hamiltonian,
        device,
        interface="autograd",
        diff_method="best",
        optimize=False,
        **kwargs,
    ):
        coeffs, observables = hamiltonian.terms

        self.hamiltonian = hamiltonian
        """Hamiltonian: the hamiltonian defining the VQE problem."""

        self.qnodes = None
        """QNodeCollection: The QNodes to be evaluated. Each QNode corresponds to the expectation
        value of each observable term after applying the circuit ansatz."""

        self._optimize = optimize

        if self._optimize:
            if not qml.tape_mode_active():
                raise ValueError(
                    "Observable optimization is only supported in tape mode. Tape "
                    "mode can be enabled with the command:\n"
                    "qml.enable_tape()"
                )

            obs_groupings, coeffs_groupings = qml.grouping.group_observables(observables, coeffs)

            wires = device.wires.tolist()

            @qml.qnode(device, interface=interface, diff_method=diff_method, **kwargs)
            def circuit(*qnode_args, obs, **qnode_kwargs):
                """Converting ansatz into a full circuit including measurements"""
                ansatz(*qnode_args, wires=wires, **qnode_kwargs)
                return [qml.expval(o) for o in obs]

            def cost_fn(*qnode_args, **qnode_kwargs):
                """Combine results from grouped QNode executions with grouped coefficients"""
                total = 0
                for o, c in zip(obs_groupings, coeffs_groupings):
                    res = circuit(*qnode_args, obs=o, **qnode_kwargs)
                    total += sum([r * c_ for r, c_ in zip(res, c)])
                return total

            self.cost_fn = cost_fn

        else:
            self.qnodes = qml.map(
                ansatz, observables, device, interface=interface, diff_method=diff_method, **kwargs
            )

            self.cost_fn = qml.dot(coeffs, self.qnodes)
Exemplo n.º 25
0
def _preprocess(weights, wires, ranges):
    """Validate and pre-process inputs as follows:

    * Check the shape of the weights tensor.
    * If ranges is None, define a default.

    Args:
        weights (tensor_like): trainable parameters of the template
        wires (Wires): wires that template acts on
        ranges (Sequence[int]): range for each subsequent layer

    Returns:
        int, list[int]: number of times that the ansatz is repeated and preprocessed ranges
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(weights)
        repeat = shape[0]

        if len(shape) != 3:
            raise ValueError(
                f"Weights tensor must be 3-dimensional; got shape {shape}")

        if shape[1] != len(wires):
            raise ValueError(
                f"Weights tensor must have second dimension of length {len(wires)}; got {shape[1]}"
            )

        if shape[2] != 3:
            raise ValueError(
                f"Weights tensor must have third dimension of length 3; got {shape[2]}"
            )

    else:

        repeat = check_number_of_layers([weights])

        expected_shape = (repeat, len(wires), 3)
        check_shape(
            weights,
            expected_shape,
            msg="Weights tensor must be of shape {}; got {}"
            "".format(expected_shape, get_shape(weights)),
        )

    if len(wires) > 1:
        if ranges is None:
            # tile ranges with iterations of range(1, n_wires)
            ranges = [(l % (len(wires) - 1)) + 1 for l in range(repeat)]
    else:
        ranges = [0] * repeat

    return repeat, ranges
Exemplo n.º 26
0
    def wrapper(*args, **kwargs):
        import pennylane as qml

        recorder_class = OperationRecorder

        if qml.tape_mode_active():
            recorder_class = qml.tape.TapeOperationRecorder

        with recorder_class() as rec:
            func(*args, **kwargs)

        return rec.queue
Exemplo n.º 27
0
    def test_train_model_dm(self, model_dm, batch_size, n_qubits, output_dim):
        """Test if a model can train using the KerasLayer when QNode returns a density_matrix().
        The model is composed of two KerasLayers sandwiched between Dense neural network layers,
        and the dataset is simply input and output vectors of zeros."""

        if not qml.tape_mode_active():
            pytest.skip()

        x = np.zeros((batch_size, n_qubits))
        y = np.zeros((batch_size, output_dim[0] * output_dim[1]))

        model_dm.compile(optimizer="sgd", loss="mse")

        model_dm.fit(x, y, batch_size=batch_size, verbose=0)
Exemplo n.º 28
0
def _preprocess(basis_state, wires):
    """Validate and pre-process inputs as follows:

    * Check the shape of the basis state.
    * Cast basis state to a numpy array.

    Args:
        basis_state (tensor_like): basis state to prepare
        wires (Wires): wires that template acts on

    Returns:
        array: preprocessed basis state
    """

    if qml.tape_mode_active():

        shape = qml.math.shape(basis_state)

        if len(shape) != 1:
            raise ValueError(f"Basis state must be one-dimensional; got shape {shape}.")

        n_bits = shape[0]
        if n_bits != len(wires):
            raise ValueError(f"Basis state must be of length {len(wires)}; got length {n_bits}.")

        basis_state = list(qml.math.toarray(basis_state))

        if not all(bit in [0, 1] for bit in basis_state):
            raise ValueError(f"Basis state must only consist of 0s and 1s; got {basis_state}")

        # we return the input as a list of values, since
        # it is not differentiable
        return basis_state

    expected_shape = (len(wires),)
    check_shape(
        basis_state,
        expected_shape,
        msg="Basis state must be of shape {}; got {}."
        "".format(expected_shape, get_shape(basis_state)),
    )

    # basis_state is guaranteed to be a list of binary values
    if any([b not in [0, 1] for b in basis_state]):
        raise ValueError(
            "Basis state must only contain values of 0 and 1; got {}".format(basis_state)
        )

    return basis_state
Exemplo n.º 29
0
    def test_non_input_defaults_tape_mode(self):
        """Test that everything works when default arguments that are not the input argument are
        present in the QNode in tape mode"""
        if not qml.tape_mode_active():
            pytest.skip("This functionality is only supported in tape mode.")

        n_qubits = 2
        output_dim = 2

        dev = qml.device("default.qubit", wires=n_qubits)
        w = {
            "w1": (3, n_qubits, 3),
            "w2": (1, ),
            "w3": 1,
            "w4": [3],
            "w5": (2, n_qubits, 3),
            "w6": 3,
            "w7": 0,
        }

        @qml.qnode(dev, interface="tf")
        def c(inputs, w1, w2, w4, w5, w6, w7, w3=0.5):
            """A circuit that embeds data using the AngleEmbedding and then performs a variety of
            operations. The output is a PauliZ measurement on the first output_dim qubits. One set of
            parameters, w5, are specified as non-trainable."""
            qml.templates.AngleEmbedding(inputs, wires=list(range(n_qubits)))
            qml.templates.StronglyEntanglingLayers(w1,
                                                   wires=list(range(n_qubits)))
            qml.RX(w2[0], wires=0 % n_qubits)
            qml.RX(w3, wires=1 % n_qubits)
            qml.Rot(*w4, wires=2 % n_qubits)
            qml.templates.StronglyEntanglingLayers(w5,
                                                   wires=list(range(n_qubits)))
            qml.Rot(*w6, wires=3 % n_qubits)
            qml.RX(w7, wires=4 % n_qubits)
            return [qml.expval(qml.PauliZ(i)) for i in range(output_dim)]

        layer = KerasLayer(c, w, output_dim=output_dim)
        x = tf.ones((2, n_qubits))

        layer_out = layer(x)
        circ_weights = layer.qnode_weights.copy()
        circ_weights["w4"] = tf.convert_to_tensor(
            circ_weights["w4"])  # To allow for slicing
        circ_weights["w6"] = tf.convert_to_tensor(circ_weights["w6"])
        circuit_out = c(x[0], **circ_weights)

        assert np.allclose(layer_out, circuit_out)
Exemplo n.º 30
0
    def test_jax_interface_gradient(self, operation, diff_method, tol):
        """Tests that the gradient of an arbitrary U3 gate is correct
        using the Jax interface, using a variety of differentiation methods."""
        dev = qml.device("default.qubit.jax", wires=1)

        @qml.qnode(dev, diff_method=diff_method, interface="jax")
        def circuit(x, weights, w=None):
            """In this example, a mixture of scalar
            arguments, array arguments, and keyword arguments are used."""
            qml.QubitStateVector(1j * jnp.array([1, -1]) / jnp.sqrt(2),
                                 wires=w)
            operation(x, weights[0], weights[1], wires=w)
            return qml.expval(qml.PauliX(w))

        # Check that the correct QNode type is being used.
        if not qml.tape_mode_active():
            if diff_method == "backprop":
                assert isinstance(circuit, qml.qnodes.PassthruQNode)
                assert not hasattr(circuit, "jacobian")
            else:
                assert not isinstance(circuit, qml.qnodes.PassthruQNode)
                assert hasattr(circuit, "jacobian")

        def cost(params):
            """Perform some classical processing"""
            return (circuit(params[0], params[1:], w=0)**2).reshape(())

        theta = 0.543
        phi = -0.234
        lam = 0.654

        params = jnp.array([theta, phi, lam])

        res = cost(params)
        expected_cost = (jnp.sin(lam) * jnp.sin(phi) -
                         jnp.cos(theta) * jnp.cos(lam) * jnp.cos(phi))**2
        assert jnp.allclose(res, expected_cost, atol=tol, rtol=0)

        res = jax.grad(cost)(params)
        expected_grad = (jnp.array([
            jnp.sin(theta) * jnp.cos(lam) * jnp.cos(phi),
            jnp.cos(theta) * jnp.cos(lam) * jnp.sin(phi) +
            jnp.sin(lam) * jnp.cos(phi),
            jnp.cos(theta) * jnp.sin(lam) * jnp.cos(phi) +
            jnp.cos(lam) * jnp.sin(phi),
        ]) * 2 * (jnp.sin(lam) * jnp.sin(phi) -
                  jnp.cos(theta) * jnp.cos(lam) * jnp.cos(phi)))
        assert jnp.allclose(res, expected_grad, atol=tol, rtol=0)