1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Anuncie Aqui
    Anuncie aqui você Também: fdantas@4each.com.br

[Python] Qiskit Problem: Why does this not work with COBYLA and how do i optimize it?

Discussão em 'Python' iniciado por Stack, Setembro 17, 2025.

  1. Stack

    Stack Membro Participativo

    i'm currently doing a small program that is supposed to classify the data of the Winsconsin 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 see: 1 - i'm using as an optimizer SPSA that 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 big (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)


    Thank you in advance!

    Continue reading...

Compartilhe esta Página