code: def validate_tucker_rank(
      tensor_shape, rank="same", rounding="round", fixed_modes=None
  ):
      r"""Returns the rank of a Tucker Decomposition
  
      Parameters
      ----------
      tensor_shape : tupe
          shape of the tensor to decompose
      rank : {'same', float, tuple, int}, default is same
          way to determine the rank, by default 'same'
          if 'same': rank is computed to keep the number of parameters (at most) the same
          if float, computes a rank so as to keep rank percent of the original number of parameters
          if int or tuple, just returns rank
      rounding = {'round', 'floor', 'ceil'}
      fixed_modes : int list or None, default is None
          if not None, a list of modes for which the rank will be the same as the original shape
          e.g. if i in fixed_modes, then rank[i] = tensor_shape[i]
  
      Returns
      -------
      rank : int tuple
          rank of the decomposition
  
      Notes
      -----
      For a fractional input rank, I want to find a Tucker rank such that:
      n_param_decomposition = rank*n_param_tensor
  
      In particular, for an input of size I_1, ..., I_N:
      I find a value c such that the rank will be (c I_1, ..., c I_N)
  
      We have sn_param_tensor = I_1 x ... x I_N
  
      We look for a Tucker decomposition of rank (c I_1, ..., c I_N )
      This decomposition will have the following n_params:
      For the core : \prod_k c I_k = c^N \prod I_k = c^N n_param_tensor
      For the factors : \sum_k c I_k^2
  
      In other words we want to solve:
      c^N n_param_tensor + \sum_k c I_k^2 = rank*n_param_tensor
      """
      if rounding == "ceil":
          rounding_fun = np.ceil
      elif rounding == "floor":
          rounding_fun = np.floor
      elif rounding == "round":
          rounding_fun = np.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 = np.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 = np.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 = np.sum([s**2 for s in tensor_shape])
  
          fun = (
              lambda x: 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))
          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)
          message = f"Given only one int for 'rank' for decomposition a tensor of order {n_modes}. Using this rank for all modes."
          warnings.warn(message, RuntimeWarning)
          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 ._factorized_tensor import FactorizedTensor
  from .base import unfold, tensor_to_vec
  from .tenalg import multi_mode_dot, mode_dot
  from .None import backend
  import numpy
  from scipy.optimize import brentq
  import warnings
label_desc: Use image node_id 8 for calling a global function with example usage: validate_tucker_rank(tensor_shape, rank, rounding, fixed_modes) and returns: rank