def target_alignment( X, Y, kernel, assume_normalized_kernel=False, rescale_class_labels=True, ): """Kernel-target alignment between kernel and labels.""" K = qml.kernels.square_kernel_matrix( X, kernel, assume_normalized_kernel=assume_normalized_kernel, ) if rescale_class_labels: nplus = np.count_nonzero(np.array(Y) == 1) nminus = len(Y) - nplus _Y = np.array([y / nplus if y == 1 else y / nminus for y in Y]) else: _Y = np.array(Y) T = np.outer(_Y, _Y) inner_product = np.sum(K * T) norm = np.sqrt(np.sum(K * K) * np.sum(T * T)) inner_product = inner_product / norm return inner_product
def test_gradient_gate_with_multiple_parameters_hamiltonian(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] ham = qml.Hamiltonian( [1.0, 0.3, 0.3], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0), qml.PauliZ(1)]) with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(ham) tape.trainable_params = {1, 2, 3} h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_D = dev.adjoint_jacobian(tape) tapes, fn = qml.gradients.finite_diff(tape, h=h) grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) assert np.count_nonzero(grad_D) == 3 # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0)
def test_reduced_row_echelon(binary_matrix, result): r"""Test that _reduced_row_echelon returns the correct result.""" # build row echelon form of the matrix shape = binary_matrix.shape for irow in range(shape[0]): pivot_index = 0 if np.count_nonzero(binary_matrix[irow, :]): pivot_index = np.nonzero(binary_matrix[irow, :])[0][0] for jrow in range(shape[0]): if jrow != irow and binary_matrix[jrow, pivot_index]: binary_matrix[jrow, :] = (binary_matrix[jrow, :] + binary_matrix[irow, :]) % 2 indices = [ irow for irow in range(shape[0] - 1) if np.array_equal(binary_matrix[irow, :], np.zeros(shape[1])) ] temp_row_echelon_matrix = binary_matrix.copy() for row in indices[::-1]: temp_row_echelon_matrix = np.delete(temp_row_echelon_matrix, row, axis=0) row_echelon_matrix = np.zeros(shape, dtype=int) row_echelon_matrix[:shape[0] - len(indices), :] = temp_row_echelon_matrix # build reduced row echelon form of the matrix from row echelon form for idx in range(len(row_echelon_matrix))[:0:-1]: nonzeros = np.nonzero(row_echelon_matrix[idx])[0] if len(nonzeros) > 0: redrow = (row_echelon_matrix[idx, :] % 2).reshape(1, -1) coeffs = ((-row_echelon_matrix[:idx, nonzeros[0]] / row_echelon_matrix[idx, nonzeros[0]]) % 2).reshape( 1, -1) row_echelon_matrix[:idx, :] = (row_echelon_matrix[:idx, :] + (coeffs.T * redrow) % 2) % 2 # get reduced row echelon form from the _reduced_row_echelon function rref_bin_mat = _reduced_row_echelon(binary_matrix) assert (rref_bin_mat == row_echelon_matrix).all() assert (rref_bin_mat == result).all()
def test_gradient_gate_with_multiple_parameters(self, tol, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.JacobianTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} grad_D = dev.adjoint_jacobian(tape) grad_F = tape.jacobian(dev, method="numeric") # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) assert np.count_nonzero(grad_D) == 3 # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0)
def test_gradient_gate_with_multiple_parameters(self, tol): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] with ReversibleTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} dev = qml.device("default.qubit", wires=1) grad_A = tape.jacobian(dev, method="analytic") grad_F = tape.jacobian(dev, method="numeric") # gradient has the correct shape and every element is nonzero assert grad_A.shape == (1, 3) assert np.count_nonzero(grad_A) == 3 # the different methods agree assert np.allclose(grad_A, grad_F, atol=tol, rtol=0)
def accuracy(classifier, X, Y_target): return 1 - np.count_nonzero(classifier.predict(X) - Y_target) / len(Y_target)
def polarity( X, Y, kernel, assume_normalized_kernel=False, rescale_class_labels=True, normalize=False, ): r"""Polarity of a given kernel function. For a dataset with feature vectors :math:`\{x_i\}` and associated labels :math:`\{y_i\}`, the polarity of the kernel function :math:`k` is given by .. math :: \operatorname{P}(k) = \sum_{i,j=1}^n y_i y_j k(x_i, x_j) If the dataset is unbalanced, that is if the numbers of datapoints in the two classes :math:`n_+` and :math:`n_-` differ, ``rescale_class_labels=True`` will apply a rescaling according to :math:`\tilde{y}_i = \frac{y_i}{n_{y_i}}`. This is activated by default and only results in a prefactor that depends on the size of the dataset for balanced datasets. The keyword argument ``assume_normalized_kernel`` is passed to :func:`~.kernels.square_kernel_matrix`, for the computation :func:`~.utils.frobenius_inner_product` is used. Args: X (list[datapoint]): List of datapoints. Y (list[float]): List of class labels of datapoints, assumed to be either -1 or 1. kernel ((datapoint, datapoint) -> float): Kernel function that maps datapoints to kernel value. assume_normalized_kernel (bool, optional): Assume that the kernel is normalized, i.e. the kernel evaluates to 1 when both arguments are the same datapoint. rescale_class_labels (bool, optional): Rescale the class labels. This is important to take care of unbalanced datasets. normalize (bool): If True, rescale the polarity to the target_alignment. Returns: float: The kernel polarity. **Example:** Consider a simple kernel function based on :class:`~.templates.embeddings.AngleEmbedding`: .. code-block :: python dev = qml.device('default.qubit', wires=2, shots=None) @qml.qnode(dev) def circuit(x1, x2): qml.templates.AngleEmbedding(x1, wires=dev.wires) qml.adjoint(qml.templates.AngleEmbedding)(x2, wires=dev.wires) return qml.probs(wires=dev.wires) kernel = lambda x1, x2: circuit(x1, x2)[0] We can then compute the polarity on a set of 4 (random) feature vectors ``X`` with labels ``Y`` via >>> X = np.random.random((4, 2)) >>> Y = np.array([-1, -1, 1, 1]) >>> qml.kernels.polarity(X, Y, kernel) tensor(0.04361349, requires_grad=True) """ K = square_kernel_matrix(X, kernel, assume_normalized_kernel=assume_normalized_kernel) if rescale_class_labels: nplus = np.count_nonzero(np.array(Y) == 1) nminus = len(Y) - nplus _Y = np.array([y / nplus if y == 1 else y / nminus for y in Y]) else: _Y = np.array(Y) T = np.outer(_Y, _Y) return frobenius_inner_product(K, T, normalize=normalize)