code-loader 1.0.195.dev1__py3-none-any.whl → 1.0.196.dev0__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 +45 -10
- code_loader/inner_leap_binder/leapbinder.py +99 -2
- code_loader/inner_leap_binder/leapbinder_decorators.py +846 -97
- code_loader/leaploader.py +247 -62
- code_loader/leaploaderbase.py +67 -3
- code_loader/utils.py +45 -0
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dev0.dist-info}/METADATA +1 -1
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dev0.dist-info}/RECORD +10 -10
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dev0.dist-info}/LICENSE +0 -0
- {code_loader-1.0.195.dev1.dist-info → code_loader-1.0.196.dev0.dist-info}/WHEEL +0 -0
code_loader/leaploader.py
CHANGED
|
@@ -7,7 +7,7 @@ import sys
|
|
|
7
7
|
from contextlib import redirect_stdout
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Dict, List, Iterable, Set, Union, Any, Type, Optional, Callable, Tuple
|
|
10
|
+
from typing import Dict, List, Iterable, Set, FrozenSet, Union, Any, Type, Optional, Callable, Tuple
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import numpy.typing as npt
|
|
@@ -27,7 +27,8 @@ from code_loader.contract.sim_config import FloatBounds, IntBounds, CategoricalB
|
|
|
27
27
|
from code_loader.inner_leap_binder import global_leap_binder
|
|
28
28
|
from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame
|
|
29
29
|
from code_loader.leaploaderbase import LeapLoaderBase
|
|
30
|
-
from code_loader.utils import get_root_exception_file_and_line_number, get_metadata_type_from_variable
|
|
30
|
+
from code_loader.utils import get_root_exception_file_and_line_number, get_metadata_type_from_variable, \
|
|
31
|
+
validate_autoregressive_state_types, autoregressive_nests_equal
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def _serialize_sim_bounds(bounds) -> dict:
|
|
@@ -83,6 +84,9 @@ class LeapLoader(LeapLoaderBase):
|
|
|
83
84
|
|
|
84
85
|
_leap_dec._mapping_dataset_is_grouped = is_grouped
|
|
85
86
|
if global_leap_binder.integration_test_func is not None:
|
|
87
|
+
from code_loader.inner_leap_binder.leapbinder_decorators import \
|
|
88
|
+
_reset_model_loop_state
|
|
89
|
+
_reset_model_loop_state()
|
|
86
90
|
mapping_preprocess = (
|
|
87
91
|
PreprocessResponse(sample_ids=[["__mapping_placeholder__"]], state=DataStateType.training)
|
|
88
92
|
if is_grouped else
|
|
@@ -230,7 +234,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
230
234
|
additional = self._preprocess_result().get(DataStateEnum.additional)
|
|
231
235
|
if additional is None:
|
|
232
236
|
return set()
|
|
233
|
-
return set(additional.
|
|
237
|
+
return set(additional.flat_sample_ids)
|
|
234
238
|
|
|
235
239
|
def _resolve_synthetic(self, sample_id: Union[int, str],
|
|
236
240
|
state: Optional[DataStateEnum] = None
|
|
@@ -505,31 +509,52 @@ class LeapLoader(LeapLoaderBase):
|
|
|
505
509
|
try:
|
|
506
510
|
preprocess_result = self._preprocess_result()
|
|
507
511
|
input_shapes: Dict[str, List[int]] = {}
|
|
512
|
+
expected_keys: Optional[FrozenSet[str]] = None
|
|
513
|
+
expected_keys_state = ''
|
|
508
514
|
for state, preprocess_response in preprocess_result.items():
|
|
509
515
|
if preprocess_response.sample_ids_to_instance_mappings:
|
|
510
516
|
raise Exception('Element instances are not supported together with '
|
|
511
517
|
'tensorleap_autoregressive_step.')
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
518
|
+
# Grouped preprocess is fine: grouping only amortizes the shared base reads
|
|
519
|
+
# (GT/metadata/custom_latent) — the AR hook always drives a single scalar chain
|
|
520
|
+
# and is never handed a group.
|
|
521
|
+
sample_id = preprocess_response.flat_sample_ids[0]
|
|
522
|
+
first_result = handler.function(sample_id, None, None, None, preprocess_response)
|
|
523
|
+
second_result = handler.function(sample_id, None, None, None, preprocess_response)
|
|
524
|
+
if not isinstance(first_result, tuple) or len(first_result) != 2:
|
|
525
|
+
raise Exception('The autoregressive step hook must return a (next_inputs, '
|
|
526
|
+
f'state) tuple, got {type(first_result)} '
|
|
527
|
+
f'(state: {state.name}).')
|
|
528
|
+
first_inputs, first_state = first_result
|
|
529
|
+
if not isinstance(first_inputs, dict) or not first_inputs:
|
|
516
530
|
raise Exception('The autoregressive step hook must return a dict of initial model '
|
|
517
|
-
f'inputs on its first call, got {type(
|
|
531
|
+
f'inputs on its first call, got {type(first_inputs)} '
|
|
518
532
|
f'(state: {state.name}).')
|
|
519
|
-
|
|
520
|
-
|
|
533
|
+
validate_autoregressive_state_types(first_state)
|
|
534
|
+
if not isinstance(second_result, tuple) or len(second_result) != 2 or \
|
|
535
|
+
not isinstance(second_result[0], dict) or \
|
|
536
|
+
set(second_result[0].keys()) != set(first_inputs.keys()) or \
|
|
537
|
+
not autoregressive_nests_equal(first_state, second_result[1]):
|
|
521
538
|
raise Exception('The autoregressive step hook is not deterministic — two calls '
|
|
522
539
|
'with identical arguments returned different results '
|
|
523
540
|
f'(state: {state.name}). Seed any stochasticity from '
|
|
524
541
|
'sample_id: the engine replays steps after crash recovery '
|
|
525
542
|
'and relies on identical results.')
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
f'
|
|
532
|
-
|
|
543
|
+
if expected_keys is None:
|
|
544
|
+
expected_keys = frozenset(first_inputs)
|
|
545
|
+
expected_keys_state = state.name
|
|
546
|
+
elif frozenset(first_inputs) != expected_keys:
|
|
547
|
+
raise Exception(f'The autoregressive step hook returned different model input '
|
|
548
|
+
f'keys across states: {sorted(expected_keys)} '
|
|
549
|
+
f'({expected_keys_state}) vs {sorted(first_inputs)} '
|
|
550
|
+
f'({state.name}). The key set must be identical for every '
|
|
551
|
+
f'state.')
|
|
552
|
+
for key, value in first_inputs.items():
|
|
553
|
+
if key.startswith('_'):
|
|
554
|
+
raise Exception(f'The autoregressive step hook returned model input key '
|
|
555
|
+
f'"{key}" — underscore passthrough keys were replaced by '
|
|
556
|
+
f'the explicit state channel; move passthrough values '
|
|
557
|
+
f'into the returned state. State: {state.name}.')
|
|
533
558
|
if not isinstance(value, np.ndarray):
|
|
534
559
|
raise Exception(f'The autoregressive step hook returned a non-numpy value for '
|
|
535
560
|
f'model input "{key}" ({type(value)}). State: {state.name}.')
|
|
@@ -538,7 +563,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
538
563
|
f'model input "{key}" across states: {input_shapes[key]} vs '
|
|
539
564
|
f'{list(value.shape)}. Shapes must be fixed.')
|
|
540
565
|
input_shapes[key] = list(value.shape)
|
|
541
|
-
if not
|
|
566
|
+
if not autoregressive_nests_equal(value, second_result[0][key]):
|
|
542
567
|
raise Exception('The autoregressive step hook is not deterministic — two calls '
|
|
543
568
|
'with identical arguments returned different outputs for model '
|
|
544
569
|
f'input "{key}". Seed any stochasticity from sample_id: the '
|
|
@@ -552,19 +577,39 @@ class LeapLoader(LeapLoaderBase):
|
|
|
552
577
|
test_result.is_passed = False
|
|
553
578
|
return test_result
|
|
554
579
|
|
|
580
|
+
@staticmethod
|
|
581
|
+
def _validate_autoregressive_step_inputs(step_inputs: Dict[str, Any],
|
|
582
|
+
sample_id: Union[int, str]) -> None:
|
|
583
|
+
# The parse-time check only covers the first sample of each state; the engine runtime
|
|
584
|
+
# paths must enforce the same key/value contract for every sample they touch.
|
|
585
|
+
for key, value in step_inputs.items():
|
|
586
|
+
if key.startswith('_'):
|
|
587
|
+
raise Exception(f'The autoregressive step hook returned model input key "{key}" '
|
|
588
|
+
f'for sample {sample_id} — underscore passthrough keys were '
|
|
589
|
+
'replaced by the explicit state channel; move passthrough values '
|
|
590
|
+
'into the returned state.')
|
|
591
|
+
if not isinstance(value, np.ndarray):
|
|
592
|
+
raise Exception('The autoregressive step hook returned a non-numpy value for '
|
|
593
|
+
f'model input "{key}" ({type(value).__name__}) for sample '
|
|
594
|
+
f'{sample_id}.')
|
|
595
|
+
|
|
555
596
|
def _ensure_autoregressive_input_shapes(self) -> Dict[str, List[int]]:
|
|
556
597
|
handler = global_leap_binder.setup_container.autoregressive_step
|
|
557
598
|
assert handler is not None
|
|
558
599
|
if handler.input_shapes is None:
|
|
559
600
|
preprocess_result = self._preprocess_result()
|
|
560
601
|
first_state_response = next(iter(preprocess_result.values()))
|
|
561
|
-
|
|
602
|
+
first_sample_id = first_state_response.flat_sample_ids[0]
|
|
603
|
+
first_result = handler.function(first_sample_id, None, None, None,
|
|
562
604
|
first_state_response)
|
|
563
|
-
if not isinstance(first_result,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
605
|
+
if not isinstance(first_result, tuple) or len(first_result) != 2 or \
|
|
606
|
+
not isinstance(first_result[0], dict):
|
|
607
|
+
raise Exception('The autoregressive step hook must return a (next_inputs, state) '
|
|
608
|
+
'tuple with a dict of initial model inputs on its first call, '
|
|
609
|
+
f'got {type(first_result)}.')
|
|
610
|
+
self._validate_autoregressive_step_inputs(first_result[0], first_sample_id)
|
|
611
|
+
handler.input_shapes = {key: list(value.shape)
|
|
612
|
+
for key, value in first_result[0].items()}
|
|
568
613
|
return handler.input_shapes
|
|
569
614
|
|
|
570
615
|
def run_simulation(self, sim_name, params=None, n_samples=1, seed=0,
|
|
@@ -833,6 +878,21 @@ class LeapLoader(LeapLoaderBase):
|
|
|
833
878
|
metric_inst = MetricInstance(metric.metric_handler_data.name, metric.metric_handler_data.arg_names)
|
|
834
879
|
metrics.append(metric_inst)
|
|
835
880
|
|
|
881
|
+
# Autoregressive decorators are reported alongside the regular ones so the platform sees
|
|
882
|
+
# their names; the engine tells them apart by name via get_autoregressive_decorator_names.
|
|
883
|
+
for autoregressive_metric in setup.autoregressive_metrics:
|
|
884
|
+
metrics.append(MetricInstance(autoregressive_metric.metric_handler_data.name,
|
|
885
|
+
autoregressive_metric.metric_handler_data.arg_names))
|
|
886
|
+
for autoregressive_loss in setup.autoregressive_losses:
|
|
887
|
+
custom_losses.append(CustomLossInstance(
|
|
888
|
+
autoregressive_loss.custom_loss_handler_data.name,
|
|
889
|
+
autoregressive_loss.custom_loss_handler_data.arg_names))
|
|
890
|
+
for autoregressive_visualizer in setup.autoregressive_visualizers:
|
|
891
|
+
visualizers.append(VisualizerInstance(
|
|
892
|
+
autoregressive_visualizer.visualizer_handler_data.name,
|
|
893
|
+
autoregressive_visualizer.visualizer_handler_data.type,
|
|
894
|
+
autoregressive_visualizer.visualizer_handler_data.arg_names))
|
|
895
|
+
|
|
836
896
|
simulations = []
|
|
837
897
|
for sim in setup.simulations:
|
|
838
898
|
sim_config_serialized = {
|
|
@@ -909,13 +969,12 @@ class LeapLoader(LeapLoaderBase):
|
|
|
909
969
|
|
|
910
970
|
def _locate_group(self, preprocess_state: PreprocessResponse,
|
|
911
971
|
sample_id: Union[int, str]) -> Tuple[List[Union[int, str]], int]:
|
|
912
|
-
"""Map a sample id to (its group, its position in that group). Cached
|
|
913
|
-
PreprocessResponse so grouped single-sample
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
mapping = cache.get(id(preprocess_state))
|
|
972
|
+
"""Map a sample id to (its group, its position in that group). Cached on the
|
|
973
|
+
PreprocessResponse itself (not keyed by id() on this loader) so grouped single-sample
|
|
974
|
+
fetches don't rescan the groups, and the cache can never go stale: when the response is
|
|
975
|
+
replaced (e.g. unlabeled preprocess refresh), the old object and its cache are simply
|
|
976
|
+
discarded together instead of lingering under a possibly-reused id()."""
|
|
977
|
+
mapping = preprocess_state._group_pos_cache
|
|
919
978
|
if mapping is None:
|
|
920
979
|
mapping = {}
|
|
921
980
|
for group in preprocess_state.groups:
|
|
@@ -925,7 +984,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
925
984
|
f"Duplicate sample id {sid!r} across groups in the preprocess response; "
|
|
926
985
|
f"sample ids must be unique within and across groups.")
|
|
927
986
|
mapping[sid] = (group, pos)
|
|
928
|
-
|
|
987
|
+
preprocess_state._group_pos_cache = mapping
|
|
929
988
|
if sample_id not in mapping:
|
|
930
989
|
raise KeyError(f"Sample id {sample_id!r} is not present in any group of this preprocess response.")
|
|
931
990
|
return mapping[sample_id]
|
|
@@ -961,18 +1020,47 @@ class LeapLoader(LeapLoaderBase):
|
|
|
961
1020
|
samples into the model's B is the engine's job. This is the path a group-aware engine calls
|
|
962
1021
|
to read a file once per group. Row order matches group_ids exactly."""
|
|
963
1022
|
self.exec_script()
|
|
964
|
-
|
|
965
|
-
if
|
|
966
|
-
|
|
967
|
-
#
|
|
968
|
-
#
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1023
|
+
preprocess_result = self._preprocess_result()
|
|
1024
|
+
if state == DataStateEnum.unlabeled and any(
|
|
1025
|
+
sid not in preprocess_result[state].flat_sample_ids for sid in group_ids):
|
|
1026
|
+
# Mirrors get_sample's refresh: the unlabeled preprocess can grow between calls, so a
|
|
1027
|
+
# group_id absent from the current snapshot may just not have been generated yet.
|
|
1028
|
+
self._preprocess_result(update_unlabeled_preprocess=True)
|
|
1029
|
+
preprocess_state = preprocess_result[state]
|
|
1030
|
+
assert preprocess_state.is_grouped, (
|
|
1031
|
+
"get_samples is the group-aware fetch path and requires a grouped preprocess "
|
|
1032
|
+
"response; call get_sample for a flat (non-grouped) dataset.")
|
|
1033
|
+
# All requested ids must belong to a single group (one file). A cross-group request
|
|
1034
|
+
# would force the encoder to load multiple files, defeating the one-load-per-group
|
|
1035
|
+
# contract; partitioning a scattered request by group is the engine's responsibility.
|
|
1036
|
+
distinct_groups = {id(self._locate_group(preprocess_state, sid)[0]) for sid in group_ids}
|
|
1037
|
+
assert len(distinct_groups) == 1, (
|
|
1038
|
+
f"get_samples received sample ids spanning {len(distinct_groups)} groups; a request "
|
|
1039
|
+
f"must be confined to a single group. The engine partitions cross-group requests and "
|
|
1040
|
+
f"issues one get_samples call per group.")
|
|
974
1041
|
inputs = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
975
1042
|
for handler in global_leap_binder.setup_container.inputs}
|
|
1043
|
+
autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
|
|
1044
|
+
if autoregressive_handler is not None:
|
|
1045
|
+
# AR integrations have no input encoders — the hook's first call supplies each
|
|
1046
|
+
# chain's step-0 model inputs. The hook is strictly per-scalar-chain (never handed
|
|
1047
|
+
# a group), so it is called once per flat id in the group and the per-sample results
|
|
1048
|
+
# are collected into per-key lists of length B, matching the grouped (never-stacked)
|
|
1049
|
+
# contract used for every other encoder here.
|
|
1050
|
+
step_zero_per_sample = []
|
|
1051
|
+
for sid in group_ids:
|
|
1052
|
+
step_zero_result = autoregressive_handler.function(sid, None, None, None,
|
|
1053
|
+
preprocess_state)
|
|
1054
|
+
if not isinstance(step_zero_result, tuple) or len(step_zero_result) != 2 or \
|
|
1055
|
+
not isinstance(step_zero_result[0], dict):
|
|
1056
|
+
raise Exception(
|
|
1057
|
+
'The autoregressive step hook must return a (next_inputs, state) tuple '
|
|
1058
|
+
f'with a dict of initial model inputs on its first call, got '
|
|
1059
|
+
f'{type(step_zero_result)} for sample {sid}.')
|
|
1060
|
+
self._validate_autoregressive_step_inputs(step_zero_result[0], sid)
|
|
1061
|
+
step_zero_per_sample.append(step_zero_result[0])
|
|
1062
|
+
for key in (step_zero_per_sample[0].keys() if step_zero_per_sample else []):
|
|
1063
|
+
inputs[key] = [row[key] for row in step_zero_per_sample]
|
|
976
1064
|
gt = None
|
|
977
1065
|
if state != DataStateEnum.unlabeled:
|
|
978
1066
|
gt = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
@@ -1014,13 +1102,16 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1014
1102
|
# the sample's (step-0) model inputs, so every sample-fetching flow (graph validation,
|
|
1015
1103
|
# analysis, import checks) sees a fully-populated inputs dict.
|
|
1016
1104
|
preprocess_response = self._preprocess_result()[state]
|
|
1017
|
-
|
|
1018
|
-
|
|
1105
|
+
step_zero_result = autoregressive_handler.function(sample_id, None, None, None,
|
|
1106
|
+
preprocess_response)
|
|
1107
|
+
if not isinstance(step_zero_result, tuple) or len(step_zero_result) != 2 or \
|
|
1108
|
+
not isinstance(step_zero_result[0], dict):
|
|
1019
1109
|
raise Exception(
|
|
1020
|
-
'The autoregressive step hook must return a
|
|
1021
|
-
f'first call, got {type(
|
|
1022
|
-
|
|
1023
|
-
|
|
1110
|
+
'The autoregressive step hook must return a (next_inputs, state) tuple with a '
|
|
1111
|
+
f'dict of initial model inputs on its first call, got {type(step_zero_result)} '
|
|
1112
|
+
f'for sample {sample_id}.')
|
|
1113
|
+
self._validate_autoregressive_step_inputs(step_zero_result[0], sample_id)
|
|
1114
|
+
inputs.update(step_zero_result[0])
|
|
1024
1115
|
return inputs
|
|
1025
1116
|
|
|
1026
1117
|
def _get_instances_masks(self, state: DataStateEnum, sample_id: Union[int, str], instance_id: int) -> Optional[Dict[str, ElementInstance]]:
|
|
@@ -1227,12 +1318,15 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1227
1318
|
def run_autoregressive_step(self, sample_id: Union[int, str],
|
|
1228
1319
|
prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
1229
1320
|
prev_outputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
1230
|
-
|
|
1321
|
+
chain_state: Any,
|
|
1322
|
+
state: DataStateEnum
|
|
1323
|
+
) -> Tuple[Optional[Dict[str, npt.NDArray[np.float32]]], Any]:
|
|
1231
1324
|
"""Execute the user's autoregressive step hook against the in-process preprocess result.
|
|
1232
1325
|
|
|
1233
1326
|
This is the engine rollout's per-step entry point (invoked on the generic pod). The first
|
|
1234
|
-
call per chain passes prev_inputs=None, prev_outputs=None and returns
|
|
1235
|
-
inputs; a None
|
|
1327
|
+
call per chain passes prev_inputs=None, prev_outputs=None, chain_state=None and returns
|
|
1328
|
+
the initial model inputs plus the initial state; a None next_inputs ends the chain — the
|
|
1329
|
+
terminating call still returns the final state.
|
|
1236
1330
|
"""
|
|
1237
1331
|
self.exec_script()
|
|
1238
1332
|
handler = global_leap_binder.setup_container.autoregressive_step
|
|
@@ -1240,28 +1334,119 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1240
1334
|
raise Exception('run_autoregressive_step was called but the integration has no '
|
|
1241
1335
|
'tensorleap_autoregressive_step hook.')
|
|
1242
1336
|
preprocess_response = self._preprocess_result()[state]
|
|
1243
|
-
result = handler.function(sample_id, prev_inputs, prev_outputs,
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1337
|
+
result = handler.function(sample_id, prev_inputs, prev_outputs, chain_state,
|
|
1338
|
+
preprocess_response)
|
|
1339
|
+
if not isinstance(result, tuple) or len(result) != 2:
|
|
1340
|
+
raise Exception('The autoregressive step hook must return a (next_inputs, state) '
|
|
1341
|
+
f'tuple, got {type(result)} for sample {sample_id}.')
|
|
1342
|
+
next_inputs, new_chain_state = result
|
|
1343
|
+
validate_autoregressive_state_types(new_chain_state)
|
|
1344
|
+
if next_inputs is None:
|
|
1345
|
+
if prev_inputs is None:
|
|
1346
|
+
raise Exception('The autoregressive step hook returned None for next_inputs on '
|
|
1347
|
+
f'the first call of the chain (sample {sample_id}) — the first '
|
|
1348
|
+
'call must return the initial model inputs; None would produce '
|
|
1349
|
+
'an empty chain with no forward pass.')
|
|
1350
|
+
return None, new_chain_state
|
|
1351
|
+
if not isinstance(next_inputs, dict):
|
|
1247
1352
|
raise Exception('The autoregressive step hook must return a dict of model inputs or '
|
|
1248
|
-
f'None to end the chain, got {type(
|
|
1353
|
+
f'None to end the chain, got {type(next_inputs)} for sample '
|
|
1354
|
+
f'{sample_id}.')
|
|
1249
1355
|
# Parse-time validation only exercises step 0; enforce the fixed key-set/shape contract
|
|
1250
1356
|
# on every step here so drift fails with an actionable message instead of a cryptic
|
|
1251
1357
|
# engine error on a compiled graph.
|
|
1252
1358
|
expected_shapes = self._ensure_autoregressive_input_shapes()
|
|
1253
|
-
|
|
1254
|
-
if set(model_inputs) != set(expected_shapes):
|
|
1359
|
+
if set(next_inputs) != set(expected_shapes):
|
|
1255
1360
|
raise Exception('The autoregressive step hook must return the same model input keys '
|
|
1256
1361
|
f'on every call. Expected {sorted(expected_shapes)}, got '
|
|
1257
|
-
f'{sorted(
|
|
1258
|
-
for key, value in
|
|
1259
|
-
if isinstance(value, np.ndarray)
|
|
1362
|
+
f'{sorted(next_inputs)} for sample {sample_id}.')
|
|
1363
|
+
for key, value in next_inputs.items():
|
|
1364
|
+
if not isinstance(value, np.ndarray):
|
|
1365
|
+
raise Exception('The autoregressive step hook must return numpy arrays for every '
|
|
1366
|
+
f'model input on every call — input "{key}" is a '
|
|
1367
|
+
f'{type(value).__name__} for sample {sample_id} (forgot .numpy() '
|
|
1368
|
+
'on a fed-back model output?).')
|
|
1369
|
+
if list(value.shape) != expected_shapes[key]:
|
|
1260
1370
|
raise Exception('The autoregressive step hook must return fixed tensor shapes on '
|
|
1261
1371
|
f'every call (use fixed-length padding + mask for growing '
|
|
1262
1372
|
f'sequences). Input "{key}" was {expected_shapes[key]} on the '
|
|
1263
1373
|
f'first call and {list(value.shape)} now (sample {sample_id}).')
|
|
1264
|
-
return
|
|
1374
|
+
return next_inputs, new_chain_state
|
|
1375
|
+
|
|
1376
|
+
@lru_cache()
|
|
1377
|
+
def autoregressive_visualizer_by_name(self) -> Dict[str, VisualizerHandlerData]:
|
|
1378
|
+
self.exec_script()
|
|
1379
|
+
return {handler.visualizer_handler_data.name: handler.visualizer_handler_data
|
|
1380
|
+
for handler in global_leap_binder.setup_container.autoregressive_visualizers}
|
|
1381
|
+
|
|
1382
|
+
@lru_cache()
|
|
1383
|
+
def get_autoregressive_decorator_names(self) -> Dict[str, List[str]]:
|
|
1384
|
+
self.exec_script()
|
|
1385
|
+
setup = global_leap_binder.setup_container
|
|
1386
|
+
return {
|
|
1387
|
+
'metrics': [handler.metric_handler_data.name
|
|
1388
|
+
for handler in setup.autoregressive_metrics],
|
|
1389
|
+
'losses': [handler.custom_loss_handler_data.name
|
|
1390
|
+
for handler in setup.autoregressive_losses],
|
|
1391
|
+
'visualizers': [handler.visualizer_handler_data.name
|
|
1392
|
+
for handler in setup.autoregressive_visualizers],
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
@staticmethod
|
|
1396
|
+
def _autoregressive_callable_kwargs(function: Callable[..., Any],
|
|
1397
|
+
inputs: Dict[str, npt.NDArray[np.float32]],
|
|
1398
|
+
outputs: Dict[str, npt.NDArray[np.float32]],
|
|
1399
|
+
chain_state: Any,
|
|
1400
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]
|
|
1401
|
+
) -> Dict[str, Any]:
|
|
1402
|
+
implicit_values = {'inputs': inputs, 'outputs': outputs, 'state': chain_state}
|
|
1403
|
+
kwargs: Dict[str, Any] = {}
|
|
1404
|
+
for arg_name in inspect.getfullargspec(function)[0]:
|
|
1405
|
+
if arg_name in implicit_values:
|
|
1406
|
+
kwargs[arg_name] = implicit_values[arg_name]
|
|
1407
|
+
elif arg_name in wired_args:
|
|
1408
|
+
kwargs[arg_name] = wired_args[arg_name]
|
|
1409
|
+
else:
|
|
1410
|
+
raise Exception(f'Autoregressive callable is missing wired argument '
|
|
1411
|
+
f'"{arg_name}" — the platform mapping supplies only '
|
|
1412
|
+
f'{sorted(wired_args)}.')
|
|
1413
|
+
return kwargs
|
|
1414
|
+
|
|
1415
|
+
def run_autoregressive_metric(self, name: str,
|
|
1416
|
+
inputs: Dict[str, npt.NDArray[np.float32]],
|
|
1417
|
+
outputs: Dict[str, npt.NDArray[np.float32]],
|
|
1418
|
+
chain_state: Any,
|
|
1419
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
1420
|
+
self.exec_script()
|
|
1421
|
+
for handler in global_leap_binder.setup_container.autoregressive_metrics:
|
|
1422
|
+
if handler.metric_handler_data.name == name:
|
|
1423
|
+
return handler.function(**self._autoregressive_callable_kwargs(
|
|
1424
|
+
handler.function, inputs, outputs, chain_state, wired_args))
|
|
1425
|
+
raise Exception(f'No autoregressive metric named "{name}" is registered.')
|
|
1426
|
+
|
|
1427
|
+
def run_autoregressive_loss(self, name: str,
|
|
1428
|
+
inputs: Dict[str, npt.NDArray[np.float32]],
|
|
1429
|
+
outputs: Dict[str, npt.NDArray[np.float32]],
|
|
1430
|
+
chain_state: Any,
|
|
1431
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
1432
|
+
self.exec_script()
|
|
1433
|
+
for handler in global_leap_binder.setup_container.autoregressive_losses:
|
|
1434
|
+
if handler.custom_loss_handler_data.name == name:
|
|
1435
|
+
return handler.function(**self._autoregressive_callable_kwargs(
|
|
1436
|
+
handler.function, inputs, outputs, chain_state, wired_args))
|
|
1437
|
+
raise Exception(f'No autoregressive loss named "{name}" is registered.')
|
|
1438
|
+
|
|
1439
|
+
def run_autoregressive_visualizer(self, name: str,
|
|
1440
|
+
inputs: Dict[str, npt.NDArray[np.float32]],
|
|
1441
|
+
outputs: Dict[str, npt.NDArray[np.float32]],
|
|
1442
|
+
chain_state: Any,
|
|
1443
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
1444
|
+
self.exec_script()
|
|
1445
|
+
for handler in global_leap_binder.setup_container.autoregressive_visualizers:
|
|
1446
|
+
if handler.visualizer_handler_data.name == name:
|
|
1447
|
+
return handler.function(**self._autoregressive_callable_kwargs(
|
|
1448
|
+
handler.function, inputs, outputs, chain_state, wired_args))
|
|
1449
|
+
raise Exception(f'No autoregressive visualizer named "{name}" is registered.')
|
|
1265
1450
|
|
|
1266
1451
|
def get_prediction_names_in_order(self) -> List[str]:
|
|
1267
1452
|
"""Model output names by declared prediction-type order, used to key prev_outputs for the
|
code_loader/leaploaderbase.py
CHANGED
|
@@ -149,10 +149,74 @@ class LeapLoaderBase:
|
|
|
149
149
|
def has_custom_latent_space_decorator(self) -> bool:
|
|
150
150
|
pass
|
|
151
151
|
|
|
152
|
+
# The autoregressive entry points raise instead of `pass`-ing like the abstract methods
|
|
153
|
+
# above: this class has no ABCMeta, so @abstractmethod is not enforced at runtime and an
|
|
154
|
+
# un-overridden `pass` body would return None — has_autoregressive_step would silently
|
|
155
|
+
# report False for an AR integration and the engine would skip the chain rollout with no
|
|
156
|
+
# error. A stale loader subclass keeps working for non-AR flows and fails loudly here the
|
|
157
|
+
# moment an AR flow reaches it.
|
|
158
|
+
@abstractmethod
|
|
152
159
|
def has_autoregressive_step(self) -> bool:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
161
|
+
'has_autoregressive_step — it must be overridden '
|
|
162
|
+
'(LeapLoader in-process, LeapLoaderWithRedis via RPC).')
|
|
163
|
+
|
|
164
|
+
@abstractmethod
|
|
165
|
+
def get_gt(self, state: DataStateEnum, sample_id: Union[int, str]
|
|
166
|
+
) -> Dict[str, npt.NDArray[np.float32]]:
|
|
167
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement get_gt.')
|
|
168
|
+
|
|
169
|
+
@abstractmethod
|
|
170
|
+
def get_prediction_names_in_order(self) -> List[str]:
|
|
171
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
172
|
+
'get_prediction_names_in_order.')
|
|
173
|
+
|
|
174
|
+
@abstractmethod
|
|
175
|
+
def get_autoregressive_latent_space_aggregation(self) -> str:
|
|
176
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
177
|
+
'get_autoregressive_latent_space_aggregation.')
|
|
178
|
+
|
|
179
|
+
@abstractmethod
|
|
180
|
+
def get_autoregressive_decorator_names(self) -> Dict[str, List[str]]:
|
|
181
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
182
|
+
'get_autoregressive_decorator_names.')
|
|
183
|
+
|
|
184
|
+
@abstractmethod
|
|
185
|
+
def run_autoregressive_step(self, sample_id: Union[int, str],
|
|
186
|
+
prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
187
|
+
prev_outputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
188
|
+
chain_state: Any,
|
|
189
|
+
state: DataStateEnum
|
|
190
|
+
) -> Tuple[Optional[Dict[str, npt.NDArray[np.float32]]], Any]:
|
|
191
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
192
|
+
'run_autoregressive_step.')
|
|
193
|
+
|
|
194
|
+
@abstractmethod
|
|
195
|
+
def run_autoregressive_metric(self, name: str, inputs: Dict[str, npt.NDArray[np.float32]],
|
|
196
|
+
outputs: Dict[str, npt.NDArray[np.float32]], chain_state: Any,
|
|
197
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
198
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
199
|
+
'run_autoregressive_metric.')
|
|
200
|
+
|
|
201
|
+
@abstractmethod
|
|
202
|
+
def run_autoregressive_loss(self, name: str, inputs: Dict[str, npt.NDArray[np.float32]],
|
|
203
|
+
outputs: Dict[str, npt.NDArray[np.float32]], chain_state: Any,
|
|
204
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
205
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
206
|
+
'run_autoregressive_loss.')
|
|
207
|
+
|
|
208
|
+
@abstractmethod
|
|
209
|
+
def run_autoregressive_visualizer(self, name: str, inputs: Dict[str, npt.NDArray[np.float32]],
|
|
210
|
+
outputs: Dict[str, npt.NDArray[np.float32]],
|
|
211
|
+
chain_state: Any,
|
|
212
|
+
wired_args: Dict[str, npt.NDArray[np.float32]]) -> Any:
|
|
213
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
214
|
+
'run_autoregressive_visualizer.')
|
|
215
|
+
|
|
216
|
+
@abstractmethod
|
|
217
|
+
def autoregressive_visualizer_by_name(self) -> Dict[str, VisualizerHandlerData]:
|
|
218
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
219
|
+
'autoregressive_visualizer_by_name.')
|
|
156
220
|
|
|
157
221
|
@abstractmethod
|
|
158
222
|
def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
|
code_loader/utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import sys
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from types import TracebackType
|
|
@@ -106,3 +107,47 @@ def get_metadata_type_from_variable(variable: Union[Optional[str], int, bool, Op
|
|
|
106
107
|
f"The return type should be one of [int, float, str, bool]. Got {metadata_type}")
|
|
107
108
|
return dataset_metadata_type
|
|
108
109
|
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
AUTOREGRESSIVE_STATE_LEAF_TYPES = (np.ndarray, np.generic, int, float, str, bool, type(None))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def validate_autoregressive_state_types(value: Any, path: str = 'state') -> None:
|
|
116
|
+
if isinstance(value, dict):
|
|
117
|
+
for key, item in value.items():
|
|
118
|
+
if not isinstance(key, str):
|
|
119
|
+
raise AssertionError(
|
|
120
|
+
f'autoregressive state validation failed: dict key {key!r} at {path} must be '
|
|
121
|
+
f'a string. Got {type(key).__name__}.')
|
|
122
|
+
validate_autoregressive_state_types(item, f'{path}[{key!r}]')
|
|
123
|
+
return
|
|
124
|
+
if isinstance(value, (list, tuple)):
|
|
125
|
+
for i, item in enumerate(value):
|
|
126
|
+
validate_autoregressive_state_types(item, f'{path}[{i}]')
|
|
127
|
+
return
|
|
128
|
+
if not isinstance(value, AUTOREGRESSIVE_STATE_LEAF_TYPES):
|
|
129
|
+
raise AssertionError(
|
|
130
|
+
f'autoregressive state validation failed: {path} holds a {type(value).__name__}. '
|
|
131
|
+
f'State must be built only from numpy arrays, numbers, strings, bools and nests of '
|
|
132
|
+
f'dicts/lists of those — it is serialized to the platform queue on every chain step.')
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def autoregressive_nests_equal(a: Any, b: Any) -> bool:
|
|
136
|
+
if isinstance(a, dict) or isinstance(b, dict):
|
|
137
|
+
return isinstance(a, dict) and isinstance(b, dict) and set(a) == set(b) and \
|
|
138
|
+
all(autoregressive_nests_equal(a[key], b[key]) for key in a)
|
|
139
|
+
if isinstance(a, (list, tuple)) or isinstance(b, (list, tuple)):
|
|
140
|
+
return isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)) and \
|
|
141
|
+
len(a) == len(b) and all(autoregressive_nests_equal(x, y) for x, y in zip(a, b))
|
|
142
|
+
if isinstance(a, np.ndarray) or isinstance(b, np.ndarray):
|
|
143
|
+
if not (isinstance(a, np.ndarray) and isinstance(b, np.ndarray) and a.shape == b.shape):
|
|
144
|
+
return False
|
|
145
|
+
# NaN leaves must equal themselves: this comparison backs the determinism checks, where
|
|
146
|
+
# NaN padding / running stats in a perfectly deterministic hook are legitimate.
|
|
147
|
+
if a.dtype.kind in 'fc' and b.dtype.kind in 'fc':
|
|
148
|
+
return bool(np.array_equal(a, b, equal_nan=True))
|
|
149
|
+
return bool(np.array_equal(a, b))
|
|
150
|
+
if isinstance(a, (float, np.floating)) and isinstance(b, (float, np.floating)) and \
|
|
151
|
+
math.isnan(a) and math.isnan(b):
|
|
152
|
+
return True
|
|
153
|
+
return bool(a == b)
|
|
@@ -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=CX4HO3xiuBUiXaeSBEyjAFV7i1NGS7KZ3JnLGxofH28,16164
|
|
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=hTkOL-JLtIQSaCqg8EVxnWWaHMbChrnTkgrT1Tj5AxE,54488
|
|
25
|
+
code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=1zKGCW-TgC9AYv8oc_jEplIVlnpJEwqM9o2IpGxOwTI,183070
|
|
26
|
+
code_loader/leaploader.py,sha256=k1yfKV6SBqxggUp3Yw1sNGfd50xsCaNb8-CXBsgsVY8,83459
|
|
27
|
+
code_loader/leaploaderbase.py,sha256=PvBU_2OQqBfJLDMsbK1s372954eYr4Y3O4r18y5S7uY,10739
|
|
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=mt1V191TL-6goWPnsqjW76wffGFGNdx8xdQ0J4ZT9bU,6779
|
|
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.196.dev0.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
|
|
36
|
+
code_loader-1.0.196.dev0.dist-info/METADATA,sha256=okTBjK87MBiyLc7eEgcuXgtl2XjCMCk_FSOtBTAnXQw,1095
|
|
37
|
+
code_loader-1.0.196.dev0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
38
|
+
code_loader-1.0.196.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|