docs: def calc_probabilities(beta,
                         design,
                         alt_IDs,
                         rows_to_obs,
                         rows_to_alts,
                         utility_transform,
                         intercept_params=None,
                         shape_params=None,
                         chosen_row_to_obs=None,
                         return_long_probs=False):
      """
      Parameters
      ----------
      beta : 1D or 2D ndarray.
          All elements should by ints, floats, or longs. If 1D, should have 1
          element for each utility coefficient being estimated (i.e.
          num_features). If 2D, should have 1 column for each set of coefficients
          being used to predict the probabilities of each alternative being
          chosen. There should be one row per index coefficient.
      design : 2D or 3D ndarray.
          There should be one row per observation per available alternative.
          There should be one column per utility coefficient being estimated. All
          elements should be ints, floats, or longs. If `len(design.shape) == 3`,
          then beta MUST be 1D.
      alt_IDs : 1D ndarray.
          All elements should be ints. There should be one row per obervation per
          available alternative for the given observation. Elements denote the
          alternative corresponding to the given row of the design matrix.
      rows_to_obs : 2D ndarray.
          There should be one row per observation per available alternative and
          one column per observation. This matrix maps the rows of the design
          matrix to the unique observations (on the columns).
      rows_to_alts : 2D ndarray.
          There should be one row per observation per available alternative and
          one column per possible alternative. This matrix maps the rows of the
          design matrix to the possible alternatives for this dataset.
      utility_transform : callable.
          Should accept a 1D array of systematic utility values, a 1D array of
          alternative IDs, and miscellaneous args and kwargs. Should return a 1D
          array whose elements contain the appropriately transformed systematic
          utility values, based on the current model being evaluated.
      intercept_params : 1D ndarray, or None, optional.
          If an array, each element should be an int, float, or long. For
          identifiability, there should be J- 1 elements where J is the total
          number of observed alternatives for this dataset. Default == None.
      shape_params : 1D ndarray, or None, optional.
          If an array, each element should be an int, float, or long. There
          should be one value per shape parameter of the model being used.
          Default == None.
      chosen_row_to_obs :  2D scipy sparse array, or None, optional.
          There should be one row per observation per available alternative and
          one column per observation. This matrix indicates, for each observation
          (on the columns), which rows of the design matrix were the realized
          outcome. If an array is passed then an array of shape
          (num_observations,) will be returned and each element will be the
          probability of the realized outcome of the given observation.
          Default == None.
      return_long_probs :  bool, optional.
          Indicates whether or not the long format probabilites (a 1D numpy array
          with one element per observation per available alternative) should be
          returned. Default == False.
  
      Returns
      -------
      numpy array or tuple of two numpy arrays.
          If `chosen_row_to_obs` is passed AND `return_long_probs is True`, then
          the tuple `(chosen_probs, long_probs)` is returned. If
          `return_long_probs is True` and `chosen_row_to_obs is None`, then
          `long_probs` is returned. If `chosen_row_to_obs` is passed and
          `return_long_probs is False` then `chosen_probs` is returned.
  
          `chosen_probs` is a 1D numpy array of shape (num_observations,). Each
          element is the probability of the corresponding observation being
          associated with its realized outcome.
  
          `long_probs` is a 1D numpy array with one element per observation per
          available alternative for that observation. Each element is the
          probability of the corresponding observation being associated with that
          rows corresponding alternative.
  
          If `beta` is a 2D array, `chosen_probs` and `long_probs` will also be
          2D arrays, with as many columns as there are sets of parameters being
          used to calculate probabilities with.
  
          It is NOT valid to have `chosen_row_to_obs == None` and
          `return_long_probs == False`.
      """
      # Check argument validity
      if (len(beta.shape) >= 2) and (len(design.shape) >= 3):
          msg_1 = "Cannot calculate probabilities with both 3D design matrix AND"
          msg_2 = " 2D coefficient array."
          raise ValueError(msg_1 + msg_2)
      if chosen_row_to_obs is None and return_long_probs is False:
          msg = "chosen_row_to_obs is None AND return_long_probs is False"
          raise ValueError(msg)
  
      # Calculate the systematic utility for each alternative for each individual
      sys_utilities = design.dot(beta)
  
      # Calculate the probability from the transformed utilities
      # The transformed utilities will be of shape (num_rows, 1)
      transformed_utilities = utility_transform(sys_utilities,
                                                alt_IDs,
                                                rows_to_alts,
                                                shape_params,
                                                intercept_params)
  
      # The following commands are to guard against numeric under/over-flow
      too_small_idx = transformed_utilities < min_exponent_val
      too_large_idx = transformed_utilities > max_exponent_val
  
      transformed_utilities[too_small_idx] = min_exponent_val
      transformed_utilities[too_large_idx] = max_exponent_val
  
      # Exponentiate the transformed utilities
      long_exponentials = np.exp(transformed_utilities)
  
      # long_probs will be of shape (num_rows,) Each element will provide the
      # probability of the observation associated with that row having the
      # alternative associated with that row as the observation's outcome
      individual_denominators = np.asarray(rows_to_obs.transpose().dot(
                                                      long_exponentials))
      long_denominators = np.asarray(rows_to_obs.dot(individual_denominators))
      if len(long_exponentials.shape) > 1 and long_exponentials.shape[1] > 1:
          long_probs = (long_exponentials / long_denominators)
      else:
          long_probs = (long_exponentials / long_denominators).ravel()
  
      # Guard against underflow
      long_probs[long_probs == 0] = min_comp_value
  
      if chosen_row_to_obs is None:
          chosen_probs = None
      else:
          # chosen_probs will be of shape (num_observations,)
          chosen_exponentials = np.asarray(
                           chosen_row_to_obs.transpose().dot(long_exponentials))
          if len(long_exponentials.shape) > 1 and long_exponentials.shape[1] > 1:
              chosen_probs = chosen_exponentials / individual_denominators
          else:
              chosen_probs = (chosen_exponentials /
                              individual_denominators).ravel()
  
      # Return the long form and chosen probabilities if desired
      if return_long_probs and chosen_probs is not None:
          return chosen_probs, long_probs
      # If working with predictions, return just the long form probabilities
      elif return_long_probs and chosen_probs is None:
          return long_probs
      # If estimating the model and storing fitted probabilities or testing the
      # model on data for which we know the chosen alternative, just return the
      # chosen probabilities.
      elif chosen_probs is not None:
          return chosen_probs