code: def _get_initial_step(parameters, lower_bounds, upper_bounds, max_step_sizes):
      
      nmr_params = parameters.shape[1]
  
      initial_step = np.zeros_like(parameters)
  
      if max_step_sizes is None:
          max_step_sizes = 0.1
      if isinstance(max_step_sizes, Number):
          max_step_sizes = [max_step_sizes] * nmr_params
      max_step_sizes = np.array(max_step_sizes)
  
      for ind in range(parameters.shape[1]):
          minimum_allowed_step = np.minimum(np.abs(parameters[:, ind] - lower_bounds[ind]),
                                            np.abs(upper_bounds[ind] - parameters[:, ind]))
          initial_step[:, ind] = np.minimum(minimum_allowed_step, max_step_sizes[ind])
  
      return initial_step / 2.
docs: Get an initial step size to use for every parameter.
  
      This chooses the step sizes based on the maximum step size and the lower and upper bounds.
  
      Args:
          parameters (ndarray): The parameters at which to evaluate the gradient. A (d, p) matrix with d problems,
              p parameters and n samples.
          lower_bounds (list): lower bounds
          upper_bounds (list): upper bounds
          max_step_sizes (list or None): the maximum step size, or the maximum step size per parameter. Defaults to 0.1
  
      Returns:
          ndarray: for every problem instance the vector with the initial step size for each parameter.