code-loader 1.0.196.dev1__py3-none-any.whl → 1.0.197.dev1__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 +12 -59
- code_loader/inner_leap_binder/leapbinder.py +49 -77
- code_loader/inner_leap_binder/leapbinder_decorators.py +69 -191
- code_loader/leaploader.py +44 -255
- code_loader/leaploaderbase.py +8 -0
- code_loader/utils.py +0 -7
- {code_loader-1.0.196.dev1.dist-info → code_loader-1.0.197.dev1.dist-info}/METADATA +1 -1
- {code_loader-1.0.196.dev1.dist-info → code_loader-1.0.197.dev1.dist-info}/RECORD +10 -10
- {code_loader-1.0.196.dev1.dist-info → code_loader-1.0.197.dev1.dist-info}/LICENSE +0 -0
- {code_loader-1.0.196.dev1.dist-info → code_loader-1.0.197.dev1.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, Tuple
|
|
3
|
+
from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, Tuple
|
|
4
4
|
import re
|
|
5
5
|
import numpy as np
|
|
6
6
|
import numpy.typing as npt
|
|
@@ -15,8 +15,6 @@ custom_latent_space_attribute = "custom_latent_space"
|
|
|
15
15
|
|
|
16
16
|
_simulation_context: Dict[str, bool] = {"active": False}
|
|
17
17
|
|
|
18
|
-
SampleId = Union[int, str]
|
|
19
|
-
|
|
20
18
|
|
|
21
19
|
@dataclass
|
|
22
20
|
class PreprocessResponse:
|
|
@@ -41,46 +39,27 @@ class PreprocessResponse:
|
|
|
41
39
|
"""
|
|
42
40
|
length: Optional[int] = None # Deprecated. Please use sample_ids instead
|
|
43
41
|
data: Any = None
|
|
44
|
-
sample_ids: Optional[Union[List[
|
|
42
|
+
sample_ids: Optional[Union[List[str], List[int]]] = None
|
|
45
43
|
state: Optional[DataStateType] = None
|
|
46
44
|
sample_id_type: Optional[Union[Type[str], Type[int]]] = None
|
|
47
45
|
sample_ids_to_instance_mappings: Optional[Dict[str, List[str]]] = None # in use only for element instance
|
|
48
46
|
instance_to_sample_ids_mappings: Optional[Dict[str, str]] = None # in use only for element instance
|
|
49
47
|
tl_generated: bool = False
|
|
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)
|
|
53
48
|
|
|
54
49
|
def __post_init__(self) -> None:
|
|
55
50
|
assert self.sample_ids_to_instance_mappings is None, f"Keep sample_ids_to_instance_mappings None when initializing PreprocessResponse"
|
|
56
51
|
assert self.instance_to_sample_ids_mappings is None, f"Keep instance_to_sample_ids_mappings None when initializing PreprocessResponse"
|
|
57
52
|
|
|
58
53
|
if self.length is not None and self.sample_ids is None:
|
|
59
|
-
self.sample_ids =
|
|
54
|
+
self.sample_ids = [i for i in range(self.length)]
|
|
60
55
|
self.sample_id_type = int
|
|
61
|
-
self._grouped = False
|
|
62
56
|
elif self.length is None and self.sample_ids is not None:
|
|
63
|
-
self.
|
|
64
|
-
if self.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
for
|
|
68
|
-
assert isinstance(
|
|
69
|
-
"Each group in a grouped PreprocessResponse must be a non-empty list."
|
|
70
|
-
self.sample_id_type = type(groups[0][0])
|
|
71
|
-
for group in groups:
|
|
72
|
-
for sample_id in group:
|
|
73
|
-
assert isinstance(sample_id, self.sample_id_type), \
|
|
74
|
-
f"Sample id should be of type {self.sample_id_type.__name__}. Got: {type(sample_id)}"
|
|
75
|
-
self.length = sum(len(group) for group in groups)
|
|
76
|
-
else:
|
|
77
|
-
flat_ids = cast(List[SampleId], self.sample_ids)
|
|
78
|
-
self.length = len(flat_ids)
|
|
79
|
-
if self.sample_id_type is None:
|
|
80
|
-
self.sample_id_type = str
|
|
81
|
-
if self.sample_id_type == str:
|
|
82
|
-
for sample_id in flat_ids:
|
|
83
|
-
assert isinstance(sample_id, str), f"Sample id should be of type str. Got: {type(sample_id)}"
|
|
57
|
+
self.length = len(self.sample_ids)
|
|
58
|
+
if self.sample_id_type is None:
|
|
59
|
+
self.sample_id_type = str
|
|
60
|
+
if self.sample_id_type == str:
|
|
61
|
+
for sample_id in self.sample_ids:
|
|
62
|
+
assert isinstance(sample_id, str), f"Sample id should be of type str. Got: {type(sample_id)}"
|
|
84
63
|
else:
|
|
85
64
|
raise Exception("length is deprecated, please use sample_ids instead.")
|
|
86
65
|
|
|
@@ -100,36 +79,9 @@ class PreprocessResponse:
|
|
|
100
79
|
return id(self)
|
|
101
80
|
|
|
102
81
|
def __len__(self) -> int:
|
|
103
|
-
assert self.length is not None
|
|
104
|
-
return self.length
|
|
105
|
-
|
|
106
|
-
@property
|
|
107
|
-
def is_grouped(self) -> bool:
|
|
108
|
-
return self._grouped
|
|
109
|
-
|
|
110
|
-
@property
|
|
111
|
-
def num_groups(self) -> int:
|
|
112
|
-
# Grouped: the number of groups. Flat: the (flat) sample count, since sample_ids is the
|
|
113
|
-
# flat id list and there is one sample per "group".
|
|
114
82
|
assert self.sample_ids is not None
|
|
115
83
|
return len(self.sample_ids)
|
|
116
84
|
|
|
117
|
-
@property
|
|
118
|
-
def groups(self) -> Optional[List[List[SampleId]]]:
|
|
119
|
-
# The nested list of groups when grouped, otherwise None. Callers should
|
|
120
|
-
# branch on is_grouped before relying on this.
|
|
121
|
-
return cast(List[List[SampleId]], self.sample_ids) if self._grouped else None
|
|
122
|
-
|
|
123
|
-
@property
|
|
124
|
-
def flat_sample_ids(self) -> List[SampleId]:
|
|
125
|
-
# All sample ids in order: concatenation of the groups when grouped,
|
|
126
|
-
# otherwise the flat sample_ids list as-is.
|
|
127
|
-
assert self.sample_ids is not None
|
|
128
|
-
if self._grouped:
|
|
129
|
-
groups = cast(List[List[SampleId]], self.sample_ids)
|
|
130
|
-
return [sample_id for group in groups for sample_id in group]
|
|
131
|
-
return cast(List[SampleId], self.sample_ids)
|
|
132
|
-
|
|
133
85
|
@dataclass
|
|
134
86
|
class ElementInstance:
|
|
135
87
|
name: str
|
|
@@ -300,6 +252,7 @@ class MetadataHandler:
|
|
|
300
252
|
class CustomLatentSpaceHandler:
|
|
301
253
|
function: SectionCallableInterface
|
|
302
254
|
name: str = 'custom_latent_space'
|
|
255
|
+
use_ls_for_analysis: bool = False
|
|
303
256
|
|
|
304
257
|
|
|
305
258
|
# How a chain's latent-space vectors are derived from its per-step forward passes.
|
|
@@ -382,7 +335,7 @@ class DatasetIntegrationSetup:
|
|
|
382
335
|
metrics: List[MetricHandler] = field(default_factory=list)
|
|
383
336
|
instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
|
|
384
337
|
custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
|
|
385
|
-
|
|
338
|
+
custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
|
|
386
339
|
simulations: List[SimulationHandler] = field(default_factory=list)
|
|
387
340
|
autoregressive_step: Optional[AutoregressiveStepHandler] = None
|
|
388
341
|
autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
|
|
@@ -398,6 +351,6 @@ class DatasetSample:
|
|
|
398
351
|
metadata_is_none: Dict[str, bool]
|
|
399
352
|
index: Union[int, str]
|
|
400
353
|
state: DataStateEnum
|
|
401
|
-
|
|
354
|
+
custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
|
|
402
355
|
instance_masks: Optional[Dict[str, ElementInstance]] = None
|
|
403
356
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import builtins
|
|
3
2
|
import inspect
|
|
4
|
-
import
|
|
5
|
-
from contextlib import contextmanager
|
|
6
|
-
from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast, Iterator, Set
|
|
3
|
+
from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args
|
|
7
4
|
|
|
8
5
|
import numpy as np
|
|
9
6
|
import numpy.typing as npt
|
|
@@ -19,8 +16,7 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
|
|
|
19
16
|
SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
|
|
20
17
|
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
|
|
21
18
|
AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
|
|
22
|
-
from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
|
|
23
|
-
TestingSectionEnum
|
|
19
|
+
from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
|
|
24
20
|
from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
|
|
25
21
|
from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
|
|
26
22
|
from code_loader.contract.visualizer_classes import map_leap_data_type_to_visualizer_class
|
|
@@ -37,25 +33,6 @@ from code_loader.visualizers.default_visualizers import DefaultVisualizer, \
|
|
|
37
33
|
mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
|
|
38
34
|
|
|
39
35
|
|
|
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
|
-
|
|
59
36
|
def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
|
|
60
37
|
"""Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
|
|
61
38
|
import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
|
|
@@ -536,18 +513,54 @@ class LeapBinder:
|
|
|
536
513
|
"""
|
|
537
514
|
self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
|
|
538
515
|
|
|
539
|
-
def set_custom_latent_space(self, function: SectionCallableInterface
|
|
516
|
+
def set_custom_latent_space(self, function: SectionCallableInterface,
|
|
517
|
+
name: Optional[str] = None,
|
|
518
|
+
use_ls_for_analysis: bool = False) -> None:
|
|
540
519
|
"""
|
|
541
|
-
|
|
520
|
+
Register a custom latent space function.
|
|
521
|
+
|
|
522
|
+
Multiple custom latent spaces may be registered as long as each has a
|
|
523
|
+
unique name. They are stored name-keyed (insertion order preserved), so a
|
|
524
|
+
later registration no longer overrides an earlier one.
|
|
542
525
|
|
|
543
526
|
Args:
|
|
544
|
-
function (SectionCallableInterface): The
|
|
527
|
+
function (SectionCallableInterface): The latent-space handler function.
|
|
545
528
|
This function receives:
|
|
546
529
|
subset (PreprocessResponse): The subset of the data.
|
|
547
530
|
index (int): The index of the sample within the subset.
|
|
548
|
-
This function should numpy float32 array
|
|
531
|
+
This function should return a numpy float32 array containing the latent
|
|
532
|
+
space vec of the sample.
|
|
533
|
+
name (Optional[str]): Unique name for this custom latent space. Defaults to
|
|
534
|
+
the reserved single-LS name for backward compatibility.
|
|
535
|
+
use_ls_for_analysis (bool): When True, the engine uses this custom latent
|
|
536
|
+
space for the Out-Of-Distribution and Domain-Gap insights instead of the
|
|
537
|
+
built-in defaults. At most one registered custom latent space may set this;
|
|
538
|
+
registering a second one with the flag raises.
|
|
549
539
|
"""
|
|
550
|
-
|
|
540
|
+
if name is None:
|
|
541
|
+
name = custom_latent_space_attribute
|
|
542
|
+
if name in self.setup_container.custom_latent_spaces:
|
|
543
|
+
raise Exception(
|
|
544
|
+
f"A custom latent space named '{name}' is already registered. Each "
|
|
545
|
+
f"@tensorleap_custom_latent_space must have a unique name "
|
|
546
|
+
f"(pass name='...' to distinguish them)."
|
|
547
|
+
)
|
|
548
|
+
if use_ls_for_analysis:
|
|
549
|
+
already_flagged = [
|
|
550
|
+
existing_name
|
|
551
|
+
for existing_name, handler in self.setup_container.custom_latent_spaces.items()
|
|
552
|
+
if handler.use_ls_for_analysis
|
|
553
|
+
]
|
|
554
|
+
if already_flagged:
|
|
555
|
+
raise Exception(
|
|
556
|
+
f"use_ls_for_analysis=True is already set on custom latent space "
|
|
557
|
+
f"'{already_flagged[0]}'. Only one custom latent space may set "
|
|
558
|
+
f"use_ls_for_analysis=True (it selects the latent space used for the "
|
|
559
|
+
f"Out-Of-Distribution and Domain-Gap insights). Set it on '{name}' "
|
|
560
|
+
f"or '{already_flagged[0]}', not both."
|
|
561
|
+
)
|
|
562
|
+
self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
|
|
563
|
+
function, name, use_ls_for_analysis)
|
|
551
564
|
|
|
552
565
|
def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
|
|
553
566
|
latent_space_aggregation: str = 'last_step') -> None:
|
|
@@ -572,9 +585,7 @@ class LeapBinder:
|
|
|
572
585
|
# Builtin chain metadata, declared at parse time so it survives the reporter's
|
|
573
586
|
# metadata type mapping; the placeholder values are overwritten by the engine when a
|
|
574
587
|
# chain finalizes (realized length, truncated-by-safety-cap flag).
|
|
575
|
-
def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Any:
|
|
576
|
-
if isinstance(idx, list):
|
|
577
|
-
return [{'length': -1, 'truncated': False} for _ in idx]
|
|
588
|
+
def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
|
|
578
589
|
return {'length': -1, 'truncated': False}
|
|
579
590
|
|
|
580
591
|
self.set_metadata(builtin_chain_metadata, 'builtin_chain')
|
|
@@ -738,15 +749,6 @@ class LeapBinder:
|
|
|
738
749
|
if state_enum in preprocess_result_dict:
|
|
739
750
|
preprocess_result_dict_in_correct_order[state_enum] = preprocess_result_dict[state_enum]
|
|
740
751
|
|
|
741
|
-
# All-or-none: every state must agree on grouped (nested sample_ids) vs flat.
|
|
742
|
-
if len({r.is_grouped for r in preprocess_result_dict_in_correct_order.values()}) > 1:
|
|
743
|
-
shapes = ', '.join(
|
|
744
|
-
f"{state.name}={'grouped' if r.is_grouped else 'flat'}"
|
|
745
|
-
for state, r in preprocess_result_dict_in_correct_order.items())
|
|
746
|
-
raise Exception(
|
|
747
|
-
"All preprocess states must be either all grouped (nested sample_ids) or all flat, "
|
|
748
|
-
f"but got a mix: {shapes}.")
|
|
749
|
-
|
|
750
752
|
return preprocess_result_dict_in_correct_order
|
|
751
753
|
|
|
752
754
|
def get_preprocess_unlabeled_result(self) -> Optional[PreprocessResponse]:
|
|
@@ -767,34 +769,7 @@ class LeapBinder:
|
|
|
767
769
|
preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
|
|
768
770
|
dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
|
|
769
771
|
assert preprocess_response.sample_ids is not None
|
|
770
|
-
|
|
771
|
-
if preprocess_response.is_grouped:
|
|
772
|
-
# Grouped: probe with the first group, then reduce to a single sample's result so the
|
|
773
|
-
# recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
|
|
774
|
-
# (casts: sample_ids[0] is a group list here, and the grouped result is a per-sample list.)
|
|
775
|
-
group = preprocess_response.sample_ids[0]
|
|
776
|
-
with _track_opened_files() as opened_files:
|
|
777
|
-
raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
|
|
778
|
-
else:
|
|
779
|
-
raw_result = dataset_base_handler.function(
|
|
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
|
-
|
|
772
|
+
raw_result = dataset_base_handler.function(preprocess_response.sample_ids[0], preprocess_response)
|
|
798
773
|
handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
|
|
799
774
|
if isinstance(dataset_base_handler, MetadataHandler):
|
|
800
775
|
if isinstance(raw_result, dict):
|
|
@@ -834,7 +809,6 @@ class LeapBinder:
|
|
|
834
809
|
else:
|
|
835
810
|
if raw_result is None:
|
|
836
811
|
if state != DataStateEnum.training:
|
|
837
|
-
_warn_if_group_spans_multiple_files(test_result)
|
|
838
812
|
return test_result
|
|
839
813
|
|
|
840
814
|
if dataset_base_handler.metadata_type is None:
|
|
@@ -856,7 +830,6 @@ class LeapBinder:
|
|
|
856
830
|
# setting shape in setup for all encoders
|
|
857
831
|
if isinstance(dataset_base_handler, (InputHandler, GroundTruthHandler)):
|
|
858
832
|
dataset_base_handler.shape = result_shape
|
|
859
|
-
_warn_if_group_spans_multiple_files(test_result)
|
|
860
833
|
return test_result
|
|
861
834
|
|
|
862
835
|
def check_handlers(self, preprocess_result: Dict[DataStateEnum, PreprocessResponse]) -> None:
|
|
@@ -892,11 +865,10 @@ class LeapBinder:
|
|
|
892
865
|
preprocess_response.tl_generated = True
|
|
893
866
|
if not preprocess_response.length or preprocess_response.length < 1:
|
|
894
867
|
raise Exception("Simulation '{}' returned PreprocessResponse with length < 1".format(sim.name))
|
|
895
|
-
preprocess_response.sample_ids =
|
|
896
|
-
sim_sample_ids = cast(List[Union[int, str]], preprocess_response.sample_ids)
|
|
868
|
+
preprocess_response.sample_ids = [0]
|
|
897
869
|
for handler in self.setup_container.inputs:
|
|
898
|
-
out1 = handler.function(
|
|
899
|
-
out2 = handler.function(
|
|
870
|
+
out1 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
|
|
871
|
+
out2 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
|
|
900
872
|
if not np.allclose(out1, out2):
|
|
901
873
|
raise Exception(
|
|
902
874
|
"Simulation '{}': encoder '{}' is non-deterministic — consecutive calls with seed=0 returned different outputs".format(
|
|
@@ -954,7 +926,7 @@ class LeapBinder:
|
|
|
954
926
|
"Element instances are not supported together with tensorleap_autoregressive_step: "
|
|
955
927
|
"instance generation masks the sample's encoded inputs, which do not exist in an "
|
|
956
928
|
"autoregressive integration. Remove the instance encoders or the autoregressive hook.")
|
|
957
|
-
if self.setup_container.
|
|
929
|
+
if self.setup_container.custom_latent_spaces:
|
|
958
930
|
raise Exception(
|
|
959
931
|
"tensorleap_custom_latent_space is not supported together with "
|
|
960
932
|
"tensorleap_autoregressive_step: the custom latent space function only sees "
|
|
@@ -40,7 +40,6 @@ _called_from_inside_tl_integration_test_decorator = False
|
|
|
40
40
|
# to tell a raise in the test body (report as code-flow failure) from a module-level crash
|
|
41
41
|
# before any test ran (report as a plain script crash).
|
|
42
42
|
_integration_test_started = False
|
|
43
|
-
_mapping_dataset_is_grouped = False
|
|
44
43
|
_call_from_tl_platform = os.environ.get('IS_TENSORLEAP_PLATFORM') == 'true'
|
|
45
44
|
|
|
46
45
|
# ---- warnings store (module-level) ----
|
|
@@ -214,53 +213,6 @@ def batch_warning(result, func_name):
|
|
|
214
213
|
)
|
|
215
214
|
|
|
216
215
|
|
|
217
|
-
def _validate_id_or_group(sample_id, preprocess_response, func_name):
|
|
218
|
-
"""Accept either a single sample id (flat dataset) or a group — a list of ids (grouped
|
|
219
|
-
dataset). The argument shape must match the dataset: a group requires a grouped
|
|
220
|
-
PreprocessResponse and a single id requires a flat one. Every id must match the declared
|
|
221
|
-
sample_id_type."""
|
|
222
|
-
is_group = isinstance(sample_id, list)
|
|
223
|
-
if is_group and not preprocess_response.is_grouped:
|
|
224
|
-
raise AssertionError(
|
|
225
|
-
f'{func_name}() validation failed: got a group (list of sample ids) but the '
|
|
226
|
-
f'PreprocessResponse is not grouped. Pass a single sample id for a flat dataset.')
|
|
227
|
-
if not is_group and preprocess_response.is_grouped:
|
|
228
|
-
raise AssertionError(
|
|
229
|
-
f'{func_name}() validation failed: got a single sample id but the PreprocessResponse '
|
|
230
|
-
f'is grouped. Pass a group (list of sample ids) for a grouped dataset.')
|
|
231
|
-
ids = sample_id if is_group else [sample_id]
|
|
232
|
-
for sid in ids:
|
|
233
|
-
assert type(sid) == preprocess_response.sample_id_type, \
|
|
234
|
-
(f'{func_name}() validation failed: '
|
|
235
|
-
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
236
|
-
f'{preprocess_response.sample_id_type}. Got {type(sid)}.')
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def _validate_grouped_result(result, group_size, func_name, validate_single):
|
|
240
|
-
"""A grouped (group) encoder result must be either a single ndarray with leading dim
|
|
241
|
-
== group_size, or a list of `group_size` per-sample results (each validated by
|
|
242
|
-
validate_single). Metadata validates its grouped result separately (list only).
|
|
243
|
-
An `(B, *)` array is validated per-sample slice (not as the whole batched array) so
|
|
244
|
-
per-sample checks (e.g. channel_dim <= rank) use the sample shape, not the batch shape."""
|
|
245
|
-
if isinstance(result, np.ndarray):
|
|
246
|
-
assert len(result.shape) > 0 and result.shape[0] == group_size, \
|
|
247
|
-
(f'{func_name}() validation failed: expected a grouped output for a group of '
|
|
248
|
-
f'{group_size}: an array with leading dim {group_size}, got shape {result.shape}.')
|
|
249
|
-
for single in result:
|
|
250
|
-
validate_single(single)
|
|
251
|
-
elif isinstance(result, list):
|
|
252
|
-
assert len(result) == group_size, \
|
|
253
|
-
(f'{func_name}() validation failed: expected a grouped output for a group of '
|
|
254
|
-
f'{group_size}: a list of {group_size} arrays, got {len(result)}.')
|
|
255
|
-
for r in result:
|
|
256
|
-
validate_single(r)
|
|
257
|
-
else:
|
|
258
|
-
raise AssertionError(
|
|
259
|
-
f'{func_name}() validation failed: expected a grouped output for a group of '
|
|
260
|
-
f'{group_size}: either an array with leading dim {group_size} or a list of '
|
|
261
|
-
f'{group_size} arrays, got {type(result)}.')
|
|
262
|
-
|
|
263
|
-
|
|
264
216
|
def _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type):
|
|
265
217
|
connection_destinations = [connection_destination for connection_destination in connection_destinations
|
|
266
218
|
if not isinstance(connection_destination, SamplePreprocessResponse)]
|
|
@@ -338,7 +290,11 @@ def tensorleap_integration_test():
|
|
|
338
290
|
|
|
339
291
|
def _validate_input_args(*args, **kwargs):
|
|
340
292
|
sample_id, preprocess_response = args
|
|
341
|
-
|
|
293
|
+
assert type(sample_id) == preprocess_response.sample_id_type, (
|
|
294
|
+
f"tensorleap_integration_test validation failed: "
|
|
295
|
+
f"sample_id type ({type(sample_id).__name__}) does not match the expected "
|
|
296
|
+
f"type ({preprocess_response.sample_id_type}) from the PreprocessResponse."
|
|
297
|
+
)
|
|
342
298
|
|
|
343
299
|
def inner(*args, **kwargs):
|
|
344
300
|
if not _call_from_tl_platform:
|
|
@@ -349,7 +305,7 @@ def tensorleap_integration_test():
|
|
|
349
305
|
expected_names = inspect.getfullargspec(integration_test_function)[0][:2]
|
|
350
306
|
if len(expected_names) < 2:
|
|
351
307
|
expected_names = ['idx', 'preprocess']
|
|
352
|
-
validate_args_structure(*args, types_order=[Union[int, str
|
|
308
|
+
validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
|
|
353
309
|
func_name='integration_test', expected_names=expected_names,
|
|
354
310
|
**kwargs)
|
|
355
311
|
sample_id, preprocess_response = (
|
|
@@ -373,8 +329,6 @@ def tensorleap_integration_test():
|
|
|
373
329
|
_reset_model_loop_state()
|
|
374
330
|
ret = integration_test_function(*args, **kwargs)
|
|
375
331
|
|
|
376
|
-
global _mapping_dataset_is_grouped
|
|
377
|
-
_mapping_dataset_is_grouped = args[1].is_grouped
|
|
378
332
|
try:
|
|
379
333
|
os.environ[mapping_runtime_mode_env_var_mame] = 'True'
|
|
380
334
|
_reset_model_loop_state()
|
|
@@ -395,7 +349,6 @@ def tensorleap_integration_test():
|
|
|
395
349
|
f'Integration test is only allowed to call Tensorleap decorators. '
|
|
396
350
|
f'Ensure any arithmetics, external library use, Python logic is placed within Tensorleap decoders')
|
|
397
351
|
finally:
|
|
398
|
-
_mapping_dataset_is_grouped = False
|
|
399
352
|
if mapping_runtime_mode_env_var_mame in os.environ:
|
|
400
353
|
del os.environ[mapping_runtime_mode_env_var_mame]
|
|
401
354
|
finally:
|
|
@@ -1608,10 +1561,13 @@ def tensorleap_metadata(
|
|
|
1608
1561
|
raise Exception(f'Metadata with name {name} already exists. '
|
|
1609
1562
|
f'Please choose another')
|
|
1610
1563
|
|
|
1611
|
-
def _validate_input_args(sample_id: Union[int, str
|
|
1612
|
-
|
|
1564
|
+
def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
|
|
1565
|
+
assert type(sample_id) == preprocess_response.sample_id_type, \
|
|
1566
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1567
|
+
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
1568
|
+
f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
|
|
1613
1569
|
|
|
1614
|
-
def
|
|
1570
|
+
def _validate_result(result):
|
|
1615
1571
|
supported_result_types = (type(None), int, str, bool, float, dict, np.floating,
|
|
1616
1572
|
np.bool_, np.unsignedinteger, np.signedinteger, np.integer)
|
|
1617
1573
|
if isinstance(result, tuple):
|
|
@@ -1629,20 +1585,6 @@ def tensorleap_metadata(
|
|
|
1629
1585
|
(f'{user_function.__name__}() validation failed: '
|
|
1630
1586
|
f'Values in the return dict should be of type {str(supported_result_types)}. Got {type(value)}.')
|
|
1631
1587
|
|
|
1632
|
-
def _validate_result(result, grouped=False, group_size=None):
|
|
1633
|
-
if not grouped:
|
|
1634
|
-
_validate_single(result)
|
|
1635
|
-
return
|
|
1636
|
-
# Grouped metadata: a list of `group_size` per-sample scalars/dicts.
|
|
1637
|
-
assert isinstance(result, list), \
|
|
1638
|
-
(f'{user_function.__name__}() validation failed: expected a grouped output for a group '
|
|
1639
|
-
f'of {group_size}: a list of {group_size} metadata values, got {type(result)}.')
|
|
1640
|
-
assert len(result) == group_size, \
|
|
1641
|
-
(f'{user_function.__name__}() validation failed: expected a grouped output for a group '
|
|
1642
|
-
f'of {group_size}: a list of {group_size} metadata values, got {len(result)}.')
|
|
1643
|
-
for value in result:
|
|
1644
|
-
_validate_single(value)
|
|
1645
|
-
|
|
1646
1588
|
def inner_without_validate(sample_id, preprocess_response):
|
|
1647
1589
|
|
|
1648
1590
|
global _called_from_inside_tl_decorator
|
|
@@ -1662,16 +1604,14 @@ def tensorleap_metadata(
|
|
|
1662
1604
|
set_current('tensorleap_metadata')
|
|
1663
1605
|
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
1664
1606
|
return None
|
|
1665
|
-
validate_args_structure(*args, types_order=[Union[int, str
|
|
1607
|
+
validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
|
|
1666
1608
|
func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
|
|
1667
1609
|
sample_id, preprocess_response = args if len(args) != 0 else kwargs.values()
|
|
1668
1610
|
_validate_input_args(sample_id, preprocess_response)
|
|
1669
1611
|
|
|
1670
|
-
grouped = isinstance(sample_id, list)
|
|
1671
|
-
group_size = len(sample_id) if grouped else None
|
|
1672
1612
|
result = inner_without_validate(sample_id, preprocess_response)
|
|
1673
1613
|
|
|
1674
|
-
_validate_result(result
|
|
1614
|
+
_validate_result(result)
|
|
1675
1615
|
if not _call_from_tl_platform:
|
|
1676
1616
|
update_env_params_func("tensorleap_metadata", "v")
|
|
1677
1617
|
return result
|
|
@@ -1681,52 +1621,42 @@ def tensorleap_metadata(
|
|
|
1681
1621
|
return decorating_function
|
|
1682
1622
|
|
|
1683
1623
|
|
|
1684
|
-
def tensorleap_custom_latent_space():
|
|
1624
|
+
def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
|
|
1625
|
+
assert isinstance(use_ls_for_analysis, bool), \
|
|
1626
|
+
("tensorleap_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
|
|
1627
|
+
f"Got {type(use_ls_for_analysis)}.")
|
|
1628
|
+
|
|
1685
1629
|
def decorating_function(user_function: SectionCallableInterface):
|
|
1686
|
-
|
|
1687
|
-
|
|
1630
|
+
ls_name = name if name is not None else user_function.__name__
|
|
1631
|
+
def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
|
|
1632
|
+
assert isinstance(sample_id, (int, str)), \
|
|
1633
|
+
(f'tensorleap_custom_latent_space validation failed: '
|
|
1634
|
+
f'Argument sample_id should be either int or str. Got {type(sample_id)}.')
|
|
1635
|
+
assert isinstance(preprocess_response, PreprocessResponse), \
|
|
1636
|
+
(f'tensorleap_custom_latent_space validation failed: '
|
|
1637
|
+
f'Argument preprocess_response should be a PreprocessResponse. Got {type(preprocess_response)}.')
|
|
1638
|
+
assert type(sample_id) == preprocess_response.sample_id_type, \
|
|
1639
|
+
(f'tensorleap_custom_latent_space validation failed: '
|
|
1640
|
+
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
1641
|
+
f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
|
|
1688
1642
|
|
|
1689
|
-
def
|
|
1690
|
-
assert isinstance(
|
|
1643
|
+
def _validate_result(result):
|
|
1644
|
+
assert isinstance(result, np.ndarray), \
|
|
1691
1645
|
(f'tensorleap_custom_latent_space validation failed: '
|
|
1692
|
-
f'The return type should be a numpy array. Got {type(
|
|
1693
|
-
if
|
|
1694
|
-
flat_dim = int(np.prod(
|
|
1646
|
+
f'The return type should be a numpy array. Got {type(result)}.')
|
|
1647
|
+
if result.ndim > 1:
|
|
1648
|
+
flat_dim = int(np.prod(result.shape))
|
|
1695
1649
|
store_general_warning(
|
|
1696
|
-
key=("tensorleap_custom_latent_space_flatten", tuple(
|
|
1650
|
+
key=("tensorleap_custom_latent_space_flatten", ls_name, tuple(result.shape)),
|
|
1697
1651
|
message=(
|
|
1698
|
-
f"tensorleap_custom_latent_space returned per-sample shape {tuple(
|
|
1699
|
-
f"(ndim={
|
|
1652
|
+
f"tensorleap_custom_latent_space '{ls_name}' returned per-sample shape {tuple(result.shape)} "
|
|
1653
|
+
f"(ndim={result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
|
|
1700
1654
|
f"flatten to ({flat_dim},) before downstream visualization and clustering. "
|
|
1701
1655
|
f"If you want a different aggregation (e.g. global average pooling), do it "
|
|
1702
1656
|
f"inside your function."
|
|
1703
1657
|
),
|
|
1704
1658
|
)
|
|
1705
1659
|
|
|
1706
|
-
def _validate_result(result, grouped=False, group_size=None):
|
|
1707
|
-
if not grouped:
|
|
1708
|
-
_validate_single(result)
|
|
1709
|
-
return
|
|
1710
|
-
# Grouped: (B, *) array or list of B per-sample arrays. Validate each per-sample slice so
|
|
1711
|
-
# the per-sample flatten check/warning uses the sample shape, not the (B, *) batch shape.
|
|
1712
|
-
if isinstance(result, np.ndarray):
|
|
1713
|
-
assert result.ndim >= 1 and result.shape[0] == group_size, \
|
|
1714
|
-
(f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
|
|
1715
|
-
f'group of {group_size}: an array with leading dim {group_size}, got shape {result.shape}.')
|
|
1716
|
-
for single in result:
|
|
1717
|
-
_validate_single(single)
|
|
1718
|
-
elif isinstance(result, list):
|
|
1719
|
-
assert len(result) == group_size, \
|
|
1720
|
-
(f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
|
|
1721
|
-
f'group of {group_size}: a list of {group_size} arrays, got {len(result)}.')
|
|
1722
|
-
for single in result:
|
|
1723
|
-
_validate_single(single)
|
|
1724
|
-
else:
|
|
1725
|
-
raise AssertionError(
|
|
1726
|
-
f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
|
|
1727
|
-
f'group of {group_size}: either an array with leading dim {group_size} or a list of '
|
|
1728
|
-
f'{group_size} arrays, got {type(result)}.')
|
|
1729
|
-
|
|
1730
1660
|
def inner_without_validate(sample_id, preprocess_response):
|
|
1731
1661
|
global _called_from_inside_tl_decorator
|
|
1732
1662
|
_called_from_inside_tl_decorator += 1
|
|
@@ -1738,7 +1668,8 @@ def tensorleap_custom_latent_space():
|
|
|
1738
1668
|
|
|
1739
1669
|
return result
|
|
1740
1670
|
|
|
1741
|
-
leap_binder.set_custom_latent_space(inner_without_validate
|
|
1671
|
+
leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
|
|
1672
|
+
use_ls_for_analysis=use_ls_for_analysis)
|
|
1742
1673
|
|
|
1743
1674
|
def inner(sample_id, preprocess_response):
|
|
1744
1675
|
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
@@ -1746,11 +1677,9 @@ def tensorleap_custom_latent_space():
|
|
|
1746
1677
|
|
|
1747
1678
|
_validate_input_args(sample_id, preprocess_response)
|
|
1748
1679
|
|
|
1749
|
-
grouped = isinstance(sample_id, list)
|
|
1750
|
-
group_size = len(sample_id) if grouped else None
|
|
1751
1680
|
result = inner_without_validate(sample_id, preprocess_response)
|
|
1752
1681
|
|
|
1753
|
-
_validate_result(result
|
|
1682
|
+
_validate_result(result)
|
|
1754
1683
|
return result
|
|
1755
1684
|
|
|
1756
1685
|
return inner
|
|
@@ -2719,15 +2648,6 @@ def tensorleap_preprocess():
|
|
|
2719
2648
|
assert len(set(result)) == len(result), \
|
|
2720
2649
|
(f'{user_function.__name__}() validation failed: '
|
|
2721
2650
|
f'The return list should not contain duplicate PreprocessResponse objects.')
|
|
2722
|
-
# All-or-none: a dataset is either all grouped (nested sample_ids) or all flat.
|
|
2723
|
-
if len({response.is_grouped for response in result}) != 1:
|
|
2724
|
-
shapes = ', '.join(
|
|
2725
|
-
f"#{i}={'grouped' if response.is_grouped else 'flat'}"
|
|
2726
|
-
for i, response in enumerate(result))
|
|
2727
|
-
raise AssertionError(
|
|
2728
|
-
f'{user_function.__name__}() validation failed: '
|
|
2729
|
-
f'All PreprocessResponses must be either all grouped (nested sample_ids) '
|
|
2730
|
-
f'or all flat, but got a mix: {shapes}.')
|
|
2731
2651
|
|
|
2732
2652
|
def inner(*args, **kwargs):
|
|
2733
2653
|
if not _call_from_tl_platform:
|
|
@@ -3065,10 +2985,14 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
|
|
|
3065
2985
|
f"channel axis in the batched tensor (batch = axis 0), so 0 is invalid. Use 1 "
|
|
3066
2986
|
f"for channel-first (NCHW) and -1 for channel-last (NHWC).")
|
|
3067
2987
|
|
|
3068
|
-
def _validate_input_args(sample_id: Union[int, str
|
|
3069
|
-
|
|
2988
|
+
def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
|
|
2989
|
+
assert type(sample_id) == preprocess_response.sample_id_type, \
|
|
2990
|
+
(f'{user_function.__name__}() validation failed: '
|
|
2991
|
+
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
2992
|
+
f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
|
|
3070
2993
|
|
|
3071
|
-
def
|
|
2994
|
+
def _validate_result(result):
|
|
2995
|
+
validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray")
|
|
3072
2996
|
assert isinstance(result, np.ndarray), \
|
|
3073
2997
|
(f'{user_function.__name__}() validation failed: '
|
|
3074
2998
|
f'Unsupported return type. Should be a numpy array. Got {type(result)}.')
|
|
@@ -3078,13 +3002,6 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
|
|
|
3078
3002
|
assert channel_dim - 1 <= len(result.shape), (f'{user_function.__name__}() validation failed: '
|
|
3079
3003
|
f'The channel_dim ({channel_dim}) should be <= to the rank of the resulting input rank ({len(result.shape)}).')
|
|
3080
3004
|
|
|
3081
|
-
def _validate_result(result, grouped=False, group_size=None):
|
|
3082
|
-
if not grouped:
|
|
3083
|
-
validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray")
|
|
3084
|
-
_validate_single(result)
|
|
3085
|
-
return
|
|
3086
|
-
_validate_grouped_result(result, group_size, user_function.__name__, _validate_single)
|
|
3087
|
-
|
|
3088
3005
|
def inner_without_validate(sample_id, preprocess_response):
|
|
3089
3006
|
global _called_from_inside_tl_decorator
|
|
3090
3007
|
_called_from_inside_tl_decorator += 1
|
|
@@ -3101,29 +3018,18 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
|
|
|
3101
3018
|
def inner(*args, **kwargs):
|
|
3102
3019
|
if not _call_from_tl_platform:
|
|
3103
3020
|
set_current("tensorleap_input_encoder")
|
|
3104
|
-
validate_args_structure(*args, types_order=[Union[int, str
|
|
3021
|
+
validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
|
|
3105
3022
|
func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
|
|
3106
3023
|
sample_id, preprocess_response = args if len(args) != 0 else kwargs.values()
|
|
3107
3024
|
_validate_input_args(sample_id, preprocess_response)
|
|
3108
3025
|
|
|
3109
|
-
grouped = isinstance(sample_id, list)
|
|
3110
|
-
group_size = len(sample_id) if grouped else None
|
|
3111
3026
|
result = inner_without_validate(sample_id, preprocess_response)
|
|
3112
3027
|
|
|
3113
|
-
_validate_result(result
|
|
3028
|
+
_validate_result(result)
|
|
3114
3029
|
|
|
3115
3030
|
if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
# B=1-only model can be fed one sample at a time). A user may also return a
|
|
3119
|
-
# supported (B, *) array; split it back into per-sample rows first (mirrors
|
|
3120
|
-
# LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
|
|
3121
|
-
# a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
|
|
3122
|
-
rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
|
|
3123
|
-
result = [np.expand_dims(r, axis=0) for r in rows]
|
|
3124
|
-
else:
|
|
3125
|
-
batch_warning(result, user_function.__name__)
|
|
3126
|
-
result = np.expand_dims(result, axis=0)
|
|
3031
|
+
batch_warning(result, user_function.__name__)
|
|
3032
|
+
result = np.expand_dims(result, axis=0)
|
|
3127
3033
|
# Emit integration test event once per test
|
|
3128
3034
|
try:
|
|
3129
3035
|
emit_integration_event_once(AnalyticsEvent.INPUT_ENCODER_INTEGRATION_TEST, {
|
|
@@ -3144,15 +3050,8 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
|
|
|
3144
3050
|
inner.node_mapping = NodeMapping(name, node_mapping_type)
|
|
3145
3051
|
|
|
3146
3052
|
def mapping_inner(*args, **kwargs):
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
def __getitem__(self, key):
|
|
3150
|
-
# Indexing a grouped input group (input_list[i]) selects a sample from the
|
|
3151
|
-
# same input source; the model wiring is identical to the un-grouped input.
|
|
3152
|
-
return self
|
|
3153
|
-
else:
|
|
3154
|
-
class TempMapping:
|
|
3155
|
-
pass
|
|
3053
|
+
class TempMapping:
|
|
3054
|
+
pass
|
|
3156
3055
|
|
|
3157
3056
|
ret = TempMapping()
|
|
3158
3057
|
ret.node_mapping = mapping_inner.node_mapping
|
|
@@ -3182,10 +3081,15 @@ def tensorleap_gt_encoder(name: str):
|
|
|
3182
3081
|
raise Exception(f'GT with name {name} already exists. '
|
|
3183
3082
|
f'Please choose another')
|
|
3184
3083
|
|
|
3185
|
-
def _validate_input_args(sample_id: Union[int, str
|
|
3186
|
-
|
|
3084
|
+
def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
|
|
3085
|
+
assert type(sample_id) == preprocess_response.sample_id_type, \
|
|
3086
|
+
(f'{user_function.__name__}() validation failed: '
|
|
3087
|
+
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
3088
|
+
f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
|
|
3187
3089
|
|
|
3188
|
-
def
|
|
3090
|
+
def _validate_result(result):
|
|
3091
|
+
validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray",
|
|
3092
|
+
gt_flag=True)
|
|
3189
3093
|
assert isinstance(result, np.ndarray), \
|
|
3190
3094
|
(f'{user_function.__name__}() validation failed: '
|
|
3191
3095
|
f'Unsupported return type. Should be a numpy array. Got {type(result)}.')
|
|
@@ -3193,14 +3097,6 @@ def tensorleap_gt_encoder(name: str):
|
|
|
3193
3097
|
(f'{user_function.__name__}() validation failed: '
|
|
3194
3098
|
f'The return type should be a numpy array of type float32. Got {result.dtype}.')
|
|
3195
3099
|
|
|
3196
|
-
def _validate_result(result, grouped=False, group_size=None):
|
|
3197
|
-
if not grouped:
|
|
3198
|
-
validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray",
|
|
3199
|
-
gt_flag=True)
|
|
3200
|
-
_validate_single(result)
|
|
3201
|
-
return
|
|
3202
|
-
_validate_grouped_result(result, group_size, user_function.__name__, _validate_single)
|
|
3203
|
-
|
|
3204
3100
|
def inner_without_validate(sample_id, preprocess_response):
|
|
3205
3101
|
global _called_from_inside_tl_decorator
|
|
3206
3102
|
_called_from_inside_tl_decorator += 1
|
|
@@ -3217,29 +3113,18 @@ def tensorleap_gt_encoder(name: str):
|
|
|
3217
3113
|
def inner(*args, **kwargs):
|
|
3218
3114
|
if not _call_from_tl_platform:
|
|
3219
3115
|
set_current("tensorleap_gt_encoder")
|
|
3220
|
-
validate_args_structure(*args, types_order=[Union[int, str
|
|
3116
|
+
validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
|
|
3221
3117
|
func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
|
|
3222
3118
|
sample_id, preprocess_response = args
|
|
3223
3119
|
_validate_input_args(sample_id, preprocess_response)
|
|
3224
3120
|
|
|
3225
|
-
grouped = isinstance(sample_id, list)
|
|
3226
|
-
group_size = len(sample_id) if grouped else None
|
|
3227
3121
|
result = inner_without_validate(sample_id, preprocess_response)
|
|
3228
3122
|
|
|
3229
|
-
_validate_result(result
|
|
3123
|
+
_validate_result(result)
|
|
3230
3124
|
|
|
3231
3125
|
if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
# B=1-only model can be fed one sample at a time). A user may also return a
|
|
3235
|
-
# supported (B, *) array; split it back into per-sample rows first (mirrors
|
|
3236
|
-
# LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
|
|
3237
|
-
# a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
|
|
3238
|
-
rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
|
|
3239
|
-
result = [np.expand_dims(r, axis=0) for r in rows]
|
|
3240
|
-
else:
|
|
3241
|
-
batch_warning(result, user_function.__name__)
|
|
3242
|
-
result = np.expand_dims(result, axis=0)
|
|
3126
|
+
batch_warning(result, user_function.__name__)
|
|
3127
|
+
result = np.expand_dims(result, axis=0)
|
|
3243
3128
|
_register_chain_artifact(result, 'ground_truth')
|
|
3244
3129
|
# Emit integration test event once per test
|
|
3245
3130
|
try:
|
|
@@ -3255,15 +3140,8 @@ def tensorleap_gt_encoder(name: str):
|
|
|
3255
3140
|
inner.node_mapping = NodeMapping(name, NodeMappingType.GroundTruth)
|
|
3256
3141
|
|
|
3257
3142
|
def mapping_inner(*args, **kwargs):
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
def __getitem__(self, key):
|
|
3261
|
-
# Indexing a grouped GT group (gt_list[i]) selects a sample from the same
|
|
3262
|
-
# ground-truth source; wiring is identical to the un-grouped GT node.
|
|
3263
|
-
return self
|
|
3264
|
-
else:
|
|
3265
|
-
class TempMapping:
|
|
3266
|
-
pass
|
|
3143
|
+
class TempMapping:
|
|
3144
|
+
pass
|
|
3267
3145
|
|
|
3268
3146
|
ret = TempMapping()
|
|
3269
3147
|
ret.node_mapping = mapping_inner.node_mapping
|
code_loader/leaploader.py
CHANGED
|
@@ -60,36 +60,14 @@ class LeapLoader(LeapLoaderBase):
|
|
|
60
60
|
|
|
61
61
|
@lru_cache()
|
|
62
62
|
def exec_script(self) -> None:
|
|
63
|
-
from code_loader.inner_leap_binder import leapbinder_decorators as _leap_dec
|
|
64
63
|
try:
|
|
65
64
|
os.environ[mapping_runtime_mode_env_var_mame] = 'TRUE'
|
|
66
65
|
self.evaluate_module()
|
|
67
|
-
|
|
68
|
-
# A grouped integration test may index a grouped input/gt (image_list[i]) to wire a
|
|
69
|
-
# single sample of a group; that indexing is only valid when the mapping graph is
|
|
70
|
-
# built in grouped mode. Detect grouped-ness so the placeholder pass below matches
|
|
71
|
-
# the dataset. Preprocess is stubbed while mapping mode is on, so run it with mapping
|
|
72
|
-
# mode momentarily disabled, and cache it so _preprocess_result() doesn't re-run it.
|
|
73
|
-
is_grouped = False
|
|
74
|
-
if global_leap_binder.integration_test_func is not None:
|
|
75
|
-
del os.environ[mapping_runtime_mode_env_var_mame]
|
|
76
|
-
try:
|
|
77
|
-
preprocess_result = global_leap_binder.get_preprocess_result()
|
|
78
|
-
self._preprocess_result_cached = preprocess_result
|
|
79
|
-
is_grouped = any(r.is_grouped for r in preprocess_result.values())
|
|
80
|
-
finally:
|
|
81
|
-
os.environ[mapping_runtime_mode_env_var_mame] = 'TRUE'
|
|
82
|
-
|
|
83
|
-
_leap_dec._mapping_dataset_is_grouped = is_grouped
|
|
84
66
|
if global_leap_binder.integration_test_func is not None:
|
|
85
67
|
from code_loader.inner_leap_binder.leapbinder_decorators import \
|
|
86
68
|
_reset_model_loop_state
|
|
87
69
|
_reset_model_loop_state()
|
|
88
|
-
|
|
89
|
-
PreprocessResponse(sample_ids=[["__mapping_placeholder__"]], state=DataStateType.training)
|
|
90
|
-
if is_grouped else
|
|
91
|
-
PreprocessResponse(state=DataStateType.training, length=0))
|
|
92
|
-
global_leap_binder.integration_test_func(None, mapping_preprocess)
|
|
70
|
+
global_leap_binder.integration_test_func(None, PreprocessResponse(state=DataStateType.training, length=0))
|
|
93
71
|
except TypeError as e:
|
|
94
72
|
import traceback
|
|
95
73
|
global_leap_binder.setup_container = DatasetIntegrationSetup()
|
|
@@ -102,7 +80,6 @@ class LeapLoader(LeapLoaderBase):
|
|
|
102
80
|
raise DatasetScriptException(getattr(e, 'message', repr(e))) from e
|
|
103
81
|
finally:
|
|
104
82
|
# ensure that the environment variable is removed after the script execution
|
|
105
|
-
_leap_dec._mapping_dataset_is_grouped = False
|
|
106
83
|
if mapping_runtime_mode_env_var_mame in os.environ:
|
|
107
84
|
del os.environ[mapping_runtime_mode_env_var_mame]
|
|
108
85
|
|
|
@@ -232,7 +209,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
232
209
|
additional = self._preprocess_result().get(DataStateEnum.additional)
|
|
233
210
|
if additional is None:
|
|
234
211
|
return set()
|
|
235
|
-
return set(additional.
|
|
212
|
+
return set(additional.sample_ids)
|
|
236
213
|
|
|
237
214
|
def _resolve_synthetic(self, sample_id: Union[int, str],
|
|
238
215
|
state: Optional[DataStateEnum] = None
|
|
@@ -273,20 +250,12 @@ class LeapLoader(LeapLoaderBase):
|
|
|
273
250
|
)
|
|
274
251
|
|
|
275
252
|
preprocess_result = self._preprocess_result()
|
|
276
|
-
if state == DataStateEnum.unlabeled and sample_id not in preprocess_result[state].
|
|
253
|
+
if state == DataStateEnum.unlabeled and sample_id not in preprocess_result[state].sample_ids:
|
|
277
254
|
self._preprocess_result(update_unlabeled_preprocess=True)
|
|
278
255
|
|
|
279
256
|
metadata, metadata_is_none = self.get_metadata(state, sample_id)
|
|
280
257
|
|
|
281
|
-
|
|
282
|
-
if global_leap_binder.setup_container.custom_latent_space is not None:
|
|
283
|
-
latent_fn = global_leap_binder.setup_container.custom_latent_space.function
|
|
284
|
-
preprocess_state = preprocess_result[state]
|
|
285
|
-
if preprocess_state.is_grouped:
|
|
286
|
-
group_ids, pos = self._locate_group(preprocess_state, sample_id)
|
|
287
|
-
custom_latent_space = self._to_grouped_list(latent_fn(group_ids, preprocess_state))[pos]
|
|
288
|
-
else:
|
|
289
|
-
custom_latent_space = latent_fn(sample_id, preprocess_state)
|
|
258
|
+
custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
|
|
290
259
|
instance_mask = self._get_instances_masks(state, sample_id, instance_id)
|
|
291
260
|
sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
|
|
292
261
|
gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
|
|
@@ -294,7 +263,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
294
263
|
metadata_is_none=metadata_is_none,
|
|
295
264
|
index=sample_id,
|
|
296
265
|
state=state,
|
|
297
|
-
|
|
266
|
+
custom_latent_spaces=custom_latent_spaces,
|
|
298
267
|
instance_masks=instance_mask)
|
|
299
268
|
return sample
|
|
300
269
|
|
|
@@ -325,11 +294,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
325
294
|
handler.name, handler_result
|
|
326
295
|
)
|
|
327
296
|
|
|
328
|
-
|
|
329
|
-
if global_leap_binder.setup_container.custom_latent_space is not None:
|
|
330
|
-
custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(
|
|
331
|
-
original_sample_id, preprocess
|
|
332
|
-
)
|
|
297
|
+
custom_latent_spaces = self._get_custom_latent_spaces(original_sample_id, preprocess)
|
|
333
298
|
|
|
334
299
|
return DatasetSample(
|
|
335
300
|
inputs=inputs,
|
|
@@ -338,7 +303,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
338
303
|
metadata_is_none=metadata_is_none,
|
|
339
304
|
index=synthetic_index,
|
|
340
305
|
state=DataStateEnum.additional,
|
|
341
|
-
|
|
306
|
+
custom_latent_spaces=custom_latent_spaces,
|
|
342
307
|
instance_masks=None,
|
|
343
308
|
)
|
|
344
309
|
|
|
@@ -417,7 +382,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
417
382
|
if self.get_sample_id_type() is str:
|
|
418
383
|
max_allowed_item_size = np.dtype('<U256').itemsize
|
|
419
384
|
for state, preprocess_response in preprocess_result.items():
|
|
420
|
-
sample_ids_array = np.array(preprocess_response.
|
|
385
|
+
sample_ids_array = np.array(preprocess_response.sample_ids)
|
|
421
386
|
if sample_ids_array.dtype.itemsize > max_allowed_item_size:
|
|
422
387
|
raise Exception(f"Sample id are too long. Max allowed length is 256 charecters.")
|
|
423
388
|
|
|
@@ -513,10 +478,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
513
478
|
if preprocess_response.sample_ids_to_instance_mappings:
|
|
514
479
|
raise Exception('Element instances are not supported together with '
|
|
515
480
|
'tensorleap_autoregressive_step.')
|
|
516
|
-
|
|
517
|
-
# (GT/metadata/custom_latent) — the AR hook always drives a single scalar chain
|
|
518
|
-
# and is never handed a group.
|
|
519
|
-
sample_id = preprocess_response.flat_sample_ids[0]
|
|
481
|
+
sample_id = preprocess_response.sample_ids[0]
|
|
520
482
|
first_result = handler.function(sample_id, None, None, None, preprocess_response)
|
|
521
483
|
second_result = handler.function(sample_id, None, None, None, preprocess_response)
|
|
522
484
|
if not isinstance(first_result, tuple) or len(first_result) != 2:
|
|
@@ -597,7 +559,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
597
559
|
if handler.input_shapes is None:
|
|
598
560
|
preprocess_result = self._preprocess_result()
|
|
599
561
|
first_state_response = next(iter(preprocess_result.values()))
|
|
600
|
-
first_sample_id = first_state_response.
|
|
562
|
+
first_sample_id = first_state_response.sample_ids[0]
|
|
601
563
|
first_result = handler.function(first_sample_id, None, None, None,
|
|
602
564
|
first_state_response)
|
|
603
565
|
if not isinstance(first_result, tuple) or len(first_result) != 2 or \
|
|
@@ -936,57 +898,6 @@ class LeapLoader(LeapLoaderBase):
|
|
|
936
898
|
|
|
937
899
|
return sample_ids
|
|
938
900
|
|
|
939
|
-
@staticmethod
|
|
940
|
-
def _grouped_dict_keys(rows: List[Dict[str, Any]], handler_name: str) -> List[str]:
|
|
941
|
-
"""Keys for a group of dict-metadata rows, requiring every row to share the same keys.
|
|
942
|
-
A group must expose a uniform metadata schema; otherwise the per-key lists would silently
|
|
943
|
-
drop or KeyError on rows that differ from row 0."""
|
|
944
|
-
keys = list(rows[0].keys())
|
|
945
|
-
key_set = set(keys)
|
|
946
|
-
for pos, row in enumerate(rows):
|
|
947
|
-
if set(row.keys()) != key_set:
|
|
948
|
-
raise ValueError(
|
|
949
|
-
f"Metadata handler {handler_name!r} returned inconsistent dict keys across the "
|
|
950
|
-
f"group: row 0 has {sorted(key_set)} but row {pos} has {sorted(row.keys())}. "
|
|
951
|
-
f"All rows in a group must share the same metadata keys.")
|
|
952
|
-
return keys
|
|
953
|
-
|
|
954
|
-
@staticmethod
|
|
955
|
-
def _to_grouped_list(raw: Any) -> List[npt.NDArray[np.float32]]:
|
|
956
|
-
"""Normalize a grouped encoder result to a list of per-sample arrays.
|
|
957
|
-
|
|
958
|
-
Grouped results are NEVER stacked into a (B, *) batch. Grouping is only a storage
|
|
959
|
-
read-unit (one file load per group); assembling the model batch (B) is the engine's
|
|
960
|
-
single responsibility, so it stays the only place samples get batched. Keeping
|
|
961
|
-
per-sample arrays lets the engine batch to any B — including a B=1-only model — without
|
|
962
|
-
an intermediate stack/unstack. A user encoder that already returned a stacked (B, *)
|
|
963
|
-
array is split back into its per-sample rows so the contract is a list either way."""
|
|
964
|
-
if isinstance(raw, list):
|
|
965
|
-
return raw
|
|
966
|
-
return [row for row in np.asarray(raw)]
|
|
967
|
-
|
|
968
|
-
def _locate_group(self, preprocess_state: PreprocessResponse,
|
|
969
|
-
sample_id: Union[int, str]) -> Tuple[List[Union[int, str]], int]:
|
|
970
|
-
"""Map a sample id to (its group, its position in that group). Cached on the
|
|
971
|
-
PreprocessResponse itself (not keyed by id() on this loader) so grouped single-sample
|
|
972
|
-
fetches don't rescan the groups, and the cache can never go stale: when the response is
|
|
973
|
-
replaced (e.g. unlabeled preprocess refresh), the old object and its cache are simply
|
|
974
|
-
discarded together instead of lingering under a possibly-reused id()."""
|
|
975
|
-
mapping = preprocess_state._group_pos_cache
|
|
976
|
-
if mapping is None:
|
|
977
|
-
mapping = {}
|
|
978
|
-
for group in preprocess_state.groups:
|
|
979
|
-
for pos, sid in enumerate(group):
|
|
980
|
-
if sid in mapping:
|
|
981
|
-
raise ValueError(
|
|
982
|
-
f"Duplicate sample id {sid!r} across groups in the preprocess response; "
|
|
983
|
-
f"sample ids must be unique within and across groups.")
|
|
984
|
-
mapping[sid] = (group, pos)
|
|
985
|
-
preprocess_state._group_pos_cache = mapping
|
|
986
|
-
if sample_id not in mapping:
|
|
987
|
-
raise KeyError(f"Sample id {sample_id!r} is not present in any group of this preprocess response.")
|
|
988
|
-
return mapping[sample_id]
|
|
989
|
-
|
|
990
901
|
def _get_dataset_handlers(self, handlers: Iterable[DatasetBaseHandler],
|
|
991
902
|
state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
992
903
|
result_agg = {}
|
|
@@ -998,100 +909,12 @@ class LeapLoader(LeapLoaderBase):
|
|
|
998
909
|
sample_id = original_local_id
|
|
999
910
|
else:
|
|
1000
911
|
preprocess_state = preprocess_result[state]
|
|
1001
|
-
if preprocess_state.is_grouped:
|
|
1002
|
-
# Grouped dataset: call the encoder once with the whole group, index out the sample.
|
|
1003
|
-
group_ids, pos = self._locate_group(preprocess_state, sample_id)
|
|
1004
|
-
for handler in handlers:
|
|
1005
|
-
grouped = self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
1006
|
-
result_agg[handler.name] = grouped[pos]
|
|
1007
|
-
return result_agg
|
|
1008
912
|
for handler in handlers:
|
|
1009
913
|
handler_result = handler.function(sample_id, preprocess_state)
|
|
1010
914
|
handler_name = handler.name
|
|
1011
915
|
result_agg[handler_name] = handler_result
|
|
1012
916
|
return result_agg
|
|
1013
917
|
|
|
1014
|
-
def get_samples(self, state: DataStateEnum, group_ids: List[Union[int, str]]) -> DatasetSample:
|
|
1015
|
-
"""Group-aware fetch: hand the whole group to each encoder in a single call and return a
|
|
1016
|
-
grouped DatasetSample (inputs/gt as per-sample lists of length B, metadata as per-key lists
|
|
1017
|
-
of length B, index = the group). Grouped results are never stacked here — batching the
|
|
1018
|
-
samples into the model's B is the engine's job. This is the path a group-aware engine calls
|
|
1019
|
-
to read a file once per group. Row order matches group_ids exactly."""
|
|
1020
|
-
self.exec_script()
|
|
1021
|
-
preprocess_result = self._preprocess_result()
|
|
1022
|
-
if state == DataStateEnum.unlabeled and any(
|
|
1023
|
-
sid not in preprocess_result[state].flat_sample_ids for sid in group_ids):
|
|
1024
|
-
# Mirrors get_sample's refresh: the unlabeled preprocess can grow between calls, so a
|
|
1025
|
-
# group_id absent from the current snapshot may just not have been generated yet.
|
|
1026
|
-
self._preprocess_result(update_unlabeled_preprocess=True)
|
|
1027
|
-
preprocess_state = preprocess_result[state]
|
|
1028
|
-
assert preprocess_state.is_grouped, (
|
|
1029
|
-
"get_samples is the group-aware fetch path and requires a grouped preprocess "
|
|
1030
|
-
"response; call get_sample for a flat (non-grouped) dataset.")
|
|
1031
|
-
# All requested ids must belong to a single group (one file). A cross-group request
|
|
1032
|
-
# would force the encoder to load multiple files, defeating the one-load-per-group
|
|
1033
|
-
# contract; partitioning a scattered request by group is the engine's responsibility.
|
|
1034
|
-
distinct_groups = {id(self._locate_group(preprocess_state, sid)[0]) for sid in group_ids}
|
|
1035
|
-
assert len(distinct_groups) == 1, (
|
|
1036
|
-
f"get_samples received sample ids spanning {len(distinct_groups)} groups; a request "
|
|
1037
|
-
f"must be confined to a single group. The engine partitions cross-group requests and "
|
|
1038
|
-
f"issues one get_samples call per group.")
|
|
1039
|
-
inputs = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
1040
|
-
for handler in global_leap_binder.setup_container.inputs}
|
|
1041
|
-
autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
|
|
1042
|
-
if autoregressive_handler is not None:
|
|
1043
|
-
# AR integrations have no input encoders — the hook's first call supplies each
|
|
1044
|
-
# chain's step-0 model inputs. The hook is strictly per-scalar-chain (never handed
|
|
1045
|
-
# a group), so it is called once per flat id in the group and the per-sample results
|
|
1046
|
-
# are collected into per-key lists of length B, matching the grouped (never-stacked)
|
|
1047
|
-
# contract used for every other encoder here.
|
|
1048
|
-
step_zero_per_sample = []
|
|
1049
|
-
for sid in group_ids:
|
|
1050
|
-
step_zero_result = autoregressive_handler.function(sid, None, None, None,
|
|
1051
|
-
preprocess_state)
|
|
1052
|
-
if not isinstance(step_zero_result, tuple) or len(step_zero_result) != 2 or \
|
|
1053
|
-
not isinstance(step_zero_result[0], dict):
|
|
1054
|
-
raise Exception(
|
|
1055
|
-
'The autoregressive step hook must return a (next_inputs, state) tuple '
|
|
1056
|
-
f'with a dict of initial model inputs on its first call, got '
|
|
1057
|
-
f'{type(step_zero_result)} for sample {sid}.')
|
|
1058
|
-
self._validate_autoregressive_step_inputs(step_zero_result[0], sid)
|
|
1059
|
-
step_zero_per_sample.append(step_zero_result[0])
|
|
1060
|
-
for key in (step_zero_per_sample[0].keys() if step_zero_per_sample else []):
|
|
1061
|
-
inputs[key] = [row[key] for row in step_zero_per_sample]
|
|
1062
|
-
gt = None
|
|
1063
|
-
if state != DataStateEnum.unlabeled:
|
|
1064
|
-
gt = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
1065
|
-
for handler in global_leap_binder.setup_container.ground_truths}
|
|
1066
|
-
|
|
1067
|
-
metadata: Dict[str, Any] = {}
|
|
1068
|
-
metadata_is_none: Dict[str, Any] = {}
|
|
1069
|
-
for handler in global_leap_binder.setup_container.metadata:
|
|
1070
|
-
rows = handler.function(group_ids, preprocess_state) # list of B scalars/dicts
|
|
1071
|
-
if rows and isinstance(rows[0], dict):
|
|
1072
|
-
for key in self._grouped_dict_keys(rows, handler.name):
|
|
1073
|
-
name = "{}_{}".format(handler.name, key)
|
|
1074
|
-
converted = [self._convert_metadata_to_correct_type(name, row[key]) for row in rows]
|
|
1075
|
-
metadata[name] = [v for v, _ in converted]
|
|
1076
|
-
metadata_is_none[name] = [is_none for _, is_none in converted]
|
|
1077
|
-
else:
|
|
1078
|
-
converted = [self._convert_metadata_to_correct_type(handler.name, row) for row in rows]
|
|
1079
|
-
metadata[handler.name] = [v for v, _ in converted]
|
|
1080
|
-
metadata_is_none[handler.name] = [is_none for _, is_none in converted]
|
|
1081
|
-
|
|
1082
|
-
# custom_latent_space is group-aware like the input/GT encoders: hand it the whole group in a
|
|
1083
|
-
# single call so a file-backed latent fn loads the file once, and normalize to (B, d). This
|
|
1084
|
-
# keeps the mandatory group path non-lossy. instance_masks stay None here: they are a
|
|
1085
|
-
# per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
|
|
1086
|
-
custom_latent_space = None
|
|
1087
|
-
if global_leap_binder.setup_container.custom_latent_space is not None:
|
|
1088
|
-
latent_fn = global_leap_binder.setup_container.custom_latent_space.function
|
|
1089
|
-
custom_latent_space = self._to_grouped_list(latent_fn(group_ids, preprocess_state))
|
|
1090
|
-
|
|
1091
|
-
return DatasetSample(inputs=inputs, gt=gt, metadata=metadata, metadata_is_none=metadata_is_none,
|
|
1092
|
-
index=list(group_ids), state=state, custom_latent_space=custom_latent_space,
|
|
1093
|
-
instance_masks=None)
|
|
1094
|
-
|
|
1095
918
|
def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
1096
919
|
inputs = self._get_dataset_handlers(global_leap_binder.setup_container.inputs, state, sample_id)
|
|
1097
920
|
autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
|
|
@@ -1195,18 +1018,12 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1195
1018
|
sample_id = original_local_id
|
|
1196
1019
|
else:
|
|
1197
1020
|
preprocess_state = preprocess_result[state]
|
|
1198
|
-
group_ids, pos = (None, None)
|
|
1199
|
-
if preprocess_state.is_grouped:
|
|
1200
|
-
group_ids, pos = self._locate_group(preprocess_state, sample_id)
|
|
1201
1021
|
for handler in global_leap_binder.setup_container.metadata:
|
|
1202
1022
|
if requested_metadata_names:
|
|
1203
1023
|
if not is_metadata_name_starts_with_handler_name(handler):
|
|
1204
1024
|
continue
|
|
1205
1025
|
|
|
1206
|
-
|
|
1207
|
-
handler_result = handler.function(group_ids, preprocess_state)[pos]
|
|
1208
|
-
else:
|
|
1209
|
-
handler_result = handler.function(sample_id, preprocess_state)
|
|
1026
|
+
handler_result = handler.function(sample_id, preprocess_state)
|
|
1210
1027
|
if isinstance(handler_result, dict):
|
|
1211
1028
|
for single_metadata_name, single_metadata_result in handler_result.items():
|
|
1212
1029
|
handler_name = f'{handler.name}_{single_metadata_name}'
|
|
@@ -1225,66 +1042,6 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1225
1042
|
|
|
1226
1043
|
return result_agg, is_none
|
|
1227
1044
|
|
|
1228
|
-
def _grouped_metadata_for_group(self, preprocess_state: PreprocessResponse,
|
|
1229
|
-
group_ids: List[Union[int, str]],
|
|
1230
|
-
requested_metadata_names: Optional[List[str]]
|
|
1231
|
-
) -> Tuple[Dict[str, List[Any]], Dict[str, List[bool]]]:
|
|
1232
|
-
def is_wanted(_handler):
|
|
1233
|
-
if not requested_metadata_names:
|
|
1234
|
-
return True
|
|
1235
|
-
for metadata_name in requested_metadata_names:
|
|
1236
|
-
if metadata_name.startswith(_handler.name + '_') or metadata_name == _handler.name:
|
|
1237
|
-
return True
|
|
1238
|
-
return False
|
|
1239
|
-
|
|
1240
|
-
results: Dict[str, List[Any]] = {}
|
|
1241
|
-
is_none: Dict[str, List[bool]] = {}
|
|
1242
|
-
for handler in global_leap_binder.setup_container.metadata:
|
|
1243
|
-
if requested_metadata_names and not is_wanted(handler):
|
|
1244
|
-
continue
|
|
1245
|
-
rows = handler.function(group_ids, preprocess_state)
|
|
1246
|
-
if rows and isinstance(rows[0], dict):
|
|
1247
|
-
for key in self._grouped_dict_keys(rows, handler.name):
|
|
1248
|
-
name = f'{handler.name}_{key}'
|
|
1249
|
-
if requested_metadata_names and name not in requested_metadata_names:
|
|
1250
|
-
continue
|
|
1251
|
-
converted = [self._convert_metadata_to_correct_type(name, row[key]) for row in rows]
|
|
1252
|
-
results[name] = [v for v, _ in converted]
|
|
1253
|
-
is_none[name] = [n for _, n in converted]
|
|
1254
|
-
else:
|
|
1255
|
-
name = handler.name
|
|
1256
|
-
if requested_metadata_names and name not in requested_metadata_names:
|
|
1257
|
-
continue
|
|
1258
|
-
converted = [self._convert_metadata_to_correct_type(name, row) for row in rows]
|
|
1259
|
-
results[name] = [v for v, _ in converted]
|
|
1260
|
-
is_none[name] = [n for _, n in converted]
|
|
1261
|
-
return results, is_none
|
|
1262
|
-
|
|
1263
|
-
def get_metadata_multiple_samples(self, state: DataStateEnum, sample_ids: Union[List[int], List[str]],
|
|
1264
|
-
requested_metadata_names: Optional[List[str]] = None
|
|
1265
|
-
) -> Tuple[Dict[str, Union[List[str], List[int], List[bool], List[float]]],
|
|
1266
|
-
Dict[str, List[bool]]]:
|
|
1267
|
-
preprocess_state = self._preprocess_result().get(state)
|
|
1268
|
-
if preprocess_state is None or not preprocess_state.is_grouped:
|
|
1269
|
-
return super().get_metadata_multiple_samples(state, sample_ids, requested_metadata_names)
|
|
1270
|
-
|
|
1271
|
-
sample_id_type = self.get_sample_id_type()
|
|
1272
|
-
aggregated_results: Dict[str, List[Any]] = {}
|
|
1273
|
-
aggregated_is_none: Dict[str, List[bool]] = {}
|
|
1274
|
-
group_cache: Dict[int, Tuple[Dict[str, List[Any]], Dict[str, List[bool]]]] = {}
|
|
1275
|
-
for sample_id in sample_ids:
|
|
1276
|
-
sample_id = sample_id_type(sample_id)
|
|
1277
|
-
group_ids, pos = self._locate_group(preprocess_state, sample_id)
|
|
1278
|
-
key = id(group_ids)
|
|
1279
|
-
if key not in group_cache:
|
|
1280
|
-
group_cache[key] = self._grouped_metadata_for_group(
|
|
1281
|
-
preprocess_state, group_ids, requested_metadata_names)
|
|
1282
|
-
group_results, group_is_none = group_cache[key]
|
|
1283
|
-
for name, values in group_results.items():
|
|
1284
|
-
aggregated_results.setdefault(name, []).append(values[pos])
|
|
1285
|
-
aggregated_is_none.setdefault(name, []).append(group_is_none[name][pos])
|
|
1286
|
-
return aggregated_results, aggregated_is_none
|
|
1287
|
-
|
|
1288
1045
|
@lru_cache()
|
|
1289
1046
|
def get_sample_id_type(self) -> Type:
|
|
1290
1047
|
preprocess_results = list(self._preprocess_result().values())
|
|
@@ -1295,10 +1052,42 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1295
1052
|
|
|
1296
1053
|
return id_type
|
|
1297
1054
|
|
|
1055
|
+
@staticmethod
|
|
1056
|
+
def _get_custom_latent_spaces(
|
|
1057
|
+
sample_id: Union[int, str],
|
|
1058
|
+
preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
|
|
1059
|
+
handlers = global_leap_binder.setup_container.custom_latent_spaces
|
|
1060
|
+
if not handlers:
|
|
1061
|
+
return None
|
|
1062
|
+
return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
|
|
1063
|
+
|
|
1298
1064
|
@lru_cache()
|
|
1299
1065
|
def has_custom_latent_space_decorator(self) -> bool:
|
|
1300
1066
|
self.exec_script()
|
|
1301
|
-
return global_leap_binder.setup_container.
|
|
1067
|
+
return len(global_leap_binder.setup_container.custom_latent_spaces) > 0
|
|
1068
|
+
|
|
1069
|
+
@lru_cache()
|
|
1070
|
+
def get_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
1071
|
+
"""Ordered names of all registered custom latent spaces.
|
|
1072
|
+
|
|
1073
|
+
Registration order is the canonical index mapping consumed by the engine
|
|
1074
|
+
(name i -> user_custom_i). Returns a tuple so the lru_cache value is hashable.
|
|
1075
|
+
"""
|
|
1076
|
+
self.exec_script()
|
|
1077
|
+
return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
|
|
1078
|
+
|
|
1079
|
+
@lru_cache()
|
|
1080
|
+
def get_custom_latent_space_for_analysis(self) -> Optional[str]:
|
|
1081
|
+
"""Name of the custom latent space flagged with use_ls_for_analysis=True, if any.
|
|
1082
|
+
|
|
1083
|
+
The engine uses this latent space for the Out-Of-Distribution and Domain-Gap
|
|
1084
|
+
insights instead of the built-in defaults. Returns None when no custom latent
|
|
1085
|
+
space set the flag. At most one is flagged (enforced at registration time)."""
|
|
1086
|
+
self.exec_script()
|
|
1087
|
+
for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items():
|
|
1088
|
+
if handler.use_ls_for_analysis:
|
|
1089
|
+
return name
|
|
1090
|
+
return None
|
|
1302
1091
|
|
|
1303
1092
|
@lru_cache()
|
|
1304
1093
|
def has_autoregressive_step(self) -> bool:
|
code_loader/leaploaderbase.py
CHANGED
|
@@ -218,6 +218,14 @@ class LeapLoaderBase:
|
|
|
218
218
|
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
219
219
|
'autoregressive_visualizer_by_name.')
|
|
220
220
|
|
|
221
|
+
@abstractmethod
|
|
222
|
+
def get_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
@abstractmethod
|
|
226
|
+
def get_custom_latent_space_for_analysis(self) -> Optional[str]:
|
|
227
|
+
pass
|
|
228
|
+
|
|
221
229
|
@abstractmethod
|
|
222
230
|
def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
|
|
223
231
|
pass
|
code_loader/utils.py
CHANGED
|
@@ -17,13 +17,6 @@ from code_loader.contract.enums import DatasetMetadataType
|
|
|
17
17
|
def to_numpy_return_wrapper(encoder_function: SectionCallableInterface) -> SectionCallableInterface:
|
|
18
18
|
def numpy_encoder_function(idx: Union[int, str], samples: PreprocessResponse) -> npt.NDArray[np.float32]:
|
|
19
19
|
result = encoder_function(idx, samples)
|
|
20
|
-
if isinstance(idx, list) and isinstance(result, list) and len(result) > 0 \
|
|
21
|
-
and all(isinstance(sample, np.ndarray) for sample in result):
|
|
22
|
-
# Grouped call (idx is a list of sample ids): keep the per-sample list, never stack
|
|
23
|
-
# it into a (B, *) batch — grouping is only a storage read-unit; batching the group to
|
|
24
|
-
# the model's B is the engine's single responsibility. Gate on the call shape (idx is a
|
|
25
|
-
# list), not the result shape, so a flat encoder returning a list of arrays still stacks.
|
|
26
|
-
return result
|
|
27
20
|
numpy_result: npt.NDArray[np.float32] = np.array(result)
|
|
28
21
|
return numpy_result
|
|
29
22
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
|
|
2
2
|
code_loader/__init__.py,sha256=outxRQ0M-zMfV0QGVJmAed5qWfRmyD0TV6-goEGAzBw,406
|
|
3
3
|
code_loader/contract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
code_loader/contract/datasetclasses.py,sha256=
|
|
4
|
+
code_loader/contract/datasetclasses.py,sha256=LjYe-Ub_WzjXaloFKxwoL1sT1sWOrFSFU6XXxEoYENs,13826
|
|
5
5
|
code_loader/contract/enums.py,sha256=2q-IV_5g9lLE306DIbWA1c0tn5IhDtxsKxyV1x_Lreg,1671
|
|
6
6
|
code_loader/contract/exceptions.py,sha256=jWqu5i7t-0IG0jGRsKF4DjJdrsdpJjIYpUkN1F4RiyQ,51
|
|
7
7
|
code_loader/contract/mapping.py,sha256=sWJhpng-IkOzQnWQdMT5w2ZZ3X1Z_OOzSwCLXIS7oxE,1446
|
|
@@ -21,18 +21,18 @@ code_loader/experiment_api/types.py,sha256=MY8xFARHwdVA7p4dxyhD60ShmttgTvb4qdp1o
|
|
|
21
21
|
code_loader/experiment_api/utils.py,sha256=XZHtxge12TS4H4-8PjV3sKuhp8Ud6ojAiIzTZJEqBqc,3304
|
|
22
22
|
code_loader/experiment_api/workingspace_config_utils.py,sha256=DLzXQCg4dgTV_YgaSbeTVzq-2ja_SQw4zi7LXwKL9cY,990
|
|
23
23
|
code_loader/inner_leap_binder/__init__.py,sha256=koOlJyMNYzGbEsoIbXathSmQ-L38N_pEXH_HvL7beXU,99
|
|
24
|
-
code_loader/inner_leap_binder/leapbinder.py,sha256=
|
|
25
|
-
code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=
|
|
26
|
-
code_loader/leaploader.py,sha256=
|
|
27
|
-
code_loader/leaploaderbase.py,sha256=
|
|
24
|
+
code_loader/inner_leap_binder/leapbinder.py,sha256=MsXT_1DcbCp7dQavHHe2zTEYwGJkIPTO4xaYPMYgsZg,55223
|
|
25
|
+
code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=InZSgWLrt977fIMbw_3WQE0MWhsQhnafhO3uZuMYc0I,176589
|
|
26
|
+
code_loader/leaploader.py,sha256=2_t0IQPQNp9KcSKs4-_Ji8FK4Av8iouyK0eaRYNoUOM,69344
|
|
27
|
+
code_loader/leaploaderbase.py,sha256=Aa8wCcxojf8EBn_14W5I1gKI4OMBdpdEcNAZ_6E2eV4,10940
|
|
28
28
|
code_loader/mixpanel_tracker.py,sha256=rNwRmFifNbdUoqLQvvhhgpKczWpWiEmd8MfyJe27sxw,9131
|
|
29
29
|
code_loader/plot_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
30
|
code_loader/plot_functions/plot_functions.py,sha256=2DC-zlVaN13P4VNx5d8csgs80C6SisaeP1-Kq2LW7iM,16075
|
|
31
31
|
code_loader/plot_functions/visualize.py,sha256=gsBAYYkwMh7jIpJeDMPS8G4CW-pxwx6LznoQIvi4vpo,657
|
|
32
|
-
code_loader/utils.py,sha256=
|
|
32
|
+
code_loader/utils.py,sha256=gEJCKpDWf6p9_KoEz-0EGiJqxD6QfPgZN6zdKdtSAz8,6628
|
|
33
33
|
code_loader/visualizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
code_loader/visualizers/default_visualizers.py,sha256=onRnLE_TXfgLN4o52hQIOOhUcFexGlqJ3xSpQDVLuZM,2604
|
|
35
|
-
code_loader-1.0.
|
|
36
|
-
code_loader-1.0.
|
|
37
|
-
code_loader-1.0.
|
|
38
|
-
code_loader-1.0.
|
|
35
|
+
code_loader-1.0.197.dev1.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
|
|
36
|
+
code_loader-1.0.197.dev1.dist-info/METADATA,sha256=USKyIvkEWJ6ak0J1mWQuFQ61R6BP1t3CUbfBlCDAVSo,1095
|
|
37
|
+
code_loader-1.0.197.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
38
|
+
code_loader-1.0.197.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|