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,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains functions for selecting elements based on Zipf distribution.
|
|
3
|
+
|
|
4
|
+
Zipf distribution is a discrete probability distribution that models the occurrence of elements in a dataset.
|
|
5
|
+
The functions in this module provide methods for selecting elements based on their rank and value.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from math import e, log
|
|
10
|
+
from random import Random
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def zipf_picks(
|
|
14
|
+
ranks_values: dict[int, int | float],
|
|
15
|
+
random_generator: Random,
|
|
16
|
+
shift: bool = False,
|
|
17
|
+
random_pick: bool = False,
|
|
18
|
+
) -> int:
|
|
19
|
+
"""
|
|
20
|
+
Selects an element based on its rank and value.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
ranks_values (dict[int, int | float]): A dictionary containing the ranks and values of the elements.
|
|
24
|
+
random_generator (random.Random): A random number generator.
|
|
25
|
+
shift (bool, optional): Whether to shift the ranks. Defaults to False.
|
|
26
|
+
random_pick (bool, optional): Whether to perform a random pick. Defaults to False.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
int: The rank of the selected element.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
Exception: If random_pick is True (not implemented yet).
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
shift_rank: int
|
|
36
|
+
|
|
37
|
+
if shift:
|
|
38
|
+
shift_rank = min(ranks_values.keys())
|
|
39
|
+
else:
|
|
40
|
+
shift_rank = 0
|
|
41
|
+
|
|
42
|
+
best_weight: float | None = None
|
|
43
|
+
best_rank: int = shift_rank
|
|
44
|
+
|
|
45
|
+
weights: list[float] = []
|
|
46
|
+
for rank, value in ranks_values.items():
|
|
47
|
+
shifted_rank = rank - shift_rank + 1
|
|
48
|
+
log_term: float = (log(e * shifted_rank)) ** 2
|
|
49
|
+
weight: float = value * shifted_rank * log_term + 0.0001
|
|
50
|
+
weights.append(weight)
|
|
51
|
+
|
|
52
|
+
if best_weight is None or weight < best_weight:
|
|
53
|
+
best_weight = weight
|
|
54
|
+
best_rank = rank
|
|
55
|
+
|
|
56
|
+
if random_pick:
|
|
57
|
+
choices_ = random_generator.choices(
|
|
58
|
+
list(ranks_values.keys()), weights=weights, k=1
|
|
59
|
+
)
|
|
60
|
+
raise NotImplementedError("not coded yet", choices_) # not codeed properly yet
|
|
61
|
+
|
|
62
|
+
return best_rank
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def zipf_picks_random[T](ordered_list_elements: list[T], random_generator: Random) -> T:
|
|
66
|
+
"""
|
|
67
|
+
Selects a random element from an ordered list based on Zipf distribution.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
ordered_list_elements (list[T]): A list of elements.
|
|
71
|
+
random_generator (random.Random): A random number generator.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
T: The selected element.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
AssertionError: If the length of the list is 0.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
length_list = len(ordered_list_elements)
|
|
81
|
+
assert length_list > 0
|
|
82
|
+
weights = [
|
|
83
|
+
1 / (index + 1) / (log(e * (index + 1))) ** 0 for index in range(length_list)
|
|
84
|
+
]
|
|
85
|
+
picked_element = random_generator.choices(
|
|
86
|
+
ordered_list_elements, weights=weights, k=1
|
|
87
|
+
)
|
|
88
|
+
return picked_element[0]
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains classes and functions related to opening instructions in a chess game.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from random import Random
|
|
8
|
+
from typing import Any, ItemsView, Iterator, Self, ValuesView
|
|
9
|
+
|
|
10
|
+
from valanga import BranchKey
|
|
11
|
+
|
|
12
|
+
from anemone import nodes
|
|
13
|
+
from anemone.nodes.utils import (
|
|
14
|
+
a_branch_str_sequence_from_root,
|
|
15
|
+
a_move_key_sequence_from_root,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
type OpeningInstructionKey = tuple[int, BranchKey]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(slots=True)
|
|
22
|
+
class OpeningInstruction[NodeT: nodes.ITreeNode[Any] = nodes.ITreeNode[Any]]:
|
|
23
|
+
"""
|
|
24
|
+
Represents an opening instruction for a specific node in the game tree.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
node_to_open: NodeT
|
|
28
|
+
branch: BranchKey
|
|
29
|
+
|
|
30
|
+
def print_info(self) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Prints information about the opening instruction.
|
|
33
|
+
"""
|
|
34
|
+
print(
|
|
35
|
+
f"OpeningInstruction: node_to_open {self.node_to_open.id} at hm {self.node_to_open.tree_depth} {self.node_to_open.state}| "
|
|
36
|
+
f"a path from root to node_to_open is {a_move_key_sequence_from_root(self.node_to_open)} {a_branch_str_sequence_from_root(self.node_to_open)}| "
|
|
37
|
+
f"self.branch {self.branch} {self.node_to_open.state.branch_name_from_key(self.branch)}"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OpeningInstructions[NodeT: nodes.ITreeNode[Any] = nodes.ITreeNode[Any]]:
|
|
42
|
+
# todo do we need a dict? why not a set? verify
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
Represents a collection of opening instructions.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
batch: dict[OpeningInstructionKey, OpeningInstruction[NodeT]]
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
dictionary: dict[OpeningInstructionKey, OpeningInstruction[NodeT]]
|
|
53
|
+
| None = None,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Initializes the OpeningInstructions object.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
dictionary: A dictionary of opening instructions (optional).
|
|
60
|
+
"""
|
|
61
|
+
# here i use a dictionary because they are insertion ordered until there is an ordered set in python
|
|
62
|
+
# order is important because some method give a batch where the last element in the batch are prioritary
|
|
63
|
+
self.batch = {}
|
|
64
|
+
|
|
65
|
+
if dictionary is not None:
|
|
66
|
+
for key in dictionary:
|
|
67
|
+
self[key] = dictionary[key]
|
|
68
|
+
|
|
69
|
+
def __setitem__(
|
|
70
|
+
self, key: OpeningInstructionKey, value: OpeningInstruction[NodeT]
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Sets an opening instruction in the collection.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
key: The key for the opening instruction.
|
|
77
|
+
value: The opening instruction.
|
|
78
|
+
"""
|
|
79
|
+
# key is supposed to be a tuple with (node_to_open, move_to_play)
|
|
80
|
+
self.batch[key] = value
|
|
81
|
+
|
|
82
|
+
def __getitem__(self, key: OpeningInstructionKey) -> OpeningInstruction[NodeT]:
|
|
83
|
+
"""
|
|
84
|
+
Retrieves an opening instruction from the collection.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
key: The key for the opening instruction.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The opening instruction.
|
|
91
|
+
"""
|
|
92
|
+
# assert(0==1)
|
|
93
|
+
return self.batch[key]
|
|
94
|
+
|
|
95
|
+
def __iter__(self) -> Iterator[OpeningInstructionKey]:
|
|
96
|
+
"""
|
|
97
|
+
Returns an iterator over the keys of the opening instructions.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
An iterator over the keys.
|
|
101
|
+
"""
|
|
102
|
+
return iter(self.batch)
|
|
103
|
+
|
|
104
|
+
def __bool__(self) -> bool:
|
|
105
|
+
"""
|
|
106
|
+
Checks if the collection is non-empty.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if the collection is non-empty, False otherwise.
|
|
110
|
+
"""
|
|
111
|
+
return bool(self.batch)
|
|
112
|
+
|
|
113
|
+
def merge(self, another_opening_instructions_batch: Self) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Merges another batch of opening instructions into the current collection.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
another_opening_instructions_batch: Another OpeningInstructions object.
|
|
119
|
+
"""
|
|
120
|
+
for (
|
|
121
|
+
opening_instruction_key,
|
|
122
|
+
opening_instruction,
|
|
123
|
+
) in another_opening_instructions_batch.items():
|
|
124
|
+
if opening_instruction_key not in self.batch:
|
|
125
|
+
self.batch[opening_instruction_key] = opening_instruction
|
|
126
|
+
|
|
127
|
+
def pop_items(self, how_many: int, popped: Self) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Pops a specified number of opening instructions from the collection.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
how_many: The number of opening instructions to pop.
|
|
133
|
+
popped: An OpeningInstructions object to store the popped instructions.
|
|
134
|
+
"""
|
|
135
|
+
how_many = min(how_many, len(self.batch))
|
|
136
|
+
for _ in range(how_many):
|
|
137
|
+
key, value = self.batch.popitem()
|
|
138
|
+
popped[key] = value
|
|
139
|
+
|
|
140
|
+
def values(self) -> ValuesView[OpeningInstruction[NodeT]]:
|
|
141
|
+
"""
|
|
142
|
+
Returns a view of the values in the collection.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
A view of the values.
|
|
146
|
+
"""
|
|
147
|
+
return self.batch.values()
|
|
148
|
+
|
|
149
|
+
def items(self) -> ItemsView[OpeningInstructionKey, OpeningInstruction[NodeT]]:
|
|
150
|
+
"""
|
|
151
|
+
Returns a view of the items (key-value pairs) in the collection.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A view of the items.
|
|
155
|
+
"""
|
|
156
|
+
return self.batch.items()
|
|
157
|
+
|
|
158
|
+
def print_info(self) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Prints information about the opening instructions in the collection.
|
|
161
|
+
"""
|
|
162
|
+
print("OpeningInstructionsBatch: batch contains", len(self.batch), "elements:")
|
|
163
|
+
for _key, opening_instructions in self.batch.items():
|
|
164
|
+
opening_instructions.print_info()
|
|
165
|
+
|
|
166
|
+
def __len__(self) -> int:
|
|
167
|
+
"""
|
|
168
|
+
Returns the number of opening instructions in the collection.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
The number of opening instructions.
|
|
172
|
+
"""
|
|
173
|
+
return len(self.batch)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def create_instructions_to_open_all_branches[NodeT: nodes.ITreeNode[Any]](
|
|
177
|
+
branches_to_play: list[BranchKey], node_to_open: NodeT
|
|
178
|
+
) -> OpeningInstructions[NodeT]:
|
|
179
|
+
"""
|
|
180
|
+
Creates opening instructions for all possible moves to play from a given node.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
moves_to_play: A list of chess moves.
|
|
184
|
+
node_to_open: The node to open.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
An OpeningInstructions object containing the opening instructions.
|
|
188
|
+
"""
|
|
189
|
+
opening_instructions_batch: OpeningInstructions[NodeT] = OpeningInstructions()
|
|
190
|
+
|
|
191
|
+
for branch_to_play in branches_to_play:
|
|
192
|
+
# at the moment it looks redundant keys are almost the same as values but its clean
|
|
193
|
+
# the keys are here for fast and redundant proof insertion
|
|
194
|
+
# and the values are here for clean data processing
|
|
195
|
+
opening_instructions_batch[(node_to_open.id, branch_to_play)] = (
|
|
196
|
+
OpeningInstruction(node_to_open=node_to_open, branch=branch_to_play)
|
|
197
|
+
)
|
|
198
|
+
# node_to_open.non_opened_legal_moves.add(move_to_play)
|
|
199
|
+
return opening_instructions_batch
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class OpeningType(Enum):
|
|
203
|
+
"""
|
|
204
|
+
Represents the type of opening to use.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
ALL_CHILDREN = "all_children"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class OpeningInstructor:
|
|
211
|
+
"""
|
|
212
|
+
Represents an opening instructor that provides opening instructions based on a specific opening type.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
def __init__(self, opening_type: OpeningType, random_generator: Random) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Initializes the OpeningInstructor object.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
opening_type: The type of opening to use.
|
|
221
|
+
random_generator: A random number generator.
|
|
222
|
+
"""
|
|
223
|
+
self.opening_type = opening_type
|
|
224
|
+
self.random_generator = random_generator
|
|
225
|
+
|
|
226
|
+
def all_branches_to_open(
|
|
227
|
+
self, node_to_open: nodes.ITreeNode[Any]
|
|
228
|
+
) -> list[BranchKey]:
|
|
229
|
+
"""
|
|
230
|
+
Returns a list of all possible branches to open from a given node.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
node_to_open: The node to open.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
A list of chess moves.
|
|
237
|
+
"""
|
|
238
|
+
if self.opening_type == OpeningType.ALL_CHILDREN:
|
|
239
|
+
node_to_open.all_branches_generated = True
|
|
240
|
+
branches_to_play: list[BranchKey] = node_to_open.state.branch_keys.get_all()
|
|
241
|
+
|
|
242
|
+
# this shuffling add randomness to the playing style
|
|
243
|
+
# (it stills depends on the random seed, but if random seed varies then the behavior will be more random)
|
|
244
|
+
# DEACTIVATED ATM BECAUSE I DO NOT UNDERSTAND or FORGOT THE USE CASE: MAYBE DEAD SINCE SEED SYSTEM CHANGED
|
|
245
|
+
# self.random_generator.shuffle(moves_to_play)
|
|
246
|
+
|
|
247
|
+
else:
|
|
248
|
+
raise NotImplementedError("Hello-la")
|
|
249
|
+
return branches_to_play
|
|
File without changes
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the implementation of the RecurZipfBase class, which is a node selector for a move selector tree.
|
|
3
|
+
|
|
4
|
+
The RecurZipfBase class is responsible for selecting the next node to explore in a move selector tree based on the RecurZipf algorithm.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
- RecurZipfBase: The RecurZipfBase Node selector.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from random import Random
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
14
|
+
|
|
15
|
+
from anemone import trees
|
|
16
|
+
from anemone.node_selector.branch_explorer import SamplingPriorities, ZipfBranchExplorer
|
|
17
|
+
from anemone.node_selector.node_selector_types import NodeSelectorType
|
|
18
|
+
from anemone.node_selector.opening_instructions import (
|
|
19
|
+
OpeningInstructions,
|
|
20
|
+
OpeningInstructor,
|
|
21
|
+
create_instructions_to_open_all_branches,
|
|
22
|
+
)
|
|
23
|
+
from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from anemone import tree_manager as tree_man
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class RecurZipfBaseArgs:
|
|
31
|
+
"""
|
|
32
|
+
Arguments for the RecurZipfBase node selector.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
move_explorer_priority (SamplingPriorities): The priority for move exploration.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
type: Literal[NodeSelectorType.RECUR_ZIPF_BASE]
|
|
39
|
+
branch_explorer_priority: SamplingPriorities
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RecurZipfBase[NodeT: AlgorithmNode[Any] = AlgorithmNode[Any]]:
|
|
43
|
+
"""The RecurZipfBase Node selector"""
|
|
44
|
+
|
|
45
|
+
opening_instructor: OpeningInstructor
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
args: RecurZipfBaseArgs,
|
|
50
|
+
random_generator: Random,
|
|
51
|
+
opening_instructor: OpeningInstructor,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Initializes a new instance of the RecurZipfBase class.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
- args (RecurZipfBaseArgs): The arguments for the RecurZipfBase node selector.
|
|
58
|
+
- random_generator (random.Random): The random number generator.
|
|
59
|
+
- opening_instructor (OpeningInstructor): The opening instructor.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
self.opening_instructor = opening_instructor
|
|
63
|
+
self.branch_explorer = ZipfBranchExplorer(
|
|
64
|
+
args.branch_explorer_priority, random_generator
|
|
65
|
+
)
|
|
66
|
+
self.random_generator = random_generator
|
|
67
|
+
|
|
68
|
+
def choose_node_and_branch_to_open(
|
|
69
|
+
self,
|
|
70
|
+
tree: trees.Tree[NodeT],
|
|
71
|
+
latest_tree_expansions: "tree_man.TreeExpansions[NodeT]",
|
|
72
|
+
) -> OpeningInstructions[NodeT]:
|
|
73
|
+
"""
|
|
74
|
+
Chooses the next node to explore and the move to open.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
- tree (trees.Tree[AlgorithmNode]): The move selector tree.
|
|
78
|
+
- latest_tree_expansions (tree_man.TreeExpansions): The latest tree expansions.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
- OpeningInstructions: The instructions for opening the selected move.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
# todo maybe proportions and proportions can be valuesorted dict with smart updates
|
|
85
|
+
|
|
86
|
+
_ = latest_tree_expansions # not used here
|
|
87
|
+
opening_instructions: OpeningInstructions[NodeT]
|
|
88
|
+
# TODO make sure this block is put in chipiron now with a wrapper
|
|
89
|
+
# best_node_sequence = best_node_sequence_from_node(tree.root_node)
|
|
90
|
+
# if best_node_sequence:
|
|
91
|
+
# last_node_in_best_line = best_node_sequence[-1]
|
|
92
|
+
# assert isinstance(last_node_in_best_line, AlgorithmNode)
|
|
93
|
+
# if (
|
|
94
|
+
# last_node_in_best_line.state.is_attacked(
|
|
95
|
+
# not last_node_in_best_line.tree_node.player_to_move
|
|
96
|
+
# )
|
|
97
|
+
# and not last_node_in_best_line.minmax_evaluation.is_over()
|
|
98
|
+
# ):
|
|
99
|
+
# # print('best line is underattacked')
|
|
100
|
+
# if self.random_generator.random() > 0.5:
|
|
101
|
+
# # print('best line is underattacked and i do')
|
|
102
|
+
# all_moves_to_open: list[BranchKey] = (
|
|
103
|
+
# self.opening_instructor.all_branches_to_open(
|
|
104
|
+
# node_to_open=last_node_in_best_line.tree_node
|
|
105
|
+
# )
|
|
106
|
+
# )
|
|
107
|
+
# opening_instructions = create_instructions_to_open_all_branches(
|
|
108
|
+
# branches_to_play=all_moves_to_open,
|
|
109
|
+
# node_to_open=last_node_in_best_line,
|
|
110
|
+
# )
|
|
111
|
+
# return opening_instructions
|
|
112
|
+
|
|
113
|
+
wandering_node: NodeT = tree.root_node
|
|
114
|
+
|
|
115
|
+
while wandering_node.tree_evaluation.branches_not_over:
|
|
116
|
+
assert not wandering_node.is_over()
|
|
117
|
+
branch = self.branch_explorer.sample_branch_to_explore(
|
|
118
|
+
tree_node_to_sample_from=wandering_node
|
|
119
|
+
)
|
|
120
|
+
next_node = wandering_node.branches_children[branch]
|
|
121
|
+
assert next_node is not None
|
|
122
|
+
wandering_node = next_node
|
|
123
|
+
|
|
124
|
+
all_branches_to_open = self.opening_instructor.all_branches_to_open(
|
|
125
|
+
node_to_open=wandering_node
|
|
126
|
+
)
|
|
127
|
+
opening_instructions = create_instructions_to_open_all_branches(
|
|
128
|
+
branches_to_play=all_branches_to_open, node_to_open=wandering_node
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return opening_instructions
|
|
132
|
+
|
|
133
|
+
def __str__(self) -> str:
|
|
134
|
+
"""
|
|
135
|
+
Returns a string representation of the RecurZipfBase node selector.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
- str: The string representation of the RecurZipfBase node selector.
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
return "RecurZipfBase"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides functionality for creating and managing Sequool objects.
|
|
3
|
+
|
|
4
|
+
A Sequool object represents a sequence of operations that can be applied to a dataset.
|
|
5
|
+
|
|
6
|
+
Example usage:
|
|
7
|
+
from .factory import create_sequool, SequoolArgs
|
|
8
|
+
|
|
9
|
+
# Create a Sequool object
|
|
10
|
+
sequool = create_sequool()
|
|
11
|
+
|
|
12
|
+
# Define the arguments for the Sequool object
|
|
13
|
+
sequool_args = SequoolArgs()
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .factory import SequoolArgs, create_sequool
|
|
18
|
+
|
|
19
|
+
__all__ = ["create_sequool", "SequoolArgs"]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
factory for sequool node selector
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from functools import partial
|
|
7
|
+
from random import Random
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from anemone import trees
|
|
11
|
+
from anemone.node_selector.node_selector_types import (
|
|
12
|
+
NodeSelectorType,
|
|
13
|
+
)
|
|
14
|
+
from anemone.node_selector.opening_instructions import (
|
|
15
|
+
OpeningInstructor,
|
|
16
|
+
)
|
|
17
|
+
from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
|
|
18
|
+
|
|
19
|
+
from .sequool import (
|
|
20
|
+
ConsiderNodesFromTreeDepths,
|
|
21
|
+
RandomAllSelector,
|
|
22
|
+
Sequool,
|
|
23
|
+
StaticNotOpenedSelector,
|
|
24
|
+
TreeDepthSelector,
|
|
25
|
+
consider_nodes_from_all_lesser_tree_depths_in_descendants,
|
|
26
|
+
consider_nodes_from_all_lesser_tree_depths_in_sub_stree,
|
|
27
|
+
consider_nodes_only_from_tree_depths_in_descendants,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class SequoolArgs:
|
|
33
|
+
"""
|
|
34
|
+
Arguments for the Sequool node selector.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
recursive_selection_on_all_nodes (bool): Flag indicating whether to perform recursive selection on all nodes.
|
|
38
|
+
random_depth_pick (bool): Flag indicating whether to randomly pick a depth for selection.
|
|
39
|
+
consider_all_lesser_half_move (bool): Flag indicating whether to consider all lesser half moves.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
type: Literal[NodeSelectorType.SEQUOOL]
|
|
43
|
+
recursive_selection_on_all_nodes: bool
|
|
44
|
+
random_depth_pick: bool
|
|
45
|
+
consider_all_lesser_half_move: bool
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_sequool(
|
|
49
|
+
opening_instructor: OpeningInstructor,
|
|
50
|
+
args: SequoolArgs,
|
|
51
|
+
random_generator: Random,
|
|
52
|
+
) -> Sequool[AlgorithmNode]:
|
|
53
|
+
"""
|
|
54
|
+
Create a sequool node selector object.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
opening_instructor: An opening instructor object.
|
|
58
|
+
args: Dictionary of arguments.
|
|
59
|
+
random_generator: Random generator object.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
A sequool node selector object.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
all_nodes_not_opened = trees.Descendants[AlgorithmNode]()
|
|
66
|
+
tree_depth_selector: TreeDepthSelector
|
|
67
|
+
if args.recursive_selection_on_all_nodes:
|
|
68
|
+
tree_depth_selector = RandomAllSelector()
|
|
69
|
+
else:
|
|
70
|
+
tree_depth_selector = StaticNotOpenedSelector(
|
|
71
|
+
all_nodes_not_opened=all_nodes_not_opened
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
consider_nodes_from_tree_depths: ConsiderNodesFromTreeDepths[AlgorithmNode]
|
|
75
|
+
if args.recursive_selection_on_all_nodes:
|
|
76
|
+
consider_nodes_from_tree_depths = (
|
|
77
|
+
consider_nodes_from_all_lesser_tree_depths_in_sub_stree
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
if args.consider_all_lesser_half_move:
|
|
81
|
+
consider_nodes_from_all_lesser_tree_depths = partial(
|
|
82
|
+
consider_nodes_from_all_lesser_tree_depths_in_descendants,
|
|
83
|
+
descendants=all_nodes_not_opened,
|
|
84
|
+
)
|
|
85
|
+
consider_nodes_from_tree_depths = consider_nodes_from_all_lesser_tree_depths
|
|
86
|
+
else:
|
|
87
|
+
consider_nodes_only_from_tree_depths = partial(
|
|
88
|
+
consider_nodes_only_from_tree_depths_in_descendants,
|
|
89
|
+
descendants=all_nodes_not_opened,
|
|
90
|
+
)
|
|
91
|
+
consider_nodes_from_tree_depths = consider_nodes_only_from_tree_depths
|
|
92
|
+
sequool: Sequool[AlgorithmNode] = Sequool(
|
|
93
|
+
opening_instructor=opening_instructor,
|
|
94
|
+
all_nodes_not_opened=all_nodes_not_opened,
|
|
95
|
+
recursif=args.recursive_selection_on_all_nodes,
|
|
96
|
+
tree_depth_selector=tree_depth_selector,
|
|
97
|
+
random_depth_pick=args.random_depth_pick,
|
|
98
|
+
random_generator=random_generator,
|
|
99
|
+
consider_nodes_from_tree_depths=consider_nodes_from_tree_depths,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return sequool
|