From dcd60082be2a98afba75870d37385323380aa78d Mon Sep 17 00:00:00 2001 From: JESSIE YU Date: Wed, 24 Mar 2021 10:34:46 -0400 Subject: [PATCH] remove old qka files (#23) --- QKA demo.ipynb | 281 -------------------------------------- runtime/qka_runtime.py | 300 ----------------------------------------- 2 files changed, 581 deletions(-) delete mode 100644 QKA demo.ipynb delete mode 100644 runtime/qka_runtime.py diff --git a/QKA demo.ipynb b/QKA demo.ipynb deleted file mode 100644 index 8a5f8f6ec..000000000 --- a/QKA demo.ipynb +++ /dev/null @@ -1,281 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "os.environ[\"PYTHON_EXEC\"] = \"/Users/jessieyu/.pyenv/versions/qka-demo/bin/python3\"\n", - "os.environ[\"NTC_DOC_FILE\"] = \"runtime/qka_doc.json\"\n", - "os.environ[\"NTC_PROGRAM_FILE\"] = \"runtime/qka_runtime.py\"" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from qiskit import IBMQ" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "provider = IBMQ.load_account()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Runtime Program QKA:\n", - " Description: Quantum kernel alignment algorithm.\n", - " Parameters:\n", - " feature_map:\n", - " description: quantum feature map\n", - " type: FeatureMapQuantumControl\n", - " data:\n", - " description: NxD array, where N is the number of data points, D is the feature dimension.\n", - " type: numpy.ndarray\n", - " labels:\n", - " description: Nx1 array of +/-1, where N is the number of data points\n", - " type: numpy.ndarray\n", - " lambda_plus:\n", - " description: Plus kernel parameter\n", - " type: numpy.ndarray\n", - " lambda_minus:\n", - " description: Minus kernel parameter\n", - " type: numpy.ndarray\n", - " C:\n", - " description: penalty parameter for soft-margin\n", - " type: float\n", - " Returns:\n", - " cost_plus:\n", - " description: SVM objective function evaluated at (alpha_+, lambda_+)\n", - " type: float\n", - " cost_minus:\n", - " description: SVM objective function evaluated at (alpha_-, lambda_-)\n", - " type: float\n" - ] - } - ], - "source": [ - "provider.runtime.programs()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "from qka.qka_utils import SPSA_parameters, spsa_step_one, spsa_step_two\n", - "from qka.kernel_matrix import KernelMatrix" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def align_kernel(backend, feature_map, data, labels, lambda_initial=None, spsa_steps=10, C=1):\n", - " \"\"\"Align the quantum kernel.\n", - "\n", - " Uses SPSA for minimization over kernel parameters (lambdas) and\n", - " convex optimization for maximization over lagrange multipliers (alpha):\n", - "\n", - " min_lambda max_alpha 1^T * alpha - (1/2) * alpha^T * Y * K_lambda * Y * alpha\n", - "\n", - " Args:\n", - " backend (Backend): Backend used to run the circuits\n", - " feature_map (FeatureMapForrelation): Feature map\n", - " data (numpy.ndarray): NxD array of training data, where N is the number of samples and D is the feature dimension\n", - " labels (numpy.ndarray): Nx1 array of +/-1 labels of the N training samples\n", - " lambda_initial (array): initial lambdas for feature map\n", - " spsa_steps (int): number of SPSA optimization steps\n", - " C (float): penalty parameter for the soft-margin support vector machine\n", - " C (float): penalty parameter for the soft-margin support vector machine\n", - "\n", - " Returns:\n", - " lambdas (numpy.ndarray): the optimized kernel parameters found from quantum kernel alignment\n", - " kernel_best (numpy.ndarray): the aligned quantum kernel matrix evaluated with the optimized kernel parameters on the training data\n", - " \"\"\"\n", - "\n", - " lambdas = lambda_initial\n", - " kernel_matrix = KernelMatrix(feature_map, backend)\n", - "\n", - " # Pre-computed spsa parameters:\n", - " spsa_params = SPSA_parameters()\n", - "\n", - " # Save data at each SPSA run in the following lists:\n", - " lambda_save = [] # updated kernel parameters after each spsa step\n", - "\n", - " # #####################\n", - " # Start the alignment:\n", - "\n", - " for count in range(spsa_steps):\n", - "\n", - " print('SPSA step {} of {}:'.format(count+1, spsa_steps))\n", - "\n", - " lambda_plus, lambda_minus, delta = spsa_step_one(lambdas=lambdas, spsa_params=spsa_params, count=count)\n", - " \n", - " # Call QKA runtime program.\n", - " runtime_params = {\n", - " 'feature_map': feature_map.to_dict(),\n", - " 'data': data,\n", - " 'labels': labels,\n", - " 'lambda_plus': lambda_plus,\n", - " 'lambda_minus': lambda_minus,\n", - " 'C': C\n", - " }\n", - " provider = backend.provider()\n", - " cost_plus, cost_minus = provider.runtime.run(program_name=\"QKA\",\n", - " backend=backend,\n", - " params=runtime_params).result()\n", - " \n", - " cost_final, lambda_best = spsa_step_two(cost_plus=cost_plus, cost_minus=cost_minus,\n", - " lambdas=lambdas, spsa_params=spsa_params, delta=delta, count=count)\n", - "\n", - " print('\\033[92m Cost: {}\\033[00m'.format(cost_final))\n", - "\n", - " lambdas = lambda_best # updated kernel parameters\n", - "\n", - " lambda_save.append(lambdas)\n", - "\n", - " # Evaluate aligned kernel matrix with best set of parameters averaged over last 10 steps:\n", - " lambdas = np.sum(np.array(lambda_save)[-10:, :],axis = 0)/10\n", - " kernel_best = kernel_matrix.construct_kernel_matrix(x1_vec=data, x2_vec=data, parameters=lambdas)\n", - "\n", - " return {'aligned_kernel_parameters': lambdas,\n", - " 'aligned_kernel_matrix': kernel_best}\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from qka.featuremaps import FeatureMapForrelation" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SPSA step 1 of 2:\n", - "\u001b[92m Cost: 6.493570204085309\u001b[00m\n", - "SPSA step 2 of 2:\n", - "\u001b[92m Cost: 6.074246109672153\u001b[00m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/jessieyu/.pyenv/versions/3.8.1/envs/qka-demo/lib/python3.8/site-packages/qiskit/providers/ibmq/ibmqbackend.py:800: DeprecationWarning: Passing a Qobj to Backend.run is deprecated and will be removed in a future release. Please pass in circuits or pulse schedules instead.\n", - " return super().run(circuits, job_name=job_name, job_share_level=job_share_level,\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "best_kernel_parameters: [ 0.03331271 0.16630372 0.18078404 -0.0822165 ]\n", - "best_kernel_matrix: [[1. 0.15917969 0.46972656 0.20117188 0.19824219 0.01953125\n", - " 0.57421875 0.26464844 0.82617188 0.59472656]\n", - " [0.15917969 1. 0.26660156 0.71679688 0.203125 0.07128906\n", - " 0.08007812 0.21191406 0.38769531 0.109375 ]\n", - " [0.46972656 0.26660156 1. 0.14746094 0.65429688 0.26269531\n", - " 0.17285156 0.04003906 0.39355469 0.26367188]\n", - " [0.20117188 0.71679688 0.14746094 1. 0.0859375 0.21191406\n", - " 0.29980469 0.65039062 0.17578125 0.26171875]\n", - " [0.19824219 0.203125 0.65429688 0.0859375 1. 0.59375\n", - " 0.03417969 0.1953125 0.16699219 0.14160156]\n", - " [0.01953125 0.07128906 0.26269531 0.21191406 0.59375 1.\n", - " 0.27148438 0.60644531 0.01757812 0.28808594]\n", - " [0.57421875 0.08007812 0.17285156 0.29980469 0.03417969 0.27148438\n", - " 1. 0.34960938 0.25097656 0.95800781]\n", - " [0.26464844 0.21191406 0.04003906 0.65039062 0.1953125 0.60644531\n", - " 0.34960938 1. 0.20996094 0.28417969]\n", - " [0.82617188 0.38769531 0.39355469 0.17578125 0.16699219 0.01757812\n", - " 0.25097656 0.20996094 1. 0.29589844]\n", - " [0.59472656 0.109375 0.26367188 0.26171875 0.14160156 0.28808594\n", - " 0.95800781 0.28417969 0.29589844 1. ]]\n" - ] - } - ], - "source": [ - "num_samples=5 # number of samples per class in the input data\n", - "num_features=2 # number of features in the input data\n", - "depth=2 # depth of the feature map circuit\n", - "C=1 # SVM soft-margin penalty\n", - "spsa_steps=2 # number of SPSA iterations\n", - "\n", - "backend = provider.backend.ibmq_qasm_simulator\n", - "\n", - "# create random test data and labels:\n", - "x_train = np.random.rand(2*num_samples, num_features)\n", - "y_train = np.concatenate((-1*np.ones(num_samples), np.ones(num_samples)))\n", - "\n", - "# Define the feature map and its initial parameters:\n", - "fm = FeatureMapForrelation(feature_dimension=num_features, depth=depth, entangler_map=None)\n", - "lambda_initial = np.random.uniform(-1,1, size=(fm._num_parameters))\n", - "\n", - "# Align the quantum kernel:\n", - "qka_results = align_kernel(backend=backend, feature_map=fm,\n", - " data=x_train, labels=y_train,\n", - " lambda_initial=lambda_initial,\n", - " spsa_steps=spsa_steps, C=C)\n", - "print(f\"aligned_kernel_parameters: {qka_results['aligned_kernel_parameters']}\")\n", - "print(f\"aligned_kernel_matrix: {qka_results['aligned_kernel_matrix']}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.9" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/runtime/qka_runtime.py b/runtime/qka_runtime.py deleted file mode 100644 index 4dc94989e..000000000 --- a/runtime/qka_runtime.py +++ /dev/null @@ -1,300 +0,0 @@ -import numpy as np -import itertools -from qiskit import QuantumCircuit, QuantumRegister -from qiskit.compiler import transpile -from qiskit.compiler import assemble -from cvxopt import matrix, solvers - -import sys -import json -# from ntc_provider.programruntime import QuantumProgramProvider - -from qiskit import Aer - - -class FeatureMapForrelation: - """Mapping data with the forrelation feature map from - Havlicek et al. https://www.nature.com/articles/s41586-019-0980-2. - - Data is encoded in entangling blocks (Z and ZZ terms) interleaved with - layers of either phase gates parametrized by kernel parameters, lambda, - or of Hadamard gates. - """ - - def __init__(self, feature_dimension, depth=2, entangler_map=None): - """ - Args: - feature_dimension (int): number of features - depth (int): number of repeated layers in the feature map - entangler_map (list[list]): connectivity of qubits with a list of [source, target], or None for full entanglement. - Note that the order in the list is the order of applying the two-qubit gate. - """ - - self._feature_dimension = feature_dimension - self._num_qubits = self._feature_dimension = feature_dimension - self._depth = depth - self._copies = 1 - - if entangler_map is None: - self._entangler_map = [[i, j] for i in range(self._feature_dimension) for j in range(i + 1, self._feature_dimension)] - else: - self._entangler_map = entangler_map - - self._num_parameters = self._num_qubits * self._depth - - def construct_circuit(self, x=None, parameters=None, q=None, inverse=False, name=None): - """Construct the feature map circuit. - - Args: - x (numpy.ndarray): data vector of size feature_dimension - parameters (numpy.ndarray): optional parameters in feature map - q (QauntumRegister): the QuantumRegister object for the circuit - inverse (bool): whether or not to invert the circuit - - Returns: - QuantumCircuit: a quantum circuit transforming data x - """ - - if parameters is not None: - if len(parameters) != self._num_parameters: - raise ValueError('The number of feature map parameters has to be {}'.format(self._num_parameters)) - - if q is None: - q = QuantumRegister(self._num_qubits, name='q') - - circuit=QuantumCircuit(q, name=name) - - param_idx = 0 - for layer in range(self._depth): - for i in range(self._num_qubits): - if parameters is not None: - circuit.p(np.pi + 2 * np.pi * parameters[param_idx], q[i]) - circuit.h(q[i]) - param_idx += 1 - else: - circuit.h(q[i]) - circuit.p(2 * x[i], q[i]) - for source, target in self._entangler_map: - circuit.cx(q[source], q[target]) - circuit.p(2 * (np.pi - x[source]) * (np.pi - x[target]), q[target]) - circuit.cx(q[source], q[target]) - - if inverse: - return circuit.inverse() - else: - return circuit - - -class KernelMatrix: - """Build the kernel matrix from a quantum feature map.""" - - def __init__(self, feature_map, backend): - """ - Args: - feature_map: the feature map object - backend (Backend): the backend instance - """ - - self._feature_map = feature_map - self._feature_map_circuit = self._feature_map.construct_circuit # the feature map circuit - self._backend = backend - self.results = {} # store the results object (program_data) - - def construct_kernel_matrix(self, x1_vec, x2_vec, parameters=None): - """Create the kernel matrix for a given feature map and input data. - - With the qasm simulator or real backends, compute order 'n^2' - states Phi^dag(y)Phi(x)|0> for input vectors x and y. - - Args: - x1_vec (numpy.ndarray): NxD array of training data or test data, where N is the number of samples and D is the feature dimension - x2_vec (numpy.ndarray): MxD array of training data or support vectors, where M is the number of samples and D is the feature dimension - parameters (numpy.ndarray): optional parameters in feature map - - Returns: - mat (numpy.ndarray): the kernel matrix - """ - - is_identical = False - if np.array_equal(x1_vec, x2_vec): - is_identical = True - - experiments = [] - - measurement_basis = '0' * np.shape(x1_vec)[1] - - if is_identical: - - my_product_list = list(itertools.combinations(range(len(x1_vec)), 2)) # all pairwise combos of datapoint indices - for index_1, index_2 in my_product_list: - - circuit = self._feature_map_circuit(x=x1_vec[index_1], parameters=parameters, name='{}_{}'.format(index_1, index_2)) - circuit += self._feature_map_circuit(x=x1_vec[index_2], parameters=parameters, inverse=True) - circuit.measure_all() - experiments.append(circuit) - - experiments = transpile(experiments, backend=self._backend) - qobj = assemble(experiments, backend=self._backend, shots=1024) - program_data = self._backend.run(qobj).result() - - self.results['program_data'] = program_data - - mat = np.eye(len(x1_vec), len(x1_vec)) # kernel matrix element on the diagonal is always 1 - for experiment, [index_1, index_2] in enumerate(my_product_list): - - counts = program_data.get_counts(experiment = experiment) - shots = sum(counts.values()) - - mat[index_1][index_2] = counts.get(measurement_basis, 0) / shots # kernel matrix element is the probability of measuring all 0s - mat[index_2][index_1] = mat[index_1][index_2] # kernel matrix is symmetric - - return mat ** self._feature_map._copies - - else: - - for index_1, point_1 in enumerate(x1_vec): - for index_2, point_2 in enumerate(x2_vec): - - circuit = self._feature_map_circuit(x=point_1, parameters=parameters, name='{}_{}'.format(index_1, index_2)) - circuit += self._feature_map_circuit(x=point_2, parameters=parameters, inverse=True) - circuit.measure_all() - experiments.append(circuit) - - experiments = transpile(experiments, backend=self._backend) - qobj = assemble(experiments, backend=self._backend, shots=1024) - program_data = self._backend.run(qobj).result() - - self.results['program_data'] = program_data - - mat = np.zeros((len(x1_vec), len(x2_vec))) - i = 0 - for index_1, _ in enumerate(x1_vec): - for index_2, _ in enumerate(x2_vec): - - counts = program_data.get_counts(experiment = i) - shots = sum(counts.values()) - - mat[index_1][index_2] = counts.get(measurement_basis, 0) / shots - i += 1 - - return mat ** self._feature_map._copies - - -def cvxopt_solver(K, y, C, max_iters=10000, show_progress=False): - """Convex optimization of SVM objective using cvxopt. - - Args: - K (numpy.ndarray): nxn kernel (Gram) matrix - y (numpy.ndarray): nx1 vector of labels +/-1 - C (float): soft-margin penalty - max_iters (int): maximum iterations for the solver - show_progress (bool): print progress of solver - - Returns; - ret (dict): results from the solver - """ - - if y.ndim == 1: - y = y[:, np.newaxis] - H = np.outer(y, y) * K - f = -np.ones(y.shape) - - n = K.shape[1] # number of training points - - y = y.astype('float') - - P = matrix(H) # (nxn) yy^T * K, element-wise multiplication - q = matrix(f) # (nx1) -ones - G = matrix(np.vstack((-np.eye((n)), np.eye((n))))) # for soft-margin, (2nxn) matrix with -identity (+identity) in top (bottom) half - h = matrix(np.vstack((np.zeros((n,1)), np.ones((n,1)) * C))) # for soft-margin, (2nx1) vector with 0's (C's) in top (bottom) half - A = matrix(y, y.T.shape) # (1xn) y - b = matrix(np.zeros(1), (1, 1)) # (1x1) zero - - solvers.options['maxiters'] = max_iters - solvers.options['show_progress'] = show_progress - - ret = solvers.qp(P, q, G, h, A, b, kktsolver='ldl') - - return ret - - -def align_kernel(backend, feature_map, data, labels, lambda_plus, lambda_minus, C=1): - """Align the quantum kernel. - - Uses SPSA for minimization over kernel parameters (lambdas) and - convex optimization for maximization over lagrange multipliers (alpha): - - min_lambda max_alpha 1^T * alpha - (1/2) * alpha^T * Y * K_lambda * Y * alpha - - Args: - backend (Backend): Backend used to run the circuits - feature_map (FeatureMapForrelation): Feature map - data (numpy.ndarray): NxD array of training data, where N is the number of samples and D is the feature dimension - labels (numpy.ndarray): Nx1 array of +/-1 labels of the N training samples - lambda_plus (numpy.ndarray): (+) kernel parameters - lambda_minus (numpy.ndarray): (-) kernel parameters - C (float): penalty parameter for the soft-margin support vector machine - - Returns: - cost_plus (float): SVM objective function evaluated at (alpha_+, lambda_+) - cost_minus (float): SVM objective function evaluated at (alpha_-, lambda_-) - """ - - kernel_matrix = KernelMatrix(feature_map=feature_map, backend=backend) - - # (STEP 2 OF PSEUDOCODE) - # Evaluate kernel matrix for the (+) and (-) kernel parameters. - - kernel_plus = kernel_matrix.construct_kernel_matrix(x1_vec=data, x2_vec=data, parameters=lambda_plus) - kernel_minus = kernel_matrix.construct_kernel_matrix(x1_vec=data, x2_vec=data, parameters=lambda_minus) - - # (STEP 3 OF PSEUDOCODE) - # Maximize SVM objective function over - # support vectors in the (+) and (-) directions. - - ret_plus = cvxopt_solver(K=kernel_plus, y=labels, C=C) - cost_plus = -1 * ret_plus['primal objective'] - - ret_minus = cvxopt_solver(K=kernel_minus, y=labels, C=C) - cost_minus = -1 * ret_minus['primal objective'] - - return cost_plus, cost_minus - - -class NumpyDecoder(json.JSONDecoder): - """JSON Decoder for Numpy arrays and complex numbers.""" - - def __init__(self, *args, **kwargs): - super().__init__(object_hook=self.object_hook, *args, **kwargs) - - def object_hook(self, obj): - if 'type' in obj: - if obj['type'] == 'complex': - val = obj['value'] - return val[0] + 1j * val[1] - if obj['type'] == 'array': - return np.array(obj['value']) - return obj - - -def main(backend, *args, **kwargs): - """Entry function.""" - # Reconstruct the feature map object. - feature_map = kwargs.get('feature_map') - kwargs['feature_map'] = FeatureMapForrelation(**feature_map) - kwargs['backend'] = backend - results = align_kernel(**kwargs) - print(json.dumps({'results': results})) - - -if __name__ == '__main__': - # provider = QuantumProgramProvider() - # backend = provider.backends()[0] - - # the code currently uses Aer instead of runtime provider - backend = Aer.get_backend('qasm_simulator') - user_params = {} - if len(sys.argv) > 1: - # If there are user parameters. - user_params = json.loads(sys.argv[1], cls=NumpyDecoder) - main(backend, **user_params)