Skip to content

quast_decisiontree.nodes.formulation

quast_decisiontree.nodes.formulation

logger module-attribute

logger = logging.getLogger('dt_logger')

FormulationSelectNode

Bases: Node

Selects the direct encoding formulation to use for the problem instance.

Modifications at runtime: - formulation: - {str} : name of the direct encoding mode to use

Source code in src/quast_decisiontree/nodes/formulation.py
20
21
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
55
56
57
58
59
60
61
62
63
64
65
66
67
class FormulationSelectNode(Node):
    """Selects the direct encoding formulation to use for the problem instance.

    Modifications at runtime:
    - formulation:
        - {str} : name of the direct encoding mode to use
    """

    _known_children = ["DirectEncodingNode"]
    _path_keys = dict(formulation=PathKey(str))

    def __init__(self, children: list):
        super().__init__(
            requires="problem_instance",
            creates="formulation",
            children=children,
        )
        self.problem_instance = None

    def _formulation_choices(self) -> dict:
        modes = self.problem_instance.direct_encoding_modes
        if not modes:
            return {"direct": "Perform a direct encoding with the default setting"}
        return {mode: f"Perform a direct encoding with the mode {mode!r}" for mode in modes}

    def _formulation_default(self) -> str:
        modes = self.problem_instance.direct_encoding_modes
        return modes[0] if modes else "direct"

    def _build_query(self) -> MultiChoiceQuery:
        return MultiChoiceQuery(
            question="Which formulation do you want to use?",
            answers=self._formulation_choices(),
            default=self._formulation_default(),
        )

    def execute(self, problem_data: dict, path_info: dict) -> dict:
        self.problem_instance = problem_data["problem_instance"]
        formulation = path_info.get("formulation")
        if formulation is None:
            query = self._build_query()
            if len(query.options) == 1:
                formulation = next(iter(query.options))
            else:
                formulation = query.input()
            path_info["formulation"] = formulation
        problem_data["formulation"] = formulation
        return dict(formulation=formulation)

problem_instance instance-attribute

problem_instance = None

__init__

__init__(children)
Source code in src/quast_decisiontree/nodes/formulation.py
31
32
33
34
35
36
37
def __init__(self, children: list):
    super().__init__(
        requires="problem_instance",
        creates="formulation",
        children=children,
    )
    self.problem_instance = None

execute

execute(problem_data, path_info)
Source code in src/quast_decisiontree/nodes/formulation.py
56
57
58
59
60
61
62
63
64
65
66
67
def execute(self, problem_data: dict, path_info: dict) -> dict:
    self.problem_instance = problem_data["problem_instance"]
    formulation = path_info.get("formulation")
    if formulation is None:
        query = self._build_query()
        if len(query.options) == 1:
            formulation = next(iter(query.options))
        else:
            formulation = query.input()
        path_info["formulation"] = formulation
    problem_data["formulation"] = formulation
    return dict(formulation=formulation)

DirectEncodingNode

Bases: Node

Node that performs the direct encoding of the problem instance into a QUBO.

Modifications at runtime: None

Source code in src/quast_decisiontree/nodes/formulation.py
 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
class DirectEncodingNode(Node):
    """Node that performs the direct encoding of the problem instance into a QUBO.

    Modifications at runtime: None
    """

    _known_children = ["AlgorithmSelectionNode"]
    _path_keys = dict()

    def __init__(self, children: list) -> None:
        super().__init__(
            requires=["formulation", "problem_instance"],
            creates=["qubo_matrix", "qubo_offset", "num_qubits"],
            children=children,
        )

    def execute(self, problem_data: dict, path_info: dict) -> dict:
        problem_instance = problem_data["problem_instance"]
        formulation = problem_data["formulation"]

        if formulation in problem_instance.direct_encoding_modes:
            offset, qubo = problem_instance.formulate_problem(mode=formulation)
        else:
            offset, qubo = problem_instance.formulate_problem()

        qubo = qubo_tensor_to_matrix(qubo)
        problem_data["qubo_matrix"] = qubo
        problem_data["qubo_offset"] = float(offset)
        problem_data["num_qubits"] = len(qubo)
        return dict(encoding=formulation, qubo_offset=problem_data["qubo_offset"])

    def interpret_result(
        self,
        result: dict,
        problem_data: dict,
        next_node_info: dict,
        config: dict | None = None,
    ) -> dict:
        result["qubo_offset"] = next_node_info["qubo_offset"]

        problem_instance = problem_data["problem_instance"]
        formulation = problem_data["formulation"]
        try:
            if formulation in problem_instance.direct_encoding_modes:
                sol_vector, sol_value = problem_instance.decode_result(
                    sol_bitstring=result["solution_bitstring"], mode=formulation
                )
            else:
                sol_vector, sol_value = problem_instance.decode_result(
                    sol_bitstring=result["solution_bitstring"]
                )
        except InvalidBitstring:
            logger.warning("Solution bitstring could not be decoded; storing null solution.")
            result["solution_cost_value"] = None
            result["solution_int_vector"] = None
        except (KeyError, AttributeError):
            logger.warning(
                "Could not decode result via the problem instance; "
                "falling back to raw QUBO values.",
                exc_info=True,
            )
            result["solution_cost_value"] = result.get("solution_qubo_value")
            bitstring = result.get("solution_bitstring")
            result["solution_int_vector"] = (
                [int(x) for x in reversed(bitstring)] if bitstring is not None else None
            )
        else:
            result["solution_cost_value"] = sol_value
            result["solution_int_vector"] = sol_vector

        return result

__init__

__init__(children)
Source code in src/quast_decisiontree/nodes/formulation.py
79
80
81
82
83
84
def __init__(self, children: list) -> None:
    super().__init__(
        requires=["formulation", "problem_instance"],
        creates=["qubo_matrix", "qubo_offset", "num_qubits"],
        children=children,
    )

execute

execute(problem_data, path_info)
Source code in src/quast_decisiontree/nodes/formulation.py
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def execute(self, problem_data: dict, path_info: dict) -> dict:
    problem_instance = problem_data["problem_instance"]
    formulation = problem_data["formulation"]

    if formulation in problem_instance.direct_encoding_modes:
        offset, qubo = problem_instance.formulate_problem(mode=formulation)
    else:
        offset, qubo = problem_instance.formulate_problem()

    qubo = qubo_tensor_to_matrix(qubo)
    problem_data["qubo_matrix"] = qubo
    problem_data["qubo_offset"] = float(offset)
    problem_data["num_qubits"] = len(qubo)
    return dict(encoding=formulation, qubo_offset=problem_data["qubo_offset"])

interpret_result

interpret_result(
    result, problem_data, next_node_info, config=None
)
Source code in src/quast_decisiontree/nodes/formulation.py
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
def interpret_result(
    self,
    result: dict,
    problem_data: dict,
    next_node_info: dict,
    config: dict | None = None,
) -> dict:
    result["qubo_offset"] = next_node_info["qubo_offset"]

    problem_instance = problem_data["problem_instance"]
    formulation = problem_data["formulation"]
    try:
        if formulation in problem_instance.direct_encoding_modes:
            sol_vector, sol_value = problem_instance.decode_result(
                sol_bitstring=result["solution_bitstring"], mode=formulation
            )
        else:
            sol_vector, sol_value = problem_instance.decode_result(
                sol_bitstring=result["solution_bitstring"]
            )
    except InvalidBitstring:
        logger.warning("Solution bitstring could not be decoded; storing null solution.")
        result["solution_cost_value"] = None
        result["solution_int_vector"] = None
    except (KeyError, AttributeError):
        logger.warning(
            "Could not decode result via the problem instance; "
            "falling back to raw QUBO values.",
            exc_info=True,
        )
        result["solution_cost_value"] = result.get("solution_qubo_value")
        bitstring = result.get("solution_bitstring")
        result["solution_int_vector"] = (
            [int(x) for x in reversed(bitstring)] if bitstring is not None else None
        )
    else:
        result["solution_cost_value"] = sol_value
        result["solution_int_vector"] = sol_vector

    return result