Skip to content

Follow a QAOA run from objective to samples

Run one-layer QAOA on the three-node path from graph problems. Read QUBO projection first, then use the installed environment. The small evaluation budget exposes the workflow; it is not a convergence demonstration.

For bits in (a,b,c) order, the minimized cost is f(x) = −(xa + xb + xc) + 2(xa xb + xb xc). The independent set 101 has value −2. Selecting all three nodes gives −3 + 2×2 = 1 and is infeasible. A low penalty could favor an invalid set, so the graph interface requires an appropriate penalty.

Distinguish layers, evaluations and shots

QAOA starts from equal superposition and alternates a cost evolution controlled by γ and an X mixer controlled by β. One layer has two parameters; initial_parameters=(0.2, -0.3) supplies γ then β. One layer is not one objective evaluation: the optimizer evaluates several parameter choices.

"""Run a complete local QAOA workflow for a small MIS problem.

One workflow covers problem projection, ansatz construction, real Backend
objective evaluations, SciPy optimization, final sampling, candidate decoding,
and a bounded classical baseline. No global-optimality claim is inferred.
"""

from __future__ import annotations

import json

from cascaqit import QAOA, GraphProblemIR, LocalBackend, OptimizerConfig


def main() -> None:
    """Optimize and sample a three-node path graph with fixed inputs."""
    graph = GraphProblemIR.from_edges(
        problem_id="lesson.optimization.qaoa",
        positions={"a": (0.0, 0.0), "b": (5.0, 0.0), "c": (10.0, 0.0)},
        edges=(("a", "b"), ("b", "c")),
    )
    result = QAOA(graph, layers=1, mis_penalty=2.0).run(
        backend=LocalBackend(seed=501),
        optimizer=OptimizerConfig(
            method="COBYLA", max_iterations=8, max_evaluations=5, seed=501
        ),
        initial_parameters=(0.2, -0.3),
        final_shots=64,
    )
    candidate = result.best_observed_candidate
    baseline = result.baseline
    assert candidate is not None and baseline is not None
    assert result.final_result is not None

    payload = {
        "track": "optimization_researcher",
        "level": "applied",
        "lesson": "qaoa_workflow",
        "facts": {
            "objective_estimator": result.metadata["objective_estimator"],
            "objective_total_shots": result.metadata["objective_total_shots"],
            "workflow_total_shots": result.metadata["workflow_total_shots"],
            "backend_executions": result.metadata["workflow_backend_execution_count"],
            "final_counts": result.final_result.counts,
            "final_probabilities": result.final_result.probabilities,
            "candidate_count": candidate.count,
            "best_energy": round(result.best_evaluation.energy, 10),
            "candidate_value": candidate.objective_value,
            "termination_reason": result.termination.reason,
            "evaluations": len(result.evaluations),
            "counts_total": sum(result.final_result.counts.values()),
            "candidate_bitstring": candidate.bitstring,
            "candidate_feasible": candidate.feasible,
            "baseline_value": baseline.objective_value,
            "optimality_claim": candidate.optimality_claim,
        },
        "boundaries": {
            "hardware_execution": False,
            "cloud_execution": False,
            "network_accessed": False,
            "credentials_loaded": False,
        },
    }
    print(json.dumps(payload, sort_keys=True))


if __name__ == "__main__":
    main()

Download the full script

python3 examples/user/tracks/optimization_researcher/03_applied_qaoa_workflow_en.py

The configuration permits at most five objective evaluations, then performs a separate 64-shot sampling run at the selected parameters. max_iterations=8 does not override max_evaluations=5. Read the termination reason to see which condition stopped optimization.

{
  "boundaries": {
    "cloud_execution": false,
    "credentials_loaded": false,
    "hardware_execution": false,
    "network_accessed": false
  },
  "facts": {
    "backend_executions": 6,
    "baseline_value": -2.0,
    "best_energy": -1.0259301196,
    "candidate_bitstring": "101",
    "candidate_count": 14,
    "candidate_feasible": true,
    "candidate_value": -2.0,
    "counts_total": 64,
    "evaluations": 5,
    "final_counts": {
      "001": 9,
      "010": 21,
      "011": 4,
      "100": 3,
      "101": 14,
      "110": 2,
      "111": 11
    },
    "final_probabilities": {
      "000": 0.006485865446572203,
      "001": 0.13794438752156066,
      "010": 0.31275386143691747,
      "011": 0.04281588559494963,
      "100": 0.13794438752156063,
      "101": 0.2521757366567452,
      "110": 0.042815885594949664,
      "111": 0.06706399022674457
    },
    "objective_estimator": "exact_state",
    "objective_total_shots": 0,
    "optimality_claim": "not_claimed",
    "termination_reason": "max_evaluations",
    "workflow_total_shots": 64
  },
  "lesson": "qaoa_workflow",
  "level": "applied",
  "track": "optimization_researcher"
}

The exact_state estimator and zero objective_total_shots mean the optimizer reads expectations from the simulated state. final_shots=64 affects the separate sample, not the accuracy of those expectations. Each evolution still consumes CPU.

best_energy is an expectation across all possible bitstrings. candidate_bitstring is the lowest-cost bitstring actually observed in the final counts, not necessarily the most frequent one. candidate_feasible checks the graph constraint. The classical baseline enumerates this small problem and finds 101 with value −2.

You can observe that optimal candidate while the best expectation remains above −2 and termination reports max_evaluations. One good sample does not show that the distribution concentrates on the optimum or that the optimizer converged. The SDK retains not_claimed on the candidate's optimality field.

Test a budget change

  1. Increase final_shots to 1024 with all optimizer settings fixed. Which results can change?
  2. Increase layers to two but retain the two-element initial vector. Explain the input error, then try four initial parameters.
  3. Compute the frequency of 101 and the total frequency of feasible sets from the recorded counts. How do these differ from expectation energy?

More final shots change sampling cost and counts; this exact optimization follows the same objective calculation. Two layers need four parameters. Also revisit COBYLA's minimum evaluation budget of parameter_count + 2: five is insufficient for four parameters. The feasible strings are 000, 001, 010, 100, 101; only 101 is optimal. Feasible frequency, optimal frequency and mean cost answer different questions.

See variational algorithms for interface details. Continue with VQE to change how the circuit is constructed.

中文版

SDK 1.0.8a · `8b227bff`