{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b6d1e3ec",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Classificação híbrida de conjuntos aprimorada por quantum (fluxo de trabalho de estabilidade da rede)\"\n",
        "description: \"Construa e analise um conjunto híbrido quântico-clássico para classificação da estabilidade da rede em QPUs (Unidades de Processamento Quântico) d IBM, utilizando a função Singularity Qiskit da Multiverse Computing.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore QEEC interpretability hyperparameters overfitting sublinear preds prec */}\n",
        "\n",
        "<span id=\"hybrid-quantum-enhanced-ensemble-classification-grid-stability-workflow\" />\n",
        "\n",
        "# Classificação híbrida de conjuntos aprimorada por quantum (fluxo de trabalho de estabilidade da rede)\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a6f69b77",
      "metadata": {},
      "source": [
        "*Estimativa de uso: 20 minutos em tempo de QPU para cada trabalho em um processador Eagle r3. (OBSERVAÇÃO: essa é apenas uma estimativa. Seu tempo de execução pode variar)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Segundo plano\n",
        "\n",
        "Este tutorial demonstra um fluxo de trabalho híbrido quântico-clássico que aprimora um conjunto clássico por meio de uma etapa de otimização quântica. Utilizando o “ Machine Learning o de Singularidade – Classificação” da Multiverse Computing (uma função do Qiskit), treinamos um conjunto de modelos de aprendizado convencionais (por exemplo, árvores de decisão, k-NN, regressão logística) e, em seguida, refinamos esse conjunto com uma camada quântica para melhorar a diversidade e a generalização. O objetivo é prático: em uma tarefa real de previsão de estabilidade da rede, comparamos uma linha de base clássica robusta com uma alternativa otimizada quântica, utilizando as mesmas divisões de dados, para que você possa ver em que aspectos a abordagem quântica ajuda e quais são os custos envolvidos.\n",
        "\n",
        "Por que isso é importante: selecionar um bom subconjunto de muitos alunos fracos é um problema combinatório que cresce rapidamente com o tamanho do conjunto. As heurísticas clássicas, como boosting, bagging e stacking, têm bom desempenho em escalas moderadas, mas podem ter dificuldades para explorar com eficiência bibliotecas grandes e redundantes de modelos. A função integra algoritmos quânticos - especificamente QAOA (e opcionalmente VQE em outras configurações) - para pesquisar esse espaço com mais eficiência depois que os alunos clássicos são treinados, aumentando a chance de encontrar um subconjunto compacto e diversificado que generalize melhor.\n",
        "\n",
        "Crucialmente, a escala de dados não é limitada por qubits. O trabalho pesado com os dados - pré-processamento, treinamento do pool de alunos e avaliação - continua sendo clássico e pode lidar com milhões de exemplos. Os Qubits determinam apenas o tamanho do conjunto usado na etapa de seleção quântica. Esse desacoplamento é o que torna a abordagem viável no hardware atual: você mantém os fluxos de trabalho familiares do scikit-learn para treinamento de dados e modelos e chama a etapa quântica por meio de uma interface de ação limpa em Qiskit Functions.\n",
        "\n",
        "Na prática, embora diferentes tipos de alunos possam ser fornecidos ao conjunto (por exemplo, árvores de decisão, regressão logística ou k-NN), as árvores de decisão tendem a ter o melhor desempenho. O otimizador favorece consistentemente os membros mais fortes do conjunto - quando são fornecidos alunos heterogêneos, os modelos mais fracos, como os regressores lineares, geralmente são eliminados em favor dos mais expressivos, como as árvores de decisão.\n",
        "\n",
        "O que você fará aqui: preparar e equilibrar o conjunto de dados de estabilidade de grade; estabelecer uma linha de base clássica AdaBoost; executar várias configurações quânticas que variam a largura e a regularização do conjunto; executar em simuladores IBM® ou QPUs via Qiskit Serverless; e comparar a exatidão, a precisão, a recuperação e F1 em todas as execuções. Ao longo do caminho, você usará o padrão de ação da função (`create`, `fit`, `predict`, `fit_predict`, `create_fit_predict`) e os controles de tecla:\n",
        "\n",
        "* Tipos de regularização: `onsite` (λ) para esparsidade direta e `alpha` para uma compensação baseada em proporção entre termos de interação e no local\n",
        "* Auto-regularização: defina `regularization=\"auto\"` com uma taxa de seleção de destino para adaptar a esparsidade automaticamente\n",
        "* Opções do otimizador: simulador versus QPU, repetições, otimizador clássico e suas opções, profundidade de transpilagem e configurações do Sampler ou do Estimator d Qiskit Runtime\n",
        "\n",
        "As referências na documentação mostram que a precisão melhora à medida que o número de alunos (qubits) aumenta em problemas desafiadores, com o classificador quântico igualando ou superando um conjunto clássico comparável. Neste tutorial, você reproduzirá o fluxo de trabalho de ponta a ponta e examinará quando o aumento da largura do conjunto ou a mudança para a regularização adaptável produz melhor F1 com uso razoável de recursos. O resultado é uma visão fundamentada de como uma etapa de otimização quântica pode complementar, em vez de substituir, o aprendizado de conjunto clássico em aplicativos reais.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisitos\n",
        "\n",
        "Antes de iniciar este tutorial, verifique se você tem os seguintes pacotes instalados em seu ambiente Python :\n",
        "\n",
        "* `qiskit[visualization]~=2.1.0`\n",
        "* `qiskit-serverless~=0.24.0`\n",
        "* `qiskit-ibm-runtime v0.40.1`\n",
        "* `qiskit-ibm-catalog~=0.8.0`\n",
        "* `scikit-learn==1.5.2`\n",
        "* `pandas>=2.0.0,<3.0.0`\n",
        "* `imbalanced-learn~=0.12.3`\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Instalação\n",
        "\n",
        "Nesta seção, inicializamos o cliente Qiskit Serverless e carregamos a função Singularity Machine Learning - Classification fornecida pela Multiverse Computing.\n",
        "Com o Qiskit Serverless, você pode executar fluxos de trabalho híbridos quânticos-clássicos na infraestrutura de nuvem gerenciada pelo IBM sem se preocupar com o gerenciamento de recursos.\n",
        "Você precisará de uma chave de API IBM Quantum Platform e do seu nome de recurso na nuvem (CRN) para se autenticar e acessar Qiskit Functions.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4fe7ee1-21ce-445c-b151-598cd4cf9227",
      "metadata": {},
      "source": [
        "<span id=\"download-the-dataset\" />\n",
        "\n",
        "### Baixe o conjunto de dados\n",
        "\n",
        "Para executar este tutorial, usamos um **conjunto de dados de classificação de estabilidade de grade** pré-processado que contém leituras rotuladas de sensores do sistema de energia.\n",
        "A célula a seguir cria automaticamente a estrutura de pastas necessária e faz o download dos arquivos de treinamento e teste diretamente em seu ambiente usando `wget`.\n",
        "Se você já tiver esses arquivos localmente, esta etapa os substituirá com segurança para garantir a consistência da versão.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "7a32efb3-a425-4c02-804b-65029ecffb6f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "data_tutorial/grid_ 100%[===================>] 612.94K  --.-KB/s    in 0.01s   \n",
            "data_tutorial/grid_ 100%[===================>] 108.19K  --.-KB/s    in 0.006s  \n",
            "Dataset files downloaded:\n",
            "-rw-r--r-- 1 coder coder 109K Nov  8 18:50 data_tutorial/grid_stability/test.csv\n",
            "-rw-r--r-- 1 coder coder 613K Nov  8 18:50 data_tutorial/grid_stability/train.csv\n"
          ]
        }
      ],
      "source": [
        "## Download dataset for Grid Stability Classification\n",
        "\n",
        "# Create data directory if it doesn't exist\n",
        "!mkdir -p data_tutorial/grid_stability\n",
        "\n",
        "# Download the training and test sets from the official Qiskit documentation repo\n",
        "base_url = (\n",
        "    \"https://raw.githubusercontent.com/Qiskit/documentation/\"\n",
        "    \"main/datasets/tutorials/grid_stability\"\n",
        ")\n",
        "!wget -q --show-progress -O data_tutorial/grid_stability/train.csv {base_url}/train.csv\n",
        "\n",
        "!wget -q --show-progress -O data_tutorial/grid_stability/test.csv {base_url}/test.csv\n",
        "\n",
        "# Check the files have been downloaded\n",
        "!echo \"Dataset files downloaded:\"\n",
        "!ls -lh data_tutorial/grid_stability/*.csv"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb9aa84f-ab37-412c-b056-7043b73380fa",
      "metadata": {},
      "source": [
        "<span id=\"import-required-packages\" />\n",
        "\n",
        "### Importar pacotes necessários\n",
        "\n",
        "Nesta seção, importamos todos os pacotes do Python e os módulos do Qiskit usados em todo o tutorial.\n",
        "Isso inclui as principais bibliotecas científicas para manipulação de dados e avaliação de modelos, como `NumPy`, `pandas` e `scikit-learn` , além de ferramentas de visualização e componentes Qiskit para executar o modelo quântico aprimorado.\n",
        "Também importamos o `QiskitRuntimeService` e o `QiskitFunctionsCatalog` para nos conectarmos aos serviços do IBM Quantum® e acessarmos a função Singularity Machine Learning.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f8c654f5-8355-4f67-b79d-c2b1c29ccc62",
      "metadata": {},
      "outputs": [],
      "source": [
        "from pprint import pprint\n",
        "from typing import Tuple\n",
        "import warnings\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from imblearn.over_sampling import RandomOverSampler\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "from sklearn.ensemble import AdaBoostClassifier\n",
        "from sklearn.metrics import (\n",
        "    accuracy_score,\n",
        "    f1_score,\n",
        "    precision_score,\n",
        "    recall_score,\n",
        ")\n",
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "warnings.filterwarnings(\"ignore\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3b389b34-02e8-4a63-ae24-0bf348647b48",
      "metadata": {},
      "source": [
        "<span id=\"set-constant-variables\" />\n",
        "\n",
        "### Definir variáveis constantes\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a68bf7df-914c-4b2a-827f-657578503750",
      "metadata": {},
      "outputs": [],
      "source": [
        "IBM_TOKEN = \"\"\n",
        "IBM_INSTANCE_TEST = \"\"\n",
        "IBM_INSTANCE_QUANTUM = \"\"\n",
        "FUNCTION_NAME = \"multiverse/singularity\"\n",
        "RANDOM_STATE: int = 123\n",
        "TRAIN_PATH = \"data_tutorial/grid_stability/train.csv\"\n",
        "TEST_PATH = \"data_tutorial/grid_stability/test.csv\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4709dc1c-b380-49f1-95c7-89197aa5e147",
      "metadata": {},
      "source": [
        "<span id=\"connect-to-ibm-quantum-and-load-the-singularity-function\" />\n",
        "\n",
        "### Conecte-se a IBM Quantum e carregue a função Singularity\n",
        "\n",
        "Em seguida, fazemos a autenticação com os serviços IBM Quantum e carregamos a função Singularity Machine Learning - Classification do site Qiskit Functions Catalog.\n",
        "O `QiskitRuntimeService` estabelece uma conexão segura com o IBM Quantum Platform usando seu token de API e o CRN da instância, permitindo o acesso a back-ends quânticos.\n",
        "O endereço `QiskitFunctionsCatalog` é então usado para recuperar a função Singularity pelo nome (`\"multiverse/singularity\"`), o que nos permite chamá-la posteriormente para a computação quântica-clássica híbrida.\n",
        "Se a configuração for bem-sucedida, você verá uma mensagem de confirmação indicando que a função foi carregada corretamente.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Successfully connected to IBM Qiskit Serverless and loaded the Singularity function.\n",
            "Catalog: <QiskitFunctionsCatalog>\n",
            "Singularity function: QiskitFunction(multiverse/singularity)\n"
          ]
        }
      ],
      "source": [
        "catalog = QiskitFunctionsCatalog(\n",
        "    token=IBM_TOKEN,\n",
        "    instance=IBM_INSTANCE_TEST,\n",
        "    channel=\"ibm_quantum_platform\",\n",
        ")\n",
        "backend = catalog.least_busy()\n",
        "\n",
        "singularity = catalog.load(FUNCTION_NAME)\n",
        "print(\n",
        "    \"Successfully connected to IBM Qiskit Serverless and \"\n",
        "    \"loaded the Singularity function.\"\n",
        ")\n",
        "print(\"Catalog:\", catalog)\n",
        "print(\"Singularity function:\", singularity)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "47d6a559-118a-4aa9-874d-9c009b5da60c",
      "metadata": {},
      "source": [
        "<span id=\"define-helper-functions\" />\n",
        "\n",
        "### Defina funções auxiliares\n",
        "\n",
        "Antes de executar os principais experimentos, definimos algumas pequenas funções utilitárias que simplificam o carregamento de dados e a avaliação de modelos.\n",
        "\n",
        "* `load_data()` lê os arquivos CSV de entrada em matrizes NumPy, dividindo recursos e rótulos para compatibilidade com fluxos de trabalho `scikit-learn` e quantum.\n",
        "* `evaluate_predictions()` calcula as principais métricas de desempenho - exatidão, precisão, recall e F1-score - e, opcionalmente, informa o tempo de execução se forem fornecidas informações de tempo.\n",
        "\n",
        "Essas funções auxiliares simplificam as operações repetidas posteriormente no notebook e garantem relatórios de métricas consistentes nos classificadores clássicos e quânticos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "46bc841e-7365-4508-b6bf-ae57db6050e1",
      "metadata": {},
      "outputs": [],
      "source": [
        "def load_data(data_path: str) -> Tuple[np.ndarray, np.ndarray]:\n",
        "    \"\"\"Load data from the given path to X and y arrays.\"\"\"\n",
        "    df: pd.DataFrame = pd.read_csv(data_path)\n",
        "    return df.iloc[:, :-1].values, df.iloc[:, -1].values\n",
        "\n",
        "\n",
        "def evaluate_predictions(predictions, y_true):\n",
        "    \"\"\"Compute and print accuracy, precision, recall, and F1 score.\"\"\"\n",
        "    accuracy = accuracy_score(y_true, predictions)\n",
        "    precision = precision_score(y_true, predictions)\n",
        "    recall = recall_score(y_true, predictions)\n",
        "    f1 = f1_score(y_true, predictions)\n",
        "    print(\"Accuracy:\", accuracy)\n",
        "    print(\"Precision:\", precision)\n",
        "    print(\"Recall:\", recall)\n",
        "    print(\"F1:\", f1)\n",
        "    return accuracy, precision, recall, f1"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Passo 1: Mapear entradas clássicas para um problema quântico\n",
        "\n",
        "Começamos preparando o conjunto de dados para a experimentação híbrida quântica-clássica. O objetivo dessa etapa é converter os dados brutos de estabilidade da grade em divisões equilibradas de treinamento, validação e teste que possam ser usadas de forma consistente pelos fluxos de trabalho clássico e quântico. A manutenção de divisões idênticas garante que as comparações de desempenho posteriores sejam justas e reproduzíveis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1c084cde-a5cf-4661-a00c-aa243c8b0e44",
      "metadata": {},
      "source": [
        "<span id=\"data-loading-and-preprocessing\" />\n",
        "\n",
        "### Carregamento e pré-processamento de dados\n",
        "\n",
        "Primeiro, carregamos os arquivos CSV de treinamento e teste, criamos uma divisão de validação e equilibramos o conjunto de dados usando uma superamostragem aleatória. O balanceamento evita a tendência para a classe majoritária e fornece um sinal de aprendizado mais estável para os modelos de conjunto clássico e quântico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "0db0e914-a8f2-4a04-bec7-c15bac8104b4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Shapes:\n",
            "  X_train_bal: (5104, 12)\n",
            "  y_train_bal: (5104,)\n",
            "  X_val: (850, 12)\n",
            "  y_val: (850,)\n",
            "  X_test: (750, 12)\n",
            "  y_test: (750,)\n"
          ]
        }
      ],
      "source": [
        "# Load and upload the data\n",
        "X_train, y_train = load_data(TRAIN_PATH)\n",
        "X_test, y_test = load_data(TEST_PATH)\n",
        "X_train, X_val, y_train, y_val = train_test_split(\n",
        "    X_train, y_train, test_size=0.2, random_state=RANDOM_STATE\n",
        ")\n",
        "\n",
        "# Balance the dataset through over-sampling of the positive class\n",
        "ros = RandomOverSampler(random_state=RANDOM_STATE)\n",
        "X_train_bal, y_train_bal = ros.fit_resample(X_train, y_train)\n",
        "\n",
        "print(\"Shapes:\")\n",
        "print(\"  X_train_bal:\", X_train_bal.shape)\n",
        "print(\"  y_train_bal:\", y_train_bal.shape)\n",
        "print(\"  X_val:\", X_val.shape)\n",
        "print(\"  y_val:\", y_val.shape)\n",
        "print(\"  X_test:\", X_test.shape)\n",
        "print(\"  y_test:\", y_test.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b8c214fd-09a8-4fa5-ab83-b0c71ad615d4",
      "metadata": {},
      "source": [
        "<span id=\"classical-baseline-adaboost-reference\" />\n",
        "\n",
        "### Linha de base clássica: referência AdaBoost\n",
        "\n",
        "Antes de executar qualquer otimização quântica, treinamos uma linha de base clássica forte - um classificador AdaBoost padrão - nos mesmos dados balanceados. Isso fornece um ponto de referência reproduzível para comparação posterior, ajudando a quantificar se a otimização quântica melhora a generalização ou a eficiência além de um conjunto clássico bem ajustado.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "38ecd608-35b4-4e58-8a16-85ef98bf024a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Classical AdaBoost baseline:\n",
            "Accuracy: 0.7893333333333333\n",
            "Precision: 1.0\n",
            "Recall: 0.7893333333333333\n",
            "F1: 0.8822652757078987\n"
          ]
        }
      ],
      "source": [
        "# ----- Classical baseline: AdaBoost -----\n",
        "baseline = AdaBoostClassifier(n_estimators=60, random_state=RANDOM_STATE)\n",
        "baseline.fit(X_train_bal, y_train_bal)\n",
        "baseline_pred = baseline.predict(X_test)\n",
        "print(\"Classical AdaBoost baseline:\")\n",
        "_ = evaluate_predictions(baseline_pred, y_test)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## Etapa 2: Otimizar o problema para execução em hardware quântico\n",
        "\n",
        "A tarefa de seleção de conjuntos é formulada como um problema de otimização combinatória, no qual cada aprendizado fraco é uma variável de decisão binária, e o objetivo equilibra a precisão com a esparsidade por meio de um termo de regularização. O resolve `QuantumEnhancedEnsembleClassifier` isso com QAOA em hardware d IBM, sem deixar de permitir a exploração por meio de simuladores. O controle `optimizer_options` do loop híbrido: `simulator=False` direciona os circuitos para a QPU selecionada, `num_solutions` aumenta a amplitude da busca e ( `classical_optimizer_options` para o otimizador clássico interno) controla a convergência; valores em torno de 60 iterações representam um bom equilíbrio entre qualidade e tempo de execução. As opções de tempo de execução — como profundidade moderada do circuito (`reps`) e um esforço padrão de transpilagem — ajudam a garantir um desempenho robusto em todos os dispositivos. A configuração abaixo é o perfil “melhores resultados” que usaremos para execuções em hardware; você também pode criar uma variante puramente simulada ativando a opção `simulator=True` para executar o fluxo de trabalho em modo de simulação, sem consumir tempo da QPU.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "18c0d3f9-691b-449d-83ca-e6bfce2d6b99",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Configured hardware optimization profile:\n",
            "  simulator: False\n",
            "  num_solutions: 100000\n",
            "  reps: 3\n",
            "  optimization_level: 3\n",
            "  num_transpiler_runs: 30\n",
            "  classical_optimizer: COBYLA\n",
            "  classical_optimizer_options: {'maxiter': 60}\n",
            "  estimator_options: None\n",
            "  sampler_options: None\n"
          ]
        }
      ],
      "source": [
        "# QAOA / runtime configuration for best results on hardware\n",
        "optimizer_options = {\n",
        "    \"simulator\": False,  # set True to test locally without QPU\n",
        "    \"num_solutions\": 100_000,  # broaden search over candidate ensembles\n",
        "    \"reps\": 3,  # QAOA depth (circuit layers)\n",
        "    \"optimization_level\": 3,  # transpilation effort\n",
        "    \"num_transpiler_runs\": 30,  # explore multiple layouts\n",
        "    \"classical_optimizer\": \"COBYLA\",  # robust default for this landscape\n",
        "    \"classical_optimizer_options\": {\n",
        "        \"maxiter\": 60  # practical convergence budget\n",
        "    },\n",
        "    # You can pass backend-specific options;\n",
        "    # leaving None uses least-busy routing\n",
        "    \"estimator_options\": None,\n",
        "    \"sampler_options\": None,\n",
        "}\n",
        "\n",
        "print(\"Configured hardware optimization profile:\")\n",
        "for key, value in optimizer_options.items():\n",
        "    print(f\"  {key}: {value}\")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Passo 3: Execute usando Qiskit primitives\n",
        "\n",
        "Agora, executamos o fluxo de trabalho completo usando a ação `create_fit_predict` da função Singularity para treinar, otimizar e avaliar o `QuantumEnhancedEnsembleClassifier` de ponta a ponta na infraestrutura IBM. A função cria o conjunto, aplica a otimização quântica por meio do site Qiskit primitives e retorna as previsões e os metadados do trabalho (inclusive o tempo de execução e o uso de recursos). A divisão de dados clássicos da Etapa 1 é reutilizada para fins de reprodutibilidade, com dados de validação passados pelo site `fit_params` para que a otimização possa ajustar os hiperparâmetros internamente, mantendo o conjunto de testes retido intacto.\n",
        "\n",
        "Nesta etapa, exploramos várias configurações do conjunto quântico para entender como os principais parâmetros - especificamente `num_learners` e `regularization` - afetam a qualidade dos resultados e o uso da QPU.\n",
        "\n",
        "* `num_learners` determina a largura do conjunto (e, implicitamente, o número de qubits), influenciando a capacidade e o custo computacional do modelo.\n",
        "* `regularization` controla a esparsidade e o ajuste excessivo, definindo quantos alunos permanecem ativos após a otimização.\n",
        "\n",
        "Ao variar esses parâmetros, podemos ver como a largura e a regularização do conjunto interagem: o aumento da largura normalmente melhora o F1, mas custa mais tempo de QPU, enquanto a regularização mais forte ou adaptável pode melhorar a generalização com praticamente o mesmo espaço ocupado pelo hardware. As próximas subseções apresentam três configurações representativas para ilustrar esses efeitos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8da41d4d-f06d-4b67-a8ee-b07fc0289558",
      "metadata": {},
      "source": [
        "<span id=\"baseline\" />\n",
        "\n",
        "### Linha de base\n",
        "\n",
        "Essa configuração usa `num_learners = 10` e `regularization = 7`.\n",
        "\n",
        "* `num_learners` controla a largura do conjunto - efetivamente o número de aprendizes fracos combinados e, no hardware quântico, o **número de qubits necessários**. Um valor maior expande o espaço de pesquisa combinatória e pode melhorar a precisão e a recuperação, mas também aumenta a largura do circuito, o tempo de compilação e o uso geral da QPU.\n",
        "* `regularization` define a força da penalidade para incluir alunos adicionais. Com a regularização padrão \"no local\", os valores mais altos impõem uma esparsidade mais forte (menos alunos mantidos), enquanto os valores mais baixos permitem conjuntos mais complexos.\n",
        "\n",
        "Essa configuração fornece uma linha de base de baixo custo, mostrando como um conjunto pequeno se comporta antes de dimensionar a largura ou ajustar a esparsidade.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e702c986-fd7f-4ea1-a93c-8cd5ef0c4a0b",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Problem scale and regularization\n",
        "NUM_LEARNERS = 10\n",
        "REGULARIZATION = 7"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "06c261c1-a3c1-42c9-a522-63f3fc01a970",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "-- Submitting quantum-enhanced ensemble job --\n",
            "Action status: ok\n",
            "Action message: Classifier created, fitted, and predicted.\n",
            "Metadata:\n",
            "{'resource_usage': {'RUNNING: EXECUTING_QPU': {'QPU_TIME': 1550.1889700889587},\n",
            "                    'RUNNING: MAPPING': {'CPU_TIME': 267.05158376693726},\n",
            "                    'RUNNING: POST_PROCESSING': {'CPU_TIME': 152.4274561405182},\n",
            "                    'RUNNING: WAITING_QPU': {'CPU_TIME': 3336.8785166740417}}}\n",
            "Accuracy: 0.868\n",
            "Precision: 1.0\n",
            "Recall: 0.868\n",
            "F1: 0.9293361884368309\n"
          ]
        }
      ],
      "source": [
        "# ----- Quantum-enhanced ensemble on IBM hardware -----\n",
        "print(\"\\n-- Submitting quantum-enhanced ensemble job --\")\n",
        "job_1 = singularity.run(\n",
        "    action=\"create_fit_predict\",\n",
        "    name=\"grid_stability_qeec\",\n",
        "    quantum_classifier=\"QuantumEnhancedEnsembleClassifier\",\n",
        "    num_learners=NUM_LEARNERS,\n",
        "    regularization=REGULARIZATION,\n",
        "    optimizer_options=optimizer_options,  # from Step 2\n",
        "    backend_name=backend,  # least-busy compatible backend\n",
        "    instance=IBM_INSTANCE_QUANTUM,\n",
        "    random_state=RANDOM_STATE,\n",
        "    X_train=X_train_bal,\n",
        "    y_train=y_train_bal,\n",
        "    X_test=X_test,\n",
        "    fit_params={\"validation_data\": (X_val, y_val)},\n",
        "    options={\"save\": False},\n",
        ")\n",
        "result_1 = job_1.result()\n",
        "print(\"Action status:\", result_1.get(\"status\"))\n",
        "print(\"Action message:\", result_1.get(\"message\"))\n",
        "print(\"Metadata:\")\n",
        "pprint(result_1.get(\"metadata\"))\n",
        "qeec_pred_job_1 = np.array(result_1[\"data\"][\"predictions\"])\n",
        "_ = evaluate_predictions(qeec_pred_job_1, y_test)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "1bf0a3ae-b5b8-43d7-b72d-5a9cf6c61060",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Quantum job status: DONE\n"
          ]
        }
      ],
      "source": [
        "status_1 = job_1.status()\n",
        "print(\"\\nQuantum job status:\", status_1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae62b414-db77-45aa-bf2c-a8537fd7eba0",
      "metadata": {},
      "source": [
        "<span id=\"increase-the-number-of-learners\" />\n",
        "\n",
        "### Aumentar o número de alunos\n",
        "\n",
        "Aqui, aumentamos o `num_learners` de 10 → 30, mantendo o `regularization = 7`.\n",
        "\n",
        "* Um número maior de alunos expande o espaço de hipóteses, permitindo que o modelo capture padrões mais sutis, o que pode elevar modestamente F1.\n",
        "* Na maioria dos casos, a diferença de tempo de execução entre 10 e 30 alunos não é substancial, o que indica que a largura adicional do circuito não aumenta significativamente o custo de execução.\n",
        "* O aprimoramento da qualidade ainda segue uma *curva de retorno decrescente* : os primeiros ganhos aparecem à medida que o conjunto cresce, mas eles se estabilizam quando os alunos adicionais contribuem com menos informações novas.\n",
        "\n",
        "Esse experimento destaca a compensação entre qualidade e eficiência - o aumento da largura do conjunto pode oferecer pequenos ganhos de precisão sem uma grande penalidade no tempo de execução, dependendo das condições de backend e transpilação.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "e5fe64b0-2713-4b65-b768-10fa5d8dbf79",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Problem scale and regularization\n",
        "NUM_LEARNERS = 30\n",
        "REGULARIZATION = 7"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "18a22fed-41c2-4407-88d4-02fbd32f3320",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "-- Submitting quantum-enhanced ensemble job --\n",
            "Action status: ok\n",
            "Action message: Classifier created, fitted, and predicted.\n",
            "QPU Time:\n",
            "{'resource_usage': {'RUNNING: EXECUTING_QPU': {'QPU_TIME': 1095.822762966156},\n",
            "                    'RUNNING: MAPPING': {'CPU_TIME': 680.2116754055023},\n",
            "                    'RUNNING: POST_PROCESSING': {'CPU_TIME': 154.4466371536255},\n",
            "                    'RUNNING: WAITING_QPU': {'CPU_TIME': 80.80395102500916}}}\n",
            "Accuracy: 0.8946666666666667\n",
            "Precision: 1.0\n",
            "Recall: 0.8946666666666667\n",
            "F1: 0.944405348346235\n"
          ]
        }
      ],
      "source": [
        "# ----- Quantum-enhanced ensemble on IBM hardware -----\n",
        "print(\"\\n-- Submitting quantum-enhanced ensemble job --\")\n",
        "job_2 = singularity.run(\n",
        "    action=\"create_fit_predict\",\n",
        "    name=\"grid_stability_qeec\",\n",
        "    quantum_classifier=\"QuantumEnhancedEnsembleClassifier\",\n",
        "    num_learners=NUM_LEARNERS,\n",
        "    regularization=REGULARIZATION,\n",
        "    optimizer_options=optimizer_options,  # from Step 2\n",
        "    backend_name=backend,  # least-busy compatible backend\n",
        "    instance=IBM_INSTANCE_QUANTUM,\n",
        "    random_state=RANDOM_STATE,\n",
        "    X_train=X_train_bal,\n",
        "    y_train=y_train_bal,\n",
        "    X_test=X_test,\n",
        "    fit_params={\"validation_data\": (X_val, y_val)},\n",
        "    options={\"save\": False},\n",
        ")\n",
        "result_2 = job_2.result()\n",
        "print(\"Action status:\", result_2.get(\"status\"))\n",
        "print(\"Action message:\", result_2.get(\"message\"))\n",
        "print(\"QPU Time:\")\n",
        "pprint(result_2.get(\"metadata\"))\n",
        "qeec_pred_job_2 = np.array(result_2[\"data\"][\"predictions\"])\n",
        "_ = evaluate_predictions(qeec_pred_job_2, y_test)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "edd22b98-7ae6-4444-8fe5-7279e9233c57",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Quantum job status: DONE\n"
          ]
        }
      ],
      "source": [
        "status_2 = job_2.status()\n",
        "print(\"\\nQuantum job status:\", status_2)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e94784a-3599-4911-ade9-4792b50c31bb",
      "metadata": {},
      "source": [
        "<span id=\"regularization\" />\n",
        "\n",
        "### Regularização\n",
        "\n",
        "Nessa configuração, aumentamos para `num_learners = 60` e introduzimos a regularização adaptativa para gerenciar a esparsidade de forma mais intuitiva.\n",
        "\n",
        "* Com `regularization = \"auto\"`, o otimizador encontra automaticamente uma força de regularização adequada que seleciona aproximadamente `regularization_ratio * num_learners` alunos fracos para o conjunto final, em vez de fixar a penalidade manualmente. Isso oferece uma interface mais conveniente para gerenciar o equilíbrio entre a esparsidade e o tamanho do conjunto.\n",
        "* `regularization_type = \"alpha\"` define como a penalidade é aplicada. Ao contrário do `onsite`, que não tem limites `[0, ∞]`, o `alpha` tem limites entre `[0, 1]`, o que facilita o ajuste e a interpretação. O parâmetro controla a compensação entre penalidades individuais e em pares, oferecendo uma faixa de configuração mais suave.\n",
        "* `regularization_desired_ratio ≈ 0.82` especifica a proporção-alvo de alunos a serem mantidos ativos após a regularização - aqui, cerca de 82% dos alunos são retidos, cortando automaticamente os 18% mais fracos.\n",
        "\n",
        "Embora a regularização adaptativa simplifique a configuração e ajude a manter um conjunto equilibrado, ela não garante necessariamente um desempenho melhor ou mais estável. A qualidade real depende da seleção de um parâmetro de regularização adequado, e o ajuste fino por meio de validação cruzada pode ser caro do ponto de vista computacional. A principal vantagem está no aprimoramento da usabilidade e da interpretabilidade, em vez de ganhos diretos de precisão.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "209eb51a-e44b-4f94-8975-269ec7e3d1ca",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Problem scale and regularization\n",
        "NUM_LEARNERS = 60\n",
        "REGULARIZATION = \"auto\"\n",
        "REGULARIZATION_TYPE = \"alpha\"\n",
        "REGULARIZATION_RATIO = 0.82"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "eb2aa9db-4427-48de-aae0-c5c1da1cb998",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "-- Submitting quantum-enhanced ensemble job --\n",
            "Action status: ok\n",
            "Action message: Classifier created, fitted, and predicted.\n",
            "Metadata:\n",
            "{'resource_usage': {'RUNNING: EXECUTING_QPU': {'QPU_TIME': 1146.5584812164307},\n",
            "                    'RUNNING: MAPPING': {'CPU_TIME': 1387.7451872825623},\n",
            "                    'RUNNING: POST_PROCESSING': {'CPU_TIME': 171.78878355026245},\n",
            "                    'RUNNING: WAITING_QPU': {'CPU_TIME': 95.41597843170166}}}\n",
            "Accuracy: 0.908\n",
            "Precision: 1.0\n",
            "Recall: 0.908\n",
            "F1: 0.9517819706498952\n"
          ]
        }
      ],
      "source": [
        "# ----- Quantum-enhanced ensemble on IBM hardware -----\n",
        "print(\"\\n-- Submitting quantum-enhanced ensemble job --\")\n",
        "job_3 = singularity.run(\n",
        "    action=\"create_fit_predict\",\n",
        "    name=\"grid_stability_qeec\",\n",
        "    quantum_classifier=\"QuantumEnhancedEnsembleClassifier\",\n",
        "    num_learners=NUM_LEARNERS,\n",
        "    regularization=REGULARIZATION,\n",
        "    regularization_type=REGULARIZATION_TYPE,\n",
        "    regularization_desired_ratio=REGULARIZATION_RATIO,\n",
        "    optimizer_options=optimizer_options,  # from Step 2\n",
        "    backend_name=backend,  # least-busy compatible backend\n",
        "    instance=IBM_INSTANCE_QUANTUM,\n",
        "    random_state=RANDOM_STATE,\n",
        "    X_train=X_train_bal,\n",
        "    y_train=y_train_bal,\n",
        "    X_test=X_test,\n",
        "    fit_params={\"validation_data\": (X_val, y_val)},\n",
        "    options={\"save\": False},\n",
        ")\n",
        "result_3 = job_3.result()\n",
        "print(\"Action status:\", result_3.get(\"status\"))\n",
        "print(\"Action message:\", result_3.get(\"message\"))\n",
        "print(\"Metadata:\")\n",
        "pprint(result_3.get(\"metadata\"))\n",
        "qeec_pred_job_3 = np.array(result_3[\"data\"][\"predictions\"])\n",
        "_ = evaluate_predictions(qeec_pred_job_3, y_test)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "299b36cb-0ed8-4af3-b431-a87daec04c93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Quantum job status: DONE\n"
          ]
        }
      ],
      "source": [
        "status_3 = job_3.status()\n",
        "print(\"\\nQuantum job status:\", status_3)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## Etapa 4: Pós-processamento e retorno do resultado no formato clássico desejado\n",
        "\n",
        "Agora, pós-processamos os resultados das execuções clássica e quântica, convertendo-os em um formato consistente para avaliação posterior. Essa etapa compara a qualidade da previsão usando métricas padrão - exatidão, precisão, recuperação e F1 - e analisa como a largura do conjunto (`num_learners`) e o controle de esparsidade (`regularization`) influenciam o desempenho e o comportamento computacional.\n",
        "\n",
        "A linha de base clássica do AdaBoost oferece uma referência compacta e estável para o aprendizado em pequena escala. Ele tem bom desempenho com conjuntos limitados e sobrecarga de computação insignificante, refletindo a força do reforço tradicional quando o espaço de hipóteses ainda é tratável. As configurações quânticas (`qeec_pred_job_1`, `qeec_pred_job_2` e `qeec_pred_job_3`) ampliam essa linha de base incorporando o processo de seleção de conjuntos em um loop de otimização quântica variacional. Isso permite que o sistema explore subconjuntos exponencialmente grandes de alunos simultaneamente em sobreposição, abordando a natureza combinatória da seleção de conjuntos de forma mais eficiente à medida que a escala aumenta.\n",
        "\n",
        "Os resultados mostram que o aumento de `num_learners` de 10 para 30 melhora a recuperação e F1, confirmando que um conjunto mais amplo captura interações mais ricas entre os alunos fracos. O ganho é sublinear no hardware atual - cada aprendiz adicional produz incrementos de precisão menores - mas o comportamento de escala subjacente permanece favorável porque o otimizador quântico pode pesquisar espaços de configuração mais amplos sem o aumento exponencial típico da seleção clássica de subconjuntos. A regularização introduz nuances adicionais: um λ=7 fixo impõe uma esparsidade consistente e estabiliza a convergência, enquanto a regularização α adaptativa ajusta automaticamente a esparsidade com base nas correlações entre os alunos. Esse corte dinâmico geralmente alcança um F1 ligeiramente maior para a mesma largura de qubit, equilibrando a complexidade e a generalização do modelo.\n",
        "\n",
        "Quando comparada diretamente com a linha de base AdaBoost, a menor configuração quântica ( L=10 ) reproduz precisão semelhante, validando a correção do pipeline híbrido. Em larguras maiores, as variantes quânticas - especialmente com a autorregularização - começam a superar modestamente a linha de base clássica, mostrando melhor recuperação e F1 sem crescimento linear no custo computacional. Esses aprimoramentos não indicam uma \"vantagem quântica\" imediata, mas sim **uma eficiência de escalonamento** : o otimizador quântico mantém um desempenho viável à medida que o conjunto se expande, enquanto uma abordagem clássica enfrentaria um crescimento exponencial na complexidade da seleção de subconjuntos.\n",
        "\n",
        "Na prática:\n",
        "\n",
        "* Use a **linha de base clássica** para validação rápida e benchmarking em pequenos conjuntos de dados.\n",
        "* Aplique **conjuntos quânticos** quando a largura do modelo ou a complexidade dos recursos aumentar - a pesquisa baseada em QAOA se adapta melhor a esses regimes.\n",
        "* Empregar **a α-regularização adaptativa** para manter a esparsidade e a generalização sem aumentar a largura do circuito.\n",
        "* Monitore o tempo e a profundidade da QPU para equilibrar os ganhos de qualidade com as restrições de hardware de curto prazo.\n",
        "\n",
        "Juntos, esses experimentos mostram que os conjuntos otimizados por quantum complementam os métodos clássicos: eles reproduzem a precisão da linha de base em pequenas escalas e, ao mesmo tempo, oferecem um caminho para o dimensionamento eficiente em problemas de aprendizado combinatório maiores. Com o aprimoramento do hardware, espera-se que essas vantagens de escalonamento se agravem, ampliando o tamanho e a profundidade viáveis dos modelos baseados em conjuntos para além do que é classicamente prático.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1cf1d610-f2e2-482c-af05-a882bb380528",
      "metadata": {},
      "source": [
        "<span id=\"evaluate-metrics-for-each-configuration\" />\n",
        "\n",
        "### Avalie as métricas para cada configuração\n",
        "\n",
        "Agora, avaliamos todas as configurações - a linha de base clássica AdaBoost e os três conjuntos quânticos - usando o auxiliar `evaluate_predictions` para calcular a exatidão, a precisão, a recuperação e F1 no mesmo conjunto de testes. Essa comparação esclarece como a otimização quântica é dimensionada em relação à abordagem clássica: em larguras pequenas, ambas têm desempenho semelhante; à medida que os conjuntos crescem, o método quântico pode explorar espaços de hipóteses maiores com mais eficiência. A tabela resultante captura essas tendências de forma consistente e quantitativa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "ee8cc821-8311-4c84-8f94-ffb2ec3facc3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Accuracy: 0.7893333333333333\n",
            "Precision: 1.0\n",
            "Recall: 0.7893333333333333\n",
            "F1: 0.8822652757078987\n",
            "Accuracy: 0.868\n",
            "Precision: 1.0\n",
            "Recall: 0.868\n",
            "F1: 0.9293361884368309\n",
            "Accuracy: 0.8946666666666667\n",
            "Precision: 1.0\n",
            "Recall: 0.8946666666666667\n",
            "F1: 0.944405348346235\n",
            "Accuracy: 0.908\n",
            "Precision: 1.0\n",
            "Recall: 0.908\n",
            "F1: 0.9517819706498952\n"
          ]
        },
        {
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>Config</th>\n",
              "      <th>Accuracy</th>\n",
              "      <th>Precision</th>\n",
              "      <th>Recall</th>\n",
              "      <th>F1</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>AdaBoost (Classical)</td>\n",
              "      <td>0.789333</td>\n",
              "      <td>1.0</td>\n",
              "      <td>0.789333</td>\n",
              "      <td>0.882265</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>QEEC L=10, reg=7</td>\n",
              "      <td>0.868000</td>\n",
              "      <td>1.0</td>\n",
              "      <td>0.868000</td>\n",
              "      <td>0.929336</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>QEEC L=30, reg=7</td>\n",
              "      <td>0.894667</td>\n",
              "      <td>1.0</td>\n",
              "      <td>0.894667</td>\n",
              "      <td>0.944405</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>QEEC L=60, reg=auto (α=0.82)</td>\n",
              "      <td>0.908000</td>\n",
              "      <td>1.0</td>\n",
              "      <td>0.908000</td>\n",
              "      <td>0.951782</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "                         Config  Accuracy  Precision    Recall        F1\n",
              "0          AdaBoost (Classical)  0.789333        1.0  0.789333  0.882265\n",
              "1              QEEC L=10, reg=7  0.868000        1.0  0.868000  0.929336\n",
              "2              QEEC L=30, reg=7  0.894667        1.0  0.894667  0.944405\n",
              "3  QEEC L=60, reg=auto (α=0.82)  0.908000        1.0  0.908000  0.951782"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "results = []\n",
        "\n",
        "# Classical baseline\n",
        "acc_b, prec_b, rec_b, f1_b = evaluate_predictions(baseline_pred, y_test)\n",
        "results.append(\n",
        "    {\n",
        "        \"Config\": \"AdaBoost (Classical)\",\n",
        "        \"Accuracy\": acc_b,\n",
        "        \"Precision\": prec_b,\n",
        "        \"Recall\": rec_b,\n",
        "        \"F1\": f1_b,\n",
        "    }\n",
        ")\n",
        "\n",
        "# Quantum runs\n",
        "for label, preds in [\n",
        "    (\"QEEC L=10, reg=7\", qeec_pred_job_1),\n",
        "    (\"QEEC L=30, reg=7\", qeec_pred_job_2),\n",
        "    (f\"QEEC L=60, reg=auto (α={REGULARIZATION_RATIO})\", qeec_pred_job_3),\n",
        "]:\n",
        "    acc, prec, rec, f1 = evaluate_predictions(preds, y_test)\n",
        "    results.append(\n",
        "        {\n",
        "            \"Config\": label,\n",
        "            \"Accuracy\": acc,\n",
        "            \"Precision\": prec,\n",
        "            \"Recall\": rec,\n",
        "            \"F1\": f1,\n",
        "        }\n",
        "    )\n",
        "\n",
        "\n",
        "df_results = pd.DataFrame(results)\n",
        "df_results"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "704cecf6-2c87-4356-b9be-26f4346c194f",
      "metadata": {},
      "source": [
        "<span id=\"visualize-quality-trends-across-configurations\" />\n",
        "\n",
        "### Visualize as tendências de qualidade em todas as configurações\n",
        "\n",
        "O gráfico de barras agrupado abaixo compara **a Precisão** e a **F1** entre a linha de base clássica e os conjuntos quânticos (`L=10`, `L=30` e `L=60 auto-α`). Ele ilustra como a precisão se estabiliza enquanto o F1 melhora gradualmente à medida que a largura do conjunto quântico aumenta, demonstrando que o método híbrido sustenta o escalonamento do desempenho sem o crescimento exponencial de custos típico da seleção clássica de subconjuntos.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "0f15c5fb-2450-4671-9bc2-471043414df2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/sml-classification/extracted-outputs/0f15c5fb-2450-4671-9bc2-471043414df2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "x = np.arange(len(df_results))\n",
        "width = 0.35\n",
        "plt.figure(figsize=(7.6, 4.6))\n",
        "plt.bar(x - width / 2, df_results[\"Accuracy\"], width=width, label=\"Accuracy\")\n",
        "plt.bar(x + width / 2, df_results[\"F1\"], width=width, label=\"F1\")\n",
        "plt.xticks(x, df_results[\"Config\"], rotation=10)\n",
        "plt.ylabel(\"Score\")\n",
        "plt.title(\"Classical vs Quantum ensemble performance\")\n",
        "plt.legend()\n",
        "plt.ylim(0, 1.0)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "745cd406-5166-4eb9-8506-0eedd71e9b79",
      "metadata": {},
      "source": [
        "<span id=\"interpretation\" />\n",
        "\n",
        "### Interpretação\n",
        "\n",
        "O gráfico confirma o padrão de escala esperado. O AdaBoost clássico tem um bom desempenho para conjuntos menores, mas torna-se cada vez mais caro para ser dimensionado à medida que o número de alunos fracos aumenta, porque seu problema de seleção de subconjunto se expande de forma combinatória. Os modelos com aprimoramento quântico replicam a precisão clássica em larguras baixas e começam a superá-la à medida que o tamanho do conjunto aumenta, especialmente sob a regularização α adaptativa. Isso reflete a capacidade do otimizador quântico de amostrar e avaliar muitos subconjuntos candidatos em paralelo por meio de superposição, mantendo a pesquisa tratável mesmo em larguras maiores. Embora a sobrecarga de hardware atual compense alguns dos ganhos teóricos, a tendência ilustra a vantagem da eficiência de dimensionamento da formulação quântica. Em termos práticos, o método clássico continua sendo preferível para benchmarks leves, enquanto os conjuntos aprimorados por quantum tornam-se vantajosos à medida que a dimensionalidade do modelo e o tamanho do conjunto aumentam, oferecendo melhores compensações entre precisão, generalização e crescimento computacional.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a27afb68-f959-4f4a-910c-0aadafb7e18e",
      "metadata": {},
      "source": [
        "<span id=\"appendix-scaling-benefits-and-enhancements\" />\n",
        "\n",
        "## Apêndice: Benefícios e melhorias de escalabilidade\n",
        "\n",
        "A vantagem de escalabilidade do site `QuantumEnhancedEnsembleClassifier` decorre do modo como o processo de seleção de conjuntos é mapeado para a otimização quântica.\n",
        "Os métodos clássicos de aprendizagem de conjuntos, como AdaBoost ou florestas aleatórias, tornam-se computacionalmente caros à medida que o número de alunos fracos aumenta, pois a seleção do subconjunto ideal é um problema combinatório que se expande exponencialmente.\n",
        "\n",
        "Em contrapartida, a formulação quântica - implementada aqui por meio do Algoritmo de Otimização Aproximada Quântica (QAOA) - pode explorar esses espaços de pesquisa exponencialmente grandes com mais eficiência, avaliando várias configurações em superposição.\n",
        "Como resultado, o tempo de treinamento não aumenta significativamente com o número de alunos, permitindo que o modelo permaneça eficiente mesmo com o aumento da largura do conjunto.\n",
        "\n",
        "Embora o hardware atual apresente algumas limitações de ruído e profundidade, esse fluxo de trabalho demonstra uma abordagem híbrida de curto prazo em que os componentes clássicos e quânticos cooperam: o otimizador quântico fornece um cenário de inicialização melhor para o loop clássico, melhorando a convergência e a qualidade do modelo final.\n",
        "À medida que os processadores quânticos evoluem, espera-se que esses benefícios de escalabilidade se estendam a conjuntos de dados maiores, conjuntos mais amplos e profundidades de circuito mais profundas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ee41a301",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referências\n",
        "\n",
        "1. [Introdução ao Qiskit Functions](/docs/guides/functions)\n",
        "2. [Singularidade da computação do multiverso Machine Learning](/docs/guides/multiverse-computing-singularity)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2cb5785c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Pesquisa tutorial\n",
        "\n",
        "Reserve um minuto para fazer comentários sobre este tutorial. Suas percepções nos ajudarão a melhorar nossas ofertas de conteúdo e a experiência do usuário.\n",
        "\n",
        "[Link para a pesquisa](https://your.feedback.ibm.com/jfe/form/SV_3BLFkNVEuh0QBWm)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "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"
    },
    "hours": 2,
    "qpuSeconds": 1200
  },
  "nbformat": 4,
  "nbformat_minor": 5
}