algorhino-anemone 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. algorhino_anemone-0.1.1.dist-info/METADATA +151 -0
  2. algorhino_anemone-0.1.1.dist-info/RECORD +82 -0
  3. algorhino_anemone-0.1.1.dist-info/WHEEL +5 -0
  4. algorhino_anemone-0.1.1.dist-info/licenses/LICENSE +674 -0
  5. algorhino_anemone-0.1.1.dist-info/top_level.txt +1 -0
  6. anemone/__init__.py +27 -0
  7. anemone/basics.py +36 -0
  8. anemone/factory.py +161 -0
  9. anemone/indices/__init__.py +0 -0
  10. anemone/indices/index_manager/__init__.py +12 -0
  11. anemone/indices/index_manager/factory.py +50 -0
  12. anemone/indices/index_manager/node_exploration_manager.py +549 -0
  13. anemone/indices/node_indices/__init__.py +22 -0
  14. anemone/indices/node_indices/factory.py +121 -0
  15. anemone/indices/node_indices/index_data.py +166 -0
  16. anemone/indices/node_indices/index_types.py +20 -0
  17. anemone/nn/torch_evaluator.py +108 -0
  18. anemone/node_evaluation/__init__.py +0 -0
  19. anemone/node_evaluation/node_direct_evaluation/__init__.py +22 -0
  20. anemone/node_evaluation/node_direct_evaluation/factory.py +12 -0
  21. anemone/node_evaluation/node_direct_evaluation/node_direct_evaluator.py +192 -0
  22. anemone/node_evaluation/node_tree_evaluation/node_minmax_evaluation.py +885 -0
  23. anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation.py +137 -0
  24. anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation_factory.py +43 -0
  25. anemone/node_factory/__init__.py +14 -0
  26. anemone/node_factory/algorithm_node_factory.py +123 -0
  27. anemone/node_factory/base.py +76 -0
  28. anemone/node_selector/__init__.py +32 -0
  29. anemone/node_selector/branch_explorer.py +89 -0
  30. anemone/node_selector/factory.py +65 -0
  31. anemone/node_selector/node_selector.py +44 -0
  32. anemone/node_selector/node_selector_args.py +22 -0
  33. anemone/node_selector/node_selector_types.py +15 -0
  34. anemone/node_selector/notations_and_statics.py +88 -0
  35. anemone/node_selector/opening_instructions.py +249 -0
  36. anemone/node_selector/recurzipf/__init__.py +0 -0
  37. anemone/node_selector/recurzipf/recur_zipf_base.py +141 -0
  38. anemone/node_selector/sequool/__init__.py +19 -0
  39. anemone/node_selector/sequool/factory.py +102 -0
  40. anemone/node_selector/sequool/sequool.py +395 -0
  41. anemone/node_selector/uniform/__init__.py +16 -0
  42. anemone/node_selector/uniform/uniform.py +113 -0
  43. anemone/nodes/__init__.py +15 -0
  44. anemone/nodes/algorithm_node/__init__.py +7 -0
  45. anemone/nodes/algorithm_node/algorithm_node.py +204 -0
  46. anemone/nodes/itree_node.py +136 -0
  47. anemone/nodes/tree_node.py +240 -0
  48. anemone/nodes/tree_traversal.py +108 -0
  49. anemone/nodes/utils.py +146 -0
  50. anemone/progress_monitor/__init__.py +0 -0
  51. anemone/progress_monitor/progress_monitor.py +375 -0
  52. anemone/recommender_rule/__init__.py +12 -0
  53. anemone/recommender_rule/recommender_rule.py +140 -0
  54. anemone/search_factory/__init__.py +14 -0
  55. anemone/search_factory/search_factory.py +192 -0
  56. anemone/state_transition.py +47 -0
  57. anemone/tree_and_value_branch_selector.py +99 -0
  58. anemone/tree_exploration.py +274 -0
  59. anemone/tree_manager/__init__.py +29 -0
  60. anemone/tree_manager/algorithm_node_tree_manager.py +246 -0
  61. anemone/tree_manager/factory.py +77 -0
  62. anemone/tree_manager/tree_expander.py +122 -0
  63. anemone/tree_manager/tree_manager.py +254 -0
  64. anemone/trees/__init__.py +14 -0
  65. anemone/trees/descendants.py +765 -0
  66. anemone/trees/factory.py +80 -0
  67. anemone/trees/tree.py +70 -0
  68. anemone/trees/tree_visualization.py +143 -0
  69. anemone/updates/__init__.py +33 -0
  70. anemone/updates/algorithm_node_updater.py +157 -0
  71. anemone/updates/factory.py +36 -0
  72. anemone/updates/index_block.py +91 -0
  73. anemone/updates/index_updater.py +100 -0
  74. anemone/updates/minmax_evaluation_updater.py +108 -0
  75. anemone/updates/updates_file.py +248 -0
  76. anemone/updates/value_block.py +133 -0
  77. anemone/utils/comparable.py +32 -0
  78. anemone/utils/dataclass.py +64 -0
  79. anemone/utils/dict_of_numbered_dict_with_pointer_on_max.py +128 -0
  80. anemone/utils/logger.py +94 -0
  81. anemone/utils/my_value_sorted_dict.py +27 -0
  82. anemone/utils/small_tools.py +103 -0
@@ -0,0 +1,100 @@
1
+ """
2
+ This module contains the IndexUpdater class, which is responsible for updating the indices of AlgorithmNode objects in a tree structure.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from anemone.indices.node_indices.index_data import (
8
+ MaxDepthDescendants,
9
+ )
10
+ from anemone.nodes.algorithm_node.algorithm_node import (
11
+ AlgorithmNode,
12
+ )
13
+
14
+ from .index_block import (
15
+ IndexUpdateInstructionsFromOneNode,
16
+ IndexUpdateInstructionsTowardsOneParentNode,
17
+ )
18
+ from .updates_file import UpdateInstructionsTowardsOneParentNode
19
+
20
+ if TYPE_CHECKING:
21
+ from valanga import BranchKey
22
+
23
+
24
+ class IndexUpdater:
25
+ """
26
+ The IndexUpdater class is responsible for updating the indices of AlgorithmNode objects in a tree structure.
27
+ """
28
+
29
+ def __init__(self) -> None:
30
+ """Initialize the index updater."""
31
+ ...
32
+
33
+ def create_update_instructions_after_node_birth(
34
+ self, new_node: AlgorithmNode
35
+ ) -> IndexUpdateInstructionsFromOneNode:
36
+ """
37
+ Creates the update instructions block after a new node is added to the tree.
38
+
39
+ Args:
40
+ new_node (AlgorithmNode): The newly added node.
41
+
42
+ Returns:
43
+ IndexUpdateInstructionsBlock: The update instructions block.
44
+ """
45
+ base_update_instructions: IndexUpdateInstructionsFromOneNode = (
46
+ IndexUpdateInstructionsFromOneNode(
47
+ node_sending_update=new_node, updated_index=True
48
+ )
49
+ )
50
+ return base_update_instructions
51
+
52
+ def perform_updates(
53
+ self,
54
+ node_to_update: AlgorithmNode,
55
+ updates_instructions: UpdateInstructionsTowardsOneParentNode,
56
+ ) -> IndexUpdateInstructionsFromOneNode:
57
+ """
58
+ Performs the index updates based on the given update instructions.
59
+
60
+ Args:
61
+ node_to_update (AlgorithmNode): The node to update.
62
+ updates_instructions (UpdateInstructionsTowardsOneParentNode): The update instructions toward this node.
63
+
64
+ Returns:
65
+ IndexUpdateInstructionsFromOneNode: The update instructions coming from the updated node.
66
+ """
67
+ # get the base block
68
+ updates_instructions_index: IndexUpdateInstructionsTowardsOneParentNode | None
69
+ updates_instructions_index = (
70
+ updates_instructions.index_updates_toward_one_parent_node
71
+ )
72
+ assert updates_instructions_index is not None
73
+
74
+ # UPDATE index
75
+ has_index_changed: bool = False
76
+ branch: BranchKey
77
+ for branch in updates_instructions_index.branches_with_updated_index:
78
+ # hardcoded at some point it should be linked to updater coming from search factory i believe
79
+ assert isinstance(
80
+ node_to_update.exploration_index_data, MaxDepthDescendants
81
+ )
82
+ child = node_to_update.tree_node.branches_children[branch]
83
+ assert isinstance(child, AlgorithmNode)
84
+ assert isinstance(child.exploration_index_data, MaxDepthDescendants)
85
+ has_index_changed_child: bool = (
86
+ node_to_update.exploration_index_data.update_from_child(
87
+ child.exploration_index_data.max_depth_descendants
88
+ )
89
+ )
90
+ has_index_changed = has_index_changed or has_index_changed_child
91
+
92
+ base_update_instructions: IndexUpdateInstructionsFromOneNode = (
93
+ IndexUpdateInstructionsFromOneNode(
94
+ node_sending_update=node_to_update, updated_index=has_index_changed
95
+ )
96
+ )
97
+
98
+ return base_update_instructions
99
+
100
+ # todo i dont understand anymore when the instructions stops beeing propagated back
@@ -0,0 +1,108 @@
1
+ """
2
+ This module contains the MinMaxEvaluationUpdater class, which is responsible for updating the min-max evaluation values of AlgorithmNode objects.
3
+ """
4
+
5
+ from anemone.nodes.algorithm_node.algorithm_node import (
6
+ AlgorithmNode,
7
+ )
8
+
9
+ from .updates_file import UpdateInstructionsTowardsOneParentNode
10
+ from .value_block import (
11
+ ValueUpdateInstructionsFromOneNode,
12
+ ValueUpdateInstructionsTowardsOneParentNode,
13
+ )
14
+
15
+
16
+ class MinMaxEvaluationUpdater:
17
+ """
18
+ The MinMaxEvaluationUpdater class is responsible for updating the min-max evaluation values of AlgorithmNode objects.
19
+ """
20
+
21
+ def __init__(self) -> None:
22
+ """Initializes a new instance of the MinMaxEvaluationUpdater class."""
23
+ ...
24
+
25
+ def create_update_instructions_after_node_birth(
26
+ self, new_node: AlgorithmNode
27
+ ) -> ValueUpdateInstructionsFromOneNode:
28
+ """
29
+ Creates the update instructions for a newly created AlgorithmNode.
30
+
31
+ Args:
32
+ new_node (AlgorithmNode): The newly created AlgorithmNode.
33
+
34
+ Returns:
35
+ ValueUpdateInstructionsBlock: The update instructions for the newly created node.
36
+ """
37
+ base_update_instructions: ValueUpdateInstructionsFromOneNode = (
38
+ ValueUpdateInstructionsFromOneNode(
39
+ node_sending_update=new_node,
40
+ is_node_newly_over=new_node.tree_evaluation.over_event.is_over(),
41
+ new_value_for_node=True,
42
+ new_best_move_for_node=False,
43
+ )
44
+ )
45
+ return base_update_instructions
46
+
47
+ def perform_updates(
48
+ self,
49
+ node_to_update: AlgorithmNode,
50
+ updates_instructions: UpdateInstructionsTowardsOneParentNode,
51
+ ) -> ValueUpdateInstructionsFromOneNode:
52
+ """
53
+ Performs the updates on an AlgorithmNode based on the given update instructions.
54
+
55
+ Args:
56
+ node_to_update (AlgorithmNode): The AlgorithmNode to update.
57
+ updates_instructions (UpdateInstructionsTowardsOneParentNode): The update instructions.
58
+
59
+ Returns:
60
+ ValueUpdateInstructionsFromOneNode: The update instructions for the parents of the updated node.
61
+ """
62
+ # get the base block
63
+ updates_instructions_block: ValueUpdateInstructionsTowardsOneParentNode | None
64
+ updates_instructions_block = (
65
+ updates_instructions.value_updates_toward_one_parent_node
66
+ )
67
+ assert updates_instructions_block is not None
68
+
69
+ # UPDATE VALUE
70
+ has_value_changed: bool
71
+ has_best_node_seq_changed_1: bool
72
+ has_value_changed, has_best_node_seq_changed_1 = (
73
+ node_to_update.tree_evaluation.minmax_value_update_from_children(
74
+ branches_with_updated_value=updates_instructions_block.branches_with_updated_value
75
+ )
76
+ )
77
+
78
+ # UPDATE BEST MOVE
79
+ has_best_node_seq_changed_2: bool
80
+ if updates_instructions_block.branches_with_updated_best_branch:
81
+ has_best_node_seq_changed_2 = (
82
+ node_to_update.tree_evaluation.update_best_branch_sequence(
83
+ updates_instructions_block.branches_with_updated_best_branch
84
+ )
85
+ )
86
+ else:
87
+ has_best_node_seq_changed_2 = False
88
+ has_best_node_seq_changed: bool = (
89
+ has_best_node_seq_changed_1 or has_best_node_seq_changed_2
90
+ )
91
+
92
+ # UPDATE OVER
93
+ is_newly_over = node_to_update.tree_evaluation.update_over(
94
+ updates_instructions_block.branches_with_updated_over
95
+ )
96
+
97
+ assert is_newly_over is not None
98
+
99
+ # create the new instructions for the parents
100
+ base_update_instructions_block: ValueUpdateInstructionsFromOneNode
101
+ base_update_instructions_block = ValueUpdateInstructionsFromOneNode(
102
+ node_sending_update=node_to_update,
103
+ is_node_newly_over=is_newly_over,
104
+ new_value_for_node=has_value_changed,
105
+ new_best_move_for_node=has_best_node_seq_changed,
106
+ )
107
+
108
+ return base_update_instructions_block
@@ -0,0 +1,248 @@
1
+ """
2
+ This module contains classes for managing update instructions in a batch.
3
+
4
+ Classes:
5
+ - UpdateInstructions: Represents update instructions for a single node.
6
+ - UpdateInstructionsBatch: Represents a batch of update instructions for multiple nodes.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Self
11
+
12
+ from valanga import BranchKey
13
+
14
+ from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
15
+ from anemone.utils.dict_of_numbered_dict_with_pointer_on_max import (
16
+ DictOfNumberedDictWithPointerOnMax,
17
+ )
18
+
19
+ from .index_block import (
20
+ IndexUpdateInstructionsFromOneNode,
21
+ IndexUpdateInstructionsTowardsOneParentNode,
22
+ )
23
+ from .value_block import (
24
+ ValueUpdateInstructionsFromOneNode,
25
+ ValueUpdateInstructionsTowardsOneParentNode,
26
+ )
27
+
28
+
29
+ @dataclass(slots=True)
30
+ class UpdateInstructionsFromOneNode:
31
+ """
32
+ Represents update instructions generated from a single node.
33
+
34
+ Attributes:
35
+ - value_block: The value update instructions generated from a single node.
36
+ - index_block: The index update instructions generated from a single node.
37
+ """
38
+
39
+ value_block: ValueUpdateInstructionsFromOneNode | None = None
40
+ index_block: IndexUpdateInstructionsFromOneNode | None = None
41
+
42
+
43
+ @dataclass(slots=True)
44
+ class UpdateInstructionsTowardsOneParentNode:
45
+ """
46
+ Represents update instructions for a single node.
47
+
48
+ Attributes:
49
+ - value_block: The value update instructions block.
50
+ - index_block: The index update instructions block.
51
+ """
52
+
53
+ value_updates_toward_one_parent_node: (
54
+ ValueUpdateInstructionsTowardsOneParentNode | None
55
+ ) = None
56
+ index_updates_toward_one_parent_node: (
57
+ IndexUpdateInstructionsTowardsOneParentNode | None
58
+ ) = None
59
+
60
+ def add_update_from_a_child_node(
61
+ self,
62
+ update_from_a_child_node: UpdateInstructionsFromOneNode,
63
+ branch_from_parent_to_child: BranchKey,
64
+ ) -> None:
65
+ """
66
+ Adds update instructions from a child node.
67
+
68
+ Args:
69
+ - update_from_a_child_node: The update instructions from the child node.
70
+ - branch_from_parent_to_child: The branch key from the parent to the child.
71
+ """
72
+ assert self.value_updates_toward_one_parent_node is not None
73
+ assert update_from_a_child_node.value_block is not None
74
+ self.value_updates_toward_one_parent_node.add_update_from_one_child_node(
75
+ branch_from_parent_to_child=branch_from_parent_to_child,
76
+ update_from_one_child_node=update_from_a_child_node.value_block,
77
+ )
78
+
79
+ if self.index_updates_toward_one_parent_node is None:
80
+ assert update_from_a_child_node.index_block is None
81
+ else:
82
+ if update_from_a_child_node.index_block is not None:
83
+ self.index_updates_toward_one_parent_node.add_update_from_one_child_node(
84
+ branch_from_parent_to_child=branch_from_parent_to_child,
85
+ update_from_one_child_node=update_from_a_child_node.index_block,
86
+ )
87
+
88
+ def add_updates_towards_one_parent_node(self, another_update: Self) -> None:
89
+ """
90
+ Adds update instructions from another UpdateInstructionsTowardsOneParentNode.
91
+ Args:
92
+ - another_update: The other update instructions to add.
93
+ """
94
+ assert self.value_updates_toward_one_parent_node is not None
95
+ assert another_update.value_updates_toward_one_parent_node is not None
96
+ self.value_updates_toward_one_parent_node.add_update_toward_one_parent_node(
97
+ another_update.value_updates_toward_one_parent_node
98
+ )
99
+
100
+ if self.index_updates_toward_one_parent_node is None:
101
+ assert another_update.index_updates_toward_one_parent_node is None
102
+ else:
103
+ if another_update.index_updates_toward_one_parent_node is not None:
104
+ self.index_updates_toward_one_parent_node.add_update_toward_one_parent_node(
105
+ another_update.index_updates_toward_one_parent_node
106
+ )
107
+
108
+ def print_info(self) -> None:
109
+ """
110
+ Prints information about the update instructions.
111
+ """
112
+ print("printing info of update instructions")
113
+ assert (
114
+ self.index_updates_toward_one_parent_node is not None
115
+ and self.value_updates_toward_one_parent_node is not None
116
+ )
117
+ self.value_updates_toward_one_parent_node.print_info()
118
+ self.index_updates_toward_one_parent_node.print_info()
119
+
120
+ def empty(self) -> bool:
121
+ """
122
+ Checks if the update instructions are empty.
123
+
124
+ Returns:
125
+ - True if the update instructions are empty, False otherwise.
126
+ """
127
+ assert self.value_updates_toward_one_parent_node is not None
128
+ return self.value_updates_toward_one_parent_node.empty() and (
129
+ self.index_updates_toward_one_parent_node is None
130
+ or self.index_updates_toward_one_parent_node.empty()
131
+ )
132
+
133
+
134
+ @dataclass
135
+ class UpdateInstructionsTowardsMultipleNodes[NodeT: AlgorithmNode = AlgorithmNode]:
136
+ """Represents update instructions towards multiple parent nodes."""
137
+
138
+ @staticmethod
139
+ def _new_one_node_instructions() -> DictOfNumberedDictWithPointerOnMax[
140
+ NodeT, UpdateInstructionsTowardsOneParentNode
141
+ ]:
142
+ """Return a fresh mapping for per-node update instructions."""
143
+ return DictOfNumberedDictWithPointerOnMax()
144
+
145
+ one_node_instructions: DictOfNumberedDictWithPointerOnMax[
146
+ NodeT, UpdateInstructionsTowardsOneParentNode
147
+ ] = field(default_factory=_new_one_node_instructions)
148
+
149
+ def add_update_from_one_child_node(
150
+ self,
151
+ update_from_child_node: UpdateInstructionsFromOneNode,
152
+ parent_node: NodeT,
153
+ branch_from_parent: BranchKey,
154
+ ) -> None:
155
+ """
156
+ Adds update instructions from a child node to a parent node.
157
+ Args:
158
+ update_from_child_node: The update instructions from the child node.
159
+ parent_node: The parent node to which the updates are directed.
160
+ branch_from_parent: The branch key from the parent to the child.
161
+ """
162
+ if parent_node not in self.one_node_instructions:
163
+ # build the UpdateInstructionsTowardsOneParentNode
164
+ assert update_from_child_node.value_block is not None
165
+ value_updates_toward_one_parent_node: (
166
+ ValueUpdateInstructionsTowardsOneParentNode
167
+ )
168
+ value_updates_toward_one_parent_node = (
169
+ ValueUpdateInstructionsTowardsOneParentNode(
170
+ branches_with_updated_value=(
171
+ {branch_from_parent}
172
+ if update_from_child_node.value_block.new_value_for_node
173
+ else set()
174
+ ),
175
+ branches_with_updated_over=(
176
+ {branch_from_parent}
177
+ if update_from_child_node.value_block.is_node_newly_over
178
+ else set()
179
+ ),
180
+ branches_with_updated_best_branch=(
181
+ {branch_from_parent}
182
+ if update_from_child_node.value_block.new_best_move_for_node
183
+ else set()
184
+ ),
185
+ )
186
+ )
187
+ index_updates_toward_one_parent_node: (
188
+ IndexUpdateInstructionsTowardsOneParentNode | None
189
+ )
190
+ if update_from_child_node.index_block is not None:
191
+ index_updates_toward_one_parent_node = (
192
+ IndexUpdateInstructionsTowardsOneParentNode(
193
+ branches_with_updated_index=(
194
+ {branch_from_parent}
195
+ if update_from_child_node.index_block.updated_index
196
+ else set()
197
+ ),
198
+ )
199
+ )
200
+ else:
201
+ index_updates_toward_one_parent_node = None
202
+ update_instructions_towards_parent: UpdateInstructionsTowardsOneParentNode
203
+ update_instructions_towards_parent = UpdateInstructionsTowardsOneParentNode(
204
+ value_updates_toward_one_parent_node=value_updates_toward_one_parent_node,
205
+ index_updates_toward_one_parent_node=index_updates_toward_one_parent_node,
206
+ )
207
+ self.one_node_instructions[parent_node] = update_instructions_towards_parent
208
+ else:
209
+ # update the UpdateInstructionsTowardsOneParentNode
210
+ self.one_node_instructions[parent_node].add_update_from_a_child_node(
211
+ update_from_a_child_node=update_from_child_node,
212
+ branch_from_parent_to_child=branch_from_parent,
213
+ )
214
+
215
+ def add_updates_towards_one_parent_node(
216
+ self,
217
+ update_from_child_node: UpdateInstructionsTowardsOneParentNode,
218
+ parent_node: NodeT,
219
+ ) -> None:
220
+ """
221
+ Adds update instructions from another UpdateInstructionsTowardsOneParentNode to a parent node.
222
+ Args:
223
+ update_from_child_node: The update instructions from another UpdateInstructionsTowardsOneParentNode.
224
+ parent_node: The parent node to which the updates are directed.
225
+ """
226
+ if parent_node in self.one_node_instructions:
227
+ self.one_node_instructions[parent_node].add_updates_towards_one_parent_node(
228
+ another_update=update_from_child_node
229
+ )
230
+ else:
231
+ self.one_node_instructions[parent_node] = update_from_child_node
232
+
233
+ def pop_item(self) -> tuple[NodeT, UpdateInstructionsTowardsOneParentNode]:
234
+ """
235
+ Pops an item from the update instructions.
236
+ Returns:
237
+ A tuple containing the parent node and its corresponding update instructions.
238
+ """
239
+ return self.one_node_instructions.popitem()
240
+
241
+ def __bool__(self) -> bool:
242
+ """
243
+ Checks if the data structure is non-empty.
244
+
245
+ Returns:
246
+ bool: True if the data structure is non-empty, False otherwise.
247
+ """
248
+ return bool(self.one_node_instructions)
@@ -0,0 +1,133 @@
1
+ """
2
+ This module defines the ValueUpdateInstructionsBlock class and a helper function to create instances of it.
3
+
4
+ The ValueUpdateInstructionsBlock class represents a block of update instructions for a tree value node in
5
+ a move selector algorithm. It contains sets of moves that have been updated with new values,
6
+ best moves, or are newly over.
7
+
8
+ The create_value_update_instructions_block function is a helper function that creates an instance of
9
+ the ValueUpdateInstructionsBlock class with the specified update instructions.
10
+
11
+ """
12
+
13
+ from dataclasses import dataclass, field
14
+ from typing import Self
15
+
16
+ from valanga import BranchKey
17
+
18
+ from anemone.nodes.algorithm_node.algorithm_node import (
19
+ AlgorithmNode,
20
+ )
21
+
22
+
23
+ @dataclass(slots=True)
24
+ class ValueUpdateInstructionsFromOneNode:
25
+ """Represents update instructions generated from a single node."""
26
+
27
+ node_sending_update: AlgorithmNode
28
+ is_node_newly_over: bool
29
+ new_value_for_node: bool
30
+ new_best_move_for_node: bool
31
+
32
+
33
+ def _new_branchkey_set() -> set[BranchKey]:
34
+ """Return a new empty set of branch keys."""
35
+ return set()
36
+
37
+
38
+ @dataclass(slots=True)
39
+ class ValueUpdateInstructionsTowardsOneParentNode:
40
+ """Represents a block of value-update instructions intended to a specific node in the algorithm tree."""
41
+
42
+ branches_with_updated_over: set[BranchKey] = field(
43
+ default_factory=_new_branchkey_set
44
+ )
45
+ branches_with_updated_value: set[BranchKey] = field(
46
+ default_factory=_new_branchkey_set
47
+ )
48
+ branches_with_updated_best_branch: set[BranchKey] = field(
49
+ default_factory=_new_branchkey_set
50
+ )
51
+
52
+ def add_update_from_one_child_node(
53
+ self,
54
+ update_from_one_child_node: ValueUpdateInstructionsFromOneNode,
55
+ branch_from_parent_to_child: BranchKey,
56
+ ) -> None:
57
+ """Adds an update from a child node to the parent node.
58
+
59
+ Args:
60
+ update_from_one_child_node (ValueUpdateInstructionsFromOneNode): The update instructions from the child node.
61
+ move_from_parent_to_child (moveKey): The move key representing the move from the parent to the child.
62
+ """
63
+ if update_from_one_child_node.is_node_newly_over:
64
+ self.branches_with_updated_over.add(branch_from_parent_to_child)
65
+ if update_from_one_child_node.new_value_for_node:
66
+ self.branches_with_updated_value.add(branch_from_parent_to_child)
67
+ if update_from_one_child_node.new_best_move_for_node:
68
+ self.branches_with_updated_best_branch.add(branch_from_parent_to_child)
69
+
70
+ def add_update_toward_one_parent_node(self, another_update: Self) -> None:
71
+ """Adds an update towards one parent node.
72
+
73
+ Args:
74
+ another_update (Self): The update instructions from another child node.
75
+ """
76
+ self.branches_with_updated_value = (
77
+ self.branches_with_updated_value
78
+ | another_update.branches_with_updated_value
79
+ )
80
+ self.branches_with_updated_over = (
81
+ self.branches_with_updated_over | another_update.branches_with_updated_over
82
+ )
83
+ self.branches_with_updated_best_branch = (
84
+ self.branches_with_updated_best_branch
85
+ | another_update.branches_with_updated_best_branch
86
+ )
87
+
88
+ def print_info(self) -> None:
89
+ """
90
+ Print information about the update instructions block.
91
+
92
+ Returns:
93
+ None
94
+ """
95
+ print("upInstructions printing")
96
+ print(
97
+ len(self.branches_with_updated_value),
98
+ "branches_with_updated_value",
99
+ end=" ",
100
+ )
101
+ for branch in self.branches_with_updated_value:
102
+ print(branch, end=" ")
103
+ print(
104
+ "\n",
105
+ len(self.branches_with_updated_best_branch),
106
+ "branches_with_updated_best_branch:",
107
+ end=" ",
108
+ )
109
+ for branch in self.branches_with_updated_best_branch:
110
+ print(branch, end=" ")
111
+ print(
112
+ "\n",
113
+ len(self.branches_with_updated_over),
114
+ "branches_with_updated_over",
115
+ end=" ",
116
+ )
117
+ for branch in self.branches_with_updated_over:
118
+ print(branch, end=" ")
119
+ print()
120
+
121
+ def empty(self) -> bool:
122
+ """
123
+ Check if all the components of the update instructions block are empty.
124
+
125
+ Returns:
126
+ bool: True if all components are empty, False otherwise.
127
+ """
128
+ empty_bool = (
129
+ not bool(self.branches_with_updated_value)
130
+ and not bool(self.branches_with_updated_best_branch)
131
+ and not bool(self.branches_with_updated_over)
132
+ )
133
+ return empty_bool
@@ -0,0 +1,32 @@
1
+ """
2
+ This module defines the Comparable class, which is an abstract base class for objects that can be compared.
3
+
4
+ Classes that inherit from Comparable must implement the __lt__ method, which defines the less-than comparison operation.
5
+
6
+ """
7
+
8
+ from abc import ABCMeta, abstractmethod
9
+ from typing import Any, TypeVar
10
+
11
+
12
+ class Comparable(metaclass=ABCMeta):
13
+ """
14
+ An abstract base class for objects that can be compared.
15
+
16
+ Subclasses of `Comparable` must implement the `__lt__` method to define the less-than comparison.
17
+
18
+ Attributes:
19
+ None
20
+
21
+ Methods:
22
+ __lt__(self, other: Any) -> bool: Abstract method that compares the object with another object and
23
+ returns True if the object is less than the other object, False otherwise.
24
+ """
25
+
26
+ @abstractmethod
27
+ def __lt__(self, other: Any) -> bool:
28
+ """Return True if this object is less than the other."""
29
+ ...
30
+
31
+
32
+ CT = TypeVar("CT", bound=Comparable)
@@ -0,0 +1,64 @@
1
+ """
2
+ Module to check if an object is a dataclass
3
+ """
4
+
5
+ from collections.abc import Iterable
6
+ from enum import Enum
7
+ from typing import Any, ClassVar, Protocol, no_type_check
8
+
9
+
10
+ class DataClass(Protocol):
11
+ """
12
+ Protocol to represent a dataclass.
13
+
14
+ This protocol is used to check if an object is a dataclass by checking
15
+ for the presence of the `__dataclass_fields__` attribute.
16
+ """
17
+
18
+ __dataclass_fields__: dict[str, Any]
19
+
20
+
21
+ class IsDataclass(Protocol):
22
+ """
23
+ Protocol to represent a dataclass.
24
+
25
+ This protocol is used to check if an object is a dataclass by checking
26
+ for the presence of the `__dataclass_fields__` attribute.
27
+ """
28
+
29
+ __dataclass_fields__: ClassVar[dict[Any, Any]]
30
+
31
+
32
+ # used as factory function in one of the option of as_dict in order to make the enum be replaced by str (useful for yamlization)
33
+ # pyright: ignore
34
+ def custom_asdict_factory(data: Iterable[tuple[Any, Any]]) -> dict[Any, Any]:
35
+ """Custom asdict factory function.
36
+
37
+ Args:
38
+ data (Iterable[tuple[Any, Any]]): The input data to be converted.
39
+
40
+ Returns:
41
+ dict[Any, Any]: The converted dictionary.
42
+ """
43
+
44
+ @no_type_check
45
+ def convert_value(obj: Any) -> Any:
46
+ """Converts a value to a serializable format.
47
+
48
+ Args:
49
+ obj (Any): The input value to be converted.
50
+
51
+ Returns:
52
+ Any: The converted value.
53
+ """
54
+ if isinstance(obj, Enum):
55
+ return obj.value
56
+ elif isinstance(obj, list):
57
+ return [convert_value(item) for item in obj]
58
+ elif isinstance(obj, dict):
59
+ result: dict[Any, Any] = {k: convert_value(v) for k, v in obj.items()}
60
+ return result
61
+ else:
62
+ return obj
63
+
64
+ return dict((k, convert_value(v)) for k, v in data)