A version of `constant_value()` that returns a `TensorShape`. This version should be used when a constant tensor value is interpreted as a (possibly partial) shape, e.g. in the shape function for `tf.reshape()`. By explicitly requesting a `TensorShape` as the return value, it is possible to
(tensor)
| 803 | |
| 804 | |
| 805 | def constant_value_as_shape(tensor): # pylint: disable=invalid-name |
| 806 | """A version of `constant_value()` that returns a `TensorShape`. |
| 807 | |
| 808 | This version should be used when a constant tensor value is |
| 809 | interpreted as a (possibly partial) shape, e.g. in the shape |
| 810 | function for `tf.reshape()`. By explicitly requesting a |
| 811 | `TensorShape` as the return value, it is possible to represent |
| 812 | unknown dimensions; by contrast, `constant_value()` is |
| 813 | all-or-nothing. |
| 814 | |
| 815 | Args: |
| 816 | tensor: The rank-0 or rank-1 Tensor to be evaluated. |
| 817 | |
| 818 | Returns: |
| 819 | A `TensorShape` based on the constant value of the given `tensor`. |
| 820 | |
| 821 | Raises: |
| 822 | ValueError: If the shape is rank-0 and is not statically known to be -1. |
| 823 | """ |
| 824 | if isinstance(tensor, ops.EagerTensor): |
| 825 | return tensor_shape.as_shape( |
| 826 | [dim if dim != -1 else None for dim in tensor.numpy()]) |
| 827 | |
| 828 | if tensor.get_shape().ndims == 0: |
| 829 | value = constant_value(tensor) |
| 830 | if value is None: |
| 831 | raise ValueError( |
| 832 | "Received a scalar with unknown value as shape; require a statically " |
| 833 | "known scalar with value '-1' to describe an unknown shape.") |
| 834 | if value != -1: |
| 835 | raise ValueError( |
| 836 | "Received a scalar value '%s' as shape; require a statically known " |
| 837 | "scalar with value '-1' to describe an unknown shape." % value) |
| 838 | return tensor_shape.unknown_shape() |
| 839 | |
| 840 | shape = tensor.get_shape().with_rank(1) |
| 841 | if shape == [0]: |
| 842 | return tensor_shape.TensorShape([]) |
| 843 | elif tensor.op.type == "Shape": |
| 844 | return tensor.op.inputs[0].get_shape() |
| 845 | elif tensor.op.type == "Pack": |
| 846 | ret = tensor_shape.TensorShape([]) # Empty list. |
| 847 | # Since we expect rank 1 inputs, Pack's axis must be zero, otherwise it |
| 848 | # would not be rank 1. |
| 849 | assert tensor.op.get_attr("axis") == 0 |
| 850 | for pack_input in tensor.op.inputs: |
| 851 | # `pack_input` must be a scalar. Attempt to evaluate it, and append it |
| 852 | # to `ret`. |
| 853 | pack_input_val = constant_value(pack_input) |
| 854 | if pack_input_val is None or pack_input_val < 0: |
| 855 | new_dim = tensor_shape.Dimension(None) |
| 856 | else: |
| 857 | new_dim = tensor_shape.Dimension(pack_input_val) |
| 858 | ret = ret.concatenate([new_dim]) |
| 859 | return ret |
| 860 | elif tensor.op.type == "Concat": |
| 861 | # We assume that `tensor.op.inputs[0]` evaluates to 0, as this is |
| 862 | # the only legal value when concatenating vectors, and it will |
no test coverage detected