Group tensors together. This creates a tuple of tensors with the same values as the `tensors` argument, except that the value of each tensor is only returned after the values of all tensors have been computed. `control_inputs` contains additional ops that have to finish before this op fi
(tensors, name=None, control_inputs=None)
| 2946 | |
| 2947 | @tf_export(v1=["tuple"]) |
| 2948 | def tuple(tensors, name=None, control_inputs=None): # pylint: disable=redefined-builtin |
| 2949 | """Group tensors together. |
| 2950 | |
| 2951 | This creates a tuple of tensors with the same values as the `tensors` |
| 2952 | argument, except that the value of each tensor is only returned after the |
| 2953 | values of all tensors have been computed. |
| 2954 | |
| 2955 | `control_inputs` contains additional ops that have to finish before this op |
| 2956 | finishes, but whose outputs are not returned. |
| 2957 | |
| 2958 | This can be used as a "join" mechanism for parallel computations: all the |
| 2959 | argument tensors can be computed in parallel, but the values of any tensor |
| 2960 | returned by `tuple` are only available after all the parallel computations |
| 2961 | are done. |
| 2962 | |
| 2963 | See also `tf.group` and |
| 2964 | `tf.control_dependencies`. |
| 2965 | |
| 2966 | Args: |
| 2967 | tensors: A list of `Tensor`s or `IndexedSlices`, some entries can be `None`. |
| 2968 | name: (optional) A name to use as a `name_scope` for the operation. |
| 2969 | control_inputs: List of additional ops to finish before returning. |
| 2970 | |
| 2971 | Returns: |
| 2972 | Same as `tensors`. |
| 2973 | |
| 2974 | Raises: |
| 2975 | ValueError: If `tensors` does not contain any `Tensor` or `IndexedSlices`. |
| 2976 | TypeError: If `control_inputs` is not a list of `Operation` or `Tensor` |
| 2977 | objects. |
| 2978 | |
| 2979 | """ |
| 2980 | if context.executing_eagerly(): |
| 2981 | return tensors |
| 2982 | with ops.name_scope(name, "tuple", tensors) as name: |
| 2983 | tensors = [ |
| 2984 | t if (isinstance(t, ops.Operation) or tensor_util.is_tensor(t) or |
| 2985 | t is None) else ops.convert_to_tensor(t) for t in tensors |
| 2986 | ] |
| 2987 | gating_ops = [ |
| 2988 | t if isinstance(t, ops.Operation) else t.op |
| 2989 | for t in tensors |
| 2990 | if t is not None |
| 2991 | ] |
| 2992 | if control_inputs: |
| 2993 | for c in control_inputs: |
| 2994 | if isinstance(c, ops.Tensor): |
| 2995 | c = c.op |
| 2996 | elif not isinstance(c, ops.Operation): |
| 2997 | raise TypeError("Control input must be Operation or Tensor: %s" % c) |
| 2998 | gating_ops.append(c) |
| 2999 | # Note that in order to ensure ordering in the pbtxt, we must take care to |
| 3000 | # ensure the order here. |
| 3001 | gating_ops = sorted(set(gating_ops), key=lambda op: op._id) # Uniquify ops. |
| 3002 | if not gating_ops: |
| 3003 | raise ValueError("Must have at least one Tensor: %s" % tensors) |
| 3004 | gate = group(*gating_ops) |
| 3005 | tpl = [] |