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.
- algorhino_anemone-0.1.1.dist-info/METADATA +151 -0
- algorhino_anemone-0.1.1.dist-info/RECORD +82 -0
- algorhino_anemone-0.1.1.dist-info/WHEEL +5 -0
- algorhino_anemone-0.1.1.dist-info/licenses/LICENSE +674 -0
- algorhino_anemone-0.1.1.dist-info/top_level.txt +1 -0
- anemone/__init__.py +27 -0
- anemone/basics.py +36 -0
- anemone/factory.py +161 -0
- anemone/indices/__init__.py +0 -0
- anemone/indices/index_manager/__init__.py +12 -0
- anemone/indices/index_manager/factory.py +50 -0
- anemone/indices/index_manager/node_exploration_manager.py +549 -0
- anemone/indices/node_indices/__init__.py +22 -0
- anemone/indices/node_indices/factory.py +121 -0
- anemone/indices/node_indices/index_data.py +166 -0
- anemone/indices/node_indices/index_types.py +20 -0
- anemone/nn/torch_evaluator.py +108 -0
- anemone/node_evaluation/__init__.py +0 -0
- anemone/node_evaluation/node_direct_evaluation/__init__.py +22 -0
- anemone/node_evaluation/node_direct_evaluation/factory.py +12 -0
- anemone/node_evaluation/node_direct_evaluation/node_direct_evaluator.py +192 -0
- anemone/node_evaluation/node_tree_evaluation/node_minmax_evaluation.py +885 -0
- anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation.py +137 -0
- anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation_factory.py +43 -0
- anemone/node_factory/__init__.py +14 -0
- anemone/node_factory/algorithm_node_factory.py +123 -0
- anemone/node_factory/base.py +76 -0
- anemone/node_selector/__init__.py +32 -0
- anemone/node_selector/branch_explorer.py +89 -0
- anemone/node_selector/factory.py +65 -0
- anemone/node_selector/node_selector.py +44 -0
- anemone/node_selector/node_selector_args.py +22 -0
- anemone/node_selector/node_selector_types.py +15 -0
- anemone/node_selector/notations_and_statics.py +88 -0
- anemone/node_selector/opening_instructions.py +249 -0
- anemone/node_selector/recurzipf/__init__.py +0 -0
- anemone/node_selector/recurzipf/recur_zipf_base.py +141 -0
- anemone/node_selector/sequool/__init__.py +19 -0
- anemone/node_selector/sequool/factory.py +102 -0
- anemone/node_selector/sequool/sequool.py +395 -0
- anemone/node_selector/uniform/__init__.py +16 -0
- anemone/node_selector/uniform/uniform.py +113 -0
- anemone/nodes/__init__.py +15 -0
- anemone/nodes/algorithm_node/__init__.py +7 -0
- anemone/nodes/algorithm_node/algorithm_node.py +204 -0
- anemone/nodes/itree_node.py +136 -0
- anemone/nodes/tree_node.py +240 -0
- anemone/nodes/tree_traversal.py +108 -0
- anemone/nodes/utils.py +146 -0
- anemone/progress_monitor/__init__.py +0 -0
- anemone/progress_monitor/progress_monitor.py +375 -0
- anemone/recommender_rule/__init__.py +12 -0
- anemone/recommender_rule/recommender_rule.py +140 -0
- anemone/search_factory/__init__.py +14 -0
- anemone/search_factory/search_factory.py +192 -0
- anemone/state_transition.py +47 -0
- anemone/tree_and_value_branch_selector.py +99 -0
- anemone/tree_exploration.py +274 -0
- anemone/tree_manager/__init__.py +29 -0
- anemone/tree_manager/algorithm_node_tree_manager.py +246 -0
- anemone/tree_manager/factory.py +77 -0
- anemone/tree_manager/tree_expander.py +122 -0
- anemone/tree_manager/tree_manager.py +254 -0
- anemone/trees/__init__.py +14 -0
- anemone/trees/descendants.py +765 -0
- anemone/trees/factory.py +80 -0
- anemone/trees/tree.py +70 -0
- anemone/trees/tree_visualization.py +143 -0
- anemone/updates/__init__.py +33 -0
- anemone/updates/algorithm_node_updater.py +157 -0
- anemone/updates/factory.py +36 -0
- anemone/updates/index_block.py +91 -0
- anemone/updates/index_updater.py +100 -0
- anemone/updates/minmax_evaluation_updater.py +108 -0
- anemone/updates/updates_file.py +248 -0
- anemone/updates/value_block.py +133 -0
- anemone/utils/comparable.py +32 -0
- anemone/utils/dataclass.py +64 -0
- anemone/utils/dict_of_numbered_dict_with_pointer_on_max.py +128 -0
- anemone/utils/logger.py +94 -0
- anemone/utils/my_value_sorted_dict.py +27 -0
- 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)
|