예제 #1
0
 def perturb(
     self,
     amount: Optional[Union[int, float]] = None,
     mode: PerturbationMode = PerturbationMode.INVERT,
     value: Optional[float] = np.pi,
 ):
     count = self._count_for_amount(amount=amount)
     if count == 0:
         return
     if mode == PerturbationMode.SET:  # INVERT might be added here
         indices = np.array([list(index) for index in np.ndindex(*self.mask.shape)])
     elif mode == PerturbationMode.RESET:
         indices = np.argwhere(self.mask != 0)
     else:
         raise NotImplementedError(f"The perturbation mode {mode} is not supported")
     if indices.size == 0:
         return
     indices = tuple(
         zip(
             *indices[
                 np.random.choice(
                     len(indices), min(count, len(indices)), replace=False
                 )
             ]
         )
     )
     if mode == PerturbationMode.SET and value is not None:
         self[indices] = value
     elif mode == PerturbationMode.RESET:
         self[indices] = 0
예제 #2
0
    def compute_grad(
        self, objective_fn, args, kwargs
    ):  # pylint: disable=signature-differs,arguments-differ
        r"""Compute gradient of the objective function, as well as the variance of the gradient,
        at the given point.

        Args:
            objective_fn (function): the objective function for optimization
            args: arguments to the objective function
            kwargs: keyword arguments to the objective function

        Returns:
            tuple[array[float], array[float]]: a tuple of NumPy arrays containing the gradient
            :math:`\nabla f(x^{(t)})` and the variance of the gradient
        """
        if isinstance(objective_fn, qml.ExpvalCost):
            grads = self._single_shot_expval_gradients(objective_fn, args, kwargs)
        elif isinstance(objective_fn, qml.QNode) or hasattr(objective_fn, "device"):
            grads = self._single_shot_qnode_gradients(objective_fn, args, kwargs)
        else:
            raise ValueError(
                "The objective function must either be encoded as a single QNode or "
                "an ExpvalCost object for the Shot adaptive optimizer. "
            )

        # grads will have dimension [max(self.s), *params.shape]
        # For each parameter, we want to truncate the number of shots to self.s[idx],
        # and take the mean and the variance.
        gradients = []
        gradient_variances = []

        for i, grad in enumerate(grads):
            p_ind = np.ndindex(*grad.shape[1:])

            g = np.zeros_like(grad[0])
            s = np.zeros_like(grad[0])

            for idx in p_ind:
                grad_slice = grad[(slice(0, self.s[i][idx]),) + idx]
                g[idx] = np.mean(grad_slice)
                s[idx] = np.var(grad_slice, ddof=1)

            gradients.append(g)
            gradient_variances.append(s)

        return gradients, gradient_variances
예제 #3
0
    def perturb(
        self,
        amount: Optional[Union[int, float]] = None,
        mode: PerturbationMode = PerturbationMode.INVERT,
    ):
        """
        Perturbs the Mask by the given ``mode`` of type :py:class:`~.PerturbationMode`
        ``amount`` times. If no amount is given or ``amount=None``, a random ``amount``
        is determined given by the actual size of the py:attr:`~.mask`. If ``amount``
        is smaller than `1`, it is interpreted as the fraction of the py:attr:`~.mask` s
        size.
        Note that the ``amount`` is automatically limited to the actual size of the
        py:attr:`~.mask`.

        :param amount: Number of items to perturb given either by an absolute amount
            when amount >= 1 or a fraction of the mask, defaults to None
        :param mode: How to perturb, defaults to PerturbationMode.INVERT
        :raises NotImplementedError: Raised in case of an unknown mode
        """
        assert (
            amount is None or amount >= 0
        ), "Negative values are not supported, please use PerturbationMode.REMOVE"
        if amount is not None:
            if amount < 1:
                amount *= self.mask.size
            amount = round(amount)
        count = abs(amount) if amount is not None else rand.randrange(
            0, self.mask.size)
        if count == 0:
            return
        if mode == PerturbationMode.ADD:
            indices = np.argwhere(~self.mask)
        elif mode == PerturbationMode.INVERT:
            indices = np.array(
                [list(index) for index in np.ndindex(*self.mask.shape)])
        elif mode == PerturbationMode.REMOVE:
            indices = np.argwhere(self.mask)
        else:
            raise NotImplementedError(
                f"The perturbation mode {mode} is not supported")
        if indices.size == 0:
            return
        indices = tuple(
            zip(*indices[np.random.choice(
                len(indices), min(count, len(indices)), replace=False)]))
        self[indices] = ~self.mask[indices]
예제 #4
0
def plot_decision_boundaries(classifier, ax, N_gridpoints=14):
    _xx, _yy = np.meshgrid(np.linspace(-1, 1, N_gridpoints),
                           np.linspace(-1, 1, N_gridpoints))

    _zz = np.zeros_like(_xx)
    for idx in np.ndindex(*_xx.shape):
        _zz[idx] = classifier.predict(
            np.array([_xx[idx], _yy[idx]])[np.newaxis, :])

    plot_data = {'_xx': _xx, '_yy': _yy, '_zz': _zz}
    ax.contourf(_xx,
                _yy,
                _zz,
                cmap=mpl.colors.ListedColormap(['#FF0000', '#0000FF']),
                alpha=.2,
                levels=[-1, 0, 1])
    dataset.plot(ax)

    return plot_data
예제 #5
0
def plot_decision_boundaries(classifier, ax, N_gridpoints=14):
    _xx, _yy = np.meshgrid(np.linspace(-1, 1, N_gridpoints),
                           np.linspace(-1, 1, N_gridpoints))

    _zz = np.zeros_like(_xx)
    for idx in np.ndindex(*_xx.shape):
        _zz[idx] = classifier.predict(
            np.array([_xx[idx], _yy[idx]])[np.newaxis, :])

    plot_data = {"_xx": _xx, "_yy": _yy, "_zz": _zz}
    ax.contourf(
        _xx,
        _yy,
        _zz,
        cmap=mpl.colors.ListedColormap(["#FF0000", "#0000FF"]),
        alpha=0.2,
        levels=[-1, 0, 1],
    )
    plot_double_cake_data(X, Y, ax)

    return plot_data
예제 #6
0
    def test_Rot_gradient(self, mocker, theta, shift, tol):
        """Tests that the automatic gradient of an arbitrary Euler-angle-parameterized gate is correct."""
        spy = mocker.spy(qml.gradients.parameter_shift,
                         "_get_operation_recipe")
        dev = qml.device("default.qubit", wires=1)
        params = np.array([theta, theta**3, np.sqrt(2) * theta])

        with qml.tape.JacobianTape() as tape:
            qml.QubitStateVector(np.array([1.0, -1.0], requires_grad=False) /
                                 np.sqrt(2),
                                 wires=0)
            qml.Rot(*params, wires=[0])
            qml.expval(qml.PauliZ(0))

        tape.trainable_params = {1, 2, 3}

        tapes, fn = qml.gradients.param_shift(tape, shift=shift)
        assert len(tapes) == 2 * len(tape.trainable_params)

        autograd_val = fn(dev.batch_execute(tapes))
        manualgrad_val = np.zeros_like(autograd_val)

        for idx in list(np.ndindex(*params.shape)):
            s = np.zeros_like(params)
            s[idx] += np.pi / 2

            forward = tape.execute(dev, params=params + s)
            backward = tape.execute(dev, params=params - s)

            manualgrad_val[0, idx] = (forward - backward) / 2

        assert np.allclose(autograd_val, manualgrad_val, atol=tol, rtol=0)
        assert spy.call_args[1]["shift"] == shift

        # compare to finite differences
        tapes, fn = qml.gradients.finite_diff(tape)
        numeric_val = fn(dev.batch_execute(tapes))
        assert np.allclose(autograd_val, numeric_val, atol=tol, rtol=0)