2

I'm doing a small program that is supposed to classify the data of the Wisconsin Breast Cancer database contained in sklearn.datasets using Quantum Neural Networks (specifically EstimatorQNN).

I think the code does the job just fine, but I have 2 major problems that I would like to fix:

  1. I'm using SPSA as an optimizer, which seems to work, but if I try to use COBYLA, it doesn't work. Why is that?
  2. While the code works, it takes a long time to do the job, and the accuracy that I seem to get is not that high (around 40%). Can you guys help me out to optimize this and make it a bit better?

The code is this one:

from qiskit import QuantumCircuit
from qiskit.circuit.library import ZZFeatureMap, RealAmplitudes
from qiskit_machine_learning.neural_networks import EstimatorQNN
from qiskit_algorithms.optimizers import COBYLA
from qiskit_algorithms.optimizers import SPSA
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.feature_selection import SelectKBest, f_classif
import numpy as np
import matplotlib.pyplot as plt
def print_circuit(circuit):
 print("*****************************************************")
 print(f"Circuit depth: {circuit.depth()}")
 print(f"Number of qubits: {circuit.num_qubits}")
 print(f"Number of classical bits: {circuit.num_clbits}")
 print(f"Total operations: {len(circuit)}")
 print("\nCircuit composition:")
 print(circuit)
 print("*****************************************************")
 print("Decomposed Circuit:")
 circuit_decomposed = circuit.decompose().decompose() # Multiple decompositions for better view
 print(circuit_decomposed)
 with open("Quantum_Circuit_Result.txt", "w", encoding="utf-8") as ResultFile:
 ResultFile.write("COMPLETE QUANTUM CIRCUIT ANALYSIS\n")
 ResultFile.write("*****************************************************\n\n")
 
 # Individual components
 ResultFile.write("INDIVIDUAL COMPONENTS:\n")
 ResultFile.write("*****************************************************\n\n")
 
 ResultFile.write("ZZFeatureMap:\n")
 ResultFile.write("-"*15 + "\n")
 ResultFile.write(str(feature_map) + "\n\n")
 
 ResultFile.write("RealAmplitudes Ansatz:\n")
 ResultFile.write("-"*20 + "\n")
 ResultFile.write(str(ansatz) + "\n\n")
 
 # Combined circuit
 ResultFile.write("COMBINED CIRCUIT:\n")
 ResultFile.write("-"*15 + "\n")
 ResultFile.write(str(circuit) + "\n\n")
 
 # Decomposed versions
 ResultFile.write("DECOMPOSED CIRCUITS:\n")
 ResultFile.write("*****************************************************\n\n")
 
 ResultFile.write("Level 1 Decomposition:\n")
 ResultFile.write("-"*20 + "\n")
 decomposed1 = circuit.decompose()
 ResultFile.write(str(decomposed1) + "\n\n")
 
 ResultFile.write("Level 2 Decomposition:\n")
 ResultFile.write("-"*20 + "\n")
 decomposed2 = circuit.decompose().decompose()
 ResultFile.write(str(decomposed2) + "\n\n")
 
 # Gate analysis
 ResultFile.write("GATE COUNT ANALYSIS:\n")
 ResultFile.write("*****************************************************\n\n")
 
 gate_counts = {}
 for instruction in circuit.data:
 gate_name = instruction.operation.name
 gate_counts[gate_name] = gate_counts.get(gate_name, 0) + 1
 
 for gate, count in sorted(gate_counts.items()):
 ResultFile.write(f"{gate}: {count}\n")
 
 ResultFile.write(f"\nTotal gates: {len(circuit)}\n")
 print("Detailed analysis saved to: Quantum_Circuit_Detailed.txt")
 print("*****************************************************")
 print("")
# per stampare tutti i dettagli del QNN
def print_qnn_details(qnn, feature_map, ansatz):
 print("*****************************************************")
 print("ESTIMATOR QNN CONSTRUCTION DETAILS")
 print("*****************************************************")
 
 print(f"\nFeature Map (ZZFeatureMap):")
 print(f" - Number of features: {len(feature_map.parameters)}")
 print(f" - Repetitions: {feature_map.reps}")
 print(f" - Number of parameters: {len(feature_map.parameters)}")
 print(f" - Parameters: {list(feature_map.parameters)}")
 
 print(f"\nAnsatz (RealAmplitudes):")
 print(f" - Number of qubits: {ansatz.num_qubits}")
 print(f" - Repetitions: {ansatz.reps}")
 print(f" - Number of parameters: {ansatz.num_parameters}")
 print(f" - Entanglement type: {ansatz.entanglement}")
 print(f" - Parameters: {list(ansatz.parameters)}")
 
 print(f"\nQuantum Circuit Composition:")
 print(f" - Total qubits: {qnn.circuit.num_qubits}")
 print(f" - Total gates: {len(qnn.circuit)}")
 print(f" - Depth: {qnn.circuit.depth()}")
 print(f" - Number of parameters: {len(qnn.circuit.parameters)}")
 
 print(f"\nEstimatorQNN Configuration:")
 print(f" - Input parameters: {len(qnn.input_params)}")
 print(f" - Weight parameters: {len(qnn.weight_params)}")
 print(f" - Input parameter names: {[str(p) for p in qnn.input_params]}")
 print(f" - Weight parameter names: {[str(p) for p in qnn.weight_params]}")
 print(f"How the EstimatorQNN is made:\n")
 print(qnn)
 print("*****************************************************")
 print("")
# una funzione per plottare i valori predetti e metterli a confronto con quelli veri
def plot_predictions(y_true, y_pred, title="Predicted vs Actual Values"):
 plt.figure(figsize=(12, 6))
 
 # Plot actual vs predicted
 indices = np.arange(len(y_true))
 plt.scatter(indices, y_true, alpha=0.7, label='Actual Values', color='blue')
 plt.scatter(indices, y_pred, alpha=0.7, label='Predicted Values', color='red', marker='x')
 
 plt.xlabel('Sample Index')
 plt.ylabel('Value')
 plt.title(title)
 plt.legend()
 plt.grid(True, alpha=0.3)
 
 # Add some statistics
 mse = np.mean((y_true - y_pred) ** 2)
 plt.figtext(0.15, 0.02, f'MSE: {mse:.4f}', fontsize=10)
 
 plt.tight_layout()
 plt.show()
# una funzione per plottare i progressi di training
def plot_training_progress(callback):
 plt.figure(figsize=(10, 5))
 plt.plot(callback.loss_history)
 plt.xlabel('Iteration')
 plt.ylabel('Loss')
 plt.title('Training Progress')
 plt.grid(True)
 plt.show()
# callback for SPSA
class SPSACallback:
 def __init__(self):
 self.loss_history = []
 self.iteration = 0
 
 def __call__(self, nfev, weights, loss_value, *args):
 self.loss_history.append(loss_value)
 self.iteration += 1
 if self.iteration % 10 == 0:
 print(f"Iteration {self.iteration}, Loss: {loss_value:.4f}")
# Callback for COBYLA
class COBYLACallback:
 def __init__(self):
 self.loss_history = []
 self.iteration = 0
 
 def __call__(self, loss_value):
 self.loss_history.append(loss_value)
 self.iteration += 1
 if self.iteration % 10 == 0:
 print(f"Iteration {self.iteration}, Loss: {loss_value:.4f}")
# Carica il dataset contente il numero di cancri al seno nel Winsconsin
data = load_breast_cancer()
# recupero sia la matrice dei dati, sia il classification target
X, y = data.data, data.target
print("Dataset shape:", X.shape) # shape del dataset
print("Target distribution:", np.bincount(y))
# Normalizzo le feature dei dati recuperati tra [0, 1] (cruciale per la feature map quantistica)
X_scaled = MinMaxScaler().fit_transform(X)
# Feature selection con PCA per preservare varianza
#pca = PCA(n_components=4)
#X_processed = pca.fit_transform(X_scaled)
# Feature selection
selector = SelectKBest(f_classif, k=4)
X_processed = selector.fit_transform(X_scaled, y)
# Split training-test
# la maggior parte dei dati sono usati per train (80% circa), mentre la restante parte è per la validazione del training
X_train, X_test, y_train, y_test = train_test_split(X_processed, y, test_size=0.2, random_state=42,stratify=y)
# cercare un altro set diverso su cui fare il test (?)
print(f"Training set: {X_train.shape[0]} samples")
print(f"Test set: {X_test.shape[0]} samples")
# Circuito quantistico: Feature Map + Ansatz
num_features = X_train.shape[1] # Usiamo solo 4 features per limitare i qubit (puoi aumentare se il PC lo permette)
feature_map = ZZFeatureMap(feature_dimension=num_features, reps=2)
# definisco qui il mio "ansatz" (ovvero la mia ipotesi/funzione di prova)
# utilizzo RealAmplitudes come entanglement perfetto. Questo pattern viene fortemente usato per lavori di classificazione.
# It is called RealAmplitudes since the prepared quantum states will only have real amplitudes, the complex part is always 0.
ansatz = RealAmplitudes(num_qubits=num_features, reps=3, entanglement='full') 
# creazione del circuito di quantum
qc = QuantumCircuit(num_features)
qc.compose(feature_map, inplace=True)
qc.compose(ansatz, inplace=True)
# Creo qui il mio EstimatorQNN, passandogli il circuito e i parametri di input e di weight
qnn = EstimatorQNN(
 circuit=qc,
 input_params=feature_map.parameters,
 weight_params=ansatz.parameters
)
#stampo tutti i dettagli del QNN appena creato
print_qnn_details(qnn, feature_map, ansatz)
# Funzione di loss (Mean Squared Error)
def loss_fnOLD(weights, X, y):
 predictions = qnn.forward(X, weights)
 return np.mean((predictions - y) ** 2)
# funzione di loss
def loss_fn(weights, X, y):
 predictions = qnn.forward(X, weights)
 epsilon = 1e-8
 predictions = np.clip(predictions, epsilon, 1-epsilon)
 return -np.mean(y * np.log(predictions) + (1-y) * np.log(1-predictions))
# Ottimizzazione
# COBYLA svolge un lavoro di grid search (studio più approfondito a riguardo)
# cercare cosa altro usare magari come funzione di loss o altro
# ho usato alla fine SPSA
callback = SPSACallback()
optimizer = SPSA(maxiter=120, callback=callback)
initial_weights = 0.1 * np.random.rand(ansatz.num_parameters)
result = optimizer.minimize(
 fun=lambda w: loss_fn(w, X_train, y_train),
 x0=initial_weights)
print(f"\nOptimization completed:")
print(f" - Final loss: {result.fun:.4f}")
print(f" - Function evaluations: {result.nfev}")
print(f" - Optimal weights shape: {result.x.shape}")
# Valutazione sul test set
test_predictions = qnn.forward(X_test, result.x)
test_accuracy = np.mean((test_predictions > 0.5) == y_test)
print(f"Accuracy sul test set: {test_accuracy * 100:.2f}%")
# Plot training progress
plot_training_progress(callback)
#Plot predicted vs actual values
plot_predictions(y_test, test_predictions, "Test Set: Predicted vs Actual Values")
# Additional analysis
print(f"\nDetailed Performance Analysis:")
print(f"Mean Squared Error: {np.mean((test_predictions - y_test) ** 2):.4f}")
print(f"Mean Absolute Error: {np.mean(np.abs(test_predictions - y_test)):.4f}")
# mostriamo qui il circuito Quantum creato ed EstimatorQNN
#mostro qui il circuito
print_circuit(qc)
desertnaut
60.7k32 gold badges155 silver badges183 bronze badges
asked Sep 17 at 7:38
1

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.