]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
fix(quickadapter): prefix `compose_sample_weights` and `sanitize_and_renormalize...
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 22 Jun 2026 01:34:16 +0000 (03:34 +0200)
committerGitHub <noreply@github.com>
Mon, 22 Jun 2026 01:34:16 +0000 (03:34 +0200)
`compose_sample_weights` and `sanitize_and_renormalize` accept a
required keyword-only `context: str` parameter; every warning, error,
and inner `sanitize_and_renormalize` call uses `context` as its sole
prefix.

- `compose_sample_weights(..., *, logger, context, on_collapse=...)`:
  `context: str` keyword-only, required. The 4 internal
  warnings/errors (shape-mismatch `ValueError`, all-dropped
  `LabelWeightSupportError`, sparse-mass warning, collapse-on-
  survivors `LabelWeightSupportError` and fallback warning) prefix
  with `{context}:`. Internal `sanitize_and_renormalize` calls
  compose sub-contexts: `{context}:base_only`,
  `{context}:label_weighted`, `{context}:base_fallback`. The sparse-
  mass message reads `sparse weighting mass`, accurate for both
  train and eval paths.
- `sanitize_and_renormalize(..., *, logger=None, context: str)`:
  `context` keyword-only, required. The 5 warnings/errors
  (`drop_mask` shape `ValueError`, `drop_mask` dtype `ValueError`,
  rescale-overflow warning, weights-collapsed warning,
  mask-covers-all warning) prefix with `{context}:`. The redundant
  `(context=%s, ...)` subfield is dropped.
- 5 `compose_sample_weights` call sites in
  `QuickAdapterRegressorV3.py` pass `context=context`. The 2
  external `sanitize_and_renormalize` call sites
  (`post_feature_pipeline:train`, `post_feature_pipeline:test`)
  already pass `context=`.

Log format goes from
`compose_sample_weights: sparse training mass (59/2603 rows ...)`
to
`[ETH/USDT] train_test_split:train: sparse weighting mass (59/2603 rows ...)`.
The pair, split method, and train/eval side are traceable in the
log line.

quickadapter/user_data/freqaimodels/QuickAdapterRegressorV3.py
quickadapter/user_data/strategies/Utils.py

index ac7b0ff45588eb1a650420697e4833d81bb3b96a..524ca30878b6ad8bd286ed1beef05ce03dd0a675 100644 (file)
@@ -567,6 +567,7 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                 base_weights,
                 label_weights,
                 logger=logger,
+                context=context,
                 on_collapse="fallback",
             )
         except LabelWeightSupportError as exc:
@@ -575,7 +576,9 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                 context,
                 exc,
             )
-            return compose_sample_weights(base_weights, None, logger=logger)
+            return compose_sample_weights(
+                base_weights, None, logger=logger, context=context
+            )
 
     @staticmethod
     def _apply_support_policy(
@@ -599,7 +602,9 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                     context,
                     reason_text,
                 )
-                return compose_sample_weights(base_weights, None, logger=logger)
+                return compose_sample_weights(
+                base_weights, None, logger=logger, context=context
+            )
             case _:
                 assert_never(policy)
 
@@ -631,11 +636,13 @@ class QuickAdapterRegressorV3(BaseRegressionModel):
                         f"no label weights available (no pivots detected)"
                     ],
                 )
-            return compose_sample_weights(base_weights, None, logger=logger)
+            return compose_sample_weights(
+                base_weights, None, logger=logger, context=context
+            )
 
         try:
             composed = compose_sample_weights(
-                base_weights, label_weights, logger=logger
+                base_weights, label_weights, logger=logger, context=context
             )
         except LabelWeightSupportError as exc:
             return QuickAdapterRegressorV3._apply_support_policy(
index 7182f2418d80260c4155ce8e6f745bf9da904380..9380cd5c2269de2aa4c2aed8a70045e63408b55d 100644 (file)
@@ -1210,7 +1210,7 @@ def sanitize_and_renormalize(
     drop_mask: NDArray[np.bool_] | None = None,
     *,
     logger: Logger | None = None,
-    context: str | None = None,
+    context: str,
 ) -> NDArray[np.floating]:
     """Sanitize a weight vector and renormalize so ``mean(out) == 1``.
 
@@ -1218,6 +1218,9 @@ def sanitize_and_renormalize(
     ``drop_mask`` are forced to ``0``. On collapse (no positive finite
     entry survives), returns ones on surviving rows and zeros on dropped
     rows, rescaled so ``mean(out) == 1`` still holds.
+
+    ``context`` is the caller-supplied prefix attached to every warning
+    and error emitted from this helper.
     """
     arr = np.asarray(arr, dtype=float)
     n = arr.size
@@ -1228,13 +1231,13 @@ def sanitize_and_renormalize(
         drop_mask = np.asarray(drop_mask)
         if drop_mask.shape != arr.shape:
             raise ValueError(
-                f"sanitize_and_renormalize: drop_mask shape "
-                f"{drop_mask.shape} != arr shape {arr.shape}"
+                f"{context}: drop_mask shape {drop_mask.shape} != arr "
+                f"shape {arr.shape}"
             )
         if not np.issubdtype(drop_mask.dtype, np.bool_):
             raise ValueError(
-                f"sanitize_and_renormalize: drop_mask dtype "
-                f"{drop_mask.dtype} is not boolean"
+                f"{context}: drop_mask dtype {drop_mask.dtype} is not "
+                f"boolean"
             )
         safe = np.where(drop_mask, 0.0, safe)
     total = safe.sum()
@@ -1247,18 +1250,17 @@ def sanitize_and_renormalize(
     if logger is not None:
         if rescale_overflow:
             logger.warning(
-                "sanitize_and_renormalize: rescale factor non-finite "
-                "(context=%s, n=%d, total=%r); falling back to uniform "
-                "weights",
-                context or "unspecified",
+                "%s: rescale factor non-finite (n=%d, total=%r); "
+                "falling back to uniform weights",
+                context,
                 n,
                 total,
             )
         else:
             logger.warning(
-                "sanitize_and_renormalize: weights collapsed (context=%s, "
-                "total=%r, n=%d); falling back to uniform weights",
-                context or "unspecified",
+                "%s: weights collapsed (total=%r, n=%d); falling back "
+                "to uniform weights",
+                context,
                 total,
                 n,
             )
@@ -1270,9 +1272,9 @@ def sanitize_and_renormalize(
             return masked * (n / total)
         if logger is not None:
             logger.warning(
-                "sanitize_and_renormalize: drop_mask covers all rows in "
-                "fallback; ignoring mask to preserve mean=1 (context=%s)",
-                context or "unspecified",
+                "%s: drop_mask covers all rows in fallback; ignoring "
+                "mask to preserve mean=1",
+                context,
             )
     return fallback
 
@@ -1377,6 +1379,7 @@ def compose_sample_weights(
     label_weights: NDArray[np.floating] | None,
     *,
     logger: Logger,
+    context: str,
     on_collapse: Literal["raise", "fallback"] = "raise",
 ) -> NDArray[np.floating]:
     """Combine base sample weights with the label importance weights.
@@ -1386,6 +1389,13 @@ def compose_sample_weights(
     (``out[i] == 0``); surviving rows carry ``base_weights * label_weights``
     rescaled to global ``mean == 1``.
 
+    ``context`` is the caller-supplied prefix attached to every warning
+    and error emitted from this helper (for example
+    ``"[ETH/USDT] train_test_split:train"``); the inner
+    ``sanitize_and_renormalize`` calls receive
+    ``f"{context}:base_only"`` / ``f"{context}:label_weighted"`` /
+    ``f"{context}:base_fallback"`` to mark the routing branch.
+
     ``on_collapse`` controls the response when the label-weighted product
     collapses on every surviving row: ``"raise"`` (default) surfaces the
     collapse as ``LabelWeightSupportError`` so callers can route it through
@@ -1400,27 +1410,27 @@ def compose_sample_weights(
     base_weights = np.asarray(base_weights, dtype=float)
     if label_weights is None:
         return sanitize_and_renormalize(
-            base_weights, logger=logger, context="compose:base_only"
+            base_weights, logger=logger, context=f"{context}:base_only"
         )
     n = base_weights.shape[0]
     arr = np.asarray(label_weights, dtype=float)
     if arr.shape != (n,):
         raise ValueError(
-            f"compose_sample_weights: label_weights has shape {arr.shape}, "
-            f"expected ({n},)"
+            f"{context}: label_weights shape {arr.shape}, expected ({n},)"
         )
     drop_mask = ~np.isfinite(arr) | (arr <= 0.0)
     if drop_mask.all():
         raise LabelWeightSupportError(
-            "compose_sample_weights: all rows dropped by zero or non-finite "
-            "label weights; no surviving training samples"
+            f"{context}: all rows dropped by zero or non-finite label "
+            f"weights; no surviving training samples"
         )
     nonzero = _pivot_equivalent_count(arr, drop_mask)
     if nonzero / n < SPARSE_TRAINING_MASS_THRESHOLD:
         logger.warning(
-            "compose_sample_weights: sparse training mass "
+            "%s: sparse weighting mass "
             "(%d/%d rows above %.0f%% of surviving max = %.2f%%, "
             "threshold=%.2f%%)",
+            context,
             nonzero,
             n,
             100.0 * _PIVOT_EQUIVALENT_MAX_FRACTION,
@@ -1435,25 +1445,26 @@ def compose_sample_weights(
             combined,
             drop_mask=drop_mask,
             logger=logger,
-            context="compose:label_weighted",
+            context=f"{context}:label_weighted",
         )
     match on_collapse:
         case "raise":
             raise LabelWeightSupportError(
-                f"compose_sample_weights: composed weights collapsed on "
-                f"surviving rows (survivor_total={survivor_total:.6g})"
+                f"{context}: composed weights collapsed on surviving rows "
+                f"(survivor_total={survivor_total:.6g})"
             )
         case "fallback":
             logger.warning(
-                "compose_sample_weights: composed weights collapsed on surviving "
-                "rows (survivor_total=%.6g); falling back to base weights",
+                "%s: composed weights collapsed on surviving rows "
+                "(survivor_total=%.6g); falling back to base weights",
+                context,
                 survivor_total,
             )
             return sanitize_and_renormalize(
                 base_weights,
                 drop_mask=drop_mask,
                 logger=logger,
-                context="compose:base_fallback",
+                context=f"{context}:base_fallback",
             )
         case _:
             assert_never(on_collapse)