docstr: get factors to update
  
  :param gradients: ([TensorFlow Tensor]) The gradients
  :param varlist: ([TensorFlow Tensor]) The parameters
  :return: ([TensorFlow Tensor]) The factors to update
docstr_tok: ["get","factors","to","update",":","param","gradients",":","(","[","TensorFlow","Tensor","]",")","The","gradients",":","param","varlist",":","(","[","TensorFlow","Tensor","]",")","The","parameters",":","return",":","(","[","TensorFlow","Tensor","]",")","The","factors","to","update"]
code: def get_factors(self, gradients, varlist):
      """
      get factors to update
  
      :param gradients: ([TensorFlow Tensor]) The gradients
      :param varlist: ([TensorFlow Tensor]) The parameters
      :return: ([TensorFlow Tensor]) The factors to update
      """
      default_graph = tf.get_default_graph()
      factor_tensors = {}
      fprop_tensors = []
      bprop_tensors = []
      op_types = []
  
      def _search_factors(gradient, graph):
          # hard coded search stratergy
          bprop_op = gradient.op
          bprop_op_name = bprop_op.name
  
          b_tensors = []
          f_tensors = []
  
          # combining additive gradient, assume they are the same op type and
          # indepedent
          if "AddN" in bprop_op_name:
              factors = []
              for grad in gradient.op.inputs:
                  factors.append(_search_factors(grad, graph))
              op_names = [_item["opName"] for _item in factors]
              if self.verbose > 1:
                  # TODO: need to check all the attribute of the ops as well
                  print(gradient.name)
                  print(op_names)
                  print(len(np.unique(op_names)))
              assert (
                  len(np.unique(op_names)) == 1
              ), "Error: {} is shared among different computation OPs".format(
                  gradient.name
              )
  
              b_tensors = reduce(
                  lambda x, y: x + y,
                  [_item["bpropFactors"] for _item in factors],
              )
              if len(factors[0]["fpropFactors"]) > 0:
                  f_tensors = reduce(
                      lambda x, y: x + y,
                      [_item["fpropFactors"] for _item in factors],
                  )
              fprop_op_name = op_names[0]
              fprop_op = factors[0]["op"]
          else:
              fprop_op_match = re.search(
                  "gradientsSampled(_[0-9]+|)/(.+?)_grad", bprop_op_name
              )
              assert fprop_op_match is not None
              fprop_op_name = fprop_op_match.group(2)
              fprop_op = graph.get_operation_by_name(fprop_op_name)
              if fprop_op.op_def.name in KFAC_OPS:
                  # Known OPs
                  b_tensor = [
                      _i
                      for _i in bprop_op.inputs
                      if "gradientsSampled" in _i.name
                  ][-1]
                  b_tensor_shape = fprop_op.outputs[0].get_shape()
                  if b_tensor.get_shape()[0].value is None:
                      b_tensor.set_shape(b_tensor_shape)
                  b_tensors.append(b_tensor)
  
                  if fprop_op.op_def.name == "BiasAdd":
                      f_tensors = []
                  else:
                      f_tensors.append(
                          [
                              _i
                              for _i in fprop_op.inputs
                              if param.op.name not in _i.name
                          ][0]
                      )
                  fprop_op_name = fprop_op.op_def.name
              else:
                  # unknown OPs, block approximation used
                  b_inputs_list = [
                      _i
                      for _i in bprop_op.inputs[0].op.inputs
                      if "gradientsSampled" in _i.name
                      if "Shape" not in _i.name
                  ]
                  if len(b_inputs_list) > 0:
                      b_tensor = b_inputs_list[0]
                      # only if tensor shape is defined, usually this will prevent tensor like Sum:0 to be used.
                      if b_tensor.get_shape():
                          b_tensor_shape = fprop_op.outputs[
                              0
                          ].get_shape()
                          if (
                              len(b_tensor.get_shape()) > 0
                              and b_tensor.get_shape()[0].value is None
                          ):
                              b_tensor.set_shape(b_tensor_shape)
                          b_tensors.append(b_tensor)
                  fprop_op_name = "UNK-" + fprop_op.op_def.name
                  op_types.append(fprop_op_name)
  
          return {
              "opName": fprop_op_name,
              "op": fprop_op,
              "fpropFactors": f_tensors,
              "bpropFactors": b_tensors,
          }
  
      for _grad, param in zip(gradients, varlist):
          if KFAC_DEBUG:
              print(("get factor for " + param.name))
          found_factors = _search_factors(_grad, default_graph)
          factor_tensors[param] = found_factors
  
      # check associated weights and bias for homogeneous coordinate representation
      # and check redundent factors
      # TODO: there may be a bug to detect associate bias and weights for forking layer, e.g. in inception models.
      for param in varlist:
          factor_tensors[param]["assnWeights"] = None
          factor_tensors[param]["assnBias"] = None
      for param in varlist:
          if factor_tensors[param]["opName"] == "BiasAdd":
              factor_tensors[param]["assnWeights"] = None
              for item in varlist:
                  if len(factor_tensors[item]["bpropFactors"]) > 0:
                      if (
                          set(factor_tensors[item]["bpropFactors"])
                          == set(factor_tensors[param]["bpropFactors"])
                      ) and (
                          len(factor_tensors[item]["fpropFactors"]) > 0
                      ):
                          factor_tensors[param]["assnWeights"] = item
                          factor_tensors[item]["assnBias"] = param
                          factor_tensors[param][
                              "bpropFactors"
                          ] = factor_tensors[item]["bpropFactors"]
  
      # concatenate the additive gradients along the batch dimension, i.e. assuming independence structure
      for key in ["fpropFactors", "bpropFactors"]:
          for i, param in enumerate(varlist):
              if len(factor_tensors[param][key]) > 0:
                  if (key + "_concat") not in factor_tensors[param]:
                      tensor = factor_tensors[param][key][
                          0
                      ]  # type: tf.Tensor
                      name_scope = tensor.name.split(":")[0]
                      with tf.name_scope(name_scope):
                          factor_tensors[param][
                              key + "_concat"
                          ] = tf.concat(factor_tensors[param][key], 0)
              else:
                  factor_tensors[param][key + "_concat"] = None
              for _, param2 in enumerate(varlist[(i + 1) :]):
                  if (len(factor_tensors[param][key]) > 0) and (
                      set(factor_tensors[param2][key])
                      == set(factor_tensors[param][key])
                  ):
                      factor_tensors[param2][key] = factor_tensors[
                          param
                      ][key]
                      factor_tensors[param2][
                          key + "_concat"
                      ] = factor_tensors[param][key + "_concat"]
  
      if KFAC_DEBUG:
          for items in zip(
              varlist, fprop_tensors, bprop_tensors, op_types
          ):
              print((items[0].name, factor_tensors[item]))
      self.factors = factor_tensors
      return factor_tensors
  
imports: import re
  from functools import reduce
  import tensorflow
  import numpy
  from stable_baselines.acktr.kfac_utils import detect_min_val, factor_reshape, gmatmul
label_desc: Use image node_id 2 for calling the KfacOptimizer obj's underlying member method code with example usage: obj.get_factors(gradients, varlist) and returns: factor_tensors, dict