hydraflow 0.11.1__py3-none-any.whl → 0.12.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.
- hydraflow/core/main.py +2 -1
- hydraflow/executor/conf.py +2 -2
- hydraflow/executor/io.py +21 -2
- hydraflow/executor/job.py +2 -2
- hydraflow/executor/parser.py +104 -44
- {hydraflow-0.11.1.dist-info → hydraflow-0.12.1.dist-info}/METADATA +1 -1
- {hydraflow-0.11.1.dist-info → hydraflow-0.12.1.dist-info}/RECORD +10 -10
- {hydraflow-0.11.1.dist-info → hydraflow-0.12.1.dist-info}/WHEEL +0 -0
- {hydraflow-0.11.1.dist-info → hydraflow-0.12.1.dist-info}/entry_points.txt +0 -0
- {hydraflow-0.11.1.dist-info → hydraflow-0.12.1.dist-info}/licenses/LICENSE +0 -0
hydraflow/core/main.py
CHANGED
@@ -16,6 +16,7 @@ used to wrap experiment entry points. This decorator handles:
|
|
16
16
|
|
17
17
|
Example:
|
18
18
|
```python
|
19
|
+
import hydraflow
|
19
20
|
from dataclasses import dataclass
|
20
21
|
from mlflow.entities import Run
|
21
22
|
|
@@ -24,7 +25,7 @@ Example:
|
|
24
25
|
learning_rate: float
|
25
26
|
batch_size: int
|
26
27
|
|
27
|
-
@main(Config)
|
28
|
+
@hydraflow.main(Config)
|
28
29
|
def train(run: Run, config: Config):
|
29
30
|
# Your training code here
|
30
31
|
pass
|
hydraflow/executor/conf.py
CHANGED
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field
|
|
7
7
|
class Step:
|
8
8
|
batch: str = ""
|
9
9
|
args: str = ""
|
10
|
-
|
10
|
+
with_: str = ""
|
11
11
|
|
12
12
|
|
13
13
|
@dataclass
|
@@ -15,7 +15,7 @@ class Job:
|
|
15
15
|
name: str = ""
|
16
16
|
run: str = ""
|
17
17
|
call: str = ""
|
18
|
-
|
18
|
+
with_: str = ""
|
19
19
|
steps: list[Step] = field(default_factory=list)
|
20
20
|
|
21
21
|
|
hydraflow/executor/io.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
from pathlib import Path
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from omegaconf import OmegaConf
|
8
|
+
from omegaconf import DictConfig, ListConfig, OmegaConf
|
9
9
|
|
10
10
|
from .conf import HydraflowConf
|
11
11
|
|
@@ -35,7 +35,26 @@ def load_config() -> HydraflowConf:
|
|
35
35
|
|
36
36
|
cfg = OmegaConf.load(path)
|
37
37
|
|
38
|
-
|
38
|
+
if not isinstance(cfg, DictConfig):
|
39
|
+
return schema
|
40
|
+
|
41
|
+
rename_with(cfg)
|
42
|
+
|
43
|
+
return OmegaConf.merge(schema, cfg) # type: ignore[return-value]
|
44
|
+
|
45
|
+
|
46
|
+
def rename_with(cfg: DictConfig) -> None:
|
47
|
+
"""Rename the `with` field to `with_`."""
|
48
|
+
if "with" in cfg:
|
49
|
+
cfg["with_"] = cfg.pop("with")
|
50
|
+
|
51
|
+
for key in list(cfg.keys()):
|
52
|
+
if isinstance(cfg[key], DictConfig):
|
53
|
+
rename_with(cfg[key])
|
54
|
+
elif isinstance(cfg[key], ListConfig):
|
55
|
+
for item in cfg[key]:
|
56
|
+
if isinstance(item, DictConfig):
|
57
|
+
rename_with(item)
|
39
58
|
|
40
59
|
|
41
60
|
def get_job(name: str) -> Job:
|
hydraflow/executor/job.py
CHANGED
@@ -69,10 +69,10 @@ def iter_batches(job: Job) -> Iterator[list[str]]:
|
|
69
69
|
|
70
70
|
"""
|
71
71
|
job_name = f"hydra.job.name={job.name}"
|
72
|
-
job_configs = shlex.split(job.
|
72
|
+
job_configs = shlex.split(job.with_)
|
73
73
|
|
74
74
|
for step in job.steps:
|
75
|
-
configs = shlex.split(step.
|
75
|
+
configs = shlex.split(step.with_) or job_configs
|
76
76
|
|
77
77
|
for args in iter_args(step.batch, step.args):
|
78
78
|
sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ULID()}"
|
hydraflow/executor/parser.py
CHANGED
@@ -83,7 +83,7 @@ def count_decimal_places(x: str) -> int:
|
|
83
83
|
|
84
84
|
|
85
85
|
def is_number(x: str) -> bool:
|
86
|
-
"""Check if a string
|
86
|
+
"""Check if a string represents a valid number.
|
87
87
|
|
88
88
|
Args:
|
89
89
|
x (str): The string to check.
|
@@ -140,18 +140,54 @@ def _get_range(arg: str) -> tuple[float, float, float]:
|
|
140
140
|
|
141
141
|
|
142
142
|
def _arange(start: float, step: float, stop: float) -> list[float]:
|
143
|
+
"""Generate a range of floating point numbers.
|
144
|
+
|
145
|
+
This function generates a range of floating point numbers
|
146
|
+
with protection against rounding errors.
|
147
|
+
|
148
|
+
Args:
|
149
|
+
start (float): The starting value.
|
150
|
+
step (float): The step size.
|
151
|
+
stop (float): The end value (inclusive).
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
list[float]: A list of floating point numbers from start to stop
|
155
|
+
(inclusive) with the given step.
|
156
|
+
|
157
|
+
"""
|
158
|
+
if step == 0:
|
159
|
+
raise ValueError("Step cannot be zero")
|
160
|
+
|
161
|
+
epsilon = 1e-10
|
162
|
+
|
163
|
+
decimal_places = max(
|
164
|
+
count_decimal_places(str(start)),
|
165
|
+
count_decimal_places(str(step)),
|
166
|
+
count_decimal_places(str(stop)),
|
167
|
+
)
|
168
|
+
|
143
169
|
result = []
|
144
170
|
current = start
|
145
171
|
|
146
|
-
|
147
|
-
|
148
|
-
|
172
|
+
if step > 0:
|
173
|
+
while current <= stop + epsilon:
|
174
|
+
rounded = round(current, decimal_places)
|
175
|
+
result.append(rounded)
|
176
|
+
current += step
|
177
|
+
else:
|
178
|
+
while current >= stop - epsilon:
|
179
|
+
rounded = round(current, decimal_places)
|
180
|
+
result.append(rounded)
|
181
|
+
current += step
|
149
182
|
|
150
183
|
return result
|
151
184
|
|
152
185
|
|
153
186
|
def split_suffix(arg: str) -> tuple[str, str]:
|
154
|
-
"""Split a string into prefix and suffix.
|
187
|
+
"""Split a string into the prefix and suffix.
|
188
|
+
|
189
|
+
The suffix is the part of the string that starts with a colon (:).
|
190
|
+
The prefix is the part of the string that precedes the suffix.
|
155
191
|
|
156
192
|
Args:
|
157
193
|
arg (str): The string to split.
|
@@ -194,6 +230,16 @@ def add_exponent(value: str, exponent: str) -> str:
|
|
194
230
|
Returns:
|
195
231
|
str: The value with the exponent added.
|
196
232
|
|
233
|
+
Examples:
|
234
|
+
>>> add_exponent("1", "e3")
|
235
|
+
'1e3'
|
236
|
+
>>> add_exponent("1", "")
|
237
|
+
'1'
|
238
|
+
>>> add_exponent("0", "e-3")
|
239
|
+
'0'
|
240
|
+
>>> add_exponent("0.0", "e-3")
|
241
|
+
'0.0'
|
242
|
+
|
197
243
|
"""
|
198
244
|
if value in ["0", "0.", "0.0"] or not exponent:
|
199
245
|
return value
|
@@ -201,43 +247,6 @@ def add_exponent(value: str, exponent: str) -> str:
|
|
201
247
|
return f"{value}{exponent}"
|
202
248
|
|
203
249
|
|
204
|
-
def collect_values(arg: str) -> list[str]:
|
205
|
-
"""Collect a list of values from a range argument.
|
206
|
-
|
207
|
-
Collect all individual values within a numeric range
|
208
|
-
represented by a string (e.g., `1:4`) and return them
|
209
|
-
as a list of strings.
|
210
|
-
Support both integer and floating-point ranges.
|
211
|
-
|
212
|
-
Args:
|
213
|
-
arg (str): The argument to collect.
|
214
|
-
|
215
|
-
Returns:
|
216
|
-
list[str]: A list of the collected values.
|
217
|
-
|
218
|
-
"""
|
219
|
-
if "(" in arg:
|
220
|
-
return collect_parentheses(arg)
|
221
|
-
|
222
|
-
if ":" not in arg:
|
223
|
-
return [arg]
|
224
|
-
|
225
|
-
arg, exponent = split_suffix(arg)
|
226
|
-
|
227
|
-
if ":" not in arg:
|
228
|
-
return [f"{arg}{exponent}"]
|
229
|
-
|
230
|
-
rng = _get_range(arg)
|
231
|
-
|
232
|
-
if all(isinstance(x, int) for x in rng):
|
233
|
-
values = [str(x) for x in _arange(*rng)]
|
234
|
-
else:
|
235
|
-
n = max(*(count_decimal_places(x) for x in arg.split(":")))
|
236
|
-
values = [str(round(x, n)) for x in _arange(*rng)]
|
237
|
-
|
238
|
-
return [add_exponent(x, exponent) for x in values]
|
239
|
-
|
240
|
-
|
241
250
|
def split_parentheses(arg: str) -> Iterator[str]:
|
242
251
|
"""Split a string with parentheses into a list of strings.
|
243
252
|
|
@@ -290,8 +299,59 @@ def collect_parentheses(arg: str) -> list[str]:
|
|
290
299
|
return ["".join(x[::-1]) for x in product(*it[::-1])]
|
291
300
|
|
292
301
|
|
302
|
+
def collect_values(arg: str) -> list[str]:
|
303
|
+
"""Collect a list of values from a range argument.
|
304
|
+
|
305
|
+
Collect all individual values within a numeric range
|
306
|
+
represented by a string (e.g., `1:4`) and return them
|
307
|
+
as a list of strings.
|
308
|
+
Support both integer and floating-point ranges.
|
309
|
+
|
310
|
+
Args:
|
311
|
+
arg (str): The argument to collect.
|
312
|
+
|
313
|
+
Returns:
|
314
|
+
list[str]: A list of the collected values.
|
315
|
+
|
316
|
+
Examples:
|
317
|
+
>>> collect_values("1:4")
|
318
|
+
['1', '2', '3', '4']
|
319
|
+
>>> collect_values("1.2:0.1:1.4:k")
|
320
|
+
['1.2e3', '1.3e3', '1.4e3']
|
321
|
+
>>> collect_values("0.1")
|
322
|
+
['0.1']
|
323
|
+
>>> collect_values("4:M")
|
324
|
+
['4e6']
|
325
|
+
>>> collect_values("(1:3,5:7)M")
|
326
|
+
['1e6', '2e6', '3e6', '5e6', '6e6', '7e6']
|
327
|
+
>>> collect_values("(1,5)e-(1:3)")
|
328
|
+
['1e-1', '5e-1', '1e-2', '5e-2', '1e-3', '5e-3']
|
329
|
+
|
330
|
+
"""
|
331
|
+
if "(" in arg:
|
332
|
+
return collect_parentheses(arg)
|
333
|
+
|
334
|
+
if ":" not in arg:
|
335
|
+
return [arg]
|
336
|
+
|
337
|
+
arg, exponent = split_suffix(arg)
|
338
|
+
|
339
|
+
if ":" not in arg:
|
340
|
+
return [f"{arg}{exponent}"]
|
341
|
+
|
342
|
+
rng = _get_range(arg)
|
343
|
+
|
344
|
+
if all(isinstance(x, int) for x in rng):
|
345
|
+
values = [str(x) for x in _arange(*rng)]
|
346
|
+
else:
|
347
|
+
n = max(*(count_decimal_places(x) for x in arg.split(":")))
|
348
|
+
values = [str(round(x, n)) for x in _arange(*rng)]
|
349
|
+
|
350
|
+
return [add_exponent(x, exponent) for x in values]
|
351
|
+
|
352
|
+
|
293
353
|
def split(arg: str) -> list[str]:
|
294
|
-
|
354
|
+
"""Split a string by top-level commas.
|
295
355
|
|
296
356
|
Splits a string by commas while respecting nested structures.
|
297
357
|
Commas inside brackets and quotes are ignored, only splitting
|
@@ -389,7 +449,7 @@ def split_arg(arg: str) -> tuple[str, str, str]:
|
|
389
449
|
key, value = arg.split("=")
|
390
450
|
|
391
451
|
if "/" in key:
|
392
|
-
key, suffix = key.split("/"
|
452
|
+
key, suffix = key.split("/")
|
393
453
|
return key, suffix, value
|
394
454
|
|
395
455
|
return key, "", value
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hydraflow
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.1
|
4
4
|
Summary: Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments.
|
5
5
|
Project-URL: Documentation, https://daizutabi.github.io/hydraflow/
|
6
6
|
Project-URL: Source, https://github.com/daizutabi/hydraflow
|
@@ -5,7 +5,7 @@ hydraflow/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
hydraflow/core/config.py,sha256=SJzjgsO_kzB78_whJ3lmy7GlZvTvwZONH1BJBn8zCuI,3817
|
6
6
|
hydraflow/core/context.py,sha256=QPyPg1xrTlmhviKNn-0nDY9bXcVky1zInqRqPN-VNhc,4741
|
7
7
|
hydraflow/core/io.py,sha256=C207DsGAU4CLvlHySyyIl0_aKZ83ysJ-W_wNM2n4RPI,6754
|
8
|
-
hydraflow/core/main.py,sha256=
|
8
|
+
hydraflow/core/main.py,sha256=66-X2-IJVEcJ4XrEC2BIKNzdd_M1RfVIAHME8k70Jo0,5131
|
9
9
|
hydraflow/core/mlflow.py,sha256=M3MhiChnMzKnKRmjBl4h_SRGkAZKL7GAmFr3DdzwRuQ,5666
|
10
10
|
hydraflow/core/param.py,sha256=LHU9j9_7oA99igasoOyKofKClVr9FmGA3UABJ-KmyS0,4538
|
11
11
|
hydraflow/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -13,12 +13,12 @@ hydraflow/entities/run_collection.py,sha256=E8IRBgxCnJE_IPCaSmS2mc9GtDXXLBfc7GHv
|
|
13
13
|
hydraflow/entities/run_data.py,sha256=Y2_Lc-BdQ7nXhcEIjdHGHIkLrXsmAktOftESEwYOY8o,1602
|
14
14
|
hydraflow/entities/run_info.py,sha256=FRC6ICOlzB2u_xi_33Qs-YZLt677UotuNbYqI7XSmHY,1017
|
15
15
|
hydraflow/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
hydraflow/executor/conf.py,sha256=
|
17
|
-
hydraflow/executor/io.py,sha256=
|
18
|
-
hydraflow/executor/job.py,sha256=
|
19
|
-
hydraflow/executor/parser.py,sha256=
|
20
|
-
hydraflow-0.
|
21
|
-
hydraflow-0.
|
22
|
-
hydraflow-0.
|
23
|
-
hydraflow-0.
|
24
|
-
hydraflow-0.
|
16
|
+
hydraflow/executor/conf.py,sha256=2dv6_PlsynRmia-fGZlmBEVt8GopT0f32N13qY7tYnM,402
|
17
|
+
hydraflow/executor/io.py,sha256=yZMcBVmAbPZZ82cAXhgiJfj9p8WvHmzOCMBg_vtEVek,1509
|
18
|
+
hydraflow/executor/job.py,sha256=IL7ek0Vwa3Bl_gANq0wCbldNCUclo8YBckeEeO6W6xg,4852
|
19
|
+
hydraflow/executor/parser.py,sha256=YiA-Upn7H39BZYyvmCumtXXS27an6aDdHKkaF7Nl8Vk,13617
|
20
|
+
hydraflow-0.12.1.dist-info/METADATA,sha256=DWSgcm2oCZt8udPqgoTpQJzyIepIrjfujSggYn3MZ_U,4549
|
21
|
+
hydraflow-0.12.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
22
|
+
hydraflow-0.12.1.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
|
23
|
+
hydraflow-0.12.1.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
24
|
+
hydraflow-0.12.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|