code: def test_npts_custom_features(
      use_seasonal_model: bool,
      feature_scale: float,
      min_frac_samples_from_seasons: float,
      context_length_frac: Optional[float],
  ) -> None:
      """
      Same as `test_npts_forecaster` except that we use the weekly frequency and
      a dummy custom feature to define seasonality. The dummy feature defines 52
      weeks as one cycle.
  
      We explicitly disable `use_default_time_features` so that the seasonality
      is defined based only on the custom feature.
  
      Parameters
      ----------
      use_seasonal_model
          use the seasonal variant?
      feature_scale
          scale for the seasonal features to enforce strict sampling over the
          past seasons
      min_frac_samples_from_seasons
          the minimum threshold for fraction of times the predictions should come
          exclusively from past seasons
      context_length_frac
          fraction of history length that should be used as context length
      """
      freq = "W"
      history_length = 52 * 8  # approx. 8 years (seasons)
      train_ts = get_test_data(history_length=history_length, freq=freq)
      context_length = (
          int(context_length_frac * history_length)
          if context_length_frac is not None
          else None
      )
  
      num_seasons = 52 if use_seasonal_model else 1
      pred_length = num_seasons
  
      # Custom features should be defined both in training and prediction ranges
      full_time_index = pd.date_range(
          train_ts.index.min(),
          periods=len(train_ts) + pred_length,
          freq=train_ts.index.freq,
      )
      # Dummy feature defining 52 seasons
      feat_dynamic_real = [
          [
              (ix % 52) / 51.0 - 0.5
              for ix, timestamp in enumerate(full_time_index)
          ]
      ]
  
      predictor = NPTSPredictor(
          prediction_length=pred_length,
          context_length=context_length,
          kernel_type=KernelType.exponential,
          feature_scale=feature_scale,
          use_seasonal_model=use_seasonal_model,
          use_default_time_features=False,  # disable default time features
      )
  
      dataset = [
          {
              "start": pd.Period(train_ts.index[0], freq=freq),
              "target": train_ts.values,
              "feat_dynamic_real": np.array(feat_dynamic_real),
          }
      ]
  
      # validate that the predictor works with targets with NaNs
      _test_nans_in_target(predictor, dataset)
  
      forecast = next(predictor.predict(dataset, num_samples=2000))
  
      train_targets = (
          train_ts.values
          if context_length is None
          else train_ts.values[-min(history_length, context_length) :]
      )
      targets_outside_context = (
          None
          if context_length is None or context_length >= history_length
          else train_ts.values[:context_length]
      )
      targets_str = "seasons" if use_seasonal_model else "targets"
      seasonal_str = "seasonal" if use_seasonal_model else ""
      for t in range(pred_length):
          prev_seasons_ix = range(t, len(train_targets), num_seasons)
  
          # Prediction distribution over all the training targets
          prediction_dist, _ = np.histogram(
              forecast.samples[:, t], np.append(train_targets, np.inf)
          )
          prediction_dist = prediction_dist / sum(prediction_dist)
  
          # The fraction of times the targets from past seasons are sampled
          # exclusively should be above some threshold.
          assert (
              sum(prediction_dist[prev_seasons_ix])
              > min_frac_samples_from_seasons
          ), (
              f"Predictions of {seasonal_str} NPTS are not sampled from past "
              f"{targets_str} enough number of times.\n"
              f"Expected frequency over past {targets_str}: "
              f"{min_frac_samples_from_seasons}\n"
              f"Sampled frequency: {sum(prediction_dist[prev_seasons_ix])}"
          )
  
          # Here we use large value of `feature_scale`, so we sample mostly
          # from past seasons. In this case, the past seasons must be sampled
          # with exponentially decaying weights which depend only
          # on the time index feature: f(t) = t / (train_length + pred_length)
          distance_to_prev_seasons = np.arange(
              len(train_targets) + 1, 1, -num_seasons
          ) / (len(train_targets) + pred_length)
          expected_dist_seasons = np.exp(-distance_to_prev_seasons)
          expected_dist_seasons /= sum(expected_dist_seasons)
  
          prediction_dist_seasons = prediction_dist[prev_seasons_ix]
          prediction_dist_seasons /= sum(prediction_dist_seasons)
  
          np.testing.assert_almost_equal(
              expected_dist_seasons,
              prediction_dist_seasons,
              1,
              f"Predictions of {seasonal_str} NPTS are not sampled with "
              f"exponentially decaying weights over the "
              f"past {targets_str}.\nExpected distribution over the past "
              f"{targets_str}: {expected_dist_seasons}\n"
              f"Prediction_dist: {prediction_dist_seasons}",
          )
  
          if targets_outside_context is not None:
              # Predictions should never be from the targets outside the context
              # length
              assert not set.intersection(
                  set(forecast.samples[:, t]),
                  set(targets_outside_context),
              ), (
                  f"Predictions of NPTS forecaster are sampled from targets"
                  f"outside the context length.\n"
                  f"Targets outside the context length:"
                  f"{targets_outside_context}\n"
                  f"Predictions: {set(forecast.samples[:, t])}"
              )
  
file_path: gluonts/test/model/npts/test_npts.py
imports: from typing import Optional
  import numpy
  import pandas
  import pytest
  from gluonts.dataset.common import DataEntry, Dataset
  from gluonts.exceptions import GluonTSDataError
  from gluonts.model.npts import KernelType, NPTSPredictor
  from gluonts.model.npts._weighted_sampler import WeightedSampler
label_desc: Use image node_id 4 for calling a global function with example usage: test_npts_custom_features(use_seasonal_model, feature_scale, min_frac_samples_from_seasons, context_length_frac) without return types