'Args:
model (ModelDesc):
input (InputSource):
ps_device (str):
tower (list[int]):
Returns:
tf.Operation: the training op
[Callback]: the callbacks to be added'

@staticmethod
def setup_graph(model, input, ps_device, tower):

    callbacks = input.setup(model.get_inputs_desc())
    raw_devices = ['/gpu:{}'.format(k) for k in tower]
    if (ps_device == 'gpu'):
        devices = [LeastLoadedDeviceSetter(d, raw_devices) for d in raw_devices]
    else:
        devices = [tf.train.replica_device_setter(worker_device=d, ps_device='/cpu:0', ps_tasks=1) for d in raw_devices]
    grad_list = MultiGPUTrainerBase.build_on_multi_tower(tower, (lambda : MultiGPUTrainerBase._build_graph_get_grads(model, input)), devices)
    MultiGPUTrainerBase._check_grad_list(grad_list)
    grads = SyncMultiGPUTrainerParameterServer._average_grads(grad_list)
    train_op = model.get_optimizer().apply_gradients(grads, name='train_op')
    return (train_op, callbacks)
