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,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for DictOfNumberedDictWithPointerOnMax class.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HasTreeDepth(Protocol):
|
|
9
|
+
"""Protocol for objects that have a tree_depth attribute."""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def tree_depth(self) -> int:
|
|
13
|
+
"""
|
|
14
|
+
Get the half move count of the node.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
The half move count of the node.
|
|
18
|
+
"""
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DictOfNumberedDictWithPointerOnMax[T_Key: HasTreeDepth, T_Value]:
|
|
23
|
+
"""
|
|
24
|
+
A dictionary-like data structure that stores numbered dictionaries and keeps track of the maximum half move.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
tree_depths (dict[int, dict[T_Key, T_Value]]): A dictionary that stores numbered dictionaries.
|
|
28
|
+
max_tree_depth (int | None): The maximum half move value.
|
|
29
|
+
|
|
30
|
+
Methods:
|
|
31
|
+
__setitem__(self, node: T_Key, value: T_Value) -> None: Adds an item to the data structure.
|
|
32
|
+
__getitem__(self, node: T_Key) -> T_Value: Retrieves an item from the data structure.
|
|
33
|
+
__bool__(self) -> bool: Checks if the data structure is non-empty.
|
|
34
|
+
__contains__(self, node: T_Key) -> bool: Checks if an item is present in the data structure.
|
|
35
|
+
popitem(self) -> tuple[T_Key, T_Value]: Removes and returns the item with the maximum half move value.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
"""Initialize the depth-indexed mapping."""
|
|
40
|
+
self.tree_depths: dict[int, dict[T_Key, T_Value]] = {}
|
|
41
|
+
self.max_tree_depth: int | None = None
|
|
42
|
+
|
|
43
|
+
def __setitem__(self, node: T_Key, value: T_Value) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Adds an item to the data structure.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
node (T_Key): The key of the item.
|
|
49
|
+
value (T_Value): The value of the item.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
None
|
|
53
|
+
"""
|
|
54
|
+
tree_depth = node.tree_depth
|
|
55
|
+
if self.max_tree_depth is None:
|
|
56
|
+
self.max_tree_depth = tree_depth
|
|
57
|
+
else:
|
|
58
|
+
self.max_tree_depth = max(tree_depth, self.max_tree_depth)
|
|
59
|
+
if tree_depth in self.tree_depths:
|
|
60
|
+
self.tree_depths[tree_depth][node] = value
|
|
61
|
+
else:
|
|
62
|
+
self.tree_depths[tree_depth] = {node: value}
|
|
63
|
+
|
|
64
|
+
assert self.max_tree_depth == max(self.tree_depths)
|
|
65
|
+
|
|
66
|
+
def __getitem__(self, node: T_Key) -> T_Value:
|
|
67
|
+
"""
|
|
68
|
+
Retrieves an item from the data structure.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
node (T_Key): The key of the item.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
T_Value: The value of the item.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
KeyError: If the item is not found in the data structure.
|
|
78
|
+
"""
|
|
79
|
+
return self.tree_depths[node.tree_depth][node]
|
|
80
|
+
|
|
81
|
+
def __bool__(self) -> bool:
|
|
82
|
+
"""
|
|
83
|
+
Checks if the data structure is non-empty.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
bool: True if the data structure is non-empty, False otherwise.
|
|
87
|
+
"""
|
|
88
|
+
return bool(self.tree_depths)
|
|
89
|
+
|
|
90
|
+
def __contains__(self, node: T_Key) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Checks if an item is present in the data structure.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
node (T_Key): The key of the item.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
bool: True if the item is present, False otherwise.
|
|
99
|
+
"""
|
|
100
|
+
if node.tree_depth not in self.tree_depths:
|
|
101
|
+
return False
|
|
102
|
+
else:
|
|
103
|
+
return node in self.tree_depths[node.tree_depth]
|
|
104
|
+
|
|
105
|
+
def popitem(self) -> tuple[T_Key, T_Value]:
|
|
106
|
+
"""
|
|
107
|
+
Removes and returns the item with the maximum half move value.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
tuple[T_Key, T_Value]: The key-value pair of the removed item.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
AssertionError: If the data structure is empty.
|
|
114
|
+
"""
|
|
115
|
+
assert self.max_tree_depth is not None
|
|
116
|
+
popped: tuple[T_Key, T_Value] = self.tree_depths[self.max_tree_depth].popitem()
|
|
117
|
+
if not self.tree_depths[self.max_tree_depth]:
|
|
118
|
+
del self.tree_depths[self.max_tree_depth]
|
|
119
|
+
if self.tree_depths:
|
|
120
|
+
self.max_tree_depth = max(self.tree_depths.keys())
|
|
121
|
+
else:
|
|
122
|
+
self.max_tree_depth = None
|
|
123
|
+
|
|
124
|
+
return popped
|
|
125
|
+
|
|
126
|
+
# def sort_dic(self):
|
|
127
|
+
# self.dic = dict(sorted(self.dic.items(), key=lambda item: item[0]))
|
|
128
|
+
# {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
|
anemone/utils/logger.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# logger_module.py
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from logging import DEBUG, ERROR, WARNING, Logger, getLogger
|
|
5
|
+
from typing import Generator
|
|
6
|
+
|
|
7
|
+
from rich.logging import RichHandler
|
|
8
|
+
|
|
9
|
+
anemone_logger = getLogger("anemone_app")
|
|
10
|
+
anemone_logger.setLevel(DEBUG)
|
|
11
|
+
|
|
12
|
+
if not anemone_logger.handlers:
|
|
13
|
+
console_handler = RichHandler()
|
|
14
|
+
console_handler.setLevel(DEBUG)
|
|
15
|
+
anemone_logger.addHandler(console_handler)
|
|
16
|
+
anemone_logger.propagate = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def set_chipiron_logger_level(level: int) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Set the logging level for the chipiron logger and all its handlers.
|
|
22
|
+
|
|
23
|
+
This ensures that both the logger and its handlers are set to the same level,
|
|
24
|
+
so log messages at the specified level will actually be displayed.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
level (int): The logging level to set (e.g., logging.DEBUG, logging.INFO,
|
|
28
|
+
logging.WARNING, logging.ERROR, logging.CRITICAL)
|
|
29
|
+
"""
|
|
30
|
+
anemone_logger.setLevel(level)
|
|
31
|
+
for handler in anemone_logger.handlers:
|
|
32
|
+
handler.setLevel(level)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@contextmanager
|
|
36
|
+
def suppress_logging(
|
|
37
|
+
logger: Logger, level: int = WARNING
|
|
38
|
+
) -> Generator[None, None, None]:
|
|
39
|
+
"""
|
|
40
|
+
Context manager to temporarily suppress logging for a specific logger to a given level.
|
|
41
|
+
|
|
42
|
+
Sets the logger's level to the specified value for the duration of the context, then restores
|
|
43
|
+
its original level afterwards. Useful for silencing output from a particular logger during
|
|
44
|
+
benchmarking or other operations.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
logger (logging.Logger): The logger to suppress.
|
|
48
|
+
level (int): The logging level to set (e.g., logging.ERROR, logging.WARNING).
|
|
49
|
+
|
|
50
|
+
Yields:
|
|
51
|
+
None
|
|
52
|
+
"""
|
|
53
|
+
previous_level = logger.level
|
|
54
|
+
logger.setLevel(level)
|
|
55
|
+
try:
|
|
56
|
+
yield
|
|
57
|
+
finally:
|
|
58
|
+
logger.setLevel(previous_level)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Suppress all logging from all loggers (global)
|
|
62
|
+
@contextmanager
|
|
63
|
+
def suppress_all_logging(level: int = ERROR) -> Generator[None, None, None]:
|
|
64
|
+
"""
|
|
65
|
+
Context manager to temporarily suppress logging from all loggers to a specified level.
|
|
66
|
+
|
|
67
|
+
This sets the level of all loggers (including the root logger) to the given level for the duration
|
|
68
|
+
of the context, then restores their original levels afterwards. Useful for benchmarking or
|
|
69
|
+
situations where you want to silence all logging output temporarily.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
level (int): The logging level to set (e.g., logging.ERROR, logging.WARNING).
|
|
73
|
+
|
|
74
|
+
Yields:
|
|
75
|
+
None
|
|
76
|
+
"""
|
|
77
|
+
logger_dict = getLogger().manager.loggerDict
|
|
78
|
+
original_levels: dict[str, int] = {}
|
|
79
|
+
|
|
80
|
+
for name in logger_dict:
|
|
81
|
+
logger = getLogger(name)
|
|
82
|
+
original_levels[name] = logger.level
|
|
83
|
+
logger.setLevel(level)
|
|
84
|
+
|
|
85
|
+
root_logger = getLogger()
|
|
86
|
+
original_root_level = root_logger.level
|
|
87
|
+
root_logger.setLevel(level)
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
yield
|
|
91
|
+
finally:
|
|
92
|
+
for name, original_level in original_levels.items():
|
|
93
|
+
getLogger(name).setLevel(original_level)
|
|
94
|
+
root_logger.setLevel(original_root_level)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for sorting a dictionary by ascending order
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _Sortable(Protocol):
|
|
9
|
+
def __lt__(self, other: Any, /) -> bool:
|
|
10
|
+
"""Return True if this object is less than the other."""
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def sort_dic[K, V: _Sortable](dic: dict[K, V]) -> dict[K, V]:
|
|
15
|
+
"""
|
|
16
|
+
Sorts a dictionary by ascending order of values.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
dic (dict[K, V]): The dictionary to be sorted.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
dict[K, V]: The sorted dictionary.
|
|
23
|
+
"""
|
|
24
|
+
z = dic.items()
|
|
25
|
+
a = sorted(z, key=lambda item: item[1])
|
|
26
|
+
sorted_dic = dict(a)
|
|
27
|
+
return sorted_dic
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from itertools import islice
|
|
3
|
+
from math import exp
|
|
4
|
+
from os import PathLike
|
|
5
|
+
from typing import Annotated, List, Sequence
|
|
6
|
+
|
|
7
|
+
path = Annotated[str | PathLike[str], "path"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def nth_key[_T, _V](dct: dict[_T, _V], n: int) -> _T:
|
|
11
|
+
"""
|
|
12
|
+
Get the nth key from a dictionary.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
dct: The dictionary.
|
|
16
|
+
n: The index of the key to retrieve.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
The nth key from the dictionary.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
it = iter(dct)
|
|
23
|
+
# Consume n elements.
|
|
24
|
+
next(islice(it, n, n), None)
|
|
25
|
+
# Return the value at the current position.
|
|
26
|
+
# This raises StopIteration if n is beyond the limits.
|
|
27
|
+
# Use next(it, None) to suppress that exception.
|
|
28
|
+
return next(it)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Interval:
|
|
33
|
+
"""
|
|
34
|
+
Represents an interval with a minimum and maximum value.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
min_value: float | None = None
|
|
38
|
+
max_value: float | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def intersect_intervals(interval_1: Interval, interval_2: Interval) -> Interval | None:
|
|
42
|
+
"""
|
|
43
|
+
Find the intersection of two intervals.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
interval_1: The first interval.
|
|
47
|
+
interval_2: The second interval.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
The intersection of the two intervals, or None if there is no intersection.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
AssertionError: If any of the intervals have missing values.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
assert interval_1.max_value is not None and interval_1.min_value is not None
|
|
57
|
+
assert interval_2.max_value is not None and interval_2.min_value is not None
|
|
58
|
+
min_value: float = max(interval_1.min_value, interval_2.min_value)
|
|
59
|
+
max_value: float = min(interval_1.max_value, interval_2.max_value)
|
|
60
|
+
if max_value < min_value:
|
|
61
|
+
return None
|
|
62
|
+
else:
|
|
63
|
+
interval_res = Interval(max_value=max_value, min_value=min_value)
|
|
64
|
+
return interval_res
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def distance_number_to_interval(value: float, interval: Interval) -> float:
|
|
68
|
+
"""
|
|
69
|
+
Calculate the distance between a number and an interval.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
value: The number.
|
|
73
|
+
interval: The interval.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
The distance between the number and the interval.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
AssertionError: If the interval has missing values.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
assert interval.max_value is not None and interval.min_value is not None
|
|
83
|
+
if value < interval.min_value:
|
|
84
|
+
return interval.min_value - value
|
|
85
|
+
elif value > interval.max_value:
|
|
86
|
+
return value - interval.max_value
|
|
87
|
+
else:
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def softmax(x: Sequence[float], temperature: float = 1.0) -> List[float]:
|
|
92
|
+
"""Compute a softmax distribution over input values."""
|
|
93
|
+
if not x:
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
# numerical stability
|
|
97
|
+
m = max(x)
|
|
98
|
+
scaled = [(v - m) * temperature for v in x]
|
|
99
|
+
|
|
100
|
+
exp_vals = [exp(v) for v in scaled]
|
|
101
|
+
s = sum(exp_vals)
|
|
102
|
+
|
|
103
|
+
return [v / s for v in exp_vals]
|