class_docstr: PS Synchronizer.
  
  Synchronizes gradient updates using a Parameter Server.
  
  For in-graph synchronization, this just aggregates gradients on a worker's CPU.
  
  For between-graph synchronization, this aggregates gradients on a pre-defined
  (defined in the Strategy) parameter server.
  
  To keep this gradient aggregation in sync, the chief gives each worker a token
  for each variable for the workers to mark when their variable update is complete.
code: def _place_post_grad_agg_ops(
      ps_device, var_op_to_agg_grad, var_op_to_apply_grad_op
  ):
      op_to_task = {}
      agg_grad_ops = []
      for var_op, agg_grad in var_op_to_agg_grad.items():
          var_device = device_spec.DeviceSpecV2.from_string(
              var_op.device
          )
          if agg_grad[0] is not None:
              agg_grad_ops.append(agg_grad[0].op)
              op_to_task[agg_grad[0].op] = var_device.task
          agg_grad_ops.append(agg_grad[1].op)
          op_to_task[agg_grad[1].op] = var_device.task
  
      apply_grad_ops = []
      for var_op, apply_grad_op in var_op_to_apply_grad_op.items():
          var_device = device_spec.DeviceSpecV1.from_string(
              var_op.device
          )
          apply_grad_ops.append(apply_grad_op)
          # colocate apply_grad and variable
          apply_grad_op._set_device(var_device)
          op_to_task[apply_grad_op] = var_device.task
  
      # Make sure that the agg_grad_ops and apply_grad_ops are assigned the same task, if possible
      PSGradientTaskAssigner(
          op_to_task, agg_grad_ops, apply_grad_ops, ps_device
      ).assign()
  
imports: from functools import partial
  from typing import List
  from tensorflow.python import ops
  from tensorflow.python.framework import device_spec, dtypes, constant_op
  from tensorflow.python.platform import tf_logging
  from tensorflow.python.ops import math_ops, data_flow_ops, gen_control_flow_ops, control_flow_ops, gen_math_ops, gen_array_ops
  from autodist.const import MAX_INT64, AUTODIST_PREFIX
  from autodist.kernel.common import utils, variable_utils
  from autodist.kernel.common.op_info import UPDATE_OP_VAR_POS
  from autodist.kernel.common.proxy_variable import ProxyVariable
  from autodist.kernel.common.utils import get_op_name, get_consumers, get_ancestors, traverse, update_consumers, update_control_consumers, replica_prefix, strip_replica_prefix, get_control_consumers, remove_from_control_consumers, get_index_from_tensor_name, update_colocation_group
  from autodist.kernel.common.variable_utils import get_read_var_ops
  from autodist.kernel.synchronization.synchronizer import Synchronizer
  from autodist.proto import synchronizers_pb2
label_desc: Use image node_id 16 for calling the PSSynchronizer obj's underlying member method code with example usage: obj._place_post_grad_agg_ops(ps_device, var_op_to_agg_grad, var_op_to_apply_grad_op) without return types