code-loader 1.0.196.dev0__tar.gz → 1.0.197.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.196.dev0 → code_loader-1.0.197.dev0}/PKG-INFO +1 -1
  2. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/datasetclasses.py +11 -59
  3. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/inner_leap_binder/leapbinder.py +27 -31
  4. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/inner_leap_binder/leapbinder_decorators.py +68 -190
  5. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/leaploader.py +31 -257
  6. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/leaploaderbase.py +4 -0
  7. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/utils.py +23 -22
  8. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/pyproject.toml +2 -1
  9. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/LICENSE +0 -0
  10. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/README.md +0 -0
  11. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/__init__.py +0 -0
  12. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/__init__.py +0 -0
  13. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/enums.py +0 -0
  14. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/exceptions.py +0 -0
  15. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/mapping.py +0 -0
  16. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/responsedataclasses.py +0 -0
  17. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/sim_config.py +0 -0
  18. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/contract/visualizer_classes.py +0 -0
  19. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/default_losses.py +0 -0
  20. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/default_metrics.py +0 -0
  21. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/__init__.py +0 -0
  22. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/api.py +0 -0
  23. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/cli_config_utils.py +0 -0
  24. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/client.py +0 -0
  25. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/epoch.py +0 -0
  26. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/experiment.py +0 -0
  27. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/experiment_context.py +0 -0
  28. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/types.py +0 -0
  29. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/utils.py +0 -0
  30. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  31. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/inner_leap_binder/__init__.py +0 -0
  32. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/mixpanel_tracker.py +0 -0
  33. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/plot_functions/__init__.py +0 -0
  34. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/plot_functions/plot_functions.py +0 -0
  35. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/plot_functions/visualize.py +0 -0
  36. {code_loader-1.0.196.dev0 → code_loader-1.0.197.dev0}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.196.dev0 → code_loader-1.0.197.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.196.dev0
3
+ Version: 1.0.197.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, Tuple, cast
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[SampleId], List[List[SampleId]]]] = None
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 = cast(List[SampleId], [i for i in range(self.length)])
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._grouped = len(self.sample_ids) > 0 and isinstance(self.sample_ids[0], list)
64
- if self._grouped:
65
- groups = cast(List[List[SampleId]], self.sample_ids)
66
- assert len(groups) >= 1, "Grouped PreprocessResponse must have at least one group."
67
- for group in groups:
68
- assert isinstance(group, list) and len(group) > 0, \
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
@@ -382,7 +334,7 @@ class DatasetIntegrationSetup:
382
334
  metrics: List[MetricHandler] = field(default_factory=list)
383
335
  instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
384
336
  custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
385
- custom_latent_space: Optional[CustomLatentSpaceHandler] = None
337
+ custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
386
338
  simulations: List[SimulationHandler] = field(default_factory=list)
387
339
  autoregressive_step: Optional[AutoregressiveStepHandler] = None
388
340
  autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
@@ -398,6 +350,6 @@ class DatasetSample:
398
350
  metadata_is_none: Dict[str, bool]
399
351
  index: Union[int, str]
400
352
  state: DataStateEnum
401
- custom_latent_space: Optional[npt.NDArray[np.float32]] = None
353
+ custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
402
354
  instance_masks: Optional[Dict[str, ElementInstance]] = None
403
355
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  import inspect
3
- from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast
3
+ from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args
4
4
 
5
5
  import numpy as np
6
6
  import numpy.typing as npt
@@ -513,18 +513,34 @@ class LeapBinder:
513
513
  """
514
514
  self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
515
515
 
516
- def set_custom_latent_space(self, function: SectionCallableInterface) -> None:
516
+ def set_custom_latent_space(self, function: SectionCallableInterface,
517
+ name: Optional[str] = None) -> None:
517
518
  """
518
- Set a custom latent space function.
519
+ Register a custom latent space function.
520
+
521
+ Multiple custom latent spaces may be registered as long as each has a
522
+ unique name. They are stored name-keyed (insertion order preserved), so a
523
+ later registration no longer overrides an earlier one.
519
524
 
520
525
  Args:
521
- function (SectionCallableInterface): The metadata handler function.
526
+ function (SectionCallableInterface): The latent-space handler function.
522
527
  This function receives:
523
528
  subset (PreprocessResponse): The subset of the data.
524
529
  index (int): The index of the sample within the subset.
525
- This function should numpy float32 array contains the latent space vec of the sample.
530
+ This function should return a numpy float32 array containing the latent
531
+ space vec of the sample.
532
+ name (Optional[str]): Unique name for this custom latent space. Defaults to
533
+ the reserved single-LS name for backward compatibility.
526
534
  """
527
- self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
535
+ if name is None:
536
+ name = custom_latent_space_attribute
537
+ if name in self.setup_container.custom_latent_spaces:
538
+ raise Exception(
539
+ f"A custom latent space named '{name}' is already registered. Each "
540
+ f"@tensorleap_custom_latent_space must have a unique name "
541
+ f"(pass name='...' to distinguish them)."
542
+ )
543
+ self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(function, name)
528
544
 
529
545
  def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
530
546
  latent_space_aggregation: str = 'last_step') -> None:
@@ -549,9 +565,7 @@ class LeapBinder:
549
565
  # Builtin chain metadata, declared at parse time so it survives the reporter's
550
566
  # metadata type mapping; the placeholder values are overwritten by the engine when a
551
567
  # chain finalizes (realized length, truncated-by-safety-cap flag).
552
- def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Any:
553
- if isinstance(idx, list):
554
- return [{'length': -1, 'truncated': False} for _ in idx]
568
+ def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
555
569
  return {'length': -1, 'truncated': False}
556
570
 
557
571
  self.set_metadata(builtin_chain_metadata, 'builtin_chain')
@@ -715,15 +729,6 @@ class LeapBinder:
715
729
  if state_enum in preprocess_result_dict:
716
730
  preprocess_result_dict_in_correct_order[state_enum] = preprocess_result_dict[state_enum]
717
731
 
718
- # All-or-none: every state must agree on grouped (nested sample_ids) vs flat.
719
- if len({r.is_grouped for r in preprocess_result_dict_in_correct_order.values()}) > 1:
720
- shapes = ', '.join(
721
- f"{state.name}={'grouped' if r.is_grouped else 'flat'}"
722
- for state, r in preprocess_result_dict_in_correct_order.items())
723
- raise Exception(
724
- "All preprocess states must be either all grouped (nested sample_ids) or all flat, "
725
- f"but got a mix: {shapes}.")
726
-
727
732
  return preprocess_result_dict_in_correct_order
728
733
 
729
734
  def get_preprocess_unlabeled_result(self) -> Optional[PreprocessResponse]:
@@ -744,15 +749,7 @@ class LeapBinder:
744
749
  preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
745
750
  dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
746
751
  assert preprocess_response.sample_ids is not None
747
- if preprocess_response.is_grouped:
748
- # Grouped: probe with the first group, then reduce to a single sample's result so the
749
- # recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
750
- # (casts: sample_ids[0] is a group list here, and the grouped result is a per-sample list.)
751
- group = preprocess_response.sample_ids[0]
752
- raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
753
- else:
754
- raw_result = dataset_base_handler.function(
755
- cast(Union[int, str], preprocess_response.sample_ids[0]), preprocess_response)
752
+ raw_result = dataset_base_handler.function(preprocess_response.sample_ids[0], preprocess_response)
756
753
  handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
757
754
  if isinstance(dataset_base_handler, MetadataHandler):
758
755
  if isinstance(raw_result, dict):
@@ -848,11 +845,10 @@ class LeapBinder:
848
845
  preprocess_response.tl_generated = True
849
846
  if not preprocess_response.length or preprocess_response.length < 1:
850
847
  raise Exception("Simulation '{}' returned PreprocessResponse with length < 1".format(sim.name))
851
- preprocess_response.sample_ids = cast(List[Union[int, str]], [0])
852
- sim_sample_ids = cast(List[Union[int, str]], preprocess_response.sample_ids)
848
+ preprocess_response.sample_ids = [0]
853
849
  for handler in self.setup_container.inputs:
854
- out1 = handler.function(sim_sample_ids[0], preprocess_response)
855
- out2 = handler.function(sim_sample_ids[0], preprocess_response)
850
+ out1 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
851
+ out2 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
856
852
  if not np.allclose(out1, out2):
857
853
  raise Exception(
858
854
  "Simulation '{}': encoder '{}' is non-deterministic — consecutive calls with seed=0 returned different outputs".format(