code-loader 1.0.198__tar.gz → 1.0.199__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 (38) hide show
  1. {code_loader-1.0.198 → code_loader-1.0.199}/PKG-INFO +1 -1
  2. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/datasetclasses.py +2 -3
  3. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/enums.py +1 -0
  4. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/visualizer_classes.py +43 -1
  5. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/inner_leap_binder/leapbinder.py +6 -42
  6. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/inner_leap_binder/leapbinder_decorators.py +7 -13
  7. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/leaploader.py +28 -55
  8. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/leaploaderbase.py +0 -8
  9. code_loader-1.0.199/code_loader/visualizers/default_visualizers.py +156 -0
  10. {code_loader-1.0.198 → code_loader-1.0.199}/pyproject.toml +1 -1
  11. code_loader-1.0.198/code_loader/visualizers/default_visualizers.py +0 -85
  12. {code_loader-1.0.198 → code_loader-1.0.199}/LICENSE +0 -0
  13. {code_loader-1.0.198 → code_loader-1.0.199}/README.md +0 -0
  14. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/__init__.py +0 -0
  15. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/__init__.py +0 -0
  16. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/exceptions.py +0 -0
  17. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/mapping.py +0 -0
  18. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/responsedataclasses.py +0 -0
  19. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/contract/sim_config.py +0 -0
  20. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/default_losses.py +0 -0
  21. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/default_metrics.py +0 -0
  22. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/__init__.py +0 -0
  23. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/api.py +0 -0
  24. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/cli_config_utils.py +0 -0
  25. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/client.py +0 -0
  26. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/epoch.py +0 -0
  27. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/experiment.py +0 -0
  28. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/experiment_context.py +0 -0
  29. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/types.py +0 -0
  30. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/utils.py +0 -0
  31. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  32. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/inner_leap_binder/__init__.py +0 -0
  33. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/mixpanel_tracker.py +0 -0
  34. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/plot_functions/__init__.py +0 -0
  35. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/plot_functions/plot_functions.py +0 -0
  36. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/plot_functions/visualize.py +0 -0
  37. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/utils.py +0 -0
  38. {code_loader-1.0.198 → code_loader-1.0.199}/code_loader/visualizers/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.198
3
+ Version: 1.0.199
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -308,7 +308,6 @@ class MetadataHandler:
308
308
  class CustomLatentSpaceHandler:
309
309
  function: SectionCallableInterface
310
310
  name: str = 'custom_latent_space'
311
- use_ls_for_analysis: bool = False
312
311
 
313
312
 
314
313
  # How a chain's latent-space vectors are derived from its per-step forward passes.
@@ -391,7 +390,7 @@ class DatasetIntegrationSetup:
391
390
  metrics: List[MetricHandler] = field(default_factory=list)
392
391
  instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
393
392
  custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
394
- custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
393
+ custom_latent_space: Optional[CustomLatentSpaceHandler] = None
395
394
  simulations: List[SimulationHandler] = field(default_factory=list)
396
395
  autoregressive_step: Optional[AutoregressiveStepHandler] = None
397
396
  autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
@@ -407,6 +406,6 @@ class DatasetSample:
407
406
  metadata_is_none: Dict[str, bool]
408
407
  index: Union[int, str]
409
408
  state: DataStateEnum
410
- custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
409
+ custom_latent_space: Optional[npt.NDArray[np.float32]] = None
411
410
  instance_masks: Optional[Dict[str, ElementInstance]] = None
412
411
 
@@ -28,6 +28,7 @@ class LeapDataType(Enum):
28
28
  ImageWithBBox = 'ImageWithBBox'
29
29
  ImageWithHeatmap = 'ImageWithHeatmap'
30
30
  Video = 'Video'
31
+ Audio = 'Audio'
31
32
 
32
33
 
33
34
  class MetricDirection(Enum):
@@ -343,6 +343,47 @@ class LeapImageWithHeatmap:
343
343
  'Number of heatmaps and labels must be equal')
344
344
 
345
345
 
346
+ @dataclass
347
+ class LeapAudio:
348
+ """
349
+ Visualizer representing a playable audio sample for Tensorleap.
350
+
351
+ The user plays the sample in the right panel while a playhead tick runs
352
+ through `visual` — either a spectrogram (a LeapImage) or a time-series
353
+ waveform (a LeapGraph). Build one with `default_audio_visualizer`.
354
+
355
+ Attributes:
356
+ audio (npt.NDArray): Mono waveform samples, shaped [N]. Multi-channel input
357
+ should be mixed down before constructing LeapAudio.
358
+ sample_rate (int): Samples per second — needed to encode the clip and to map
359
+ the playhead position to time.
360
+ visual (Union[LeapImage, LeapGraph]): The thing you look at.
361
+ x_range (Optional[Tuple[float, float]]): Time extent (in seconds) that the
362
+ visual's horizontal axis spans, used to align the tick. Defaults to
363
+ (0, N / sample_rate).
364
+ type (LeapDataType): The data type, default is LeapDataType.Audio.
365
+ """
366
+ audio: npt.NDArray
367
+ sample_rate: int
368
+ visual: Union[LeapImage, LeapGraph]
369
+ x_range: Optional[Tuple[float, float]] = None
370
+ type: LeapDataType = LeapDataType.Audio
371
+
372
+ def __post_init__(self) -> None:
373
+ validate_type(self.type, LeapDataType.Audio)
374
+ validate_type(type(self.audio), np.ndarray)
375
+ validate_type(len(self.audio.shape), 1, 'Audio must be mono, of shape 1 [N]')
376
+ validate_type(type(self.visual), [LeapImage, LeapGraph],
377
+ 'Audio visual must be a LeapImage (spectrogram) or LeapGraph (waveform)')
378
+ if not isinstance(self.sample_rate, int) or self.sample_rate <= 0:
379
+ raise LeapValidationError(f'sample_rate must be a positive int, got {self.sample_rate}')
380
+ if self.x_range is not None:
381
+ if len(self.x_range) != 2:
382
+ raise LeapValidationError('x_range must be a tuple of length 2')
383
+ validate_type(type(self.x_range[0]), [float, int], 'x_range must be a tuple of floats or integers')
384
+ validate_type(type(self.x_range[1]), [float, int], 'x_range must be a tuple of floats or integers')
385
+
386
+
346
387
  map_leap_data_type_to_visualizer_class = {
347
388
  LeapDataType.Image.value: LeapImage,
348
389
  LeapDataType.Graph.value: LeapGraph,
@@ -352,5 +393,6 @@ map_leap_data_type_to_visualizer_class = {
352
393
  LeapDataType.ImageMask.value: LeapImageMask,
353
394
  LeapDataType.TextMask.value: LeapTextMask,
354
395
  LeapDataType.ImageWithBBox.value: LeapImageWithBBox,
355
- LeapDataType.ImageWithHeatmap.value: LeapImageWithHeatmap
396
+ LeapDataType.ImageWithHeatmap.value: LeapImageWithHeatmap,
397
+ LeapDataType.Audio.value: LeapAudio
356
398
  }
@@ -536,54 +536,18 @@ class LeapBinder:
536
536
  """
537
537
  self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
538
538
 
539
- def set_custom_latent_space(self, function: SectionCallableInterface,
540
- name: Optional[str] = None,
541
- use_ls_for_analysis: bool = False) -> None:
539
+ def set_custom_latent_space(self, function: SectionCallableInterface) -> None:
542
540
  """
543
- Register a custom latent space function.
544
-
545
- Multiple custom latent spaces may be registered as long as each has a
546
- unique name. They are stored name-keyed (insertion order preserved), so a
547
- later registration no longer overrides an earlier one.
541
+ Set a custom latent space function.
548
542
 
549
543
  Args:
550
- function (SectionCallableInterface): The latent-space handler function.
544
+ function (SectionCallableInterface): The metadata handler function.
551
545
  This function receives:
552
546
  subset (PreprocessResponse): The subset of the data.
553
547
  index (int): The index of the sample within the subset.
554
- This function should return a numpy float32 array containing the latent
555
- space vec of the sample.
556
- name (Optional[str]): Unique name for this custom latent space. Defaults to
557
- the reserved single-LS name for backward compatibility.
558
- use_ls_for_analysis (bool): When True, the engine uses this custom latent
559
- space for the Out-Of-Distribution and Domain-Gap insights instead of the
560
- built-in defaults. At most one registered custom latent space may set this;
561
- registering a second one with the flag raises.
548
+ This function should numpy float32 array contains the latent space vec of the sample.
562
549
  """
563
- if name is None:
564
- name = custom_latent_space_attribute
565
- if name in self.setup_container.custom_latent_spaces:
566
- raise Exception(
567
- f"A custom latent space named '{name}' is already registered. Each "
568
- f"@tensorleap_custom_latent_space must have a unique name "
569
- f"(pass name='...' to distinguish them)."
570
- )
571
- if use_ls_for_analysis:
572
- already_flagged = [
573
- existing_name
574
- for existing_name, handler in self.setup_container.custom_latent_spaces.items()
575
- if handler.use_ls_for_analysis
576
- ]
577
- if already_flagged:
578
- raise Exception(
579
- f"use_ls_for_analysis=True is already set on custom latent space "
580
- f"'{already_flagged[0]}'. Only one custom latent space may set "
581
- f"use_ls_for_analysis=True (it selects the latent space used for the "
582
- f"Out-Of-Distribution and Domain-Gap insights). Set it on '{name}' "
583
- f"or '{already_flagged[0]}', not both."
584
- )
585
- self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
586
- function, name, use_ls_for_analysis)
550
+ self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
587
551
 
588
552
  def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
589
553
  latent_space_aggregation: str = 'last_step') -> None:
@@ -994,7 +958,7 @@ class LeapBinder:
994
958
  "Element instances are not supported together with tensorleap_autoregressive_step: "
995
959
  "instance generation masks the sample's encoded inputs, which do not exist in an "
996
960
  "autoregressive integration. Remove the instance encoders or the autoregressive hook.")
997
- if self.setup_container.custom_latent_spaces:
961
+ if self.setup_container.custom_latent_space is not None:
998
962
  raise Exception(
999
963
  "tensorleap_custom_latent_space is not supported together with "
1000
964
  "tensorleap_autoregressive_step: the custom latent space function only sees "
@@ -29,7 +29,7 @@ from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMet
29
29
  from code_loader import leap_binder, LeapLoader
30
30
  from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
31
31
  from code_loader.contract.visualizer_classes import LeapImage, LeapImageMask, LeapTextMask, LeapText, LeapGraph, \
32
- LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapValidationError, \
32
+ LeapHorizontalBar, LeapImageWithBBox, LeapImageWithHeatmap, LeapVideo, LeapAudio, LeapValidationError, \
33
33
  map_leap_data_type_to_visualizer_class
34
34
  from code_loader.inner_leap_binder.leapbinder import mapping_runtime_mode_env_var_mame
35
35
  from code_loader.mixpanel_tracker import clear_integration_events, AnalyticsEvent, emit_integration_event_once
@@ -1543,7 +1543,8 @@ def tensorleap_custom_visualizer(name: str, visualizer_type: LeapDataType,
1543
1543
  LeapDataType.HorizontalBar: LeapHorizontalBar,
1544
1544
  LeapDataType.ImageWithBBox: LeapImageWithBBox,
1545
1545
  LeapDataType.ImageWithHeatmap: LeapImageWithHeatmap,
1546
- LeapDataType.Video: LeapVideo
1546
+ LeapDataType.Video: LeapVideo,
1547
+ LeapDataType.Audio: LeapAudio
1547
1548
  }
1548
1549
  validate_output_structure(result, func_name=user_function.__name__,
1549
1550
  expected_type_name=result_type_map[visualizer_type])
@@ -1702,14 +1703,8 @@ def tensorleap_metadata(
1702
1703
  return decorating_function
1703
1704
 
1704
1705
 
1705
- def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
1706
- assert isinstance(use_ls_for_analysis, bool), \
1707
- ("tensorleap_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
1708
- f"Got {type(use_ls_for_analysis)}.")
1709
-
1706
+ def tensorleap_custom_latent_space():
1710
1707
  def decorating_function(user_function: SectionCallableInterface):
1711
- ls_name = name if name is not None else user_function.__name__
1712
-
1713
1708
  def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
1714
1709
  _validate_id_or_group(sample_id, preprocess_response, 'tensorleap_custom_latent_space')
1715
1710
 
@@ -1720,9 +1715,9 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
1720
1715
  if single_result.ndim > 1:
1721
1716
  flat_dim = int(np.prod(single_result.shape))
1722
1717
  store_general_warning(
1723
- key=("tensorleap_custom_latent_space_flatten", ls_name, tuple(single_result.shape)),
1718
+ key=("tensorleap_custom_latent_space_flatten", tuple(single_result.shape)),
1724
1719
  message=(
1725
- f"tensorleap_custom_latent_space '{ls_name}' returned per-sample shape {tuple(single_result.shape)} "
1720
+ f"tensorleap_custom_latent_space returned per-sample shape {tuple(single_result.shape)} "
1726
1721
  f"(ndim={single_result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
1727
1722
  f"flatten to ({flat_dim},) before downstream visualization and clustering. "
1728
1723
  f"If you want a different aggregation (e.g. global average pooling), do it "
@@ -1765,8 +1760,7 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
1765
1760
 
1766
1761
  return result
1767
1762
 
1768
- leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
1769
- use_ls_for_analysis=use_ls_for_analysis)
1763
+ leap_binder.set_custom_latent_space(inner_without_validate)
1770
1764
 
1771
1765
  def inner(sample_id, preprocess_response):
1772
1766
  if os.environ.get(mapping_runtime_mode_env_var_mame):
@@ -283,7 +283,17 @@ class LeapLoader(LeapLoaderBase):
283
283
 
284
284
  metadata, metadata_is_none = self.get_metadata(state, sample_id)
285
285
 
286
- custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
286
+ custom_latent_space = None
287
+ if global_leap_binder.setup_container.custom_latent_space is not None:
288
+ latent_fn = global_leap_binder.setup_container.custom_latent_space.function
289
+ preprocess_state = preprocess_result[state]
290
+ if preprocess_state.is_grouped:
291
+ # Single-sample fetch: encode this sample only, not the whole group (same
292
+ # memory rationale as _get_dataset_handlers; see grouped-fetch-oom-bug.md).
293
+ self._locate_group(preprocess_state, sample_id) # validates group membership
294
+ custom_latent_space = self._to_grouped_list(latent_fn([sample_id], preprocess_state))[0]
295
+ else:
296
+ custom_latent_space = latent_fn(sample_id, preprocess_state)
287
297
  instance_mask = self._get_instances_masks(state, sample_id, instance_id)
288
298
  sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
289
299
  gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
@@ -291,7 +301,7 @@ class LeapLoader(LeapLoaderBase):
291
301
  metadata_is_none=metadata_is_none,
292
302
  index=sample_id,
293
303
  state=state,
294
- custom_latent_spaces=custom_latent_spaces,
304
+ custom_latent_space=custom_latent_space,
295
305
  instance_masks=instance_mask)
296
306
  return sample
297
307
 
@@ -322,7 +332,11 @@ class LeapLoader(LeapLoaderBase):
322
332
  handler.name, handler_result
323
333
  )
324
334
 
325
- custom_latent_spaces = self._get_custom_latent_spaces(original_sample_id, preprocess)
335
+ custom_latent_space = None
336
+ if global_leap_binder.setup_container.custom_latent_space is not None:
337
+ custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(
338
+ original_sample_id, preprocess
339
+ )
326
340
 
327
341
  return DatasetSample(
328
342
  inputs=inputs,
@@ -331,7 +345,7 @@ class LeapLoader(LeapLoaderBase):
331
345
  metadata_is_none=metadata_is_none,
332
346
  index=synthetic_index,
333
347
  state=DataStateEnum.additional,
334
- custom_latent_spaces=custom_latent_spaces,
348
+ custom_latent_space=custom_latent_space,
335
349
  instance_masks=None,
336
350
  )
337
351
 
@@ -1100,20 +1114,17 @@ class LeapLoader(LeapLoaderBase):
1100
1114
  metadata[handler.name] = [v for v, _ in converted]
1101
1115
  metadata_is_none[handler.name] = [is_none for _, is_none in converted]
1102
1116
 
1103
- # custom_latent_spaces are group-aware like the input/GT encoders: hand each the whole group
1104
- # in a single call so a file-backed latent fn loads the file once, and normalize to per-sample
1105
- # lists. This keeps the mandatory group path non-lossy. instance_masks stay None here: they are
1106
- # a per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
1107
- latent_handlers = global_leap_binder.setup_container.custom_latent_spaces
1108
- custom_latent_spaces = None
1109
- if latent_handlers:
1110
- custom_latent_spaces = {
1111
- name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
1112
- for name, handler in latent_handlers.items()
1113
- }
1117
+ # custom_latent_space is group-aware like the input/GT encoders: hand it the whole group in a
1118
+ # single call so a file-backed latent fn loads the file once, and normalize to (B, d). This
1119
+ # keeps the mandatory group path non-lossy. instance_masks stay None here: they are a
1120
+ # per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
1121
+ custom_latent_space = None
1122
+ if global_leap_binder.setup_container.custom_latent_space is not None:
1123
+ latent_fn = global_leap_binder.setup_container.custom_latent_space.function
1124
+ custom_latent_space = self._to_grouped_list(latent_fn(group_ids, preprocess_state))
1114
1125
 
1115
1126
  return DatasetSample(inputs=inputs, gt=gt, metadata=metadata, metadata_is_none=metadata_is_none,
1116
- index=list(group_ids), state=state, custom_latent_spaces=custom_latent_spaces,
1127
+ index=list(group_ids), state=state, custom_latent_space=custom_latent_space,
1117
1128
  instance_masks=None)
1118
1129
 
1119
1130
  def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
@@ -1326,48 +1337,10 @@ class LeapLoader(LeapLoaderBase):
1326
1337
 
1327
1338
  return id_type
1328
1339
 
1329
- def _get_custom_latent_spaces(
1330
- self,
1331
- sample_id: Union[int, str],
1332
- preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
1333
- handlers = global_leap_binder.setup_container.custom_latent_spaces
1334
- if not handlers:
1335
- return None
1336
- if preprocess.is_grouped:
1337
- # Single-sample fetch: encode this sample only, not the whole group (same
1338
- # memory rationale as _get_dataset_handlers; see grouped-fetch-oom-bug.md).
1339
- self._locate_group(preprocess, sample_id) # validates group membership
1340
- return {name: self._to_grouped_list(handler.function([sample_id], preprocess))[0]
1341
- for name, handler in handlers.items()}
1342
- return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
1343
-
1344
1340
  @lru_cache()
1345
1341
  def has_custom_latent_space_decorator(self) -> bool:
1346
1342
  self.exec_script()
1347
- return len(global_leap_binder.setup_container.custom_latent_spaces) > 0
1348
-
1349
- @lru_cache()
1350
- def get_custom_latent_space_names(self) -> Tuple[str, ...]:
1351
- """Ordered names of all registered custom latent spaces.
1352
-
1353
- Registration order is the canonical index mapping consumed by the engine
1354
- (name i -> user_custom_i). Returns a tuple so the lru_cache value is hashable.
1355
- """
1356
- self.exec_script()
1357
- return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
1358
-
1359
- @lru_cache()
1360
- def get_custom_latent_space_for_analysis(self) -> Optional[str]:
1361
- """Name of the custom latent space flagged with use_ls_for_analysis=True, if any.
1362
-
1363
- The engine uses this latent space for the Out-Of-Distribution and Domain-Gap
1364
- insights instead of the built-in defaults. Returns None when no custom latent
1365
- space set the flag. At most one is flagged (enforced at registration time)."""
1366
- self.exec_script()
1367
- for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items():
1368
- if handler.use_ls_for_analysis:
1369
- return name
1370
- return None
1343
+ return global_leap_binder.setup_container.custom_latent_space is not None
1371
1344
 
1372
1345
  @lru_cache()
1373
1346
  def has_autoregressive_step(self) -> bool:
@@ -218,14 +218,6 @@ class LeapLoaderBase:
218
218
  raise NotImplementedError(f'{type(self).__name__} does not implement '
219
219
  'autoregressive_visualizer_by_name.')
220
220
 
221
- @abstractmethod
222
- def get_custom_latent_space_names(self) -> Tuple[str, ...]:
223
- pass
224
-
225
- @abstractmethod
226
- def get_custom_latent_space_for_analysis(self) -> Optional[str]:
227
- pass
228
-
229
221
  @abstractmethod
230
222
  def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
231
223
  pass
@@ -0,0 +1,156 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+
4
+ import numpy as np
5
+ import numpy.typing as npt
6
+
7
+ from code_loader.contract.visualizer_classes import LeapImage, LeapGraph, LeapHorizontalBar, LeapText, \
8
+ LeapImageMask, LeapTextMask, LeapVideo, LeapAudio
9
+ from code_loader.utils import rescale_min_max
10
+
11
+
12
+ class DefaultVisualizer(Enum):
13
+ Image = 'Image'
14
+ Video = 'Video'
15
+ Audio = 'Audio'
16
+ Graph = 'Graph'
17
+ HorizontalBar = 'HorizontalBar'
18
+ Text = 'Text'
19
+ ImageMask = 'ImageMask'
20
+ TextMask = 'TextMask'
21
+ RawData = 'RawData'
22
+
23
+
24
+ def default_image_visualizer(data: npt.NDArray[np.float32]) -> LeapImage:
25
+ rescaled_data = rescale_min_max(data[0])
26
+ return LeapImage(rescaled_data)
27
+
28
+
29
+ def default_video_visualizer(data: npt.NDArray[np.float32]) -> LeapVideo:
30
+ return LeapVideo(data[0])
31
+
32
+
33
+ _MAX_WAVEFORM_POINTS = 2000
34
+ _VISUAL_KINDS = ('waveform', 'spectrogram', 'image')
35
+
36
+
37
+ def _to_mono(audio: npt.NDArray) -> npt.NDArray:
38
+ a = np.squeeze(np.asarray(audio))
39
+ if a.ndim == 2: # mix down: channels are the shorter axis
40
+ a = a.mean(axis=int(np.argmin(a.shape)))
41
+ if a.ndim != 1:
42
+ raise ValueError(f'audio must be reducible to a mono 1-D signal, got shape {np.asarray(audio).shape}')
43
+ return a.astype(np.float32)
44
+
45
+
46
+ def _waveform_graph(series: npt.NDArray, duration: float) -> LeapGraph:
47
+ s = np.squeeze(np.asarray(series)).astype(np.float32)
48
+ # ponytail: recharts chokes on 100k+ points; stride down for the default view. x_range keeps full time span.
49
+ step = max(1, s.shape[0] // _MAX_WAVEFORM_POINTS)
50
+ s = s[::step].reshape(-1, 1)
51
+ return LeapGraph(s, x_label='Time [s]', y_label='Amplitude', x_range=(0.0, duration))
52
+
53
+
54
+ def default_audio_visualizer(audio: npt.NDArray,
55
+ sample_rate: int,
56
+ visual: Optional[npt.NDArray] = None,
57
+ visual_kind: Optional[str] = None,
58
+ time_axis_index: int = -1) -> LeapAudio:
59
+ """Package a playable audio sample plus the visual a playhead runs through.
60
+
61
+ audio: waveform samples (mono [N], or multi-channel that is mixed down).
62
+ sample_rate: samples per second.
63
+ visual: the array to display. For 'waveform' a 1-D series; for 'spectrogram'
64
+ a raw 2-D [freq, time] (or [time, freq]) array to be rendered grayscale;
65
+ for 'image' an already-rendered [H, W, 1] or [H, W, 3] image (e.g. a
66
+ spectrogram the caller already ran through their own colormap).
67
+ If omitted, a waveform graph is built from `audio` itself.
68
+ visual_kind: 'waveform', 'spectrogram', or 'image'. The caller states this
69
+ explicitly; the shape of `visual` is never inspected to guess it.
70
+ Required when `visual` is given; ignored when `visual` is omitted.
71
+ time_axis_index: for 'spectrogram', which axis of `visual` is time (moved to columns).
72
+ Unused for 'waveform'/'image'.
73
+ """
74
+ mono = _to_mono(audio)
75
+ duration = mono.shape[0] / float(sample_rate)
76
+ x_range = (0.0, duration)
77
+
78
+ if visual is None: # no visual given: show the raw signal as a waveform
79
+ return LeapAudio(mono, sample_rate, _waveform_graph(mono, duration), x_range)
80
+
81
+ if visual_kind not in _VISUAL_KINDS:
82
+ raise ValueError(f"visual_kind must be one of {_VISUAL_KINDS}, got {visual_kind!r}")
83
+
84
+ v = np.asarray(visual)
85
+
86
+ if visual_kind == 'waveform':
87
+ return LeapAudio(mono, sample_rate, _waveform_graph(v, duration), x_range)
88
+
89
+ if visual_kind == 'image':
90
+ return LeapAudio(mono, sample_rate, LeapImage(v), x_range)
91
+
92
+ # spectrogram
93
+ spec = np.squeeze(v)
94
+ if spec.ndim != 2:
95
+ raise ValueError(f'spectrogram visual must be 2-D (freq x time), got shape {v.shape}')
96
+ if time_axis_index % spec.ndim == 0: # move time onto the horizontal axis (columns)
97
+ spec = spec.T
98
+ img = rescale_min_max(spec.astype(np.float32))[..., None] # [freq, time, 1]
99
+ return LeapAudio(mono, sample_rate, LeapImage(img), x_range)
100
+
101
+
102
+ def default_graph_visualizer(data: npt.NDArray[np.float32]) -> LeapGraph:
103
+ return LeapGraph(data[0])
104
+
105
+
106
+ def default_horizontal_bar_visualizer(data: npt.NDArray[np.float32]) -> LeapHorizontalBar:
107
+ labels = [str(index) for index in range(data.shape[-1])]
108
+ return LeapHorizontalBar(data[0], labels)
109
+
110
+
111
+ def default_word_visualizer(data: npt.NDArray[np.float32]) -> LeapText:
112
+ if len(data.shape) == 2 and data.shape[0] == 1:
113
+ data = data[0]
114
+ if hasattr(data, 'tolist'):
115
+ data = data.tolist()
116
+ words = [str(index[0]) if type(index) is list else str(index) for index in data]
117
+ return LeapText(words)
118
+
119
+
120
+ def default_raw_data_visualizer(data: npt.NDArray[np.float32]) -> LeapText:
121
+ return LeapText([str(data)])
122
+
123
+
124
+ def default_image_mask_visualizer(mask: npt.NDArray[np.float32], image: npt.NDArray[np.float32]) -> LeapImageMask:
125
+ if image.shape[0] == 1:
126
+ image = image[0]
127
+ if mask.shape[0] == 1:
128
+ mask = mask[0]
129
+
130
+ n_different_labels = mask.shape[-1]
131
+ labels = [str(i) for i in range(n_different_labels)]
132
+
133
+ if len(mask.shape) > 2:
134
+ if mask.shape[-1] == 1:
135
+ mask = np.squeeze(mask, axis=-1)
136
+ else:
137
+ mask = np.argmax(mask, axis=-1)
138
+
139
+ return LeapImageMask(mask.astype(np.uint8), image.astype(np.float32), labels)
140
+
141
+
142
+ def default_text_mask_visualizer(mask: npt.NDArray[np.float32], text_data: npt.NDArray[np.float32]) -> LeapTextMask:
143
+ mask = mask[0]
144
+ text_data = text_data[0]
145
+
146
+ words = default_word_visualizer(text_data).data
147
+ n_different_labels = mask.shape[-1]
148
+ labels = [str(i) for i in range(n_different_labels)]
149
+
150
+ if len(mask.shape) > 1:
151
+ if mask.shape[-1] == 1:
152
+ mask = np.squeeze(mask, axis=-1)
153
+ else:
154
+ mask = np.argmax(mask, axis=-1)
155
+
156
+ return LeapTextMask(mask.astype(np.uint8), words, labels)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
- version = "1.0.198"
3
+ version = "1.0.199"
4
4
  description = ""
5
5
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
6
6
  license = "MIT"
@@ -1,85 +0,0 @@
1
- from enum import Enum
2
-
3
- import numpy as np
4
- import numpy.typing as npt
5
-
6
- from code_loader.contract.visualizer_classes import LeapImage, LeapGraph, LeapHorizontalBar, LeapText, \
7
- LeapImageMask, LeapTextMask, LeapVideo
8
- from code_loader.utils import rescale_min_max
9
-
10
-
11
- class DefaultVisualizer(Enum):
12
- Image = 'Image'
13
- Video = 'Video'
14
- Graph = 'Graph'
15
- HorizontalBar = 'HorizontalBar'
16
- Text = 'Text'
17
- ImageMask = 'ImageMask'
18
- TextMask = 'TextMask'
19
- RawData = 'RawData'
20
-
21
-
22
- def default_image_visualizer(data: npt.NDArray[np.float32]) -> LeapImage:
23
- rescaled_data = rescale_min_max(data[0])
24
- return LeapImage(rescaled_data)
25
-
26
-
27
- def default_video_visualizer(data: npt.NDArray[np.float32]) -> LeapVideo:
28
- return LeapVideo(data[0])
29
-
30
-
31
- def default_graph_visualizer(data: npt.NDArray[np.float32]) -> LeapGraph:
32
- return LeapGraph(data[0])
33
-
34
-
35
- def default_horizontal_bar_visualizer(data: npt.NDArray[np.float32]) -> LeapHorizontalBar:
36
- labels = [str(index) for index in range(data.shape[-1])]
37
- return LeapHorizontalBar(data[0], labels)
38
-
39
-
40
- def default_word_visualizer(data: npt.NDArray[np.float32]) -> LeapText:
41
- if len(data.shape) == 2 and data.shape[0] == 1:
42
- data = data[0]
43
- if hasattr(data, 'tolist'):
44
- data = data.tolist()
45
- words = [str(index[0]) if type(index) is list else str(index) for index in data]
46
- return LeapText(words)
47
-
48
-
49
- def default_raw_data_visualizer(data: npt.NDArray[np.float32]) -> LeapText:
50
- return LeapText([str(data)])
51
-
52
-
53
- def default_image_mask_visualizer(mask: npt.NDArray[np.float32], image: npt.NDArray[np.float32]) -> LeapImageMask:
54
- if image.shape[0] == 1:
55
- image = image[0]
56
- if mask.shape[0] == 1:
57
- mask = mask[0]
58
-
59
- n_different_labels = mask.shape[-1]
60
- labels = [str(i) for i in range(n_different_labels)]
61
-
62
- if len(mask.shape) > 2:
63
- if mask.shape[-1] == 1:
64
- mask = np.squeeze(mask, axis=-1)
65
- else:
66
- mask = np.argmax(mask, axis=-1)
67
-
68
- return LeapImageMask(mask.astype(np.uint8), image.astype(np.float32), labels)
69
-
70
-
71
- def default_text_mask_visualizer(mask: npt.NDArray[np.float32], text_data: npt.NDArray[np.float32]) -> LeapTextMask:
72
- mask = mask[0]
73
- text_data = text_data[0]
74
-
75
- words = default_word_visualizer(text_data).data
76
- n_different_labels = mask.shape[-1]
77
- labels = [str(i) for i in range(n_different_labels)]
78
-
79
- if len(mask.shape) > 1:
80
- if mask.shape[-1] == 1:
81
- mask = np.squeeze(mask, axis=-1)
82
- else:
83
- mask = np.argmax(mask, axis=-1)
84
-
85
- return LeapTextMask(mask.astype(np.uint8), words, labels)
File without changes
File without changes