docs: def fit_mle(self,
                  init_vals,
                  num_draws,
                  seed=None,
                  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 contain the initial values to start the optimization process
              with. There should be one value for each utility coefficient and
              shape parameter being estimated.
          num_draws : int.
              Should be greater than zero. Denotes the number of draws that we
              are making from each normal distribution.
          seed : int or None, optional.
              If an int is passed, it should be greater than zero. Denotes the
              value to be used in seeding the random generator used to generate
              the draws from the normal distribution. Default == None.
          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.` Default == None.
          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
              that 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.
          maxiter : int, optional.
              Denotes the maximum number of iterations of the algorithm specified
              by `method` that will be used to estimate the parameters of the
              given model. Default == 1000.
          ridge : int, float, long, or None, optional.
              Determines whether or not ridge regression is performed. If a float
              is passed, then that float 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 = "MNL model does not use of any of 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()
          rows_to_mixers = mapping_res["rows_to_mixers"]
  
          # Get the draws for each random coefficient
          num_mixing_units = rows_to_mixers.shape[1]
          draw_list = mlc.get_normal_draws(num_mixing_units,
                                           num_draws,
                                           len(self.mixing_pos),
                                           seed=seed)
  
          # Create the 3D design matrix
          self.design_3d = mlc.create_expanded_design_for_mixing(self.design,
                                                                 draw_list,
                                                                 self.mixing_pos,
                                                                 rows_to_mixers)
  
          # Create the estimation object
          zero_vector = np.zeros(init_vals.shape)
          mixl_estimator = MixedEstimator(self,
                                          mapping_res,
                                          ridge,
                                          zero_vector,
                                          split_param_vec,
                                          constrained_pos=constrained_pos)
  
          # Perform one final check on the length of the initial values
          mixl_estimator.check_length_of_initial_values(init_vals)
  
          # Get the estimation results
          estimation_res = estimate(init_vals,
                                    mixl_estimator,
                                    method,
                                    loss_tol,
                                    gradient_tol,
                                    maxiter,
                                    print_res,
                                    use_hessian=True,
                                    just_point=just_point)
  
          if not just_point:
              # Store the mixed logit specific estimation results
              args = [mixl_estimator, estimation_res]
              estimation_res = add_mixl_specific_results_to_estimation_res(*args)
  
              # Store the estimation results
              self.store_fit_results(estimation_res)
  
              return None
          else:
              return estimation_res