code-loader 1.0.193.dev0__tar.gz → 1.0.193.dev1__tar.gz

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.
Files changed (37) hide show
  1. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/PKG-INFO +1 -1
  2. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/inner_leap_binder/leapbinder_decorators.py +23 -16
  3. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/leaploader.py +24 -3
  4. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/leaploaderbase.py +67 -3
  5. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/utils.py +11 -2
  6. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/pyproject.toml +1 -1
  7. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/LICENSE +0 -0
  8. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/README.md +0 -0
  9. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/__init__.py +0 -0
  10. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/__init__.py +0 -0
  11. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/datasetclasses.py +0 -0
  12. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/enums.py +0 -0
  13. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/exceptions.py +0 -0
  14. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/mapping.py +0 -0
  15. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/responsedataclasses.py +0 -0
  16. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/sim_config.py +0 -0
  17. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/contract/visualizer_classes.py +0 -0
  18. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/default_losses.py +0 -0
  19. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/default_metrics.py +0 -0
  20. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/__init__.py +0 -0
  21. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/api.py +0 -0
  22. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/cli_config_utils.py +0 -0
  23. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/client.py +0 -0
  24. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/epoch.py +0 -0
  25. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/experiment.py +0 -0
  26. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/experiment_context.py +0 -0
  27. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/types.py +0 -0
  28. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/utils.py +0 -0
  29. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  30. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/inner_leap_binder/__init__.py +0 -0
  31. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/inner_leap_binder/leapbinder.py +0 -0
  32. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/mixpanel_tracker.py +0 -0
  33. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/plot_functions/__init__.py +0 -0
  34. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/plot_functions/plot_functions.py +0 -0
  35. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/plot_functions/visualize.py +0 -0
  36. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.193.dev0 → code_loader-1.0.193.dev1}/code_loader/visualizers/default_visualizers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.193.dev0
3
+ Version: 1.0.193.dev1
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -2050,7 +2050,7 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
2050
2050
  for key, value in first_inputs.items():
2051
2051
  if not isinstance(value, np.ndarray):
2052
2052
  continue
2053
- assert np.array_equal(value, second_inputs[key]), error_message
2053
+ assert _nest_equal(value, second_inputs[key]), error_message
2054
2054
  assert _nest_equal(first_state, second_state), error_message
2055
2055
 
2056
2056
  def _squeeze_test_batch_dim(tensors_dict):
@@ -2087,19 +2087,24 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
2087
2087
  sample_id, prev_inputs, prev_outputs, state, preprocess_response = \
2088
2088
  _normalize_args(args, kwargs)
2089
2089
 
2090
- is_top_level_in_test = (_called_from_inside_tl_decorator == 0
2090
+ is_top_level = _called_from_inside_tl_decorator == 0
2091
+ is_top_level_in_test = (is_top_level
2091
2092
  and _called_from_inside_tl_integration_test_decorator)
2092
2093
  loop_context = _active_model_loop
2093
- if is_top_level_in_test:
2094
- if loop_context is None:
2095
- raise LeapValidationError(
2096
- f'{user_function.__name__}() validation failed: inside the integration '
2097
- f'test the autoregressive step may only be called from within the '
2098
- f'tensorleap_model_loop function including the initial call '
2099
- f'(prev_inputs=None). The platform drives the chain through the model '
2100
- f'loop only, so a hook call outside it would not run on the platform.')
2094
+ if is_top_level_in_test and loop_context is None:
2095
+ raise LeapValidationError(
2096
+ f'{user_function.__name__}() validation failed: inside the integration '
2097
+ f'test the autoregressive step may only be called from within the '
2098
+ f'tensorleap_model_loop function including the initial call '
2099
+ f'(prev_inputs=None). The platform drives the chain through the model '
2100
+ f'loop only, so a hook call outside it would not run on the platform.')
2101
+ # Outside the integration test a model loop may still be running (local debugging);
2102
+ # the loop's phase machine must advance there too, or its model proxy always raises.
2103
+ notify_loop = is_top_level and loop_context is not None
2104
+ if notify_loop:
2101
2105
  loop_context.on_hook_call(sample_id, prev_inputs, prev_outputs, state,
2102
2106
  preprocess_response)
2107
+ if is_top_level_in_test:
2103
2108
  prev_inputs = _squeeze_test_batch_dim(prev_inputs)
2104
2109
  prev_outputs = _squeeze_test_batch_dim(prev_outputs)
2105
2110
 
@@ -2123,10 +2128,10 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
2123
2128
  _validate_consistency(next_inputs)
2124
2129
  _validate_state_types(new_state)
2125
2130
 
2126
- if is_top_level_in_test:
2127
- if next_inputs is not None:
2128
- next_inputs = {key: np.expand_dims(value, axis=0)
2129
- for key, value in next_inputs.items()}
2131
+ if is_top_level_in_test and next_inputs is not None:
2132
+ next_inputs = {key: np.expand_dims(value, axis=0)
2133
+ for key, value in next_inputs.items()}
2134
+ if notify_loop:
2130
2135
  loop_context.on_hook_return(next_inputs, new_state)
2131
2136
 
2132
2137
  if not _call_from_tl_platform:
@@ -2256,9 +2261,10 @@ def tensorleap_model_loop():
2256
2261
 
2257
2262
  def _run_with_context(model, sample_id, preprocess_response, is_mapping):
2258
2263
  global _active_model_loop, _model_loop_run_in_current_test
2264
+ in_test = bool(_called_from_inside_tl_integration_test_decorator)
2259
2265
  if _active_model_loop is not None:
2260
2266
  _fail('model loops cannot nest or run concurrently.')
2261
- if _model_loop_run_in_current_test:
2267
+ if in_test and _model_loop_run_in_current_test:
2262
2268
  _fail('only one model loop run is allowed per integration test.')
2263
2269
  if model is None or model is not _last_loaded_model:
2264
2270
  _fail('the model argument must be the object returned by the '
@@ -2278,7 +2284,8 @@ def tensorleap_model_loop():
2278
2284
  preprocess_response)
2279
2285
  finally:
2280
2286
  _active_model_loop = None
2281
- _model_loop_run_in_current_test = True
2287
+ if in_test:
2288
+ _model_loop_run_in_current_test = True
2282
2289
  if context.phase == 'awaiting_first_hook':
2283
2290
  _fail('the loop never called the autoregressive step — it must start with the '
2284
2291
  'initial hook call (prev_inputs=None, prev_outputs=None, state=None).')
@@ -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
@@ -480,6 +480,8 @@ class LeapLoader(LeapLoaderBase):
480
480
  try:
481
481
  preprocess_result = self._preprocess_result()
482
482
  input_shapes: Dict[str, List[int]] = {}
483
+ expected_keys: Optional[FrozenSet[str]] = None
484
+ expected_keys_state = ''
483
485
  for state, preprocess_response in preprocess_result.items():
484
486
  if preprocess_response.sample_ids_to_instance_mappings:
485
487
  raise Exception('Element instances are not supported together with '
@@ -506,6 +508,15 @@ class LeapLoader(LeapLoaderBase):
506
508
  f'(state: {state.name}). Seed any stochasticity from '
507
509
  'sample_id: the engine replays steps after crash recovery '
508
510
  'and relies on identical results.')
511
+ if expected_keys is None:
512
+ expected_keys = frozenset(first_inputs)
513
+ expected_keys_state = state.name
514
+ elif frozenset(first_inputs) != expected_keys:
515
+ raise Exception(f'The autoregressive step hook returned different model input '
516
+ f'keys across states: {sorted(expected_keys)} '
517
+ f'({expected_keys_state}) vs {sorted(first_inputs)} '
518
+ f'({state.name}). The key set must be identical for every '
519
+ f'state.')
509
520
  for key, value in first_inputs.items():
510
521
  if key.startswith('_'):
511
522
  raise Exception(f'The autoregressive step hook returned model input key '
@@ -520,7 +531,7 @@ class LeapLoader(LeapLoaderBase):
520
531
  f'model input "{key}" across states: {input_shapes[key]} vs '
521
532
  f'{list(value.shape)}. Shapes must be fixed.')
522
533
  input_shapes[key] = list(value.shape)
523
- if not np.array_equal(value, second_result[0][key]):
534
+ if not autoregressive_nests_equal(value, second_result[0][key]):
524
535
  raise Exception('The autoregressive step hook is not deterministic — two calls '
525
536
  'with identical arguments returned different outputs for model '
526
537
  f'input "{key}". Seed any stochasticity from sample_id: the '
@@ -1051,6 +1062,11 @@ class LeapLoader(LeapLoaderBase):
1051
1062
  next_inputs, new_chain_state = result
1052
1063
  validate_autoregressive_state_types(new_chain_state)
1053
1064
  if next_inputs is None:
1065
+ if prev_inputs is None:
1066
+ raise Exception('The autoregressive step hook returned None for next_inputs on '
1067
+ f'the first call of the chain (sample {sample_id}) — the first '
1068
+ 'call must return the initial model inputs; None would produce '
1069
+ 'an empty chain with no forward pass.')
1054
1070
  return None, new_chain_state
1055
1071
  if not isinstance(next_inputs, dict):
1056
1072
  raise Exception('The autoregressive step hook must return a dict of model inputs or '
@@ -1065,7 +1081,12 @@ class LeapLoader(LeapLoaderBase):
1065
1081
  f'on every call. Expected {sorted(expected_shapes)}, got '
1066
1082
  f'{sorted(next_inputs)} for sample {sample_id}.')
1067
1083
  for key, value in next_inputs.items():
1068
- if isinstance(value, np.ndarray) and list(value.shape) != expected_shapes[key]:
1084
+ if not isinstance(value, np.ndarray):
1085
+ raise Exception('The autoregressive step hook must return numpy arrays for every '
1086
+ f'model input on every call — input "{key}" is a '
1087
+ f'{type(value).__name__} for sample {sample_id} (forgot .numpy() '
1088
+ 'on a fed-back model output?).')
1089
+ if list(value.shape) != expected_shapes[key]:
1069
1090
  raise Exception('The autoregressive step hook must return fixed tensor shapes on '
1070
1091
  f'every call (use fixed-length padding + mask for growing '
1071
1092
  f'sequences). Input "{key}" was {expected_shapes[key]} on the '
@@ -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
- # Default False so existing LeapLoaderBase implementations stay valid; the concrete
154
- # loaders that can answer (LeapLoader in-process, LeapLoaderWithRedis via RPC) override.
155
- return False
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]:
@@ -1,3 +1,4 @@
1
+ import math
1
2
  import sys
2
3
  from pathlib import Path
3
4
  from types import TracebackType
@@ -134,6 +135,14 @@ def autoregressive_nests_equal(a: Any, b: Any) -> bool:
134
135
  return isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)) and \
135
136
  len(a) == len(b) and all(autoregressive_nests_equal(x, y) for x, y in zip(a, b))
136
137
  if isinstance(a, np.ndarray) or isinstance(b, np.ndarray):
137
- return isinstance(a, np.ndarray) and isinstance(b, np.ndarray) and \
138
- a.shape == b.shape and bool(np.array_equal(a, b))
138
+ if not (isinstance(a, np.ndarray) and isinstance(b, np.ndarray) and a.shape == b.shape):
139
+ return False
140
+ # NaN leaves must equal themselves: this comparison backs the determinism checks, where
141
+ # NaN padding / running stats in a perfectly deterministic hook are legitimate.
142
+ if a.dtype.kind in 'fc' and b.dtype.kind in 'fc':
143
+ return bool(np.array_equal(a, b, equal_nan=True))
144
+ return bool(np.array_equal(a, b))
145
+ if isinstance(a, (float, np.floating)) and isinstance(b, (float, np.floating)) and \
146
+ math.isnan(a) and math.isnan(b):
147
+ return True
139
148
  return bool(a == b)
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
3
 
4
- version = "1.0.193.dev0"
4
+ version = "1.0.193.dev1"
5
5
  description = ""
6
6
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
7
7
  license = "MIT"