About How to Use Automated Feature Map Design in your QML Pipeline
In this article, we will show you how to integrate the paper "Automatic Design of Quantum Feature Maps" in your QML exploration.
The paper titled "Automatic design of quantum feature maps" by Sergio Altares-López et al. focuses on a new technique for the automatic generation of optimal ad-hoc ansätze for classification using quantum support vector machines (QSVMs). This method employs a non-sorted genetic algorithm II and multiobjective genetic algorithms to maximize accuracy while minimizing ansatz size. The paper validates the technique with a non-linear dataset example, interprets the resulting circuit and its outputs, and compares it with classical classifiers. This approach demonstrates significant promise for quantum machine learning, particularly in the efficient creation of quantum feature maps for QSVMs.
In this article, we will implement this automated feature map design approach using the paper repository as a reference. The only caveat is that instead of using accuracy as an optimization objective for the genetic process, we will switch to AUC since it is more suitable for common real-world datasets.
Setup your environment
First of all, it is relevant to download to your environment the repository previously mentioned since a few *.py modules will be called during the process.
To reach the point where you are ready for feature map generation, please follow the instructions in this previous article (or follow your own method for preprocessing classical data):
Dimensionality Reduction for Quantum Machine Learning: Integrating LDA and K-Means
In Quantum Machine Learning (QML), the preparation of data is a crucial step that often determines the effectiveness and efficiency of the algorithms used. One of the key challenges in this process is dimensionality reduction, which involves transforming high-dimensional data into a lower-dimensional space. This is particularly important in quantum comp…
Importing libaries and modules
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import importlib
import circuit
import encoding
import qsvm
import fitness
import gsvm
importlib.reload(circuit)
importlib.reload(encoding)
importlib.reload(qsvm)
importlib.reload(fitness)
importlib.reload(gsvm)
import pandas as pd
import time
Purpose: Sets up the environment by importing necessary libraries and custom modules related to quantum computing and machine learning.
How It Works:
Standard libraries like
numpy
,pandas
, andmatplotlib
are imported for numerical operations, data handling, and plotting, respectively.Custom modules like
circuit
,encoding
,qsvm
,fitness
, andgsvm
are reloaded. These modules contain functions and classes for quantum circuit design, data encoding, quantum support vector machine implementation, fitness calculation in optimization, and genetic SVM respectively.
Evolutionary Algorithm for Quantum Circuit Design
def evol(output="sample_result.csv"):
y = y_train
X = X_train
start = time.time()
pop, pareto, logbook = gsvm.gsvm(nqubits=2, depth=4, nparameters=2,
X=X, y=y, weights=[-1.0,1.0],
mu=50,lambda_=10, ngen=150)
print(f'Simulation finished after {time.time()-start} seconds')
print(f'\nGenetic-algorithm output ({output})')
#print('generation, individual, gates/qubit, accuracy')
print('---------------------------------------------')
with open(output, "w") as f:
for ide, ind in enumerate(pareto):
genes=''.join(str(i) for i in list(ind))
gates, acc = ind.fitness.values
line = f'{ide},"{genes}",{gates},{acc}'
f.write(line)
f.write('\n')
print(line)
evol()
Purpose: Runs an evolutionary algorithm to design quantum circuits for feature mapping.
How It Works:
The function
evol
implements a genetic algorithm to evolve quantum circuits for better performance in a machine learning task.The algorithm takes quantum bits (
nqubits
), circuit depth (depth
), number of parameters (nparameters
), and training data as inputs.It evolves the population (
pop
) over several generations (ngen
) using fitness functions defined in thegsvm
to select the best-performing quantum circuits.Results are saved to an output CSV file and printed for analysis.
Post-Processing of Evolutionary Algorithm Output
import encoding2 as e2
import random
iot_result = pd.read_csv('sample_result.csv',header=None)
def ordenar_salidas_pareto(dataframe):
dataframe.columns=['ind','circ','gates','acc']
dataframe.sort_values(['acc','gates'], ascending=[False,False],inplace=True)
dataframe.reset_index(inplace=True)
dataframe.pop('index')
return dataframe
iot_salidas = ordenar_salidas_pareto(iot_result)
iot_salidas
Purpose: To process and sort the results obtained from the evolutionary algorithm.
How It Works:
Reads the results from the CSV file into a DataFrame.
The function
ordenar_salidas_pareto
sorts these results, based on Pareto efficiency criteria (considering both accuracy and complexity of the circuits).
Circuit Conversion and Visualization
from qiskit.circuit import ParameterVector, QuantumCircuit, Parameter
from qiskit import execute, Aer, IBMQ, QuantumRegister, ClassicalRegister, BasicAer
from qiskit.utils import QuantumInstance
def coding_bits(b):
c = [b[n:n+5] for n,i in enumerate(b) if n%5==0]
c_p=[]
coding_0=[]
for i in range(len(c)):
for j in c[i]:
c_p.append(str(j))
np.asarray(c_p)
c = [c_p[n:n+5] for n,i in enumerate(c_p) if n%5==0]
for i in c:
coding_0.append(''.join(i))
return coding_0
class CircuitConversor:
def __init__(self, nqubits, nparameters):
gates = {}
for n, suffix in enumerate(['00','01','10','11']):
angle = np.pi / (2**n)
gates['000'+suffix] = (self.make_H(), 1.0)
gates['001'+suffix] = (self.make_cx(), 2.0)
gates['010'+suffix] = (self.make_id(), 0.0)
gates['011'+suffix] = (self.make_rx(angle), 1.0)
gates['100'+suffix] = (self.make_rz(angle), 1.0)
gates['101'+suffix] = (self.make_id(), 0.0)
gates['110'+suffix] = (self.make_id(), 0.0)
gates['111'+suffix] = (self.make_ry(angle), 1.0)
self.gates = gates
self.nqubits = nqubits
self.register = QuantumRegister(nqubits, 'q')
self.nparameters = nparameters
self.nqubits = nqubits
self.x = ParameterVector('x', nparameters)
def __call__(self, coding_0):
print(coding_0)
circuit = QuantumCircuit(self.register)
k = 0
cost = 0
for ndx, z in enumerate(coding_0):
qubit = ndx % self.nqubits
target = (ndx + 1) % self.nqubits
fn, weight = self.gates[z]
k = fn(circuit, k, qubit, target)
cost += weight
for i in range(k, self.nparameters):
circuit.rz(self.x[i]*0, self.register[0])
return circuit, cost
def make_id(self):
def operation(circuit, k, qubit, target):
return k
return operation
def make_H(self):
def operation(circuit, k, qubit, target):
circuit.h(self.register[qubit])
return k
return operation
def make_cx(self):
def operation(circuit, k, qubit, target):
circuit.cx(self.register[qubit], self.register[target])
return k
return operation
def make_rx(self, angle):
def operation(circuit, k, qubit, target):
circuit.rx(self.x[k%self.nparameters] * angle,
self.register[qubit])
return k+1
return operation
def make_ry(self, angle):
def operation(circuit, k, qubit, target):
circuit.ry(self.x[k%self.nparameters] * angle,
self.register[qubit])
return k+1
return operation
def make_rz(self, angle):
def operation(circuit, k, qubit, target):
circuit.rz(self.x[k%self.nparameters] * angle,
self.register[qubit])
return k+1
return operation
import os
import psutil
class Fitness:
def __init__(self, nqubits, nparameters, X, y, quantum_instance):
self.nqubits = nqubits
self.nparameters = nparameters
self.cc = CircuitConversor(nqubits, nparameters)
self.instance = quantum_instance
self.X = X
self.y = y
def __call__(self, POP):
try:
return self.fitness(POP)
except Exception as e:
print(f'Exception happened during fitness():\n {e}')
process = psutil.Process(os.getpid())
print(f' RUSAGE_SELF: {process.memory_info()}')
return 1000, 100000.0
def fitness(self, POP):
print('Invoked fitness')
#Convertimos el individuo en el fenotipo (ansatz)
fm, puertas = self.cc(coding_bits(POP))
cc = CircuitConversor(nqubits=2, nparameters=2)
fm, puertas = cc(coding_bits(iot_salidas.circ[0]))
print(puertas)
fm.draw(output='mpl')
Purpose: To convert the genetic algorithm output into a quantum circuit.
How It Works:
The
CircuitConversor
class defines methods to translate genetic algorithm outputs into quantum circuits.An instance of this class is created and used to construct a quantum circuit (
fm
) based on the best result from the evolutionary algorithm.The circuit is visualized using Qiskit's circuit drawing capabilities.
Proceed with QSVC
Now your optimized feature map is adjusted to your dataset and ready to be executed (remember to setup your backend previously).
qkernel = QuantumKernel(feature_map=fm, quantum_instance=backend)
qsvc = QSVC(quantum_kernel=qkernel, max_iter=-1, probability=True)
Change accuracy per AUC
Editing the files qsvm.py
and fitness.py
can allow you to change the metric to be optimized.
In,qsvm.py
you need to add this:
def predict_proba(self, dataset_features):
return self.svc.predict_proba(dataset_features)
And in fitness.py
change this line:
# Change this
y_pred = model.predict(test_features)
# With this
y_pred = model.predict_proba(test_features)[:, 1]