{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "11ea9666",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"최적화 솔버 - Q-CTRL Fire Opal의 Qiskit 함수\"\n",
        "description: \"Q-CTRL의 Fire Opal 최적화 솔버로 유틸리티 규모 최적화 문제 해결을 시작하세요\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Sachdeva */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dde95705",
      "metadata": {},
      "source": [
        "<span id=\"optimization-solver-a-qiskit-function-by-q-ctrl-fire-opal\" />\n",
        "\n",
        "# 최적화 솔버: Q-CTRL Fire Opal의 Qiskit 함수\n",
        "\n",
        "*[API 참조](/docs/api/functions/q-ctrl-optimization-solver) 보기*\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  키스킷 기능은 IBM 퀀텀 프리미엄 요금제, 플렉스 요금제 및 온프레미스( IBM 퀀텀 플랫폼 API를 통해) 요금제 사용자에게만 제공되는 실험적 기능입니다. 프리뷰 릴리스 상태이며 변경될 수 있습니다.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "40c2085c",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"패키지 버전\">\n",
        "    이 페이지의 코드는 다음 요구 사항을 바탕으로 개발되었습니다.\n",
        "    이 버전 이상을 사용하시기를 권장합니다.\n",
        "\n",
        "    ```\n",
        "    qiskit-ibm-runtime~=0.47.0\n",
        "    sympy~=1.14.0\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e30c7881",
      "metadata": {},
      "source": [
        "<span id=\"overview\" />\n",
        "\n",
        "## 개요\n",
        "\n",
        "파이어 오팔 최적화 솔버를 사용하면 양자 전문 지식이 없어도 양자 하드웨어에서 유틸리티 규모의 최적화 문제를 해결할 수 있습니다. 높은 수준의 문제 정의만 입력하면 나머지는 솔버가 알아서 처리합니다. 전체 워크플로는 노이즈를 인식하며 내부적으로 [Fire Opal의 성능 관리](/docs/guides/q-ctrl-performance-management) 기능을 활용합니다. 솔버는 최대 규모의 최대 장치 규모( IBM® )에서도 고전적으로 까다로운 문제에 대한 정확한 솔루션을 일관되게 제공합니다.\n",
        "\n",
        "이 솔버는 유연성이 뛰어나며, 목적 함수나 임의의 그래프로 정의된 조합 최적화 문제를 해결하는 데 사용할 수 있습니다. 문제를 반드시 장치 토폴로지에 매핑할 필요는 없습니다. 제약이 없는 문제와 제약이 있는 문제 모두 풀 수 있으며, 제약 조건은 페널티 항이 아닌 하드 Hamming-weight-1 제약 조건으로 적용됩니다. 이 가이드에 포함된 예제들은 다양한 솔버 입력 유형을 사용하여 제약 조건이 없는 최적화 문제와 제약 조건이 있는 대규모 최적화 문제를 해결하는 방법을 보여줍니다. 첫 번째 예제는 156개의 정점을 가진 3-정규 그래프에서 정의된 최대 절단 문제를 다루는 반면, 두 번째 예제는 비용 함수에 의해 정의된 50개의 정점을 가진 그래프 분할 문제를 다룬다.\n",
        "\n",
        "최적화 솔버에 액세스하려면 [Q-CTRL에 문의하세요](https://form.typeform.com/to/uOAVDnGg?typeform-source=q-ctrl.com).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f761442",
      "metadata": {},
      "source": [
        "<span id=\"function-description\" />\n",
        "\n",
        "## 기능 설명\n",
        "\n",
        "솔버는 하드웨어 수준에서의 오류 억제부터 효율적인 문제 매핑 및 폐쇄 루프 클래식 최적화에 이르기까지 전체 알고리즘을 완전히 최적화하고 자동화합니다. 솔버의 파이프라인은 모든 단계에서 오류를 줄여 의미 있는 확장에 필요한 향상된 성능을 제공합니다. 기본 워크플로우는 양자-클래식 하이브리드 알고리즘인 양자 근사 최적화 알고리즘(QAOA)에서 영감을 받았습니다. 전체 최적화 솔버 워크플로우에 대한 자세한 요약은 [게시된 원고를](https://arxiv.org/abs/2406.01743) 참조하세요.\n",
        "\n",
        "![최적화 솔버 워크플로우의 시각화](https://quantum.cloud.ibm.com/docs/images/guides/qctrl-optimization/solver_workflow.svg)\n",
        "\n",
        "최적화 솔버로 일반적인 문제를 해결하려면:\n",
        "\n",
        "1. 문제를 목적 함수, 그래프 또는 `SparsePauliOp` 스핀 체인으로 정의합니다.\n",
        "2. 키스킷 함수 카탈로그를 통해 함수에 연결합니다.\n",
        "3. 솔버로 문제를 실행하고 결과를 검색합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d20af236",
      "metadata": {},
      "source": [
        "<span id=\"accepted-problem-formats\" />\n",
        "\n",
        "### 허용되는 문제 형식\n",
        "\n",
        "* 목적함수의 다항식 표현. Python 에서 기존 SymPy 폴리 개체를 사용하여 생성하고 [sympy.srepr](https://docs.sympy.org/latest/tutorials/intro-tutorial/printing.html#srepr).\n",
        "* 특정 문제 유형을 그래프로 나타낸 것. 그래프는 Python 에 있는 networkx 라이브러리를 사용하여 작성해야 합니다. 그런 다음 networkx 함수를 사용하여 이를 문자열로 변환해야 합니다 [`nx.readwrite.json_graph.adjacency_data`](https://networkx.org/documentation/stable/reference/readwrite/generated/networkx.readwrite.json_graph.adjacency_data.html).\n",
        "* 특정 문제에 대한 스핀 체인 표현. 스핀 체인은 `SparsePauliOp` 객체로 표시되어야 합니다. 자세한 내용은 [문서를](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp) 참조하세요.\n",
        "\n",
        "<Admonition type=\"note\" title=\"이 기능은 모든 IBM 백엔드를 지원합니까?\">\n",
        "  이 함수가 현재 지원하지 않는 백엔드를 사용하고자 하는 경우, [Q-CTRL에 문의하여](https://form.typeform.com/to/iuujEAEI?typeform-source=q-ctrl.com) 지원 기능을 추가해 주시기 바랍니다.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1165f4f0",
      "metadata": {},
      "source": [
        "<span id=\"benchmarks\" />\n",
        "\n",
        "## 벤치마크\n",
        "\n",
        "<Admonition type=\"caution\" title=\"면책사항\">\n",
        "  성능은 문제 인스턴스와 후속 처리 단계 모두에 따라 달라질 수 있습니다. 경우에 따라서는, 고전적 샘플과 양자 방식으로 생성된 샘플이 동등한 후처리를 거친 후 유사한 최종 솔루션 품질을 달성할 수도 있다. 따라서 평가 시에는 최적화 워크플로우 전체를 고려해야 합니다.\n",
        "</Admonition>\n",
        "\n",
        "[공개된 벤치마킹 결과에](https://arxiv.org/abs/2406.01743) 따르면 솔버는 120큐비트 이상의 문제를 성공적으로 해결했으며, 심지어 양자 어닐링 및 갇힌 이온 장치에 대해 이전에 발표된 결과보다 더 뛰어난 성능을 보였습니다. 다음 벤치마크 메트릭은 몇 가지 예를 바탕으로 문제 유형의 정확도와 확장성을 대략적으로 보여줍니다. 실제 지표는 목적 함수의 항 수(밀도)와 그 위치, 변수 수, 다항식 순서 등 다양한 문제 특징에 따라 달라질 수 있습니다.\n",
        "\n",
        "표시된 '큐비트 수'는 엄격한 제한이 아니라 매우 일관된 솔루션 정확도를 기대할 수 있는 대략적인 임계값을 나타냅니다. 더 큰 규모의 문제도 성공적으로 해결되었으며, 이러한 한계를 넘어서는 테스트가 권장됩니다.\n",
        "\n",
        "모든 문제 유형에서 임의 큐비트 연결이 지원됩니다.\n",
        "\n",
        "| 문제점 유형              | 큐비트 수 | 예                       | 정확도   | 총시간(초) | 런타임 사용량(초) | 반복 수   |\n",
        "| ------------------- | ----- | ----------------------- | ----- | ------ | ---------- | ------ |\n",
        "| 드물게 연결된 이차 문제       | 156   | 3-정규 최대 절단              | 100%로 | 1764   | 293        | 그림 16  |\n",
        "| 고차 바이너리 최적화         | 156   | 이싱 스핀 글래스 모델            | 100%로 | 1461   | 272        | 그림 16  |\n",
        "| 밀접하게 연결된 이차 문제      | 50    | 완전 연결 최대 절단             | 100%로 | 1758   | 268        | 12     |\n",
        "| 엄격한 제약 조건이 있는 제약 문제 | 50    | 8%의 간선 밀도를 가진 가중 그래프 분할 | 100%로 | 1074   | 215        | 1,000만 |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "73390a19",
      "metadata": {},
      "source": [
        "<span id=\"get-started\" />\n",
        "\n",
        "## 시작하기\n",
        "\n",
        "먼저, [IBM Quantum API 키](http://quantum.cloud.ibm.com/) 를 사용하여 인증하십시오. 그런 다음 다음과 같이 Qiskit 함수를 선택합니다. (이 코드 조각은 이미 [계정을](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) 로컬 환경에 저장해 두었다고 가정합니다.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "95a715d2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[QiskitFunction(qunova/hivqe-chemistry),\n",
              " QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),\n",
              " QiskitFunction(algorithmiq/tem),\n",
              " QiskitFunction(qedma/qesem),\n",
              " QiskitFunction(multiverse/singularity),\n",
              " QiskitFunction(ibm/circuit-function),\n",
              " QiskitFunction(q-ctrl/optimization-solver),\n",
              " QiskitFunction(colibritd/quick-pde),\n",
              " QiskitFunction(q-ctrl/performance-management),\n",
              " QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Verify that you have access to the function\n",
        "catalog.list()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "8acf84f6",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Access Function\n",
        "solver = catalog.load(\"q-ctrl/optimization-solver\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e8837f5f",
      "metadata": {},
      "source": [
        "<span id=\"example-unconstrained-optimization\" />\n",
        "\n",
        "## 예시: 제약 없는 최적화\n",
        "\n",
        "[최대 절단](https://en.wikipedia.org/wiki/Maximum_cut) (max-cut) 문제를 실행하십시오. 다음 예제는 156개의 노드를 가진 3-정규 무가중 그래프의 최대 절단 문제에 대해 솔버의 기능을 보여줍니다. 하지만 가중 그래프 문제도 해결할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a699235b",
      "metadata": {},
      "source": [
        "`qiskit-ibm-catalog` 외에도 다음 패키지를 사용하여 이 예제를 실행합니다: `networkx` 및 `numpy`. IPython 커널을 사용하는 노트북에서 이 예제를 실행하는 경우 다음 셀의 주석 처리를 해제하여 이러한 패키지를 설치할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a74fe6a9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install networkx numpy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a41c4a0d",
      "metadata": {},
      "source": [
        "<span id=\"1-define-the-problem\" />\n",
        "\n",
        "### 1. 문제 정의\n",
        "\n",
        "`problem_type='maxcut'`그래프 문제를 정의하고 를 지정하여 최대 절단 문제를 실행할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "d56e1440",
      "metadata": {},
      "outputs": [],
      "source": [
        "import networkx as nx\n",
        "import numpy as np\n",
        "\n",
        "# Generate a random graph with 156 nodes\n",
        "maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "0a7255e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/q-ctrl-optimization-solver/extracted-outputs/0a7255e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Optionally, visualize the graph\n",
        "nx.draw_networkx(\n",
        "    maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e42a8d55",
      "metadata": {},
      "source": [
        "솔버는 문제 정의 입력으로 문자열을 받습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2546e3e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Convert graph to string\n",
        "problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ab6f24f",
      "metadata": {},
      "source": [
        "<span id=\"2-run-the-problem\" />\n",
        "\n",
        "### 2. 문제를 실행하다\n",
        "\n",
        "그래프 기반 입력 방법을 사용하는 경우 문제 유형을 지정합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "16c66d64",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Solve the problem\n",
        "maxcut_job = solver.run(\n",
        "    problem=problem_as_str,\n",
        "    problem_type=\"maxcut\",\n",
        "    backend_name=backend_name,  # E.g. \"ibm_fez\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "48069093",
      "metadata": {},
      "source": [
        "다음과 같이 Qiskit 함수 워크로드의 [상태를](/docs/guides/functions-get-started#check-job-status) 확인하거나 [결과를](/docs/guides/functions-get-started#retrieve-results) 반환하세요:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "856fe992",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "34b53970-d95a-4e24-8763-fc6f3d112843\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "QUEUED\n"
          ]
        }
      ],
      "source": [
        "# Print the ID so you can use it later, if necessary\n",
        "print(maxcut_job.job_id)\n",
        "\n",
        "# Get job status\n",
        "print(maxcut_job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74f48eab",
      "metadata": {},
      "source": [
        "<span id=\"3-retrieve-the-result\" />\n",
        "\n",
        "### 3. 결과를 가져옵니다\n",
        "\n",
        "결과 사전에서 최적의 컷 값을 검색합니다.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  변수들의 비트열 매핑이 변경되었을 수 있습니다. 출력 사전에는 순서를 확인하는 데 도움이 되는 하위 `variables_to_bitstring_index_map` 사전이 포함되어 있습니다.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "6b571411",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Optimal cut value: 210.0\n"
          ]
        }
      ],
      "source": [
        "# Poll for results\n",
        "maxcut_result = maxcut_job.result()\n",
        "\n",
        "# Take the absolute value of the solution since the cost function is minimized\n",
        "qctrl_maxcut = abs(maxcut_result[\"solution_bitstring_cost\"])\n",
        "\n",
        "# Print the optimal cut value found by the Optimization Solver\n",
        "print(f\"Optimal cut value: {qctrl_maxcut}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "508e29b0",
      "metadata": {},
      "source": [
        "다음과 같은 오픈 소스 솔버를 사용하여 고전적으로 문제를 해결하여 결과의 정확성을 확인할 수 있습니다 [PuLP](https://coin-or.github.io/pulp/) 와 같은 오픈 소스 솔버를 사용하여 고전적으로 문제를 풀면 정확성을 확인할 수 있습니다. 고밀도 문제는 솔루션을 검증하기 위해 고급 클래식 솔버가 필요할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2817b13",
      "metadata": {},
      "source": [
        "<span id=\"example-constrained-optimization\" />\n",
        "\n",
        "## 예시: 제약 조건 최적화\n",
        "\n",
        "앞서 살펴본 맥스컷 예제는 일반적인 2차 제약 조건이 없는 이진 최적화 문제입니다. Q-CTRL의 최적화 솔버는 제약 조건을 목적 함수 내의 페널티 항으로 인코딩하는 대신, `constraint` 입력을 통해 솔버에 직접 전달함으로써 제약 조건이 있는 최적화 문제도 해결할 수 있습니다. 현재 솔버는 ‘ Hamming-weight-1 ’ 제약 조건을 지원합니다. 각 제약 조건은 변수 집합을 지정하며, 이 집합 내에서는 정확히 하나의 변수만 1이 되어야 하고 나머지는 모두 0이어야 합니다.\n",
        "\n",
        "다음 예제는 [그래프 분할이라는](https://en.wikipedia.org/wiki/Graph_partition) 제약 최적화 문제에 대해, 그래프의 각 정점을 여러 그룹 중 정확히 하나에 할당하면서, 같은 그룹에 속하는 양 끝점이 있는 가장자리의 총 가중치를 최소화함으로써, 비용 함수와 일련의 강제약 조건을 구성하는 방법을 보여줍니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a9fbc0e9",
      "metadata": {},
      "source": [
        "`qiskit-ibm-catalog` 및 `qiskit` 패키지 외에도 다음 패키지를 사용하여 이 예제를 실행합니다: `numpy`, `networkx`, 및 `sympy`. IPython 커널을 사용하는 노트북에서 이 예제를 실행하는 경우 다음 셀의 주석 처리를 해제하여 이러한 패키지를 설치할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "c6428fd0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install numpy networkx sympy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "98d3bfd3",
      "metadata": {},
      "source": [
        "<span id=\"1-define-the-problem\" />\n",
        "\n",
        "### 1. 문제 정의\n",
        "\n",
        "노드에 무작위 가중치를 부여하여 그래프를 생성함으로써, 무작위 그래프 분할 문제를 정의하라.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "c2ce65e3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/q-ctrl-optimization-solver/extracted-outputs/c2ce65e3-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import networkx as nx\n",
        "from sympy import Symbol, Poly, srepr\n",
        "\n",
        "# To change the weights, change the seed to any integer.\n",
        "rng_seed = 18\n",
        "_rng = np.random.default_rng(rng_seed)\n",
        "node_count = 50\n",
        "edge_probability = 0.08\n",
        "graph = nx.erdos_renyi_graph(\n",
        "    node_count, edge_probability, seed=rng_seed, directed=False\n",
        ")\n",
        "\n",
        "# add node weights\n",
        "min_weight = -1.0\n",
        "max_weight = 1.0\n",
        "for i in graph.nodes:\n",
        "    weight = (max_weight - min_weight) * _rng.random() + min_weight\n",
        "    graph.add_node(i, weight=weight)\n",
        "\n",
        "# Optionally, visualize the graph\n",
        "nx.draw_networkx(graph, nx.kamada_kawai_layout(graph), node_size=200)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89c91472",
      "metadata": {},
      "source": [
        "가중 그래프 분할에 대한 표준 최적화 모델은 다음과 같이 정식화할 수 있다. 그래프의 노드들을 세 그룹 $g \\in \\{0, 1, 2\\}$ 으로 나누고, 노드 $i$ 이 그룹 $g$ 에 할당된 경우 $n_{i,g} = 1$ 로 하고, 그렇지 않은 경우 $n_{i,g} = 0$ 로 한다. 목표는 양 끝점이 동일한 그룹에 할당된 간선의 총 가중치를 최소화하는 것이며, 여기서 간선 $(i,j)$ 의 가중치는 두 끝점 $\\omega_{i,j} = \\omega_i + \\omega_j$ 의 가중치를 합한 값이다:\n",
        "\n",
        "$\\textbf{Minimize}\\qquad y = \\sum_{(i,j)\\in E} \\omega_{i,j} \\sum_{g} n_{i,g}\\, n_{j,g}$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "91586ee9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Construct the cost function.\n",
        "group_count = 3\n",
        "variables = [\n",
        "    Symbol(f\"n[{i},{g}]\")\n",
        "    for i in range(node_count)\n",
        "    for g in range(group_count)\n",
        "]\n",
        "node_group_var = {\n",
        "    (i, g): variables[i * group_count + g]\n",
        "    for i in range(node_count)\n",
        "    for g in range(group_count)\n",
        "}\n",
        "cost_function = Poly(0, *variables)\n",
        "\n",
        "for i, j in graph.edges():\n",
        "    edge_weight = graph.nodes[i][\"weight\"] + graph.nodes[j][\"weight\"]\n",
        "    for g in range(group_count):\n",
        "        cost_function += (\n",
        "            edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]\n",
        "        )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9d01684b",
      "metadata": {},
      "source": [
        "모든 노드는 세 그룹 중 정확히 하나에 속해야 합니다. 이는 ‘ Hamming-weight-1 ’ 제약 조건입니다. 모든 노드 $i$ 에 대해, $n_{i,0}, n_{i,1}, n_{i,2}$ 중 정확히 하나만 1이어야 하며, 나머지는 모두 0이어야 합니다:\n",
        "\n",
        "$n_{i,0} + n_{i,1} + n_{i,2} = 1 \\texttt{ for all } i \\in V$\n",
        "\n",
        "이 요구 사항을 비용 함수에 벌칙 항으로 반영하기보다는, 입력을 `constraint` 사용하여 이를 고정 제약 조건으로 Solver에 직접 전달하십시오.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "1ff1ffcc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Problem constraints: {\"('n[0,0]', 'n[0,1]', 'n[0,2]')\": 1, \"('n[1,0]', 'n[1,1]', 'n[1,2]')\": 1, \"('n[2,0]', 'n[2,1]', 'n[2,2]')\": 1, \"('n[3,0]', 'n[3,1]', 'n[3,2]')\": 1, \"('n[4,0]', 'n[4,1]', 'n[4,2]')\": 1, \"('n[5,0]', 'n[5,1]', 'n[5,2]')\": 1, \"('n[6,0]', 'n[6,1]', 'n[6,2]')\": 1, \"('n[7,0]', 'n[7,1]', 'n[7,2]')\": 1, \"('n[8,0]', 'n[8,1]', 'n[8,2]')\": 1, \"('n[9,0]', 'n[9,1]', 'n[9,2]')\": 1, \"('n[10,0]', 'n[10,1]', 'n[10,2]')\": 1, \"('n[11,0]', 'n[11,1]', 'n[11,2]')\": 1, \"('n[12,0]', 'n[12,1]', 'n[12,2]')\": 1, \"('n[13,0]', 'n[13,1]', 'n[13,2]')\": 1, \"('n[14,0]', 'n[14,1]', 'n[14,2]')\": 1, \"('n[15,0]', 'n[15,1]', 'n[15,2]')\": 1, \"('n[16,0]', 'n[16,1]', 'n[16,2]')\": 1, \"('n[17,0]', 'n[17,1]', 'n[17,2]')\": 1, \"('n[18,0]', 'n[18,1]', 'n[18,2]')\": 1, \"('n[19,0]', 'n[19,1]', 'n[19,2]')\": 1, \"('n[20,0]', 'n[20,1]', 'n[20,2]')\": 1, \"('n[21,0]', 'n[21,1]', 'n[21,2]')\": 1, \"('n[22,0]', 'n[22,1]', 'n[22,2]')\": 1, \"('n[23,0]', 'n[23,1]', 'n[23,2]')\": 1, \"('n[24,0]', 'n[24,1]', 'n[24,2]')\": 1, \"('n[25,0]', 'n[25,1]', 'n[25,2]')\": 1, \"('n[26,0]', 'n[26,1]', 'n[26,2]')\": 1, \"('n[27,0]', 'n[27,1]', 'n[27,2]')\": 1, \"('n[28,0]', 'n[28,1]', 'n[28,2]')\": 1, \"('n[29,0]', 'n[29,1]', 'n[29,2]')\": 1, \"('n[30,0]', 'n[30,1]', 'n[30,2]')\": 1, \"('n[31,0]', 'n[31,1]', 'n[31,2]')\": 1, \"('n[32,0]', 'n[32,1]', 'n[32,2]')\": 1, \"('n[33,0]', 'n[33,1]', 'n[33,2]')\": 1, \"('n[34,0]', 'n[34,1]', 'n[34,2]')\": 1, \"('n[35,0]', 'n[35,1]', 'n[35,2]')\": 1, \"('n[36,0]', 'n[36,1]', 'n[36,2]')\": 1, \"('n[37,0]', 'n[37,1]', 'n[37,2]')\": 1, \"('n[38,0]', 'n[38,1]', 'n[38,2]')\": 1, \"('n[39,0]', 'n[39,1]', 'n[39,2]')\": 1, \"('n[40,0]', 'n[40,1]', 'n[40,2]')\": 1, \"('n[41,0]', 'n[41,1]', 'n[41,2]')\": 1, \"('n[42,0]', 'n[42,1]', 'n[42,2]')\": 1, \"('n[43,0]', 'n[43,1]', 'n[43,2]')\": 1, \"('n[44,0]', 'n[44,1]', 'n[44,2]')\": 1, \"('n[45,0]', 'n[45,1]', 'n[45,2]')\": 1, \"('n[46,0]', 'n[46,1]', 'n[46,2]')\": 1, \"('n[47,0]', 'n[47,1]', 'n[47,2]')\": 1, \"('n[48,0]', 'n[48,1]', 'n[48,2]')\": 1, \"('n[49,0]', 'n[49,1]', 'n[49,2]')\": 1}\n"
          ]
        }
      ],
      "source": [
        "# Build the hard constraint: exactly one group per node.\n",
        "constraint_dict = {\n",
        "    str(tuple(f\"n[{i},{g}]\" for g in range(group_count))): 1\n",
        "    for i in range(node_count)\n",
        "}\n",
        "print(f\"Problem constraints: {constraint_dict}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "753e5514",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"부분적으로 제약 조건이 주어진 문제\">\n",
        "  모든 변수를 `constraint`.에 추가할 필요는 없습니다. 사전에 포함되지 않은 변수는 제약 조건이 적용되지 않으므로, 동일한 문제 내에서 엄격한 제약 조건이 적용된 변수 그룹과 제약 조건이 없는 변수를 함께 사용할 수 있습니다.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9fd0ba9e",
      "metadata": {},
      "source": [
        "<span id=\"2-run-the-problem\" />\n",
        "\n",
        "### 2. 문제를 실행하다\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "f380b56d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Solve the problem\n",
        "partition_job = solver.run(\n",
        "    problem=srepr(cost_function),\n",
        "    constraint=constraint_dict,\n",
        "    backend_name=\"ibm_marrakesh\",  # E.g. \"ibm_marrakesh\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d154c226",
      "metadata": {},
      "source": [
        "다음과 같이 Qiskit 함수 워크로드의 [상태를](/docs/guides/functions-get-started#check-job-status) 확인하거나 [결과를](/docs/guides/functions-get-started#retrieve-results) 반환하세요:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "6de77b14",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "b8085944-f313-444e-be39-ea61b1b47ebd\n",
            "QUEUED\n"
          ]
        }
      ],
      "source": [
        "# Print the ID so you can use it later, if necessary\n",
        "print(partition_job.job_id)\n",
        "\n",
        "# Get job status\n",
        "print(partition_job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8a95618e",
      "metadata": {},
      "source": [
        "<span id=\"3-get-the-result\" />\n",
        "\n",
        "### 3. 결과를 얻다\n",
        "\n",
        "해법을 확인하고 결과를 분석하십시오. 해의 비용은 양 끝점이 동일한 그룹에 속하게 된 변들의 총 무게를 나타내므로, 비용이 낮을수록 그래프의 분할이 더 우수함을 의미합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a924bf93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total weight of same-group edges: -36.5539\n",
            "Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100\n"
          ]
        }
      ],
      "source": [
        "partition_result = partition_job.result()\n",
        "qctrl_cost = partition_result[\"solution_bitstring_cost\"]\n",
        "solution_bitstring = partition_result[\"solution_bitstring\"]\n",
        "\n",
        "# Print results\n",
        "print(f\"Total weight of same-group edges: {qctrl_cost}\")\n",
        "print(f\"Solution bitstring: {solution_bitstring}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9ec2e67",
      "metadata": {},
      "source": [
        "<span id=\"get-support\" />\n",
        "\n",
        "## 지원 받기\n",
        "\n",
        "질문이나 문제가 있는 경우 [Q-CTRL로](https://form.typeform.com/to/iuujEAEI?typeform-source=q-ctrl.com) 문의하세요.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "993aa226c6ec9f5e",
      "metadata": {},
      "source": [
        "<span id=\"changelog\" />\n",
        "\n",
        "## 변경 로그\n",
        "\n",
        "* 2026-08-10: `input `constraint` `을 통해 하드 제약 조건(해밍 가중치 1)에 대한 지원을 추가했으며, 이를 사용하도록 제약 최적화 예제를 업데이트했습니다.\n",
        "* 2026-02-11: 이제 다음을 지원합니다. `ibm_miami`\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a6a25c8",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 다음 단계\n",
        "\n",
        "<Admonition type=\"tip\" title=\"권장사항\">\n",
        "  * [Q-CTRL 최적화 솔버](/functions?id=q-ctrl-optimization-solver) 에 대한 접근 권한을 요청합니다.\n",
        "  * 이 Qiskit 함수에 대한 [API 참조](/docs/api/functions/q-ctrl-optimization-solver) 를 확인해 보세요.\n",
        "  * [Q-CTRL의 최적화 솔버 튜토리얼로 고차 이진 최적화 문제 풀기를](/docs/tutorials/solve-higher-order-binary-optimization-problems-with-q-ctrls-optimization-solver) 사용해 보세요.\n",
        "  * 서평 [사흐데바, N., 등 (2024).  127큐비트 게이트 모델 IBM 양자 컴퓨터를 이용한 양자 최적화는 비사소한 이진 최적화 문제에서 양자 어닐러보다 우수한 성능을 발휘할 수 있다. arXiv 사전 인쇄본 arXiv:2406.01743](https://arxiv.org/abs/2406.01743).\n",
        "  * 리뷰 [로코, D., 등 (2026).  양자 컴퓨터를 활용한 신약 개발을 위한 실용적 단백질 포켓 수화 부위 예측 arXiv 사전 인쇄본 arXiv:2512.08390](https://arxiv.org/abs/2512.08390).\n",
        "  * [마즈다](https://q-ctrl.com/case-study/tackling-a-costly-bottleneck-in-automotive-design) 사례 연구를 검토하십시오.\n",
        "  * [네트워크 레일](https://q-ctrl.com/case-study/accelerating-the-schedule-for-quantum-enhanced-rail) 사례 연구를 검토하십시오.\n",
        "  * [호주 육군](https://q-ctrl.com/case-study/improving-army-logistics-with-quantum-computing) 사례 연구를 검토하십시오.\n",
        "  * [뉴사우스웨일스 교통청](https://q-ctrl.com/case-study/delivering-quantum-computing-for-faster-commuting) 사례 연구를 검토하십시오.\n",
        "</Admonition>\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}