Exemple #1
0
def cross_entropy(predictions: np.ndarray,
                  targets: np.ndarray,
                  epsilon: float = 1e-15) -> float:
    """
    Cross entropy calculation between :py:attr:`targets` (encoded as one-hot vectors)
    and :py:attr:`predictions`. Predictions are normalized to sum up to `1.0`.

    .. note::

        The implementation of this function is based on the discussion on
        `StackOverflow <https://stackoverflow.com/a/47398312/10138546>`_.

        Due to ArrayBoxes that are required for automatic differentiation, we currently
        use this implementation instead of implementations provided by sklearn for
        example.

    :param predictions: Predictions in same order as targets. In case predictions for
        several samples are given, the weighted cross entropy is returned.
    :param targets: Ground truth labels for supplied samples.
    :param epsilon: Amount to clip predictions as log is not defined for `0` and `1`.
    """
    assert (
        predictions.shape == targets.shape
    ), f"Shape of predictions {predictions.shape} must match targets {targets.shape}"
    current_sum = np.sum(predictions, axis=predictions.ndim - 1)

    if predictions.ndim == 1:
        sample_count = 1
        predictions = predictions / current_sum
    else:
        sample_count = predictions.shape[0]
        predictions = predictions / current_sum[:, np.newaxis]

    predictions = np.clip(predictions, epsilon, 1.0 - epsilon)
    return -np.sum(targets * np.log(predictions)) / sample_count
Exemple #2
0
def mitigate_depolarizing_noise(K, num_wires, method, use_entries=None):
    r"""Estimate depolarizing noise rate(s) using on the diagonal entries of a kernel
    matrix and mitigate the noise, assuming a global depolarizing noise model.

    Args:
        K (array[float]): Noisy kernel matrix.
        num_wires (int): Number of wires/qubits of the quantum embedding kernel.
        method (``'single'`` | ``'average'`` | ``'split_channel'``): Strategy for mitigation

            * ``'single'``: An alias for ``'average'`` with ``len(use_entries)=1``.
            * ``'average'``: Estimate a global noise rate based on the average of the diagonal
              entries in ``use_entries``, which need to be measured on the quantum computer.
            * ``'split_channel'``: Estimate individual noise rates per embedding, requiring
              all diagonal entries to be measured on the quantum computer.
        use_entries (array[int]): Diagonal entries to use if method in ``['single', 'average']``.
            If ``None``, defaults to ``[0]`` (``'single'``) or ``range(len(K))`` (``'average'``).

    Returns:
        array[float]: Mitigated kernel matrix.

    Reference:
        This method is introduced in Section V in
        `arXiv:2105.02276 <https://arxiv.org/abs/2105.02276>`_.

    **Example:**

    For an example usage of ``mitigate_depolarizing_noise`` please refer to the
    `PennyLane demo on the kernel module <https://github.com/PennyLaneAI/qml/tree/master/demonstrations/tutorial_kernel_module.py>`_ or `the postprocessing demo for arXiv:2105.02276 <https://github.com/thubregtsen/qhack/blob/master/paper/post_processing_demo.py>`_.
    """
    dim = 2**num_wires

    if method == "single":
        if use_entries is None:
            use_entries = (0, )
        diagonal_element = K[use_entries[0], use_entries[0]]
        noise_rate = (1 - diagonal_element) * dim / (dim - 1)
        mitigated_matrix = (K - noise_rate / dim) / (1 - noise_rate)

    elif method == "average":
        if use_entries is None:
            diagonal_elements = np.diag(K)
        else:
            diagonal_elements = np.diag(K)[np.array(use_entries)]
        noise_rates = (1 - diagonal_elements) * dim / (dim - 1)
        mean_noise_rate = np.mean(noise_rates)
        mitigated_matrix = (K - mean_noise_rate / dim) / (1 - mean_noise_rate)

    elif method == "split_channel":
        eff_noise_rates = np.clip((1 - np.diag(K)) * dim / (dim - 1), 0.0, 1.0)
        noise_rates = 1 - np.sqrt(1 - eff_noise_rates)
        inverse_noise = (-np.outer(noise_rates, noise_rates) +
                         noise_rates.reshape(
                             (1, len(K))) + noise_rates.reshape((len(K), 1)))
        mitigated_matrix = (K - inverse_noise / dim) / (1 - inverse_noise)

    return mitigated_matrix
def imshow(inp, title=None):
    """Display image from tensor."""
    inp = inp.numpy().transpose((1, 2, 0))
    # Inverse of the initial normalization operation.
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
Exemple #4
0
def threshold_matrix(K):
    r"""Remove negative eigenvalues from the given kernel matrix.

    This method yields the closest positive semi-definite matrix in
    any unitarily invariant norm, e.g. the Frobenius norm.

    Args:
        K (array[float]): Kernel matrix, assumed to be symmetric.

    Returns:
        array[float]: Kernel matrix with cropped negative eigenvalues.

    **Example:**

    Consider a symmetric matrix with both positive and negative eigenvalues:

    .. code-block :: pycon

        >>> K = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 2]])
        >>> np.linalg.eigvalsh(K)
        array([-1.,  1.,  2.])

    We then can threshold/truncate the eigenvalues of the matrix via

    .. code-block :: pycon

        >>> K_thresh = qml.kernels.threshold_matrix(K)
        >>> np.linalg.eigvalsh(K_thresh)
        array([0.,  1.,  2.])

    If the input matrix does not have negative eigenvalues, ``threshold_matrix``
    does not have any effect.
    """
    w, v = np.linalg.eigh(K)

    if w[0] < 0:
        # Transform spectrum: Threshold/clip at 0.
        w0 = np.clip(w, 0, None)

        return (v * w0) @ np.transpose(v)

    return K
Exemple #5
0
    def step(self, objective_fn, *args, **kwargs):
        """Update trainable arguments with one step of the optimizer.

        Args:
            objective_fn (function): the objective function for optimization
            *args: variable length argument list for objective function
            **kwargs: variable length of keyword arguments for the objective function

        Returns:
            list[array]: The new variable values :math:`x^{(t+1)}`.
            If single arg is provided, list[array] is replaced by array.
        """

        self.trainable_args = set()

        for index, arg in enumerate(args):
            if getattr(arg, "requires_grad", True):
                self.trainable_args |= {index}

        if self.s is None:
            # Number of shots per parameter
            self.s = [
                np.zeros_like(a, dtype=np.int64) + self.min_shots
                for i, a in enumerate(args)
                if i in self.trainable_args
            ]

        # keep track of the number of shots run
        s = np.concatenate([i.flatten() for i in self.s])
        self.max_shots = max(s)
        self.shots_used = int(2 * np.sum(s))
        self.total_shots_used += self.shots_used

        # compute the gradient, as well as the variance in the gradient,
        # using the number of shots determined by the array s.
        grads, grad_variances = self.compute_grad(objective_fn, args, kwargs)
        new_args = self.apply_grad(grads, args)

        if self.xi is None:
            self.chi = [np.zeros_like(g, dtype=np.float64) for g in grads]
            self.xi = [np.zeros_like(g, dtype=np.float64) for g in grads]

        # running average of the gradient
        self.chi = [self.mu * c + (1 - self.mu) * g for c, g in zip(self.chi, grads)]

        # running average of the gradient variance
        self.xi = [self.mu * x + (1 - self.mu) * v for x, v in zip(self.xi, grad_variances)]

        for idx, (c, x) in enumerate(zip(self.chi, self.xi)):
            xi = x / (1 - self.mu ** (self.k + 1))
            chi = c / (1 - self.mu ** (self.k + 1))

            # determine the new optimum shots distribution for the next
            # iteration of the optimizer
            s = np.ceil(
                (2 * self.lipschitz * self.stepsize * xi)
                / ((2 - self.lipschitz * self.stepsize) * (chi ** 2 + self.b * (self.mu ** self.k)))
            )

            # apply an upper and lower bound on the new shot distributions,
            # to avoid the number of shots reducing below min(2, min_shots),
            # or growing too significantly.
            gamma = (
                (self.stepsize - self.lipschitz * self.stepsize ** 2 / 2) * chi ** 2
                - xi * self.lipschitz * self.stepsize ** 2 / (2 * s)
            ) / s

            argmax_gamma = np.unravel_index(np.argmax(gamma), gamma.shape)
            smax = max(s[argmax_gamma], 2)
            self.s[idx] = np.squeeze(np.int64(np.clip(s, min(2, self.min_shots), smax)))

        self.k += 1

        # unwrap from list if one argument, cleaner return
        if len(new_args) == 1:
            return new_args[0]

        return new_args