'Args:
towers: list of gpu relative ids
func: a lambda to be called inside each tower
devices: a list of devices to be used. By default will use GPUs in ``towers``.
use_vs (list[bool]): list of use_vs to passed to TowerContext
Returns:
List of outputs of ``func``, evaluated on each tower.'

@staticmethod
def build_on_multi_tower(towers, func, devices=None, use_vs=None):

    if (len(towers) > 1):
        logger.info('Training    a    model    of    {}    towers'.format(len(towers)))
        _check_tf_version()
    ret = []
    if (devices is not None):
        assert (len(devices) == len(towers))
    tower_names = ['tower{}'.format(idx) for idx in range(len(towers))]
    keys_to_freeze = TOWER_FREEZE_KEYS[:]
    if (use_vs is None):
        use_vs = ([False] * len(towers))
    assert (len(use_vs) == len(towers))
    for (idx, t) in enumerate(towers):
        device = (devices[idx] if (devices is not None) else '/gpu:{}'.format(t))
        with tf.device(device):
            with TowerContext(tower_names[idx], is_training=True, index=idx, use_vs=use_vs[idx]):
                if (idx == t):
                    logger.info('Building    graph    for    training    tower    {}...'.format(idx))
                else:
                    logger.info('Building    graph    for    training    tower    {}    on    device    {}...'.format(idx, device))
                ret.append(func())
                if (idx == 0):
                    backup = backup_collection(keys_to_freeze)
    restore_collection(backup)
    return ret
