code-loader 1.0.195.dev1__py3-none-any.whl → 1.0.196__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.
- code_loader/contract/datasetclasses.py +54 -11
- code_loader/inner_leap_binder/leapbinder.py +150 -5
- code_loader/inner_leap_binder/leapbinder_decorators.py +881 -106
- code_loader/leaploader.py +256 -68
- code_loader/leaploaderbase.py +67 -3
- code_loader/utils.py +61 -5
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dist-info}/METADATA +1 -1
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dist-info}/RECORD +10 -10
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dist-info}/LICENSE +0 -0
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
|
-
from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, cast
|
|
3
|
+
from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, Tuple, cast
|
|
4
4
|
import re
|
|
5
5
|
import numpy as np
|
|
6
6
|
import numpy.typing as npt
|
|
@@ -48,6 +48,8 @@ class PreprocessResponse:
|
|
|
48
48
|
instance_to_sample_ids_mappings: Optional[Dict[str, str]] = None # in use only for element instance
|
|
49
49
|
tl_generated: bool = False
|
|
50
50
|
_grouped: bool = field(default=False, init=False, repr=False, compare=False)
|
|
51
|
+
_group_pos_cache: Optional[Dict[SampleId, Tuple[List[SampleId], int]]] = field(
|
|
52
|
+
default=None, init=False, repr=False, compare=False)
|
|
51
53
|
|
|
52
54
|
def __post_init__(self) -> None:
|
|
53
55
|
assert self.sample_ids_to_instance_mappings is None, f"Keep sample_ids_to_instance_mappings None when initializing PreprocessResponse"
|
|
@@ -57,7 +59,8 @@ class PreprocessResponse:
|
|
|
57
59
|
self.sample_ids = cast(List[SampleId], [i for i in range(self.length)])
|
|
58
60
|
self.sample_id_type = int
|
|
59
61
|
self._grouped = False
|
|
60
|
-
elif self.
|
|
62
|
+
elif self.sample_ids is not None:
|
|
63
|
+
given_length = self.length
|
|
61
64
|
self._grouped = len(self.sample_ids) > 0 and isinstance(self.sample_ids[0], list)
|
|
62
65
|
if self._grouped:
|
|
63
66
|
groups = cast(List[List[SampleId]], self.sample_ids)
|
|
@@ -66,6 +69,8 @@ class PreprocessResponse:
|
|
|
66
69
|
assert isinstance(group, list) and len(group) > 0, \
|
|
67
70
|
"Each group in a grouped PreprocessResponse must be a non-empty list."
|
|
68
71
|
self.sample_id_type = type(groups[0][0])
|
|
72
|
+
assert issubclass(self.sample_id_type, (str, int)), \
|
|
73
|
+
f"Sample id should be of type str or int. Got: {self.sample_id_type.__name__}"
|
|
69
74
|
for group in groups:
|
|
70
75
|
for sample_id in group:
|
|
71
76
|
assert isinstance(sample_id, self.sample_id_type), \
|
|
@@ -79,6 +84,11 @@ class PreprocessResponse:
|
|
|
79
84
|
if self.sample_id_type == str:
|
|
80
85
|
for sample_id in flat_ids:
|
|
81
86
|
assert isinstance(sample_id, str), f"Sample id should be of type str. Got: {type(sample_id)}"
|
|
87
|
+
# dataclasses.replace() re-invokes __init__ with both fields already populated
|
|
88
|
+
# (length previously derived from sample_ids); only reject a genuine mismatch.
|
|
89
|
+
if given_length is not None:
|
|
90
|
+
assert given_length == self.length, \
|
|
91
|
+
f"Inconsistent PreprocessResponse: length={given_length} but sample_ids implies {self.length}."
|
|
82
92
|
else:
|
|
83
93
|
raise Exception("length is deprecated, please use sample_ids instead.")
|
|
84
94
|
|
|
@@ -138,15 +148,19 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
|
|
|
138
148
|
InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
|
|
139
149
|
InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
|
|
140
150
|
|
|
141
|
-
# (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs
|
|
142
|
-
# First call per chain receives prev_inputs=None, prev_outputs=None and returns the
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
#
|
|
151
|
+
# (sample_id, prev_inputs, prev_outputs, state, preprocess) -> (next model inputs | None, state).
|
|
152
|
+
# First call per chain receives prev_inputs=None, prev_outputs=None, state=None and returns the
|
|
153
|
+
# initial model inputs plus the initial state. Returning next_inputs=None ends the chain — the
|
|
154
|
+
# terminating call still returns state, so the final step participates in state aggregation.
|
|
155
|
+
# State is an arbitrary nest of dicts/lists holding numpy arrays, numbers, strings or bools; it is
|
|
156
|
+
# never fed to the model and rides the engine's redis queue on every step, so accumulate
|
|
157
|
+
# reductions, not raw per-step tensors. The hook must be stateless and deterministically seeded
|
|
158
|
+
# (the engine may replay a step after crash recovery and relies on identical results).
|
|
159
|
+
AutoregressiveChainState = Any
|
|
146
160
|
AutoregressiveStepCallableInterface = Callable[
|
|
147
161
|
[Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
148
|
-
PreprocessResponse],
|
|
149
|
-
Optional[Dict[str, npt.NDArray[np.float32]]]]
|
|
162
|
+
AutoregressiveChainState, PreprocessResponse],
|
|
163
|
+
Tuple[Optional[Dict[str, npt.NDArray[np.float32]]], AutoregressiveChainState]]
|
|
150
164
|
|
|
151
165
|
MetadataSectionCallableInterface = Union[
|
|
152
166
|
Callable[[Union[int, str], PreprocessResponse], int],
|
|
@@ -302,16 +316,42 @@ class CustomLatentSpaceHandler:
|
|
|
302
316
|
# dominates the model inputs). 'mean': every latent space is the elementwise mean over all steps.
|
|
303
317
|
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS = ('last_step', 'mean')
|
|
304
318
|
|
|
319
|
+
# Reserved argument names of autoregressive metrics/losses/visualizers, fed implicitly by the
|
|
320
|
+
# platform from the finished chain (per-chain dicts, no batch axis). Any other argument is wired
|
|
321
|
+
# to a ground-truth encoder through the integration test.
|
|
322
|
+
AUTOREGRESSIVE_IMPLICIT_ARG_NAMES = ('inputs', 'outputs', 'state')
|
|
323
|
+
|
|
305
324
|
|
|
306
325
|
@dataclass
|
|
307
326
|
class AutoregressiveStepHandler:
|
|
308
327
|
function: AutoregressiveStepCallableInterface
|
|
309
328
|
name: str = 'autoregressive_step'
|
|
310
|
-
# Per-model-input shapes (unbatched,
|
|
311
|
-
#
|
|
329
|
+
# Per-model-input shapes (unbatched), discovered by running the hook's first call at parse
|
|
330
|
+
# time. Fills the role InputHandler.shape plays for input encoders.
|
|
312
331
|
input_shapes: Optional[Dict[str, List[int]]] = None
|
|
313
332
|
latent_space_aggregation: str = 'last_step'
|
|
314
333
|
|
|
334
|
+
|
|
335
|
+
# Per-chain, unbatched callables: called once per finished chain with the final step's tensors —
|
|
336
|
+
# inputs/outputs/state are fed implicitly by the platform (dicts, no batch axis; outputs keyed by
|
|
337
|
+
# prediction-type names), remaining args are wired ground-truth encoder results.
|
|
338
|
+
@dataclass
|
|
339
|
+
class AutoregressiveMetricHandler:
|
|
340
|
+
metric_handler_data: MetricHandlerData
|
|
341
|
+
function: CustomCallableInterfaceMultiArgs
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@dataclass
|
|
345
|
+
class AutoregressiveLossHandler:
|
|
346
|
+
custom_loss_handler_data: CustomLossHandlerData
|
|
347
|
+
function: CustomCallableInterface
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
@dataclass
|
|
351
|
+
class AutoregressiveVisualizerHandler:
|
|
352
|
+
visualizer_handler_data: VisualizerHandlerData
|
|
353
|
+
function: VisualizerCallableInterface
|
|
354
|
+
|
|
315
355
|
@dataclass
|
|
316
356
|
class PredictionTypeHandler:
|
|
317
357
|
name: str
|
|
@@ -353,6 +393,9 @@ class DatasetIntegrationSetup:
|
|
|
353
393
|
custom_latent_space: Optional[CustomLatentSpaceHandler] = None
|
|
354
394
|
simulations: List[SimulationHandler] = field(default_factory=list)
|
|
355
395
|
autoregressive_step: Optional[AutoregressiveStepHandler] = None
|
|
396
|
+
autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
|
|
397
|
+
autoregressive_losses: List[AutoregressiveLossHandler] = field(default_factory=list)
|
|
398
|
+
autoregressive_visualizers: List[AutoregressiveVisualizerHandler] = field(default_factory=list)
|
|
356
399
|
|
|
357
400
|
|
|
358
401
|
@dataclass
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
|
|
2
|
+
import builtins
|
|
2
3
|
import inspect
|
|
3
|
-
|
|
4
|
+
import os
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast, Iterator, Set
|
|
4
7
|
|
|
5
8
|
import numpy as np
|
|
6
9
|
import numpy.typing as npt
|
|
@@ -14,8 +17,10 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
|
|
|
14
17
|
RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
|
|
15
18
|
ElementInstanceMasksHandler, InstanceCallableInterface, CustomLatentSpaceHandler, InstanceMetricHandler, \
|
|
16
19
|
SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
|
|
17
|
-
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS
|
|
18
|
-
|
|
20
|
+
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
|
|
21
|
+
AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
|
|
22
|
+
from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType, \
|
|
23
|
+
TestingSectionEnum
|
|
19
24
|
from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
|
|
20
25
|
from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
|
|
21
26
|
from code_loader.contract.visualizer_classes import map_leap_data_type_to_visualizer_class
|
|
@@ -32,6 +37,25 @@ from code_loader.visualizers.default_visualizers import DefaultVisualizer, \
|
|
|
32
37
|
mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
|
|
33
38
|
|
|
34
39
|
|
|
40
|
+
@contextmanager
|
|
41
|
+
def _track_opened_files() -> Iterator[Set[str]]:
|
|
42
|
+
opened: Set[str] = set()
|
|
43
|
+
original_open = builtins.open
|
|
44
|
+
|
|
45
|
+
def tracking_open(file: Any, *args: Any, **kwargs: Any) -> Any:
|
|
46
|
+
mode = args[0] if args else kwargs.get('mode', 'r')
|
|
47
|
+
is_read_mode = isinstance(mode, str) and not any(c in mode for c in ('a', 'w', 'x'))
|
|
48
|
+
if is_read_mode and isinstance(file, (str, os.PathLike)):
|
|
49
|
+
opened.add(os.fspath(file))
|
|
50
|
+
return original_open(file, *args, **kwargs)
|
|
51
|
+
|
|
52
|
+
builtins.open = tracking_open
|
|
53
|
+
try:
|
|
54
|
+
yield opened
|
|
55
|
+
finally:
|
|
56
|
+
builtins.open = original_open
|
|
57
|
+
|
|
58
|
+
|
|
35
59
|
def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
|
|
36
60
|
"""Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
|
|
37
61
|
import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
|
|
@@ -548,11 +572,90 @@ class LeapBinder:
|
|
|
548
572
|
# Builtin chain metadata, declared at parse time so it survives the reporter's
|
|
549
573
|
# metadata type mapping; the placeholder values are overwritten by the engine when a
|
|
550
574
|
# chain finalizes (realized length, truncated-by-safety-cap flag).
|
|
551
|
-
def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) ->
|
|
575
|
+
def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Any:
|
|
576
|
+
if isinstance(idx, list):
|
|
577
|
+
return [{'length': -1, 'truncated': False} for _ in idx]
|
|
552
578
|
return {'length': -1, 'truncated': False}
|
|
553
579
|
|
|
554
580
|
self.set_metadata(builtin_chain_metadata, 'builtin_chain')
|
|
555
581
|
|
|
582
|
+
def _autoregressive_arg_names(self, function: Callable[..., Any], decorator_name: str,
|
|
583
|
+
name: str) -> List[str]:
|
|
584
|
+
"""Split an autoregressive callable's signature into implicit chain args (validated
|
|
585
|
+
present) and wired arg names (returned) — the wired args connect to ground-truth
|
|
586
|
+
encoders through the integration test."""
|
|
587
|
+
argspec = inspect.getfullargspec(function)
|
|
588
|
+
for arg_name, arg_type in argspec.annotations.items():
|
|
589
|
+
_reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
|
|
590
|
+
if arg_type == SamplePreprocessResponse:
|
|
591
|
+
raise Exception(
|
|
592
|
+
f'{decorator_name} "{name}": SamplePreprocessResponse arguments are not '
|
|
593
|
+
f'supported for autoregressive decorators yet. Derive what you need inside '
|
|
594
|
+
f'the autoregressive step hook and carry it in the state.')
|
|
595
|
+
implicit = [arg_name for arg_name in argspec[0]
|
|
596
|
+
if arg_name in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
|
|
597
|
+
if not implicit:
|
|
598
|
+
raise Exception(
|
|
599
|
+
f'{decorator_name} "{name}": the function must declare at least one of the '
|
|
600
|
+
f'implicit chain arguments {list(AUTOREGRESSIVE_IMPLICIT_ARG_NAMES)} — they are '
|
|
601
|
+
f'fed by the platform from the finished chain.')
|
|
602
|
+
return [arg_name for arg_name in argspec[0]
|
|
603
|
+
if arg_name not in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
|
|
604
|
+
|
|
605
|
+
def _assert_unique_metric_name(self, name: str) -> None:
|
|
606
|
+
existing = [handler.metric_handler_data.name for handler in self.setup_container.metrics] \
|
|
607
|
+
+ [handler.metric_handler_data.name
|
|
608
|
+
for handler in self.setup_container.autoregressive_metrics]
|
|
609
|
+
if name in existing:
|
|
610
|
+
raise Exception(f'Metric with name {name} already exists. Please choose another')
|
|
611
|
+
|
|
612
|
+
def _assert_unique_loss_name(self, name: str) -> None:
|
|
613
|
+
existing = [handler.custom_loss_handler_data.name
|
|
614
|
+
for handler in self.setup_container.custom_loss_handlers] \
|
|
615
|
+
+ [handler.custom_loss_handler_data.name
|
|
616
|
+
for handler in self.setup_container.autoregressive_losses]
|
|
617
|
+
if name in existing:
|
|
618
|
+
raise Exception(f'Custom loss with name {name} already exists. Please choose another')
|
|
619
|
+
|
|
620
|
+
def _assert_unique_visualizer_name(self, name: str) -> None:
|
|
621
|
+
existing = [handler.visualizer_handler_data.name
|
|
622
|
+
for handler in self.setup_container.visualizers] \
|
|
623
|
+
+ [handler.visualizer_handler_data.name
|
|
624
|
+
for handler in self.setup_container.autoregressive_visualizers]
|
|
625
|
+
if name in existing:
|
|
626
|
+
raise Exception(f'Visualizer with name {name} already exists. Please choose another')
|
|
627
|
+
|
|
628
|
+
def add_autoregressive_metric(self, function: CustomCallableInterfaceMultiArgs, name: str,
|
|
629
|
+
direction: Optional[Union[MetricDirection, Dict[str, MetricDirection]]]
|
|
630
|
+
= MetricDirection.Downward,
|
|
631
|
+
compute_insights: Optional[Union[bool, Dict[str, bool]]] = None) -> None:
|
|
632
|
+
self._assert_unique_metric_name(name)
|
|
633
|
+
arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_metric',
|
|
634
|
+
name)
|
|
635
|
+
metric_handler_data = MetricHandlerData(name, arg_names, direction, compute_insights)
|
|
636
|
+
self.setup_container.autoregressive_metrics.append(
|
|
637
|
+
AutoregressiveMetricHandler(metric_handler_data, function))
|
|
638
|
+
|
|
639
|
+
def add_autoregressive_loss(self, function: CustomCallableInterface, name: str) -> None:
|
|
640
|
+
self._assert_unique_loss_name(name)
|
|
641
|
+
arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_loss',
|
|
642
|
+
name)
|
|
643
|
+
self.setup_container.autoregressive_losses.append(
|
|
644
|
+
AutoregressiveLossHandler(CustomLossHandlerData(name, arg_names), function))
|
|
645
|
+
|
|
646
|
+
def add_autoregressive_visualizer(self, function: VisualizerCallableInterface, name: str,
|
|
647
|
+
visualizer_type: LeapDataType) -> None:
|
|
648
|
+
self._assert_unique_visualizer_name(name)
|
|
649
|
+
if visualizer_type.value not in map_leap_data_type_to_visualizer_class:
|
|
650
|
+
raise Exception(
|
|
651
|
+
f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, '
|
|
652
|
+
f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
|
|
653
|
+
arg_names = self._autoregressive_arg_names(
|
|
654
|
+
function, 'tensorleap_autoregressive_visualizer', name)
|
|
655
|
+
self.setup_container.autoregressive_visualizers.append(
|
|
656
|
+
AutoregressiveVisualizerHandler(VisualizerHandlerData(name, visualizer_type,
|
|
657
|
+
arg_names), function))
|
|
658
|
+
|
|
556
659
|
def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
|
|
557
660
|
kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
|
|
558
661
|
"""
|
|
@@ -664,15 +767,34 @@ class LeapBinder:
|
|
|
664
767
|
preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
|
|
665
768
|
dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
|
|
666
769
|
assert preprocess_response.sample_ids is not None
|
|
770
|
+
opened_files: Optional[Set[str]] = None
|
|
667
771
|
if preprocess_response.is_grouped:
|
|
668
772
|
# Grouped: probe with the first group, then reduce to a single sample's result so the
|
|
669
773
|
# recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
|
|
670
774
|
# (casts: sample_ids[0] is a group list here, and the grouped result is a per-sample list.)
|
|
671
775
|
group = preprocess_response.sample_ids[0]
|
|
672
|
-
|
|
776
|
+
with _track_opened_files() as opened_files:
|
|
777
|
+
raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
|
|
673
778
|
else:
|
|
674
779
|
raw_result = dataset_base_handler.function(
|
|
675
780
|
cast(Union[int, str], preprocess_response.sample_ids[0]), preprocess_response)
|
|
781
|
+
|
|
782
|
+
def _warn_if_group_spans_multiple_files(payloads: List[DatasetTestResultPayload]) -> None:
|
|
783
|
+
if opened_files is None or len(opened_files) <= 1:
|
|
784
|
+
return
|
|
785
|
+
shown = sorted(opened_files)[:5]
|
|
786
|
+
suffix = f" (+{len(opened_files) - 5} more)" if len(opened_files) > 5 else ""
|
|
787
|
+
warning = (
|
|
788
|
+
f"Encoding declared group 0 (size {len(cast(List[Any], group))}) for '{dataset_base_handler.name}' opened "
|
|
789
|
+
f"{len(opened_files)} distinct files: {shown}{suffix}. If this group is meant to be one "
|
|
790
|
+
f"physical source (e.g. one Parquet file), it may be grouped incorrectly - check your "
|
|
791
|
+
f"preprocess grouping logic. (Only files opened via Python's open() are tracked, so this "
|
|
792
|
+
f"can miss other I/O paths or be a false positive if grouping isn't meant to reflect file "
|
|
793
|
+
f"locality.)")
|
|
794
|
+
for payload in payloads:
|
|
795
|
+
existing = payload.display.get(TestingSectionEnum.Errors.name, '')
|
|
796
|
+
payload.display[TestingSectionEnum.Errors.name] = (existing + '\n' if existing else '') + warning
|
|
797
|
+
|
|
676
798
|
handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
|
|
677
799
|
if isinstance(dataset_base_handler, MetadataHandler):
|
|
678
800
|
if isinstance(raw_result, dict):
|
|
@@ -712,6 +834,7 @@ class LeapBinder:
|
|
|
712
834
|
else:
|
|
713
835
|
if raw_result is None:
|
|
714
836
|
if state != DataStateEnum.training:
|
|
837
|
+
_warn_if_group_spans_multiple_files(test_result)
|
|
715
838
|
return test_result
|
|
716
839
|
|
|
717
840
|
if dataset_base_handler.metadata_type is None:
|
|
@@ -733,6 +856,7 @@ class LeapBinder:
|
|
|
733
856
|
# setting shape in setup for all encoders
|
|
734
857
|
if isinstance(dataset_base_handler, (InputHandler, GroundTruthHandler)):
|
|
735
858
|
dataset_base_handler.shape = result_shape
|
|
859
|
+
_warn_if_group_spans_multiple_files(test_result)
|
|
736
860
|
return test_result
|
|
737
861
|
|
|
738
862
|
def check_handlers(self, preprocess_result: Dict[DataStateEnum, PreprocessResponse]) -> None:
|
|
@@ -768,6 +892,10 @@ class LeapBinder:
|
|
|
768
892
|
preprocess_response.tl_generated = True
|
|
769
893
|
if not preprocess_response.length or preprocess_response.length < 1:
|
|
770
894
|
raise Exception("Simulation '{}' returned PreprocessResponse with length < 1".format(sim.name))
|
|
895
|
+
if preprocess_response.is_grouped:
|
|
896
|
+
raise Exception(
|
|
897
|
+
"Simulation '{}' returned a grouped PreprocessResponse; simulations must "
|
|
898
|
+
"return a flat (non-grouped) PreprocessResponse.".format(sim.name))
|
|
771
899
|
preprocess_response.sample_ids = cast(List[Union[int, str]], [0])
|
|
772
900
|
sim_sample_ids = cast(List[Union[int, str]], preprocess_response.sample_ids)
|
|
773
901
|
for handler in self.setup_container.inputs:
|
|
@@ -796,6 +924,23 @@ class LeapBinder:
|
|
|
796
924
|
appear in any order in the integration file.
|
|
797
925
|
"""
|
|
798
926
|
if self.setup_container.autoregressive_step is None:
|
|
927
|
+
declared_autoregressive = [
|
|
928
|
+
('tensorleap_autoregressive_metric',
|
|
929
|
+
[handler.metric_handler_data.name
|
|
930
|
+
for handler in self.setup_container.autoregressive_metrics]),
|
|
931
|
+
('tensorleap_autoregressive_loss',
|
|
932
|
+
[handler.custom_loss_handler_data.name
|
|
933
|
+
for handler in self.setup_container.autoregressive_losses]),
|
|
934
|
+
('tensorleap_autoregressive_visualizer',
|
|
935
|
+
[handler.visualizer_handler_data.name
|
|
936
|
+
for handler in self.setup_container.autoregressive_visualizers]),
|
|
937
|
+
]
|
|
938
|
+
for decorator_name, names in declared_autoregressive:
|
|
939
|
+
if names:
|
|
940
|
+
raise Exception(
|
|
941
|
+
f'{decorator_name} {names} requires a tensorleap_autoregressive_step '
|
|
942
|
+
f'hook — autoregressive decorators consume the finished chain the hook '
|
|
943
|
+
f'drives.')
|
|
799
944
|
return
|
|
800
945
|
if self.setup_container.inputs:
|
|
801
946
|
raise Exception(
|