docs: def fit_mle(self,
                  init_vals,
                  constrained_pos=None,
                  print_res=True,
                  method="BFGS",
                  loss_tol=1e-06,
                  gradient_tol=1e-06,
                  maxiter=1000,
                  ridge=None,
                  just_point=False,
                  **kwargs):
          """
          Parameters
          ----------
          init_vals : 1D ndarray.
              Should containn the initial values to start the optimization
              process with. There should be one value for each nest parameter
              and utility coefficient. Nest parameters not being estimated
              should still be included. Handle these parameters using the
              `constrained_pos` kwarg.
          constrained_pos : list, or None, optional.
              Denotes the positions of the array of estimated parameters that are
              not to change from their initial values. If a list is passed, the
              elements are to be integers where no such integer is greater than
              `init_values.size`.
          print_res : bool, optional.
              Determines whether the timing and initial and final log likelihood
              results will be printed as they they are determined.
          method : str, optional.
              Should be a valid string which can be passed to
              `scipy.optimize.minimize`. Determines the optimization algorithm
              which is used for this problem.
          loss_tol : float, optional.
              Determines the tolerance on the difference in objective function
              values from one iteration to the next which is needed to determine
              convergence. Default `== 1e-06`.
          gradient_tol : float, optional.
              Determines the tolerance on the difference in gradient values from
              one iteration to the next which is needed to determine convergence.
              Default `== 1e-06`.
          ridge : int, float, long, or None, optional.
              Determines whether ridge regression is performed. If a scalar is
              passed, then that scalar determines the ridge penalty for the
              optimization. Default `== None`.
          just_point : bool, optional.
              Determines whether (True) or not (False) calculations that are non-
              critical for obtaining the maximum likelihood point estimate will
              be performed. If True, this function will return the results
              dictionary from scipy.optimize. Default == False.
  
          Returns
          -------
          None. Estimation results are saved to the model instance.
          """
          # Check integrity of passed arguments
          kwargs_to_be_ignored = ["init_shapes", "init_intercepts", "init_coefs"]
          if any([x in kwargs for x in kwargs_to_be_ignored]):
              msg = "Nested Logit model does not use the following kwargs:\n{}"
              msg_2 = "Remove such kwargs and pass a single init_vals argument"
              raise ValueError(msg.format(kwargs_to_be_ignored) + msg_2)
  
          # Store the optimization method
          self.optimization_method = method
  
          # Store the ridge parameter
          self.ridge_param = ridge
  
          if ridge is not None:
              warnings.warn(_ridge_warning_msg)
  
          # Construct the mappings from alternatives to observations and from
          # chosen alternatives to observations
          mapping_res = self.get_mappings_for_fit()
  
          # Determine the degenerate nests whose nesting parameters are to be
          # constrained to one. Note the following functions assume that the nest
          # parameters are placed before the index coefficients.
          fixed_params = identify_degenerate_nests(self.nest_spec)
  
          # Include the user specified parameters that are to be constrained to
          # their initial values
          if constrained_pos is not None:
              fixed_params.extend(constrained_pos)
          final_constrained_pos = sorted(list(set(fixed_params)))
  
          # Create the estimation object
          zero_vector = np.zeros(init_vals.shape)
          estimator_args = [self,
                            mapping_res,
                            ridge,
                            zero_vector,
                            split_param_vec]
          estimator_kwargs = {"constrained_pos": final_constrained_pos}
          nested_estimator = NestedEstimator(*estimator_args,
                                             **estimator_kwargs)
  
          # Perform one final check on the length of the initial values
          nested_estimator.check_length_of_initial_values(init_vals)
  
          # Get the estimation results
          estimation_res = estimate(init_vals,
                                    nested_estimator,
                                    method,
                                    loss_tol,
                                    gradient_tol,
                                    maxiter,
                                    print_res,
                                    use_hessian=True,
                                    just_point=just_point)
  
          if not just_point:
              # Store the estimation results
              self.store_fit_results(estimation_res)
  
              return None
          else:
              return estimation_res