code-loader 1.0.195.dev1__tar.gz → 1.0.196.dev0__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.195.dev1 → code_loader-1.0.196.dev0}/PKG-INFO +1 -1
  2. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/datasetclasses.py +45 -10
  3. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/inner_leap_binder/leapbinder.py +99 -2
  4. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/inner_leap_binder/leapbinder_decorators.py +846 -97
  5. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/leaploader.py +247 -62
  6. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/leaploaderbase.py +67 -3
  7. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/utils.py +45 -0
  8. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/pyproject.toml +1 -1
  9. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/LICENSE +0 -0
  10. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/README.md +0 -0
  11. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/__init__.py +0 -0
  12. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/__init__.py +0 -0
  13. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/enums.py +0 -0
  14. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/exceptions.py +0 -0
  15. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/mapping.py +0 -0
  16. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/responsedataclasses.py +0 -0
  17. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/sim_config.py +0 -0
  18. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/contract/visualizer_classes.py +0 -0
  19. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/default_losses.py +0 -0
  20. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/default_metrics.py +0 -0
  21. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/__init__.py +0 -0
  22. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/api.py +0 -0
  23. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/cli_config_utils.py +0 -0
  24. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/client.py +0 -0
  25. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/epoch.py +0 -0
  26. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/experiment.py +0 -0
  27. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/experiment_context.py +0 -0
  28. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/types.py +0 -0
  29. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/utils.py +0 -0
  30. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  31. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/inner_leap_binder/__init__.py +0 -0
  32. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/mixpanel_tracker.py +0 -0
  33. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/plot_functions/__init__.py +0 -0
  34. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/plot_functions/plot_functions.py +0 -0
  35. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/plot_functions/visualize.py +0 -0
  36. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.195.dev1 → code_loader-1.0.196.dev0}/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.195.dev1
3
+ Version: 1.0.196.dev0
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  import warnings
2
2
  from dataclasses import dataclass, field
3
- from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, cast
3
+ from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, Tuple, cast
4
4
  import re
5
5
  import numpy as np
6
6
  import numpy.typing as npt
@@ -48,6 +48,8 @@ class PreprocessResponse:
48
48
  instance_to_sample_ids_mappings: Optional[Dict[str, str]] = None # in use only for element instance
49
49
  tl_generated: bool = False
50
50
  _grouped: bool = field(default=False, init=False, repr=False, compare=False)
51
+ _group_pos_cache: Optional[Dict[SampleId, Tuple[List[SampleId], int]]] = field(
52
+ default=None, init=False, repr=False, compare=False)
51
53
 
52
54
  def __post_init__(self) -> None:
53
55
  assert self.sample_ids_to_instance_mappings is None, f"Keep sample_ids_to_instance_mappings None when initializing PreprocessResponse"
@@ -138,15 +140,19 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
138
140
  InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
139
141
  InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
140
142
 
141
- # (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs, or None to end the chain.
142
- # First call per chain receives prev_inputs=None, prev_outputs=None and returns the initial inputs.
143
- # Keys starting with '_' are passthrough state: never fed to the model, handed back verbatim in
144
- # prev_inputs on the next call. The hook must be stateless and deterministically seeded (the engine
145
- # may replay a step after crash recovery and relies on identical results).
143
+ # (sample_id, prev_inputs, prev_outputs, state, preprocess) -> (next model inputs | None, state).
144
+ # First call per chain receives prev_inputs=None, prev_outputs=None, state=None and returns the
145
+ # initial model inputs plus the initial state. Returning next_inputs=None ends the chain the
146
+ # terminating call still returns state, so the final step participates in state aggregation.
147
+ # State is an arbitrary nest of dicts/lists holding numpy arrays, numbers, strings or bools; it is
148
+ # never fed to the model and rides the engine's redis queue on every step, so accumulate
149
+ # reductions, not raw per-step tensors. The hook must be stateless and deterministically seeded
150
+ # (the engine may replay a step after crash recovery and relies on identical results).
151
+ AutoregressiveChainState = Any
146
152
  AutoregressiveStepCallableInterface = Callable[
147
153
  [Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
148
- PreprocessResponse],
149
- Optional[Dict[str, npt.NDArray[np.float32]]]]
154
+ AutoregressiveChainState, PreprocessResponse],
155
+ Tuple[Optional[Dict[str, npt.NDArray[np.float32]]], AutoregressiveChainState]]
150
156
 
151
157
  MetadataSectionCallableInterface = Union[
152
158
  Callable[[Union[int, str], PreprocessResponse], int],
@@ -302,16 +308,42 @@ class CustomLatentSpaceHandler:
302
308
  # dominates the model inputs). 'mean': every latent space is the elementwise mean over all steps.
303
309
  AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS = ('last_step', 'mean')
304
310
 
311
+ # Reserved argument names of autoregressive metrics/losses/visualizers, fed implicitly by the
312
+ # platform from the finished chain (per-chain dicts, no batch axis). Any other argument is wired
313
+ # to a ground-truth encoder through the integration test.
314
+ AUTOREGRESSIVE_IMPLICIT_ARG_NAMES = ('inputs', 'outputs', 'state')
315
+
305
316
 
306
317
  @dataclass
307
318
  class AutoregressiveStepHandler:
308
319
  function: AutoregressiveStepCallableInterface
309
320
  name: str = 'autoregressive_step'
310
- # Per-model-input shapes (unbatched, underscore passthrough keys excluded), discovered by running
311
- # the hook's first call at parse time. Fills the role InputHandler.shape plays for input encoders.
321
+ # Per-model-input shapes (unbatched), discovered by running the hook's first call at parse
322
+ # time. Fills the role InputHandler.shape plays for input encoders.
312
323
  input_shapes: Optional[Dict[str, List[int]]] = None
313
324
  latent_space_aggregation: str = 'last_step'
314
325
 
326
+
327
+ # Per-chain, unbatched callables: called once per finished chain with the final step's tensors —
328
+ # inputs/outputs/state are fed implicitly by the platform (dicts, no batch axis; outputs keyed by
329
+ # prediction-type names), remaining args are wired ground-truth encoder results.
330
+ @dataclass
331
+ class AutoregressiveMetricHandler:
332
+ metric_handler_data: MetricHandlerData
333
+ function: CustomCallableInterfaceMultiArgs
334
+
335
+
336
+ @dataclass
337
+ class AutoregressiveLossHandler:
338
+ custom_loss_handler_data: CustomLossHandlerData
339
+ function: CustomCallableInterface
340
+
341
+
342
+ @dataclass
343
+ class AutoregressiveVisualizerHandler:
344
+ visualizer_handler_data: VisualizerHandlerData
345
+ function: VisualizerCallableInterface
346
+
315
347
  @dataclass
316
348
  class PredictionTypeHandler:
317
349
  name: str
@@ -353,6 +385,9 @@ class DatasetIntegrationSetup:
353
385
  custom_latent_space: Optional[CustomLatentSpaceHandler] = None
354
386
  simulations: List[SimulationHandler] = field(default_factory=list)
355
387
  autoregressive_step: Optional[AutoregressiveStepHandler] = None
388
+ autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
389
+ autoregressive_losses: List[AutoregressiveLossHandler] = field(default_factory=list)
390
+ autoregressive_visualizers: List[AutoregressiveVisualizerHandler] = field(default_factory=list)
356
391
 
357
392
 
358
393
  @dataclass
@@ -14,7 +14,8 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
14
14
  RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
15
15
  ElementInstanceMasksHandler, InstanceCallableInterface, CustomLatentSpaceHandler, InstanceMetricHandler, \
16
16
  SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
17
- AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS
17
+ AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
18
+ AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
18
19
  from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
19
20
  from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
20
21
  from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
@@ -548,11 +549,90 @@ class LeapBinder:
548
549
  # Builtin chain metadata, declared at parse time so it survives the reporter's
549
550
  # metadata type mapping; the placeholder values are overwritten by the engine when a
550
551
  # chain finalizes (realized length, truncated-by-safety-cap flag).
551
- def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
552
+ def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Any:
553
+ if isinstance(idx, list):
554
+ return [{'length': -1, 'truncated': False} for _ in idx]
552
555
  return {'length': -1, 'truncated': False}
553
556
 
554
557
  self.set_metadata(builtin_chain_metadata, 'builtin_chain')
555
558
 
559
+ def _autoregressive_arg_names(self, function: Callable[..., Any], decorator_name: str,
560
+ name: str) -> List[str]:
561
+ """Split an autoregressive callable's signature into implicit chain args (validated
562
+ present) and wired arg names (returned) — the wired args connect to ground-truth
563
+ encoders through the integration test."""
564
+ argspec = inspect.getfullargspec(function)
565
+ for arg_name, arg_type in argspec.annotations.items():
566
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
567
+ if arg_type == SamplePreprocessResponse:
568
+ raise Exception(
569
+ f'{decorator_name} "{name}": SamplePreprocessResponse arguments are not '
570
+ f'supported for autoregressive decorators yet. Derive what you need inside '
571
+ f'the autoregressive step hook and carry it in the state.')
572
+ implicit = [arg_name for arg_name in argspec[0]
573
+ if arg_name in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
574
+ if not implicit:
575
+ raise Exception(
576
+ f'{decorator_name} "{name}": the function must declare at least one of the '
577
+ f'implicit chain arguments {list(AUTOREGRESSIVE_IMPLICIT_ARG_NAMES)} — they are '
578
+ f'fed by the platform from the finished chain.')
579
+ return [arg_name for arg_name in argspec[0]
580
+ if arg_name not in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
581
+
582
+ def _assert_unique_metric_name(self, name: str) -> None:
583
+ existing = [handler.metric_handler_data.name for handler in self.setup_container.metrics] \
584
+ + [handler.metric_handler_data.name
585
+ for handler in self.setup_container.autoregressive_metrics]
586
+ if name in existing:
587
+ raise Exception(f'Metric with name {name} already exists. Please choose another')
588
+
589
+ def _assert_unique_loss_name(self, name: str) -> None:
590
+ existing = [handler.custom_loss_handler_data.name
591
+ for handler in self.setup_container.custom_loss_handlers] \
592
+ + [handler.custom_loss_handler_data.name
593
+ for handler in self.setup_container.autoregressive_losses]
594
+ if name in existing:
595
+ raise Exception(f'Custom loss with name {name} already exists. Please choose another')
596
+
597
+ def _assert_unique_visualizer_name(self, name: str) -> None:
598
+ existing = [handler.visualizer_handler_data.name
599
+ for handler in self.setup_container.visualizers] \
600
+ + [handler.visualizer_handler_data.name
601
+ for handler in self.setup_container.autoregressive_visualizers]
602
+ if name in existing:
603
+ raise Exception(f'Visualizer with name {name} already exists. Please choose another')
604
+
605
+ def add_autoregressive_metric(self, function: CustomCallableInterfaceMultiArgs, name: str,
606
+ direction: Optional[Union[MetricDirection, Dict[str, MetricDirection]]]
607
+ = MetricDirection.Downward,
608
+ compute_insights: Optional[Union[bool, Dict[str, bool]]] = None) -> None:
609
+ self._assert_unique_metric_name(name)
610
+ arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_metric',
611
+ name)
612
+ metric_handler_data = MetricHandlerData(name, arg_names, direction, compute_insights)
613
+ self.setup_container.autoregressive_metrics.append(
614
+ AutoregressiveMetricHandler(metric_handler_data, function))
615
+
616
+ def add_autoregressive_loss(self, function: CustomCallableInterface, name: str) -> None:
617
+ self._assert_unique_loss_name(name)
618
+ arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_loss',
619
+ name)
620
+ self.setup_container.autoregressive_losses.append(
621
+ AutoregressiveLossHandler(CustomLossHandlerData(name, arg_names), function))
622
+
623
+ def add_autoregressive_visualizer(self, function: VisualizerCallableInterface, name: str,
624
+ visualizer_type: LeapDataType) -> None:
625
+ self._assert_unique_visualizer_name(name)
626
+ if visualizer_type.value not in map_leap_data_type_to_visualizer_class:
627
+ raise Exception(
628
+ f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, '
629
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
630
+ arg_names = self._autoregressive_arg_names(
631
+ function, 'tensorleap_autoregressive_visualizer', name)
632
+ self.setup_container.autoregressive_visualizers.append(
633
+ AutoregressiveVisualizerHandler(VisualizerHandlerData(name, visualizer_type,
634
+ arg_names), function))
635
+
556
636
  def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
557
637
  kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
558
638
  """
@@ -796,6 +876,23 @@ class LeapBinder:
796
876
  appear in any order in the integration file.
797
877
  """
798
878
  if self.setup_container.autoregressive_step is None:
879
+ declared_autoregressive = [
880
+ ('tensorleap_autoregressive_metric',
881
+ [handler.metric_handler_data.name
882
+ for handler in self.setup_container.autoregressive_metrics]),
883
+ ('tensorleap_autoregressive_loss',
884
+ [handler.custom_loss_handler_data.name
885
+ for handler in self.setup_container.autoregressive_losses]),
886
+ ('tensorleap_autoregressive_visualizer',
887
+ [handler.visualizer_handler_data.name
888
+ for handler in self.setup_container.autoregressive_visualizers]),
889
+ ]
890
+ for decorator_name, names in declared_autoregressive:
891
+ if names:
892
+ raise Exception(
893
+ f'{decorator_name} {names} requires a tensorleap_autoregressive_step '
894
+ f'hook — autoregressive decorators consume the finished chain the hook '
895
+ f'drives.')
799
896
  return
800
897
  if self.setup_container.inputs:
801
898
  raise Exception(