treequest 0.3.2__tar.gz → 0.3.3__tar.gz
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.
- {treequest-0.3.2 → treequest-0.3.3}/PKG-INFO +36 -14
- {treequest-0.3.2 → treequest-0.3.3}/README.md +34 -12
- {treequest-0.3.2 → treequest-0.3.3}/pyproject.toml +9 -3
- treequest-0.3.3/src/treequest/algos/ab_mcts_m/_ab_mcts_m_imports.py +58 -0
- treequest-0.3.2/src/treequest/algos/ab_mcts_m/_ab_mcts_m_imports.py +0 -29
- {treequest-0.3.2 → treequest-0.3.3}/.gitignore +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/LICENSE +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/__init__.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/ab_mcts_a/algo.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/ab_mcts_a/prob_state.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/ab_mcts_m/algo.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/ab_mcts_m/numpyro_utils.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/ab_mcts_m/pymc_interface.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/base.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/best_first_search.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/multi_armed_bandit_ucb.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/standard_mcts.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/tree.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/algos/tree_of_thought_bfs.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/imports.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/py.typed +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/ranker.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/trial.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/types.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/__init__.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/__init__.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/ab_mcts_a.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/ab_mcts_m.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/base.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/best_first_search.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/multi_armed_bandit_ucb.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/standard_mcts.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/tree_of_thoughts_bfs.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/assets/colormaps.json +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/assets/d3.LICENSE.txt +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/assets/d3.v7.min.js +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/assets/d3_tree.html.jinja2 +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/build_snapshot.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/errors.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/render.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/__init__.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/color_utils.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/graphviz_renderer.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/html.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/json_yaml.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/renderers/mermaid.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/snapshot.py +0 -0
- {treequest-0.3.2 → treequest-0.3.3}/src/treequest/visualization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: treequest
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A Tree Search Library with Flexible API for LLM Inference-Time Scaling
|
|
5
5
|
Author-email: Kou Misaki <kou.misaki@sakana.ai>, Takuya Akiba <takiba@sakana.ai>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -57,24 +57,27 @@ import treequest as tq
|
|
|
57
57
|
# Each node is associated with a user-definable `state`.
|
|
58
58
|
State = str
|
|
59
59
|
|
|
60
|
+
|
|
60
61
|
# 1. Define a function to be used for node generation.
|
|
61
62
|
def generate(parent_state: State | None) -> tuple[State, float]:
|
|
62
63
|
"""Generates new states and scores based on the parent state."""
|
|
63
|
-
if parent_state is None:
|
|
64
|
+
if parent_state is None: # None represents the expansion from root.
|
|
64
65
|
new_state = "Initial state"
|
|
65
66
|
else:
|
|
66
67
|
new_state = f"State after {parent_state}"
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
# A score for the new state; It should be normalized to the [0, 1] range.
|
|
70
|
+
score = random.random()
|
|
69
71
|
return new_state, score
|
|
70
72
|
|
|
73
|
+
|
|
71
74
|
# 2. Instantiate the algorithm and a search tree object.
|
|
72
75
|
algo = tq.ABMCTSA()
|
|
73
76
|
search_tree = algo.init_tree()
|
|
74
77
|
|
|
75
78
|
# 3. Run the search with a generation budget (10 in this case).
|
|
76
79
|
for _ in range(10):
|
|
77
|
-
search_tree = algo.step(search_tree, {
|
|
80
|
+
search_tree = algo.step(search_tree, {"Action A": generate})
|
|
78
81
|
|
|
79
82
|
# 4. Extract the best score and state.
|
|
80
83
|
best_state, best_node_score = tq.top_k(search_tree, algo, k=1)[0]
|
|
@@ -82,7 +85,8 @@ print(f"Best state: {best_state}, Score: {best_node_score}")
|
|
|
82
85
|
|
|
83
86
|
# 5. Visualize the search tree.
|
|
84
87
|
output_file_basename = Path("ab_mcts_a_search_tree")
|
|
85
|
-
|
|
88
|
+
# Generates `ab_mcts_a_search_tree.html`
|
|
89
|
+
tq.render(search_tree, output_file_basename, format="html")
|
|
86
90
|
```
|
|
87
91
|
|
|
88
92
|
Alternatively, you can use an ask–tell interface with batched AB-MCTS sampling steps:
|
|
@@ -92,8 +96,9 @@ import treequest as tq
|
|
|
92
96
|
|
|
93
97
|
State = str
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
|
|
100
|
+
def generate(parent_state: State | None) -> tuple[State, float]: ...
|
|
101
|
+
|
|
97
102
|
|
|
98
103
|
generate_fns = {"Action A": generate}
|
|
99
104
|
actions = list(generate_fns.keys())
|
|
@@ -160,11 +165,13 @@ import dataclasses
|
|
|
160
165
|
|
|
161
166
|
import treequest as tq
|
|
162
167
|
|
|
168
|
+
|
|
163
169
|
@dataclasses.dataclass
|
|
164
170
|
class State:
|
|
165
171
|
llm_answer: str
|
|
166
172
|
score: float
|
|
167
173
|
|
|
174
|
+
|
|
168
175
|
def generate(parent_state: State | None) -> tuple[State, float]:
|
|
169
176
|
"""Generate a new node by calling an LLM."""
|
|
170
177
|
if parent_state is None:
|
|
@@ -174,12 +181,14 @@ def generate(parent_state: State | None) -> tuple[State, float]:
|
|
|
174
181
|
|
|
175
182
|
return state, state.score
|
|
176
183
|
|
|
184
|
+
|
|
177
185
|
def initial_generation() -> State:
|
|
178
186
|
"""
|
|
179
187
|
Call LLM API to generate an initial answer.
|
|
180
188
|
"""
|
|
181
189
|
...
|
|
182
190
|
|
|
191
|
+
|
|
183
192
|
def refine_answer(llm_answer: str, score: float) -> State:
|
|
184
193
|
"""
|
|
185
194
|
Call LLM API to refine an answer.
|
|
@@ -190,11 +199,11 @@ def refine_answer(llm_answer: str, score: float) -> State:
|
|
|
190
199
|
algo = tq.ABMCTSM()
|
|
191
200
|
search_tree = algo.init_tree()
|
|
192
201
|
for i in range(20):
|
|
193
|
-
search_tree = algo.step(search_tree, {
|
|
202
|
+
search_tree = algo.step(search_tree, {"Action Label": generate})
|
|
194
203
|
# Logging best node during the search.
|
|
195
204
|
if (i + 1) % 5 == 0:
|
|
196
205
|
best_interim_state, _ = tq.top_k(search_tree, algo, k=1)[0]
|
|
197
|
-
print(f"Iteration {i+1}: Best state so far = {best_interim_state}")
|
|
206
|
+
print(f"Iteration {i + 1}: Best state so far = {best_interim_state}")
|
|
198
207
|
|
|
199
208
|
best_state, _ = tq.top_k(search_tree, algo, k=1)[0]
|
|
200
209
|
print(f"Best Answer: {best_state.llm_answer}, Best Score: {best_state.score}")
|
|
@@ -209,6 +218,7 @@ from functools import partial
|
|
|
209
218
|
|
|
210
219
|
import treequest as tq
|
|
211
220
|
|
|
221
|
+
|
|
212
222
|
def generate(llm_name: str, parent_state=None):
|
|
213
223
|
"""
|
|
214
224
|
Call LLM API using litellm, vllm, etc., to generate a new node
|
|
@@ -216,9 +226,12 @@ def generate(llm_name: str, parent_state=None):
|
|
|
216
226
|
...
|
|
217
227
|
return new_state, new_score
|
|
218
228
|
|
|
229
|
+
|
|
219
230
|
llm_names = ["o4-mini", "gemini-2.5-pro"]
|
|
220
231
|
# Create dict of different actions backed by different LLMs.
|
|
221
|
-
generate_fns = {
|
|
232
|
+
generate_fns = {
|
|
233
|
+
llm_name: partial(generate, llm_name=llm_name) for llm_name in llm_names
|
|
234
|
+
}
|
|
222
235
|
|
|
223
236
|
algo = tq.StandardMCTS()
|
|
224
237
|
search_tree = algo.init_tree()
|
|
@@ -286,10 +299,12 @@ from pathlib import Path
|
|
|
286
299
|
import treequest as tq
|
|
287
300
|
from PIL import Image
|
|
288
301
|
|
|
302
|
+
|
|
289
303
|
class State:
|
|
290
304
|
text: str
|
|
291
305
|
image: Image.Image
|
|
292
306
|
|
|
307
|
+
|
|
293
308
|
def state_formatter_html(state: State) -> str:
|
|
294
309
|
"""Formats the state for HTML visualization."""
|
|
295
310
|
buffer = BytesIO()
|
|
@@ -299,13 +314,15 @@ def state_formatter_html(state: State) -> str:
|
|
|
299
314
|
# In case state.text cannot be trusted, we recommend e.g., html.escape(state.text) to avoid html injection.
|
|
300
315
|
return f'<p>{state.text}</p><br/><img src="data:image/webp;base64,{img_str}" width=100% />'
|
|
301
316
|
|
|
317
|
+
|
|
302
318
|
generate_fns = {
|
|
303
319
|
"action_1": ... # Define your node generation functions (actions) here
|
|
304
320
|
}
|
|
305
321
|
|
|
306
322
|
algo = tq.ABMCTSA()
|
|
307
323
|
search_tree = algo.init_tree()
|
|
308
|
-
output_dir = Path("search_tree")
|
|
324
|
+
output_dir = Path("search_tree")
|
|
325
|
+
output_dir.mkdir(exist_ok=True)
|
|
309
326
|
for i in range(10):
|
|
310
327
|
search_tree = algo.step(search_tree, generate_fns)
|
|
311
328
|
# Rendering "latest.html" for checking search progress on the fly
|
|
@@ -314,11 +331,16 @@ for i in range(10):
|
|
|
314
331
|
search_tree,
|
|
315
332
|
output_basename=output_dir / "latest",
|
|
316
333
|
format="html",
|
|
317
|
-
state_formatter=state_formatter_html,
|
|
334
|
+
state_formatter=state_formatter_html, # Use html custom formetter
|
|
318
335
|
)
|
|
319
336
|
|
|
320
337
|
tq.render(search_tree, output_basename=output_dir / "final", format="pdf")
|
|
321
|
-
tq.render(
|
|
338
|
+
tq.render(
|
|
339
|
+
search_tree,
|
|
340
|
+
output_basename=output_dir / "final",
|
|
341
|
+
format="html",
|
|
342
|
+
state_formatter=state_formatter_html,
|
|
343
|
+
)
|
|
322
344
|
```
|
|
323
345
|
|
|
324
346
|
> **IMPORTANT:** When using HTML format, ensure that the HTML file is securely handled, especially if the state formatter includes raw HTML content. Avoid opening untrusted HTML files in your browser. For example, XSS (cross site scripting) attacks can occur if the state includes malicious HTML/JavaScript code.
|
|
@@ -21,24 +21,27 @@ import treequest as tq
|
|
|
21
21
|
# Each node is associated with a user-definable `state`.
|
|
22
22
|
State = str
|
|
23
23
|
|
|
24
|
+
|
|
24
25
|
# 1. Define a function to be used for node generation.
|
|
25
26
|
def generate(parent_state: State | None) -> tuple[State, float]:
|
|
26
27
|
"""Generates new states and scores based on the parent state."""
|
|
27
|
-
if parent_state is None:
|
|
28
|
+
if parent_state is None: # None represents the expansion from root.
|
|
28
29
|
new_state = "Initial state"
|
|
29
30
|
else:
|
|
30
31
|
new_state = f"State after {parent_state}"
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
# A score for the new state; It should be normalized to the [0, 1] range.
|
|
34
|
+
score = random.random()
|
|
33
35
|
return new_state, score
|
|
34
36
|
|
|
37
|
+
|
|
35
38
|
# 2. Instantiate the algorithm and a search tree object.
|
|
36
39
|
algo = tq.ABMCTSA()
|
|
37
40
|
search_tree = algo.init_tree()
|
|
38
41
|
|
|
39
42
|
# 3. Run the search with a generation budget (10 in this case).
|
|
40
43
|
for _ in range(10):
|
|
41
|
-
search_tree = algo.step(search_tree, {
|
|
44
|
+
search_tree = algo.step(search_tree, {"Action A": generate})
|
|
42
45
|
|
|
43
46
|
# 4. Extract the best score and state.
|
|
44
47
|
best_state, best_node_score = tq.top_k(search_tree, algo, k=1)[0]
|
|
@@ -46,7 +49,8 @@ print(f"Best state: {best_state}, Score: {best_node_score}")
|
|
|
46
49
|
|
|
47
50
|
# 5. Visualize the search tree.
|
|
48
51
|
output_file_basename = Path("ab_mcts_a_search_tree")
|
|
49
|
-
|
|
52
|
+
# Generates `ab_mcts_a_search_tree.html`
|
|
53
|
+
tq.render(search_tree, output_file_basename, format="html")
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
Alternatively, you can use an ask–tell interface with batched AB-MCTS sampling steps:
|
|
@@ -56,8 +60,9 @@ import treequest as tq
|
|
|
56
60
|
|
|
57
61
|
State = str
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
|
|
64
|
+
def generate(parent_state: State | None) -> tuple[State, float]: ...
|
|
65
|
+
|
|
61
66
|
|
|
62
67
|
generate_fns = {"Action A": generate}
|
|
63
68
|
actions = list(generate_fns.keys())
|
|
@@ -124,11 +129,13 @@ import dataclasses
|
|
|
124
129
|
|
|
125
130
|
import treequest as tq
|
|
126
131
|
|
|
132
|
+
|
|
127
133
|
@dataclasses.dataclass
|
|
128
134
|
class State:
|
|
129
135
|
llm_answer: str
|
|
130
136
|
score: float
|
|
131
137
|
|
|
138
|
+
|
|
132
139
|
def generate(parent_state: State | None) -> tuple[State, float]:
|
|
133
140
|
"""Generate a new node by calling an LLM."""
|
|
134
141
|
if parent_state is None:
|
|
@@ -138,12 +145,14 @@ def generate(parent_state: State | None) -> tuple[State, float]:
|
|
|
138
145
|
|
|
139
146
|
return state, state.score
|
|
140
147
|
|
|
148
|
+
|
|
141
149
|
def initial_generation() -> State:
|
|
142
150
|
"""
|
|
143
151
|
Call LLM API to generate an initial answer.
|
|
144
152
|
"""
|
|
145
153
|
...
|
|
146
154
|
|
|
155
|
+
|
|
147
156
|
def refine_answer(llm_answer: str, score: float) -> State:
|
|
148
157
|
"""
|
|
149
158
|
Call LLM API to refine an answer.
|
|
@@ -154,11 +163,11 @@ def refine_answer(llm_answer: str, score: float) -> State:
|
|
|
154
163
|
algo = tq.ABMCTSM()
|
|
155
164
|
search_tree = algo.init_tree()
|
|
156
165
|
for i in range(20):
|
|
157
|
-
search_tree = algo.step(search_tree, {
|
|
166
|
+
search_tree = algo.step(search_tree, {"Action Label": generate})
|
|
158
167
|
# Logging best node during the search.
|
|
159
168
|
if (i + 1) % 5 == 0:
|
|
160
169
|
best_interim_state, _ = tq.top_k(search_tree, algo, k=1)[0]
|
|
161
|
-
print(f"Iteration {i+1}: Best state so far = {best_interim_state}")
|
|
170
|
+
print(f"Iteration {i + 1}: Best state so far = {best_interim_state}")
|
|
162
171
|
|
|
163
172
|
best_state, _ = tq.top_k(search_tree, algo, k=1)[0]
|
|
164
173
|
print(f"Best Answer: {best_state.llm_answer}, Best Score: {best_state.score}")
|
|
@@ -173,6 +182,7 @@ from functools import partial
|
|
|
173
182
|
|
|
174
183
|
import treequest as tq
|
|
175
184
|
|
|
185
|
+
|
|
176
186
|
def generate(llm_name: str, parent_state=None):
|
|
177
187
|
"""
|
|
178
188
|
Call LLM API using litellm, vllm, etc., to generate a new node
|
|
@@ -180,9 +190,12 @@ def generate(llm_name: str, parent_state=None):
|
|
|
180
190
|
...
|
|
181
191
|
return new_state, new_score
|
|
182
192
|
|
|
193
|
+
|
|
183
194
|
llm_names = ["o4-mini", "gemini-2.5-pro"]
|
|
184
195
|
# Create dict of different actions backed by different LLMs.
|
|
185
|
-
generate_fns = {
|
|
196
|
+
generate_fns = {
|
|
197
|
+
llm_name: partial(generate, llm_name=llm_name) for llm_name in llm_names
|
|
198
|
+
}
|
|
186
199
|
|
|
187
200
|
algo = tq.StandardMCTS()
|
|
188
201
|
search_tree = algo.init_tree()
|
|
@@ -250,10 +263,12 @@ from pathlib import Path
|
|
|
250
263
|
import treequest as tq
|
|
251
264
|
from PIL import Image
|
|
252
265
|
|
|
266
|
+
|
|
253
267
|
class State:
|
|
254
268
|
text: str
|
|
255
269
|
image: Image.Image
|
|
256
270
|
|
|
271
|
+
|
|
257
272
|
def state_formatter_html(state: State) -> str:
|
|
258
273
|
"""Formats the state for HTML visualization."""
|
|
259
274
|
buffer = BytesIO()
|
|
@@ -263,13 +278,15 @@ def state_formatter_html(state: State) -> str:
|
|
|
263
278
|
# In case state.text cannot be trusted, we recommend e.g., html.escape(state.text) to avoid html injection.
|
|
264
279
|
return f'<p>{state.text}</p><br/><img src="data:image/webp;base64,{img_str}" width=100% />'
|
|
265
280
|
|
|
281
|
+
|
|
266
282
|
generate_fns = {
|
|
267
283
|
"action_1": ... # Define your node generation functions (actions) here
|
|
268
284
|
}
|
|
269
285
|
|
|
270
286
|
algo = tq.ABMCTSA()
|
|
271
287
|
search_tree = algo.init_tree()
|
|
272
|
-
output_dir = Path("search_tree")
|
|
288
|
+
output_dir = Path("search_tree")
|
|
289
|
+
output_dir.mkdir(exist_ok=True)
|
|
273
290
|
for i in range(10):
|
|
274
291
|
search_tree = algo.step(search_tree, generate_fns)
|
|
275
292
|
# Rendering "latest.html" for checking search progress on the fly
|
|
@@ -278,11 +295,16 @@ for i in range(10):
|
|
|
278
295
|
search_tree,
|
|
279
296
|
output_basename=output_dir / "latest",
|
|
280
297
|
format="html",
|
|
281
|
-
state_formatter=state_formatter_html,
|
|
298
|
+
state_formatter=state_formatter_html, # Use html custom formetter
|
|
282
299
|
)
|
|
283
300
|
|
|
284
301
|
tq.render(search_tree, output_basename=output_dir / "final", format="pdf")
|
|
285
|
-
tq.render(
|
|
302
|
+
tq.render(
|
|
303
|
+
search_tree,
|
|
304
|
+
output_basename=output_dir / "final",
|
|
305
|
+
format="html",
|
|
306
|
+
state_formatter=state_formatter_html,
|
|
307
|
+
)
|
|
286
308
|
```
|
|
287
309
|
|
|
288
310
|
> **IMPORTANT:** When using HTML format, ensure that the HTML file is securely handled, especially if the state formatter includes raw HTML content. Avoid opening untrusted HTML files in your browser. For example, XSS (cross site scripting) attacks can occur if the state includes malicious HTML/JavaScript code.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "treequest"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "A Tree Search Library with Flexible API for LLM Inference-Time Scaling"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -44,16 +44,22 @@ build-backend = "hatchling.build"
|
|
|
44
44
|
[dependency-groups]
|
|
45
45
|
dev = [
|
|
46
46
|
"line-profiler>=4.2.0",
|
|
47
|
-
|
|
47
|
+
# Pin ruff and mypy so that their new releases do not change CI results.
|
|
48
|
+
# Keep ruff in sync with .pre-commit-config.yaml.
|
|
49
|
+
"mypy==2.4.0",
|
|
48
50
|
"pre-commit>=4.2.0",
|
|
49
51
|
"pytest>=8.3.4",
|
|
50
52
|
"pytest-xdist>=3.6.1",
|
|
51
|
-
"ruff
|
|
53
|
+
"ruff==0.17.1",
|
|
52
54
|
"tqdm>=4.64.0",
|
|
53
55
|
"types-tqdm>=4.64.0",
|
|
54
56
|
"types-PyYAML>=6.0.0",
|
|
55
57
|
]
|
|
56
58
|
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
# The rules Ruff enabled by default before 0.16, which expanded its default rule set.
|
|
61
|
+
select = ["E4", "E7", "E9", "F"]
|
|
62
|
+
|
|
57
63
|
[tool.hatch.build.targets.wheel]
|
|
58
64
|
packages = ["src/treequest"]
|
|
59
65
|
include = [
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from treequest.imports import try_import
|
|
5
|
+
|
|
6
|
+
_ARVIZ_IMPORT_ATTEMPTS = 3
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _import_arviz() -> None:
|
|
10
|
+
# TODO: Remove this workaround after arviz fixes the race in its daily warning
|
|
11
|
+
# https://github.com/arviz-devs/arviz/issues/2530
|
|
12
|
+
# arviz 0.23.3 and 0.23.4 update the warning's stamp file through a fixed
|
|
13
|
+
# temporary file, so importing arviz from several processes at once can raise
|
|
14
|
+
# FileNotFoundError. The process that won has already updated the stamp, so
|
|
15
|
+
# importing again normally succeeds.
|
|
16
|
+
for attempt in range(1, _ARVIZ_IMPORT_ATTEMPTS + 1):
|
|
17
|
+
try:
|
|
18
|
+
importlib.import_module("arviz")
|
|
19
|
+
return
|
|
20
|
+
except FileNotFoundError as e:
|
|
21
|
+
# The process that lost the race fails to rename the temporary file.
|
|
22
|
+
lost_race = Path(e.filename or "").name == "daily_warning.tmp"
|
|
23
|
+
if not lost_race or attempt == _ARVIZ_IMPORT_ATTEMPTS:
|
|
24
|
+
raise
|
|
25
|
+
# Leave sys.modules as it is: Python has already dropped the failed
|
|
26
|
+
# package, the leftover arviz.preview is safe to reuse, and another
|
|
27
|
+
# thread may be importing arviz by now.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
with try_import() as _import:
|
|
31
|
+
import jax
|
|
32
|
+
from packaging.version import Version
|
|
33
|
+
|
|
34
|
+
# TODO: Remove this hotfix after numpyro fixes incompatibility with jax>=0.7
|
|
35
|
+
# https://github.com/pyro-ppl/numpyro/issues/2051
|
|
36
|
+
if Version(jax.__version__) >= Version("0.7.0"):
|
|
37
|
+
import jax.experimental.pjit as _pjit
|
|
38
|
+
from jax.extend.core.primitives import jit_p
|
|
39
|
+
|
|
40
|
+
_pjit.pjit_p = jit_p # type: ignore
|
|
41
|
+
import numpy as np
|
|
42
|
+
import numpyro # type: ignore
|
|
43
|
+
import pandas as pd # type: ignore
|
|
44
|
+
|
|
45
|
+
_import_arviz() # Import arviz before pymc does; see _import_arviz.
|
|
46
|
+
import pymc as pm # type: ignore
|
|
47
|
+
from pymc.sampling.jax import sample_numpyro_nuts # type: ignore
|
|
48
|
+
from xarray import DataArray
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"jax",
|
|
52
|
+
"np",
|
|
53
|
+
"numpyro",
|
|
54
|
+
"pd",
|
|
55
|
+
"pm",
|
|
56
|
+
"sample_numpyro_nuts",
|
|
57
|
+
"DataArray",
|
|
58
|
+
]
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from treequest.imports import try_import
|
|
2
|
-
|
|
3
|
-
with try_import() as _import:
|
|
4
|
-
import jax
|
|
5
|
-
from packaging.version import Version
|
|
6
|
-
|
|
7
|
-
# TODO: Remove this hotfix after numpyro fixes incompatibility with jax>=0.7
|
|
8
|
-
# https://github.com/pyro-ppl/numpyro/issues/2051
|
|
9
|
-
if Version(jax.__version__) >= Version("0.7.0"):
|
|
10
|
-
import jax.experimental.pjit as _pjit
|
|
11
|
-
from jax.extend.core.primitives import jit_p
|
|
12
|
-
|
|
13
|
-
_pjit.pjit_p = jit_p # type: ignore
|
|
14
|
-
import numpy as np
|
|
15
|
-
import numpyro # type: ignore
|
|
16
|
-
import pandas as pd # type: ignore
|
|
17
|
-
import pymc as pm # type: ignore
|
|
18
|
-
from pymc.sampling.jax import sample_numpyro_nuts # type: ignore
|
|
19
|
-
from xarray import DataArray
|
|
20
|
-
|
|
21
|
-
__all__ = [
|
|
22
|
-
"jax",
|
|
23
|
-
"np",
|
|
24
|
-
"numpyro",
|
|
25
|
-
"pd",
|
|
26
|
-
"pm",
|
|
27
|
-
"sample_numpyro_nuts",
|
|
28
|
-
"DataArray",
|
|
29
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{treequest-0.3.2 → treequest-0.3.3}/src/treequest/vis/algo_adapters/multi_armed_bandit_ucb.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|