class_docstr: Searches for the best model using the stepwise algorithm.
  
  The stepwise algorithm fluctuates the more sensitive pieces of the ARIMA
  (the seasonal components) first, adjusting towards the direction of the
  smaller {A|B|HQ}IC(c), and continues to step down as long as the error
  shrinks. As long as the error term decreases and the best parameters have
  not shifted to a point where they can no longer change, ``k`` will
  increase, and the models will continue to be fit until the ``max_k`` is
  reached.
  
  References
  ----------
  .. [1] R's auto-arima stepwise source code: https://github.com/robjhyndman/forecast/blob/30308a4e314ff29338291462e81bf68ff0c5f86d/R/newarima2.R#L366
  .. [2] https://robjhyndman.com/hyndsight/arma-roots/
code: def solve(self):
      start_time = datetime.now()
      p, d, q = self.p, self.d, self.q
      P, D, Q, m = self.P, self.D, self.Q, self.m
      max_p, max_q = self.max_p, self.max_q
      max_P, max_Q = self.max_P, self.max_Q
  
      if self.trace:
          print(
              "Performing stepwise search to minimize %s"
              % self.information_criterion
          )
  
      # fit a baseline p, d, q model
      self._do_fit((p, d, q), (P, D, Q, m))
  
      # null model with possible constant
      if self._do_fit((0, d, 0), (0, D, 0, m)):
          p = q = P = Q = 0
  
      # A basic AR model
      if max_p > 0 or max_P > 0:
          _p = 1 if max_p > 0 else 0
          _P = 1 if (m > 1 and max_P > 0) else 0
          if self._do_fit((_p, d, 0), (_P, D, 0, m)):
              p = _p
              P = _P
              q = Q = 0
  
      # Basic MA model
      if max_q > 0 or max_Q > 0:
          _q = 1 if max_q > 0 else 0
          _Q = 1 if (m > 1 and max_Q > 0) else 0
          if self._do_fit((0, d, _q), (0, D, _Q, m)):
              p = P = 0
              Q = _Q
              q = _q
  
      # Null model with NO constant (if we haven't tried it yet)
      if self.with_intercept:
          if self._do_fit((0, d, 0), (0, D, 0, m), constant=False):
              p = q = P = Q = 0
  
      while self.start_k < self.k < self.max_k:
          self.start_k = self.k
  
          # break loop if execution time exceeds the timeout threshold. needs
          # to be at front of loop, since a single pass may reach termination
          # criteria by end and we only want to warn and break if the loop is
          # continuing again
          dur = (datetime.now() - start_time).total_seconds()
          if self.max_dur and dur > self.max_dur:
              warnings.warn(
                  "early termination of stepwise search due to "
                  "max_dur threshold (%.3f > %.3f)"
                  % (dur, self.max_dur)
              )
              break
  
          # NOTE: k changes for every fit, so we might need to bail halfway
          # through the loop, hence the multiple checks.
          if (
              P > 0
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P - 1, D, Q, m))
          ):
              P -= 1
              continue
  
          if (
              Q > 0
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P, D, Q - 1, m))
          ):
              Q -= 1
              continue
  
          if (
              P < max_P
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P + 1, D, Q, m))
          ):
              P += 1
              continue
  
          if (
              Q < max_Q
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P, D, Q + 1, m))
          ):
              Q += 1
              continue
  
          if (
              Q > 0
              and P > 0
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P - 1, D, Q - 1, m))
          ):
              Q -= 1
              P -= 1
              continue
  
          if (
              Q < max_Q
              and P > 0
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P - 1, D, Q + 1, m))
          ):
              Q += 1
              P -= 1
              continue
  
          if (
              Q > 0
              and P < max_P
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P + 1, D, Q - 1, m))
          ):
              Q -= 1
              P += 1
              continue
  
          if (
              Q < max_Q
              and P < max_P
              and self.k < self.max_k
              and self._do_fit((p, d, q), (P + 1, D, Q + 1, m))
          ):
              Q += 1
              P += 1
              continue
  
          if (
              p > 0
              and self.k < self.max_k
              and self._do_fit((p - 1, d, q), (P, D, Q, m))
          ):
              p -= 1
              continue
  
          if (
              q > 0
              and self.k < self.max_k
              and self._do_fit((p, d, q - 1), (P, D, Q, m))
          ):
              q -= 1
              continue
  
          if (
              p < max_p
              and self.k < self.max_k
              and self._do_fit((p + 1, d, q), (P, D, Q, m))
          ):
              p += 1
              continue
  
          if (
              q < max_q
              and self.k < self.max_k
              and self._do_fit((p, d, q + 1), (P, D, Q, m))
          ):
              q += 1
              continue
  
          if (
              q > 0
              and p > 0
              and self.k < self.max_k
              and self._do_fit((p - 1, d, q - 1), (P, D, Q, m))
          ):
              q -= 1
              p -= 1
              continue
  
          if (
              q < max_q
              and p > 0
              and self.k < self.max_k
              and self._do_fit((p - 1, d, q + 1), (P, D, Q, m))
          ):
              q += 1
              p -= 1
              continue
  
          if (
              q > 0
              and p < max_p
              and self.k < self.max_k
              and self._do_fit((p + 1, d, q - 1), (P, D, Q, m))
          ):
              q -= 1
              p += 1
              continue
  
          if (
              q < max_q
              and p < max_p
              and self.k < self.max_k
              and self._do_fit((p + 1, d, q + 1), (P, D, Q, m))
          ):
              q += 1
              p += 1
              continue
  
          # R: if (allowdrift || allowmean)
          # we don't have these args, so we just default this case to true to
          # evaluate all corners
          if self.k < self.max_k and self._do_fit(
              (p, d, q), (P, D, Q, m), constant=not self.with_intercept
          ):
              self.with_intercept = not self.with_intercept
              continue
  
      # check if the search has been ended after max_steps
      if (
          self.exec_context.max_steps is not None
          and self.k >= self.exec_context.max_steps
      ):
          warnings.warn(
              "stepwise search has reached the maximum number "
              "of tries to find the best fit model"
          )
  
      # TODO: if (approximation && !is.null(bestfit$arma)) - refit best w MLE
  
      filtered_models_ics = sorted(
          [
              (v, self.fit_time_dict[k], self.ic_dict[k])
              for k, v in self.results_dict.items()
              if v is not None
          ],
          key=(lambda fit_ic: fit_ic[1]),
      )
  
      sorted_fits = _sort_and_filter_fits(filtered_models_ics)
      if self.trace and sorted_fits:
          print(f"\nBest model: {str(sorted_fits[0])}")
  
      return sorted_fits
  
imports: from numpy.linalg import LinAlgError
  import numpy
  from datetime import datetime
  from joblib import Parallel, delayed
  from sklearn.utils import check_random_state
  import abc
  import functools
  import time
  import warnings
  import traceback
  from .arima import ARIMA
  from ..warnings import ModelFitWarning
  from ._context import ContextType, ContextStore
  from .None import _validation
  from ..compat import statsmodels
label_desc: Use image node_id 3 for calling the _StepwiseFitWrapper obj's underlying member method code with example usage: obj.solve() and returns: sorted_fits