def grad_method_validation(method, tape):
    """Validates if the gradient method requested is supported by the trainable
    parameters of a tape, and returns the allowed parameter gradient methods.

    This method will generate parameter gradient information for the given tape if it
    has not already been generated, and then proceed to validate the gradient method.
    In particular:

    * An exception will be raised if there exist non-differentiable trainable
      parameters on the tape.

    * An exception will be raised if the Jacobian method is ``"analytic"`` but there
      exist some trainable parameters on the tape that only support numeric differentiation.

    If all validations pass, this method will return a tuple containing the allowed parameter
    gradient methods for each trainable parameter.

    Args:
        method (str): the overall Jacobian differentiation method
        tape (.QuantumTape): the tape with associated parameter information

    Returns:
        tuple[str, None]: the allowed parameter gradient methods for each trainable parameter
    """

    diff_methods = {
        idx: info["grad_method"]
        for idx, info in tape._par_info.items()  # pylint: disable=protected-access
        if idx in tape.trainable_params
    }

    # check and raise an error if any parameters are non-differentiable
    nondiff_params = {idx for idx, g in diff_methods.items() if g is None}

    if nondiff_params:
        raise ValueError(f"Cannot differentiate with respect to parameter(s) {nondiff_params}")

    numeric_params = {idx for idx, g in diff_methods.items() if g == "F"}

    # If explicitly using analytic mode, ensure that all parameters
    # support analytic differentiation.
    if method == "analytic" and numeric_params:
        raise ValueError(
            f"The analytic gradient method cannot be used with the parameter(s) {numeric_params}."
        )

    return tuple(diff_methods.values())