def METHOD_NAME(annotation, config):
    """
    A function to convert a type/rtype typehint annotation into a :type:/:rtype: string.
    This function is a bit hacky, and specific to the type annotations we use most frequently.
    This function is recursive.
    """
    # Simple/base case: any string annotation is ready to go
    if type(annotation) == str:
        return annotation
    # Jaxtyping: shaped tensors or linear operator
    elif hasattr(annotation, "__module__") and "jaxtyping" == annotation.__module__:
        cls_annotation = _convert_internal_and_external_class_to_strings(annotation.array_type)
        shape = " x ".join([_dim_to_str(dim) for dim in annotation.dims])
        return f"{cls_annotation} ({shape})"
    # Convert Ellipsis into "..."
    elif annotation == Ellipsis:
        return "..."
    # Convert any class (i.e. torch.Tensor, LinearOperator, etc.) into appropriate strings
    elif hasattr(annotation, "__name__"):
        res = _convert_internal_and_external_class_to_strings(annotation)
    elif str(annotation).startswith("typing.Callable"):
        if len(annotation.__args__) == 2:
            res = f"Callable[{METHOD_NAME(annotation.__args__[0], config)} -> {METHOD_NAME(annotation.__args__[1], config)}]"
        else:
            res = "Callable"
    # Convert any Union[*A*, *B*, *C*] into "*A* or *B* or *C*"
    # Also, convert any Optional[*A*] into "*A*, optional"
    elif str(annotation).startswith("typing.Union"):
        is_optional_str = ""
        args = list(annotation.__args__)
        # Hack: Optional[*A*] are represented internally as Union[*A*, Nonetype]
        # This catches this case
        if args[-1] is type(None):  # noqa E721
            del args[-1]
            is_optional_str = ", optional"
        processed_args = [METHOD_NAME(arg, config) for arg in args]
        res = " or ".join(processed_args) + is_optional_str
    # Convert any Tuple[*A*, *B*] into "(*A*, *B*)"
    elif str(annotation).startswith("typing.Tuple"):
        args = list(annotation.__args__)
        res = "(" + ", ".join(METHOD_NAME(arg, config) for arg in args) + ")"
    # Callable typing annotation
    elif str(annotation).startswith("typing."):
        return str(annotation)[7:]
    # Special cases for forward references.
    # This is brittle, as it only contains case for a select few forward refs
    # All others that aren't caught by this are handled by the final case
    elif isinstance(annotation, ForwardRef):
        res = str(annotation.__forward_arg__)
        if res == "LinearOperator":
            res = "~linear_operator.LinearOperator"
        elif "LinearOperator" in res:
            res = f"~linear_operator.operators.{res}"
    # For everything we didn't catch: use the simplist string representation
    else:
        warnings.warn(f"No rule for {annotation}. Using default resolution...", RuntimeWarning)
        res = str(annotation)
    return res