Skip to content

quast_decisiontree.nodes.qrisp_vqe_setup

quast_decisiontree.nodes.qrisp_vqe_setup

Setup node for QrispVQE — assembles HybridAlgorithm + inputs for execution.

logger module-attribute

logger = logging.getLogger('dt_logger')

QrispVQESetupNode

Bases: Node

Sets up a QrispVQE instance and prepares inputs for HybridAlgorithmExecuteNode.

Assembles the hybrid algorithm with its hyperparameters and gathers the required inputs (hamiltonian, ansatz_function, num_params, num_qubits) from problem_data.

If hamiltonian is not already in problem_data, it is derived from qubo_matrix.

Modifications at runtime: None (all parameters come from prior nodes).

Source code in src/quast_decisiontree/nodes/qrisp_vqe_setup.py
 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
class QrispVQESetupNode(Node):
    """Sets up a QrispVQE instance and prepares inputs for HybridAlgorithmExecuteNode.

    Assembles the hybrid algorithm with its hyperparameters and gathers the
    required inputs (hamiltonian, ansatz_function, num_params, num_qubits) from
    problem_data.

    If hamiltonian is not already in problem_data, it is derived from qubo_matrix.

    Modifications at runtime: None (all parameters come from prior nodes).
    """

    _known_children = ["HybridAlgorithmExecuteNode"]

    def __init__(self, children: list) -> None:
        super().__init__(
            requires=[
                "optimizer",
                "ansatz_function",
                "num_params",
                "backend",
                ["hamiltonian", "qubo_matrix"],
                ["optional:", "num_qubits", "depth", "max_iter", "shots", "init_params"],
            ],
            creates=["hybrid_algorithm", "hybrid_inputs", "ising_offset"],
            children=children,
        )

    def execute(self, problem_data: dict, path_info: dict) -> dict:
        backend = problem_data["backend"]
        if isinstance(backend, str):
            backends = self.request_info("backends")
            problem_data["backend"] = backends[backend]

        if "num_qubits" not in problem_data:
            if "qubo_matrix" in problem_data:
                problem_data["num_qubits"] = len(problem_data["qubo_matrix"])
            else:
                raise ValueError(
                    "Cannot determine num_qubits: neither num_qubits nor qubo_matrix "
                    "found in problem_data."
                )

        if "hamiltonian" not in problem_data:
            if "qubo_matrix" not in problem_data:
                raise ValueError("Either hamiltonian or qubo_matrix must be in problem_data.")
            qubo_matrix = np.asarray(problem_data["qubo_matrix"], dtype=float)
            hamiltonian, ising_offset = qubo_to_qrisp_hamiltonian(qubo_matrix)
            problem_data["hamiltonian"] = hamiltonian
            problem_data["ising_offset"] = ising_offset
        else:
            hamiltonian = problem_data["hamiltonian"]
            if "ising_offset" not in problem_data:
                problem_data["ising_offset"] = 0.0

        hyperparams = {
            "depth": problem_data.get("depth", 1),
            "optimizer": problem_data["optimizer"],
            "max_iter": problem_data.get("max_iter", 50),
            "shots": problem_data.get("shots", 1024),
            "init_params": problem_data.get("init_params"),
        }

        problem_data["hybrid_algorithm"] = QrispVQE(**hyperparams)

        problem_data["hybrid_inputs"] = {
            "hamiltonian": problem_data["hamiltonian"],
            "ansatz_function": problem_data["ansatz_function"],
            "num_params": problem_data["num_params"],
            "num_qubits": problem_data["num_qubits"],
        }

        return dict(
            num_qubits=problem_data["num_qubits"],
            ising_offset=problem_data["ising_offset"],
        )

    def interpret_result(
        self,
        result: dict,
        problem_data: dict,
        next_node_info: dict | None = None,
        config: dict | None = None,
    ) -> dict:
        """Interpret VQEResult into standard result dict."""
        vqe_result: VQEResult = result["raw"]

        if vqe_result is None:
            return result

        result["eigenstate"] = vqe_result.counts
        result["eigenvalue"] = vqe_result.energy
        result["best_bitstring"] = vqe_result.best_bitstring
        result["best_energy"] = vqe_result.energy

        ising_offset = (next_node_info or {}).get("ising_offset", 0.0)
        result["qubo_offset"] = ising_offset
        result["qubo_eigenvalue"] = vqe_result.energy + ising_offset
        result["best_qubo_value"] = vqe_result.energy + ising_offset
        result["solution_bitstring"] = vqe_result.best_bitstring
        result["solution_qubo_value"] = vqe_result.energy + ising_offset

        if vqe_result.optimal_params is not None:
            result["optimal_params"] = vqe_result.optimal_params

        if vqe_result.cost_history:
            result["cost_history"] = vqe_result.cost_history
            result["num_evals"] = vqe_result.num_evals

        if config and config.get("saving_policy") == "discard_if_processed":
            result.setdefault("_no_save", set()).add("raw")

        return result

__init__

__init__(children)
Source code in src/quast_decisiontree/nodes/qrisp_vqe_setup.py
71
72
73
74
75
76
77
78
79
80
81
82
83
def __init__(self, children: list) -> None:
    super().__init__(
        requires=[
            "optimizer",
            "ansatz_function",
            "num_params",
            "backend",
            ["hamiltonian", "qubo_matrix"],
            ["optional:", "num_qubits", "depth", "max_iter", "shots", "init_params"],
        ],
        creates=["hybrid_algorithm", "hybrid_inputs", "ising_offset"],
        children=children,
    )

execute

execute(problem_data, path_info)
Source code in src/quast_decisiontree/nodes/qrisp_vqe_setup.py
 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
def execute(self, problem_data: dict, path_info: dict) -> dict:
    backend = problem_data["backend"]
    if isinstance(backend, str):
        backends = self.request_info("backends")
        problem_data["backend"] = backends[backend]

    if "num_qubits" not in problem_data:
        if "qubo_matrix" in problem_data:
            problem_data["num_qubits"] = len(problem_data["qubo_matrix"])
        else:
            raise ValueError(
                "Cannot determine num_qubits: neither num_qubits nor qubo_matrix "
                "found in problem_data."
            )

    if "hamiltonian" not in problem_data:
        if "qubo_matrix" not in problem_data:
            raise ValueError("Either hamiltonian or qubo_matrix must be in problem_data.")
        qubo_matrix = np.asarray(problem_data["qubo_matrix"], dtype=float)
        hamiltonian, ising_offset = qubo_to_qrisp_hamiltonian(qubo_matrix)
        problem_data["hamiltonian"] = hamiltonian
        problem_data["ising_offset"] = ising_offset
    else:
        hamiltonian = problem_data["hamiltonian"]
        if "ising_offset" not in problem_data:
            problem_data["ising_offset"] = 0.0

    hyperparams = {
        "depth": problem_data.get("depth", 1),
        "optimizer": problem_data["optimizer"],
        "max_iter": problem_data.get("max_iter", 50),
        "shots": problem_data.get("shots", 1024),
        "init_params": problem_data.get("init_params"),
    }

    problem_data["hybrid_algorithm"] = QrispVQE(**hyperparams)

    problem_data["hybrid_inputs"] = {
        "hamiltonian": problem_data["hamiltonian"],
        "ansatz_function": problem_data["ansatz_function"],
        "num_params": problem_data["num_params"],
        "num_qubits": problem_data["num_qubits"],
    }

    return dict(
        num_qubits=problem_data["num_qubits"],
        ising_offset=problem_data["ising_offset"],
    )

interpret_result

interpret_result(
    result, problem_data, next_node_info=None, config=None
)

Interpret VQEResult into standard result dict.

Source code in src/quast_decisiontree/nodes/qrisp_vqe_setup.py
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
def interpret_result(
    self,
    result: dict,
    problem_data: dict,
    next_node_info: dict | None = None,
    config: dict | None = None,
) -> dict:
    """Interpret VQEResult into standard result dict."""
    vqe_result: VQEResult = result["raw"]

    if vqe_result is None:
        return result

    result["eigenstate"] = vqe_result.counts
    result["eigenvalue"] = vqe_result.energy
    result["best_bitstring"] = vqe_result.best_bitstring
    result["best_energy"] = vqe_result.energy

    ising_offset = (next_node_info or {}).get("ising_offset", 0.0)
    result["qubo_offset"] = ising_offset
    result["qubo_eigenvalue"] = vqe_result.energy + ising_offset
    result["best_qubo_value"] = vqe_result.energy + ising_offset
    result["solution_bitstring"] = vqe_result.best_bitstring
    result["solution_qubo_value"] = vqe_result.energy + ising_offset

    if vqe_result.optimal_params is not None:
        result["optimal_params"] = vqe_result.optimal_params

    if vqe_result.cost_history:
        result["cost_history"] = vqe_result.cost_history
        result["num_evals"] = vqe_result.num_evals

    if config and config.get("saving_policy") == "discard_if_processed":
        result.setdefault("_no_save", set()).add("raw")

    return result

qubo_to_qrisp_hamiltonian

qubo_to_qrisp_hamiltonian(qubo_matrix)

Create a Qrisp Hamiltonian from a QUBO matrix.

Converts QUBO to Ising form and returns a Qrisp QubitOperator (Hamiltonian).

The QUBO-to-Ising conversion follows

x_i = (1 - Z_i) / 2

Parameters:

Name Type Description Default
qubo_matrix ndarray

Square numpy array representing the QUBO cost matrix Q.

required

Returns:

Type Description

Tuple of (hamiltonian, offset) where hamiltonian is a Qrisp-compatible

QubitOperator and offset is the constant energy offset.

Source code in src/quast_decisiontree/nodes/qrisp_vqe_setup.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def qubo_to_qrisp_hamiltonian(qubo_matrix: np.ndarray):
    """Create a Qrisp Hamiltonian from a QUBO matrix.

    Converts QUBO to Ising form and returns a Qrisp QubitOperator (Hamiltonian).

    The QUBO-to-Ising conversion follows:
        x_i = (1 - Z_i) / 2

    Args:
        qubo_matrix: Square numpy array representing the QUBO cost matrix Q.

    Returns:
        Tuple of (hamiltonian, offset) where hamiltonian is a Qrisp-compatible
        QubitOperator and offset is the constant energy offset.
    """

    n = len(qubo_matrix)
    hamiltonian = 0
    offset = 0.0

    for i in range(n):
        for j in range(n):
            if i == j:
                offset += qubo_matrix[i, i] / 2.0
                hamiltonian -= (qubo_matrix[i, i] / 2.0) * Z(i)
            elif i < j:
                coupling = (qubo_matrix[i, j] + qubo_matrix[j, i]) / 4.0
                offset += coupling
                hamiltonian -= coupling * Z(i)
                hamiltonian -= coupling * Z(j)
                hamiltonian += coupling * Z(i) * Z(j)

    return hamiltonian, offset