Skip to content

quast_decisiontree.algorithms.hybrid.qrisp_qaoa

quast_decisiontree.algorithms.hybrid.qrisp_qaoa

QrispQAOA — QAOA implementation delegating to Qrisp's QAOAProblem.

QAOAResult dataclass

Bases: VariationalResult

Result container for QrispQAOA execution.

Inherits from VariationalResult

counts, optimal_params, cost_history, num_evals

Additional attributes

best_bitstring: The bitstring with the lowest cost. best_cost: The cost value of the best bitstring.

Source code in src/quast_decisiontree/algorithms/hybrid/qrisp_qaoa.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@dataclass
class QAOAResult(VariationalResult):
    """Result container for QrispQAOA execution.

    Inherits from VariationalResult:
        counts, optimal_params, cost_history, num_evals

    Additional attributes:
        best_bitstring: The bitstring with the lowest cost.
        best_cost: The cost value of the best bitstring.
    """

    best_bitstring: str = ""
    best_cost: float = float("inf")

best_bitstring class-attribute instance-attribute

best_bitstring = ''

best_cost class-attribute instance-attribute

best_cost = float('inf')

__init__

__init__(
    counts,
    optimal_params=None,
    cost_history=list(),
    num_evals=0,
    best_bitstring="",
    best_cost=float("inf"),
)

QrispQAOA

Bases: HybridAlgorithm

QAOA implementation delegating to Qrisp's QAOAProblem.

Keeps all modifiable parameters (reps, optimizer, max_iter, etc.) as hyperparameters configurable via the builder system. Problem-specific inputs (cost_operator, mixer, cl_cost_function, num_qubits) are provided at execution time.

Compatible with the decision tree node flow

SelectLayersNode → sets reps QrispMixerNode → sets mixer (Qrisp-compatible callable) SelectOptimizerNode → sets optimizer QrispQAOASetupNode → instantiates this class HybridAlgorithmExecuteNode → calls execute()

Example usage (standalone)::

qaoa = QrispQAOA(reps=3, max_iter=100, optimizer="COBYLA")
result = qaoa.execute(
    backend=my_backend,
    cost_operator=my_cost_op,
    mixer=my_mixer,
    cl_cost_function=my_cl_cost,
    num_qubits=5,
)
print(result.best_bitstring, result.best_cost)
Source code in src/quast_decisiontree/algorithms/hybrid/qrisp_qaoa.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
class QrispQAOA(HybridAlgorithm):
    """QAOA implementation delegating to Qrisp's QAOAProblem.

    Keeps all modifiable parameters (reps, optimizer, max_iter, etc.) as
    hyperparameters configurable via the builder system. Problem-specific
    inputs (cost_operator, mixer, cl_cost_function, num_qubits) are provided
    at execution time.

    Compatible with the decision tree node flow:
        SelectLayersNode → sets reps
        QrispMixerNode → sets mixer (Qrisp-compatible callable)
        SelectOptimizerNode → sets optimizer
        QrispQAOASetupNode → instantiates this class
        HybridAlgorithmExecuteNode → calls execute()

    Example usage (standalone)::

        qaoa = QrispQAOA(reps=3, max_iter=100, optimizer="COBYLA")
        result = qaoa.execute(
            backend=my_backend,
            cost_operator=my_cost_op,
            mixer=my_mixer,
            cl_cost_function=my_cl_cost,
            num_qubits=5,
        )
        print(result.best_bitstring, result.best_cost)
    """

    HYPERPARAMS: ClassVar[list] = [
        HyperParam(
            name="reps",
            hparam_type=int,
            description="Number of QAOA layers (depth parameter p)",
            default=1,
            test=lambda x: x > 0,
        ),
        HyperParam(
            name="max_iter",
            hparam_type=int,
            description="Maximum number of classical optimizer iterations",
            default=50,
            test=lambda x: x > 0,
        ),
        HyperParam(
            name="optimizer",
            hparam_type=None,
            description=(
                "Classical optimizer: a string name for scipy.optimize.minimize "
                "(e.g. 'COBYLA', 'Nelder-Mead') or an object with a "
                ".minimize(fun, x0) method (e.g. from OptimizerBuilder)"
            ),
            default="COBYLA",
        ),
        HyperParam(
            name="init_type",
            hparam_type=str,
            description="Parameter initialization strategy ('random' or 'tqa')",
            default="random",
        ),
        HyperParam(
            name="init_params",
            hparam_type=None,
            description="Initial variational parameters (array-like, optional)",
            default=None,
        ),
    ]

    INPUT_KEYS: ClassVar[Sequence[str]] = (
        "cost_operator",
        "mixer",
        "cl_cost_function",
        "num_qubits",
    )

    def run_algorithm(self, backend: Backend, input: dict[str, Any]) -> QAOAResult:
        """Execute QAOA via Qrisp's QAOAProblem.

        Args:
            backend: The backend to run quantum circuits on.
            input: Dict with keys from INPUT_KEYS.

        Returns:
            QAOAResult with counts, best solution, and metadata.
        """
        problem = QAOAProblem(
            cost_operator=input["cost_operator"],
            mixer=input["mixer"],
            cl_cost_function=input["cl_cost_function"],
        )

        qarg = QuantumVariable(input["num_qubits"])

        run_kwargs = self._build_run_kwargs(backend)

        result_counts = problem.run(qarg, **run_kwargs)

        best_bitstring, best_cost = self._evaluate_best(result_counts, input["cl_cost_function"])

        return QAOAResult(
            counts=result_counts,
            best_bitstring=best_bitstring,
            best_cost=best_cost,
        )

    def _build_run_kwargs(self, backend: Backend) -> dict[str, Any]:
        """Assemble keyword arguments for QAOAProblem.run()."""
        run_kwargs: dict[str, Any] = {
            "depth": self.reps,
            "max_iter": self.max_iter,
            "init_type": self.init_type,
        }

        if self.init_params is not None:
            run_kwargs["init_point"] = np.asarray(self.init_params, dtype=float)

        adapted_optimizer = adapt_optimizer(self.optimizer)
        if adapted_optimizer is not None:
            run_kwargs["optimizer"] = adapted_optimizer

        # Only pass backend if it has a working submit function
        # Otherwise let Qrisp use its built-in simulator
        if backend is not None and backend.has_submit:
            run_kwargs["mes_kwargs"] = {"backend": backend}
        else:
            logger = logging.getLogger("dt_logger")
            logger.warning(
                "Backend '%s' has no submit function and cannot be used by Qrisp. "
                "Falling back to Qrisp's built-in simulator.",
                backend.get("name", repr(backend)),
            )
        return run_kwargs

    @staticmethod
    def _evaluate_best(counts: dict[str, float], cl_cost_function: Any) -> tuple:
        """Find the bitstring with the lowest cost from the result distribution.

        Args:
            counts: {bitstring: probability} mapping from Qrisp.
            cl_cost_function: Classical cost function mapping counts to cost.

        Returns:
            Tuple of (best_bitstring, best_cost).
        """
        best_bitstring = ""
        best_cost = float("inf")

        for bitstring in counts:
            cost = cl_cost_function({bitstring: 1})
            if cost < best_cost:
                best_cost = cost
                best_bitstring = bitstring

        return best_bitstring, best_cost

HYPERPARAMS class-attribute

HYPERPARAMS = [
    HyperParam(
        name="reps",
        hparam_type=int,
        description="Number of QAOA layers (depth parameter p)",
        default=1,
        test=lambda x: x > 0,
    ),
    HyperParam(
        name="max_iter",
        hparam_type=int,
        description="Maximum number of classical optimizer iterations",
        default=50,
        test=lambda x: x > 0,
    ),
    HyperParam(
        name="optimizer",
        hparam_type=None,
        description="Classical optimizer: a string name for scipy.optimize.minimize (e.g. 'COBYLA', 'Nelder-Mead') or an object with a .minimize(fun, x0) method (e.g. from OptimizerBuilder)",
        default="COBYLA",
    ),
    HyperParam(
        name="init_type",
        hparam_type=str,
        description="Parameter initialization strategy ('random' or 'tqa')",
        default="random",
    ),
    HyperParam(
        name="init_params",
        hparam_type=None,
        description="Initial variational parameters (array-like, optional)",
        default=None,
    ),
]

INPUT_KEYS class-attribute

INPUT_KEYS = (
    "cost_operator",
    "mixer",
    "cl_cost_function",
    "num_qubits",
)

run_algorithm

run_algorithm(backend, input)

Execute QAOA via Qrisp's QAOAProblem.

Parameters:

Name Type Description Default
backend Backend

The backend to run quantum circuits on.

required
input dict[str, Any]

Dict with keys from INPUT_KEYS.

required

Returns:

Type Description
QAOAResult

QAOAResult with counts, best solution, and metadata.

Source code in src/quast_decisiontree/algorithms/hybrid/qrisp_qaoa.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def run_algorithm(self, backend: Backend, input: dict[str, Any]) -> QAOAResult:
    """Execute QAOA via Qrisp's QAOAProblem.

    Args:
        backend: The backend to run quantum circuits on.
        input: Dict with keys from INPUT_KEYS.

    Returns:
        QAOAResult with counts, best solution, and metadata.
    """
    problem = QAOAProblem(
        cost_operator=input["cost_operator"],
        mixer=input["mixer"],
        cl_cost_function=input["cl_cost_function"],
    )

    qarg = QuantumVariable(input["num_qubits"])

    run_kwargs = self._build_run_kwargs(backend)

    result_counts = problem.run(qarg, **run_kwargs)

    best_bitstring, best_cost = self._evaluate_best(result_counts, input["cl_cost_function"])

    return QAOAResult(
        counts=result_counts,
        best_bitstring=best_bitstring,
        best_cost=best_cost,
    )