code: def validate_tucker_rank(
      tensor_shape, rank="same", rounding="round", fixed_modes=None
  ):
      if rounding == "ceil":
          rounding_fun = ivy.ceil
      elif rounding == "floor":
          rounding_fun = ivy.floor
      elif rounding == "round":
          rounding_fun = ivy.round
      else:
          raise ValueError(
              f"Rounding should be round, floor or ceil, but got {rounding}"
          )
  
      # rank is 'same' or float: choose rank so as to
      #  preserve a fraction of the original #parameters
      if rank == "same":
          rank = float(1)
  
      if isinstance(rank, float):
          n_modes_compressed = len(tensor_shape)
          n_param_tensor = int(ivy.prod(tensor_shape))
  
          if fixed_modes is not None:
              tensor_shape = list(tensor_shape)
  
              # sorted to be careful with the order when popping
              #  and reinserting to not remove/add at wrong index.
              # list (mode, shape) that we removed as they will
              #  be kept the same, rank[i] =
              fixed_modes = [
                  (mode, tensor_shape.pop(mode))
                  for mode in sorted(fixed_modes, reverse=True)
              ][::-1]
  
              # number of parameters coming from the fixed modes
              #  (these don't have a variable size as a fun of fraction_param)
              n_fixed_params = ivy.sum(
                  [s**2 for _, s in fixed_modes]
              )  # size of the factors
              n_modes_compressed -= len(fixed_modes)
          else:
              n_fixed_params = 0
  
          # Doesn't contain fixed_modes,
          #  those factors are accounted for in fixed_params
          squared_dims = ivy.sum([s**2 for s in tensor_shape])
  
          def fun(x):
              return (
                  n_param_tensor * x**n_modes_compressed
                  + squared_dims * x
                  + n_fixed_params * x
                  - rank * n_param_tensor
              )
  
          # fraction_param = brentq(fun, 0.0, max(rank, 1.0))
          fraction_param = _bisection_root_finder(
              fun, 0.0, max(rank, 1.0)
          )
          rank = [
              max(int(rounding_fun(s * fraction_param)), 1)
              for s in tensor_shape
          ]
  
          if fixed_modes is not None:
              for mode, size in fixed_modes:
                  rank.insert(mode, size)
  
      elif isinstance(rank, int):
          n_modes = len(tensor_shape)
          warnings.warn(
              "Given only one int for 'rank' for decomposition a tensor of order"
              f" {n_modes}. Using this rank for all modes."
          )
          if fixed_modes is None:
              rank = [rank] * n_modes
          else:
              rank = [
                  rank if i not in fixed_modes else s
                  for (i, s) in enumerate(tensor_shape)
              ]  # *n_mode
  
      return rank
  
imports: from .base import FactorizedTensor
  import ivy
  from copy import deepcopy
  import warnings
label_desc: Use image node_id 19 for calling the TuckerTensor obj's underlying member method code with example usage: obj.validate_tucker_rank(tensor_shape, rank, rounding, fixed_modes) and returns: rank, unknown