code-loader 1.0.192__py3-none-any.whl → 1.0.193.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.
@@ -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
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
@@ -92,15 +92,19 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
92
92
  InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
93
93
  InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
94
94
 
95
- # (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs, or None to end the chain.
96
- # First call per chain receives prev_inputs=None, prev_outputs=None and returns the initial inputs.
97
- # Keys starting with '_' are passthrough state: never fed to the model, handed back verbatim in
98
- # prev_inputs on the next call. The hook must be stateless and deterministically seeded (the engine
99
- # may replay a step after crash recovery and relies on identical results).
95
+ # (sample_id, prev_inputs, prev_outputs, state, preprocess) -> (next model inputs | None, state).
96
+ # First call per chain receives prev_inputs=None, prev_outputs=None, state=None and returns the
97
+ # initial model inputs plus the initial state. Returning next_inputs=None ends the chain the
98
+ # terminating call still returns state, so the final step participates in state aggregation.
99
+ # State is an arbitrary nest of dicts/lists holding numpy arrays, numbers, strings or bools; it is
100
+ # never fed to the model and rides the engine's redis queue on every step, so accumulate
101
+ # reductions, not raw per-step tensors. The hook must be stateless and deterministically seeded
102
+ # (the engine may replay a step after crash recovery and relies on identical results).
103
+ AutoregressiveChainState = Any
100
104
  AutoregressiveStepCallableInterface = Callable[
101
105
  [Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
102
- PreprocessResponse],
103
- Optional[Dict[str, npt.NDArray[np.float32]]]]
106
+ AutoregressiveChainState, PreprocessResponse],
107
+ Tuple[Optional[Dict[str, npt.NDArray[np.float32]]], AutoregressiveChainState]]
104
108
 
105
109
  MetadataSectionCallableInterface = Union[
106
110
  Callable[[Union[int, str], PreprocessResponse], int],
@@ -256,16 +260,42 @@ class CustomLatentSpaceHandler:
256
260
  # dominates the model inputs). 'mean': every latent space is the elementwise mean over all steps.
257
261
  AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS = ('last_step', 'mean')
258
262
 
263
+ # Reserved argument names of autoregressive metrics/losses/visualizers, fed implicitly by the
264
+ # platform from the finished chain (per-chain dicts, no batch axis). Any other argument is wired
265
+ # to a ground-truth encoder through the integration test.
266
+ AUTOREGRESSIVE_IMPLICIT_ARG_NAMES = ('inputs', 'outputs', 'state')
267
+
259
268
 
260
269
  @dataclass
261
270
  class AutoregressiveStepHandler:
262
271
  function: AutoregressiveStepCallableInterface
263
272
  name: str = 'autoregressive_step'
264
- # Per-model-input shapes (unbatched, underscore passthrough keys excluded), discovered by running
265
- # the hook's first call at parse time. Fills the role InputHandler.shape plays for input encoders.
273
+ # Per-model-input shapes (unbatched), discovered by running the hook's first call at parse
274
+ # time. Fills the role InputHandler.shape plays for input encoders.
266
275
  input_shapes: Optional[Dict[str, List[int]]] = None
267
276
  latent_space_aggregation: str = 'last_step'
268
277
 
278
+
279
+ # Per-chain, unbatched callables: called once per finished chain with the final step's tensors —
280
+ # inputs/outputs/state are fed implicitly by the platform (dicts, no batch axis; outputs keyed by
281
+ # prediction-type names), remaining args are wired ground-truth encoder results.
282
+ @dataclass
283
+ class AutoregressiveMetricHandler:
284
+ metric_handler_data: MetricHandlerData
285
+ function: CustomCallableInterfaceMultiArgs
286
+
287
+
288
+ @dataclass
289
+ class AutoregressiveLossHandler:
290
+ custom_loss_handler_data: CustomLossHandlerData
291
+ function: CustomCallableInterface
292
+
293
+
294
+ @dataclass
295
+ class AutoregressiveVisualizerHandler:
296
+ visualizer_handler_data: VisualizerHandlerData
297
+ function: VisualizerCallableInterface
298
+
269
299
  @dataclass
270
300
  class PredictionTypeHandler:
271
301
  name: str
@@ -307,6 +337,9 @@ class DatasetIntegrationSetup:
307
337
  custom_latent_space: Optional[CustomLatentSpaceHandler] = None
308
338
  simulations: List[SimulationHandler] = field(default_factory=list)
309
339
  autoregressive_step: Optional[AutoregressiveStepHandler] = None
340
+ autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
341
+ autoregressive_losses: List[AutoregressiveLossHandler] = field(default_factory=list)
342
+ autoregressive_visualizers: List[AutoregressiveVisualizerHandler] = field(default_factory=list)
310
343
 
311
344
 
312
345
  @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
@@ -553,6 +554,83 @@ class LeapBinder:
553
554
 
554
555
  self.set_metadata(builtin_chain_metadata, 'builtin_chain')
555
556
 
557
+ def _autoregressive_arg_names(self, function: Callable[..., Any], decorator_name: str,
558
+ name: str) -> List[str]:
559
+ """Split an autoregressive callable's signature into implicit chain args (validated
560
+ present) and wired arg names (returned) — the wired args connect to ground-truth
561
+ encoders through the integration test."""
562
+ argspec = inspect.getfullargspec(function)
563
+ for arg_name, arg_type in argspec.annotations.items():
564
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
565
+ if arg_type == SamplePreprocessResponse:
566
+ raise Exception(
567
+ f'{decorator_name} "{name}": SamplePreprocessResponse arguments are not '
568
+ f'supported for autoregressive decorators yet. Derive what you need inside '
569
+ f'the autoregressive step hook and carry it in the state.')
570
+ implicit = [arg_name for arg_name in argspec[0]
571
+ if arg_name in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
572
+ if not implicit:
573
+ raise Exception(
574
+ f'{decorator_name} "{name}": the function must declare at least one of the '
575
+ f'implicit chain arguments {list(AUTOREGRESSIVE_IMPLICIT_ARG_NAMES)} — they are '
576
+ f'fed by the platform from the finished chain.')
577
+ return [arg_name for arg_name in argspec[0]
578
+ if arg_name not in AUTOREGRESSIVE_IMPLICIT_ARG_NAMES]
579
+
580
+ def _assert_unique_metric_name(self, name: str) -> None:
581
+ existing = [handler.metric_handler_data.name for handler in self.setup_container.metrics] \
582
+ + [handler.metric_handler_data.name
583
+ for handler in self.setup_container.autoregressive_metrics]
584
+ if name in existing:
585
+ raise Exception(f'Metric with name {name} already exists. Please choose another')
586
+
587
+ def _assert_unique_loss_name(self, name: str) -> None:
588
+ existing = [handler.custom_loss_handler_data.name
589
+ for handler in self.setup_container.custom_loss_handlers] \
590
+ + [handler.custom_loss_handler_data.name
591
+ for handler in self.setup_container.autoregressive_losses]
592
+ if name in existing:
593
+ raise Exception(f'Custom loss with name {name} already exists. Please choose another')
594
+
595
+ def _assert_unique_visualizer_name(self, name: str) -> None:
596
+ existing = [handler.visualizer_handler_data.name
597
+ for handler in self.setup_container.visualizers] \
598
+ + [handler.visualizer_handler_data.name
599
+ for handler in self.setup_container.autoregressive_visualizers]
600
+ if name in existing:
601
+ raise Exception(f'Visualizer with name {name} already exists. Please choose another')
602
+
603
+ def add_autoregressive_metric(self, function: CustomCallableInterfaceMultiArgs, name: str,
604
+ direction: Optional[Union[MetricDirection, Dict[str, MetricDirection]]]
605
+ = MetricDirection.Downward,
606
+ compute_insights: Optional[Union[bool, Dict[str, bool]]] = None) -> None:
607
+ self._assert_unique_metric_name(name)
608
+ arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_metric',
609
+ name)
610
+ metric_handler_data = MetricHandlerData(name, arg_names, direction, compute_insights)
611
+ self.setup_container.autoregressive_metrics.append(
612
+ AutoregressiveMetricHandler(metric_handler_data, function))
613
+
614
+ def add_autoregressive_loss(self, function: CustomCallableInterface, name: str) -> None:
615
+ self._assert_unique_loss_name(name)
616
+ arg_names = self._autoregressive_arg_names(function, 'tensorleap_autoregressive_loss',
617
+ name)
618
+ self.setup_container.autoregressive_losses.append(
619
+ AutoregressiveLossHandler(CustomLossHandlerData(name, arg_names), function))
620
+
621
+ def add_autoregressive_visualizer(self, function: VisualizerCallableInterface, name: str,
622
+ visualizer_type: LeapDataType) -> None:
623
+ self._assert_unique_visualizer_name(name)
624
+ if visualizer_type.value not in map_leap_data_type_to_visualizer_class:
625
+ raise Exception(
626
+ f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, '
627
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
628
+ arg_names = self._autoregressive_arg_names(
629
+ function, 'tensorleap_autoregressive_visualizer', name)
630
+ self.setup_container.autoregressive_visualizers.append(
631
+ AutoregressiveVisualizerHandler(VisualizerHandlerData(name, visualizer_type,
632
+ arg_names), function))
633
+
556
634
  def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
557
635
  kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
558
636
  """
@@ -778,6 +856,23 @@ class LeapBinder:
778
856
  appear in any order in the integration file.
779
857
  """
780
858
  if self.setup_container.autoregressive_step is None:
859
+ declared_autoregressive = [
860
+ ('tensorleap_autoregressive_metric',
861
+ [handler.metric_handler_data.name
862
+ for handler in self.setup_container.autoregressive_metrics]),
863
+ ('tensorleap_autoregressive_loss',
864
+ [handler.custom_loss_handler_data.name
865
+ for handler in self.setup_container.autoregressive_losses]),
866
+ ('tensorleap_autoregressive_visualizer',
867
+ [handler.visualizer_handler_data.name
868
+ for handler in self.setup_container.autoregressive_visualizers]),
869
+ ]
870
+ for decorator_name, names in declared_autoregressive:
871
+ if names:
872
+ raise Exception(
873
+ f'{decorator_name} {names} requires a tensorleap_autoregressive_step '
874
+ f'hook — autoregressive decorators consume the finished chain the hook '
875
+ f'drives.')
781
876
  return
782
877
  if self.setup_container.inputs:
783
878
  raise Exception(