hydraflow 0.15.0__py3-none-any.whl → 0.15.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/run.py +38 -24
- hydraflow/executor/conf.py +6 -6
- hydraflow/executor/io.py +1 -17
- hydraflow/executor/job.py +41 -14
- hydraflow/executor/parser.py +9 -8
- {hydraflow-0.15.0.dist-info → hydraflow-0.15.1.dist-info}/METADATA +1 -1
- {hydraflow-0.15.0.dist-info → hydraflow-0.15.1.dist-info}/RECORD +10 -10
- {hydraflow-0.15.0.dist-info → hydraflow-0.15.1.dist-info}/WHEEL +0 -0
- {hydraflow-0.15.0.dist-info → hydraflow-0.15.1.dist-info}/entry_points.txt +0 -0
- {hydraflow-0.15.0.dist-info → hydraflow-0.15.1.dist-info}/licenses/LICENSE +0 -0
hydraflow/core/run.py
CHANGED
@@ -249,29 +249,40 @@ class Run[C, I = None]:
|
|
249
249
|
if force or OmegaConf.select(cfg, k, default=MISSING) is MISSING:
|
250
250
|
OmegaConf.update(cfg, k, v, force_add=True)
|
251
251
|
|
252
|
-
def get(self, key: str) -> Any:
|
252
|
+
def get(self, key: str, default: Any = MISSING) -> Any:
|
253
253
|
"""Get a value from the information or configuration.
|
254
254
|
|
255
255
|
Args:
|
256
|
-
key: The key to look for. Can use dot notation for
|
257
|
-
in configuration.
|
256
|
+
key: The key to look for. Can use dot notation for
|
257
|
+
nested keys in configuration.
|
258
|
+
default: Value to return if the key is not found.
|
259
|
+
If not provided, AttributeError will be raised.
|
258
260
|
|
259
261
|
Returns:
|
260
|
-
Any: The value associated with the key
|
262
|
+
Any: The value associated with the key, or the
|
263
|
+
default value if the key is not found and a default
|
264
|
+
is provided.
|
261
265
|
|
262
266
|
Raises:
|
263
|
-
AttributeError: If the key is not found
|
267
|
+
AttributeError: If the key is not found and
|
268
|
+
no default is provided.
|
264
269
|
|
265
270
|
"""
|
266
271
|
value = OmegaConf.select(self.cfg, key, default=MISSING) # type: ignore
|
267
272
|
if value is not MISSING:
|
268
273
|
return value
|
269
274
|
|
275
|
+
if self.impl and hasattr(self.impl, key):
|
276
|
+
return getattr(self.impl, key)
|
277
|
+
|
270
278
|
info = self.info.to_dict()
|
271
279
|
if key in info:
|
272
280
|
return info[key]
|
273
281
|
|
274
|
-
|
282
|
+
if default is not MISSING:
|
283
|
+
return default
|
284
|
+
|
285
|
+
msg = f"No such key: {key}"
|
275
286
|
raise AttributeError(msg)
|
276
287
|
|
277
288
|
def predicate(self, key: str, value: Any) -> bool:
|
@@ -298,32 +309,35 @@ class Run[C, I = None]:
|
|
298
309
|
|
299
310
|
"""
|
300
311
|
attr = self.get(key)
|
312
|
+
return _predicate(attr, value)
|
301
313
|
|
302
|
-
|
303
|
-
|
314
|
+
def to_dict(self) -> dict[str, Any]:
|
315
|
+
"""Convert the Run to a dictionary."""
|
316
|
+
info = self.info.to_dict()
|
317
|
+
cfg = OmegaConf.to_container(self.cfg)
|
318
|
+
return info | _flatten_dict(cfg) # type: ignore
|
304
319
|
|
305
|
-
if isinstance(value, ListConfig):
|
306
|
-
value = list(value)
|
307
320
|
|
308
|
-
|
309
|
-
|
321
|
+
def _predicate(attr: Any, value: Any) -> bool:
|
322
|
+
if callable(value):
|
323
|
+
return bool(value(attr))
|
310
324
|
|
311
|
-
|
312
|
-
|
325
|
+
if isinstance(value, ListConfig):
|
326
|
+
value = list(value)
|
313
327
|
|
314
|
-
|
315
|
-
|
328
|
+
if isinstance(value, list | set) and not _is_iterable(attr):
|
329
|
+
return attr in value
|
316
330
|
|
317
|
-
|
318
|
-
|
331
|
+
if isinstance(value, tuple) and len(value) == 2 and not _is_iterable(attr):
|
332
|
+
return value[0] <= attr <= value[1]
|
319
333
|
|
320
|
-
|
334
|
+
if _is_iterable(value):
|
335
|
+
value = list(value)
|
321
336
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
return info | _flatten_dict(cfg) # type: ignore
|
337
|
+
if _is_iterable(attr):
|
338
|
+
attr = list(attr)
|
339
|
+
|
340
|
+
return attr == value
|
327
341
|
|
328
342
|
|
329
343
|
def _is_iterable(value: Any) -> bool:
|
hydraflow/executor/conf.py
CHANGED
@@ -4,10 +4,10 @@ from dataclasses import dataclass, field
|
|
4
4
|
|
5
5
|
|
6
6
|
@dataclass
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class Set:
|
8
|
+
each: str = ""
|
9
|
+
all: str = ""
|
10
|
+
add: str = ""
|
11
11
|
|
12
12
|
|
13
13
|
@dataclass
|
@@ -16,8 +16,8 @@ class Job:
|
|
16
16
|
run: str = ""
|
17
17
|
call: str = ""
|
18
18
|
submit: str = ""
|
19
|
-
|
20
|
-
|
19
|
+
add: str = ""
|
20
|
+
sets: list[Set] = field(default_factory=list)
|
21
21
|
|
22
22
|
|
23
23
|
@dataclass
|
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 DictConfig,
|
8
|
+
from omegaconf import DictConfig, OmegaConf
|
9
9
|
|
10
10
|
from .conf import HydraflowConf
|
11
11
|
|
@@ -38,25 +38,9 @@ def load_config() -> HydraflowConf:
|
|
38
38
|
if not isinstance(cfg, DictConfig):
|
39
39
|
return schema
|
40
40
|
|
41
|
-
rename_with(cfg)
|
42
|
-
|
43
41
|
return OmegaConf.merge(schema, cfg) # type: ignore[return-value]
|
44
42
|
|
45
43
|
|
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)
|
58
|
-
|
59
|
-
|
60
44
|
def get_job(name: str) -> Job:
|
61
45
|
"""Get a job from the config."""
|
62
46
|
cfg = load_config()
|
hydraflow/executor/job.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
This module provides functionality for executing jobs in HydraFlow, including:
|
4
4
|
|
5
|
-
- Argument parsing and expansion for job
|
5
|
+
- Argument parsing and expansion for job parameter sets
|
6
6
|
- Batch processing of Hydra configurations
|
7
7
|
- Execution of jobs via shell commands or Python functions
|
8
8
|
|
@@ -11,8 +11,9 @@ The module supports two execution modes:
|
|
11
11
|
1. Shell command execution
|
12
12
|
2. Python function calls
|
13
13
|
|
14
|
-
Each job can consist of multiple
|
15
|
-
arguments and configurations that will be expanded
|
14
|
+
Each job can consist of multiple parameter sets, and each parameter
|
15
|
+
set can have its own arguments and configurations that will be expanded
|
16
|
+
into multiple runs.
|
16
17
|
"""
|
17
18
|
|
18
19
|
from __future__ import annotations
|
@@ -39,24 +40,24 @@ if TYPE_CHECKING:
|
|
39
40
|
from .conf import Job
|
40
41
|
|
41
42
|
|
42
|
-
def iter_args(
|
43
|
+
def iter_args(each: str, all_: str) -> Iterator[list[str]]:
|
43
44
|
"""Iterate over combinations generated from parsed arguments.
|
44
45
|
|
45
46
|
Generate all possible combinations of arguments by parsing and
|
46
47
|
expanding each one, yielding them as an iterator.
|
47
48
|
|
48
49
|
Args:
|
49
|
-
|
50
|
-
|
50
|
+
each (str): The 'each' parameter to parse.
|
51
|
+
all_ (str): The 'all' parameter to parse.
|
51
52
|
|
52
53
|
Yields:
|
53
54
|
list[str]: a list of the parsed argument combinations.
|
54
55
|
|
55
56
|
"""
|
56
|
-
|
57
|
+
all_params = collect(all_)
|
57
58
|
|
58
|
-
for
|
59
|
-
yield [*
|
59
|
+
for each_params in expand(each):
|
60
|
+
yield [*each_params, *all_params]
|
60
61
|
|
61
62
|
|
62
63
|
def iter_batches(job: Job) -> Iterator[list[str]]:
|
@@ -74,14 +75,40 @@ def iter_batches(job: Job) -> Iterator[list[str]]:
|
|
74
75
|
|
75
76
|
"""
|
76
77
|
job_name = f"hydra.job.name={job.name}"
|
77
|
-
|
78
|
+
job_add = shlex.split(job.add)
|
78
79
|
|
79
|
-
for
|
80
|
-
|
80
|
+
for set_ in job.sets:
|
81
|
+
add = merge_args(job_add, shlex.split(set_.add)) if set_.add else job_add
|
81
82
|
|
82
|
-
for args in iter_args(
|
83
|
+
for args in iter_args(set_.each, set_.all):
|
83
84
|
sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ULID()}"
|
84
|
-
yield ["--multirun", *args, job_name, sweep_dir, *
|
85
|
+
yield ["--multirun", *args, job_name, sweep_dir, *add]
|
86
|
+
|
87
|
+
|
88
|
+
def merge_args(first: list[str], second: list[str]) -> list[str]:
|
89
|
+
"""Merge two lists of arguments.
|
90
|
+
|
91
|
+
This function merges two lists of arguments by checking for conflicts
|
92
|
+
and resolving them by keeping the values from the second list.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
first (list[str]): The first list of arguments.
|
96
|
+
second (list[str]): The second list of arguments.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
list[str]: A merged list of arguments.
|
100
|
+
|
101
|
+
"""
|
102
|
+
merged = {}
|
103
|
+
|
104
|
+
for item in [*first, *second]:
|
105
|
+
if "=" in item:
|
106
|
+
key, value = item.split("=", 1)
|
107
|
+
merged[key] = value
|
108
|
+
else:
|
109
|
+
merged[item] = None
|
110
|
+
|
111
|
+
return [k if v is None else f"{k}={v}" for k, v in merged.items()]
|
85
112
|
|
86
113
|
|
87
114
|
@dataclass
|
hydraflow/executor/parser.py
CHANGED
@@ -165,25 +165,26 @@ SUFFIX_EXPONENT = {
|
|
165
165
|
|
166
166
|
|
167
167
|
def _get_range(arg: str) -> tuple[float, float, float]:
|
168
|
+
"""Return a tuple of (start, stop, step)."""
|
168
169
|
args = [to_number(x) for x in arg.split(":")]
|
169
170
|
|
170
171
|
if len(args) == 2:
|
171
172
|
if args[0] > args[1]:
|
172
173
|
raise ValueError("start cannot be greater than stop")
|
173
174
|
|
174
|
-
return (args[0], 1,
|
175
|
+
return (args[0], args[1], 1)
|
175
176
|
|
176
|
-
if args[
|
177
|
+
if args[2] == 0:
|
177
178
|
raise ValueError("step cannot be zero")
|
178
|
-
if args[
|
179
|
+
if args[2] > 0 and args[0] > args[1]:
|
179
180
|
raise ValueError("start cannot be greater than stop")
|
180
|
-
if args[
|
181
|
+
if args[2] < 0 and args[0] < args[1]:
|
181
182
|
raise ValueError("start cannot be less than stop")
|
182
183
|
|
183
184
|
return args[0], args[1], args[2]
|
184
185
|
|
185
186
|
|
186
|
-
def _arange(start: float,
|
187
|
+
def _arange(start: float, stop: float, step: float) -> list[float]:
|
187
188
|
"""Generate a range of floating point numbers.
|
188
189
|
|
189
190
|
This function generates a range of floating point numbers
|
@@ -191,8 +192,8 @@ def _arange(start: float, step: float, stop: float) -> list[float]:
|
|
191
192
|
|
192
193
|
Args:
|
193
194
|
start (float): The starting value.
|
194
|
-
step (float): The step size.
|
195
195
|
stop (float): The end value (inclusive).
|
196
|
+
step (float): The step size.
|
196
197
|
|
197
198
|
Returns:
|
198
199
|
list[float]: A list of floating point numbers from start to stop
|
@@ -323,7 +324,7 @@ def collect_parentheses(arg: str) -> list[str]:
|
|
323
324
|
list[str]: A list of the collected values.
|
324
325
|
|
325
326
|
Examples:
|
326
|
-
>>> collect_parentheses("(1:3,5:2
|
327
|
+
>>> collect_parentheses("(1:3,5:9:2,20)k")
|
327
328
|
['1e3', '2e3', '3e3', '5e3', '7e3', '9e3', '20e3']
|
328
329
|
>>> collect_parentheses("2e(-1,-2,-3)")
|
329
330
|
['2e-1', '2e-2', '2e-3']
|
@@ -352,7 +353,7 @@ def collect_values(arg: str) -> list[str]:
|
|
352
353
|
Examples:
|
353
354
|
>>> collect_values("1:4")
|
354
355
|
['1', '2', '3', '4']
|
355
|
-
>>> collect_values("1.2:0.1:
|
356
|
+
>>> collect_values("1.2:1.4:0.1:k")
|
356
357
|
['1.2e3', '1.3e3', '1.4e3']
|
357
358
|
>>> collect_values("0.1")
|
358
359
|
['0.1']
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hydraflow
|
3
|
-
Version: 0.15.
|
3
|
+
Version: 0.15.1
|
4
4
|
Summary: HydraFlow seamlessly integrates Hydra and MLflow to streamline ML experiment management, combining Hydra's configuration management with MLflow's tracking capabilities.
|
5
5
|
Project-URL: Documentation, https://daizutabi.github.io/hydraflow/
|
6
6
|
Project-URL: Source, https://github.com/daizutabi/hydraflow
|
@@ -5,17 +5,17 @@ hydraflow/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
hydraflow/core/context.py,sha256=LFPNJxmuJQ2VUt-WBU07MC3ySbjlY8rRZ8VxuAih4o4,4148
|
6
6
|
hydraflow/core/io.py,sha256=ZBXIL_jlBUiCI0L_J6S5S4OwtBMvdVVMXnekzMuC_JA,4404
|
7
7
|
hydraflow/core/main.py,sha256=b9o6Rpn3uoXfDB8o0XZdl-g1yX2SKkOT12-H7lB8Les,5158
|
8
|
-
hydraflow/core/run.py,sha256=
|
8
|
+
hydraflow/core/run.py,sha256=KqaMdRUBOzOU4vkrRUczCrPCsVx30-XUQ_e78B78BSU,12330
|
9
9
|
hydraflow/core/run_collection.py,sha256=pV3N83uBhmda9OeaNz1jqpF9z6A9j3jfUHtqy-uxCs4,15671
|
10
10
|
hydraflow/core/run_info.py,sha256=3dW9GgWnZZNwbXwMrw-85AqQ956zlQddUi9irSNLR5g,2550
|
11
11
|
hydraflow/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
hydraflow/executor/aio.py,sha256=xXsmBPIPdBlopv_1h0FdtOvoKUcuW7PQeKCV2d_lN9I,2122
|
13
|
-
hydraflow/executor/conf.py,sha256=
|
14
|
-
hydraflow/executor/io.py,sha256=
|
15
|
-
hydraflow/executor/job.py,sha256=
|
16
|
-
hydraflow/executor/parser.py,sha256=
|
17
|
-
hydraflow-0.15.
|
18
|
-
hydraflow-0.15.
|
19
|
-
hydraflow-0.15.
|
20
|
-
hydraflow-0.15.
|
21
|
-
hydraflow-0.15.
|
13
|
+
hydraflow/executor/conf.py,sha256=8Xq4UAenRKJIl1NBgNbSfv6VUTJhdwPLayZIEAsiBR0,414
|
14
|
+
hydraflow/executor/io.py,sha256=18wnHpCMQRGYL-oN2841h9W2aSW_X2SmO68Lx-3FIbU,1043
|
15
|
+
hydraflow/executor/job.py,sha256=6QeJ18OMeocXeM04rCYL46GgArfX1SvZs9_4HTomTgE,5436
|
16
|
+
hydraflow/executor/parser.py,sha256=RxP8qpDaJ8VLqZ51VlPFyVitWctObhkE_3iPIsY66Cs,14610
|
17
|
+
hydraflow-0.15.1.dist-info/METADATA,sha256=oC-UgH0sZKw2Ry1kBiMPpNobxzlLhmhQgS8W3TIvGJI,7238
|
18
|
+
hydraflow-0.15.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
19
|
+
hydraflow-0.15.1.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
|
20
|
+
hydraflow-0.15.1.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
21
|
+
hydraflow-0.15.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|