Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/framework/constant_op.py: 39%
120 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Operations that generate constants.
17See the [constants guide](https://tensorflow.org/api_guides/python/constant_op).
18"""
20# Must be separate from array_ops to avoid a cyclic dependency.
22import numpy as np
23from tensorflow.core.framework import types_pb2
24from tensorflow.python.eager import context
25from tensorflow.python.eager import execute
26from tensorflow.python.framework import dtypes
27from tensorflow.python.framework import ops
28from tensorflow.python.framework import tensor_conversion_registry
29from tensorflow.python.framework import tensor_shape
30from tensorflow.python.profiler import trace
31from tensorflow.python.util.tf_export import tf_export
34def _eager_reshape(tensor, shape, ctx):
35 """Eager-only version of Reshape op; requires tensor is an eager Tensor."""
36 attr_t = tensor._datatype_enum() # pylint: disable=protected-access
37 attr_tshape, (shape,) = execute.args_to_matching_eager(
38 [shape], ctx, [dtypes.int32, dtypes.int64], dtypes.int32)
39 inputs_flat = [tensor, shape]
40 attrs = ("T", attr_t, "Tshape", attr_tshape)
41 [result] = execute.execute(
42 b"Reshape", 1, inputs=inputs_flat, attrs=attrs, ctx=ctx)
43 return result
46def _eager_fill(dims, value, ctx):
47 """Eager-only version of Fill op; requires value is an eager Tensor."""
48 attr_t = value.dtype.as_datatype_enum
49 dims = convert_to_eager_tensor(dims, ctx, dtypes.int32)
50 inputs_flat = [dims, value]
51 attrs = ("T", attr_t, "index_type", types_pb2.DT_INT32)
52 [result] = execute.execute(
53 b"Fill", 1, inputs=inputs_flat, attrs=attrs, ctx=ctx)
54 return result
57def _eager_identity(tensor, ctx):
58 """Eager-only version of Identity op; requires tensor is an eager Tensor."""
59 attrs = ("T", tensor.dtype.as_datatype_enum)
60 [result] = execute.execute(
61 b"Identity", 1, inputs=[tensor], attrs=attrs, ctx=ctx)
62 return result
65def convert_to_eager_tensor(value, ctx, dtype=None):
66 """Converts the given `value` to an `EagerTensor`.
68 Note that this function could return cached copies of created constants for
69 performance reasons.
71 Args:
72 value: value to convert to EagerTensor.
73 ctx: value of context.context().
74 dtype: optional desired dtype of the converted EagerTensor.
76 Returns:
77 EagerTensor created from value.
79 Raises:
80 TypeError: if `dtype` is not compatible with the type of t.
81 """
82 if isinstance(value, np.ndarray):
83 # Make a copy explicitly because the EagerTensor might share the underlying
84 # memory with the input array. Without this copy, users will be able to
85 # modify the EagerTensor after its creation by changing the input array.
86 value = value.copy()
87 if isinstance(value, ops.EagerTensor):
88 if dtype is not None and value.dtype != dtype:
89 raise TypeError(f"Expected tensor {value} with dtype {dtype!r}, but got "
90 f"dtype {value.dtype!r}.")
91 return value
92 if dtype is not None:
93 try:
94 dtype = dtype.as_datatype_enum
95 except AttributeError:
96 dtype = dtypes.as_dtype(dtype).as_datatype_enum
97 ctx.ensure_initialized()
98 return ops.EagerTensor(value, ctx.device_name, dtype)
101@tf_export(v1=["constant"])
102def constant_v1(
103 value, dtype=None, shape=None, name="Const", verify_shape=False):
104 """Creates a constant tensor.
106 The resulting tensor is populated with values of type `dtype`, as
107 specified by arguments `value` and (optionally) `shape` (see examples
108 below).
110 The argument `value` can be a constant value, or a list of values of type
111 `dtype`. If `value` is a list, then the length of the list must be less
112 than or equal to the number of elements implied by the `shape` argument (if
113 specified). In the case where the list length is less than the number of
114 elements specified by `shape`, the last element in the list will be used
115 to fill the remaining entries.
117 The argument `shape` is optional. If present, it specifies the dimensions of
118 the resulting tensor. If not present, the shape of `value` is used.
120 If the argument `dtype` is not specified, then the type is inferred from
121 the type of `value`.
123 For example:
125 ```python
126 # Constant 1-D Tensor populated with value list.
127 tensor = tf.constant([1, 2, 3, 4, 5, 6, 7]) => [1 2 3 4 5 6 7]
129 # Constant 2-D tensor populated with scalar value -1.
130 tensor = tf.constant(-1.0, shape=[2, 3]) => [[-1. -1. -1.]
131 [-1. -1. -1.]]
132 ```
134 `tf.constant` differs from `tf.fill` in a few ways:
136 * `tf.constant` supports arbitrary constants, not just uniform scalar
137 Tensors like `tf.fill`.
138 * `tf.constant` creates a `Const` node in the computation graph with the
139 exact value at graph construction time. On the other hand, `tf.fill`
140 creates an Op in the graph that is expanded at runtime.
141 * Because `tf.constant` only embeds constant values in the graph, it does
142 not support dynamic shapes based on other runtime Tensors, whereas
143 `tf.fill` does.
145 Args:
146 value: A constant value (or list) of output type `dtype`.
148 dtype: The type of the elements of the resulting tensor.
150 shape: Optional dimensions of resulting tensor.
152 name: Optional name for the tensor.
154 verify_shape: Boolean that enables verification of a shape of values.
156 Returns:
157 A Constant Tensor.
159 Raises:
160 TypeError: if shape is incorrectly specified or unsupported.
161 """
162 return _constant_impl(value, dtype, shape, name, verify_shape=verify_shape,
163 allow_broadcast=False)
166@tf_export("constant", v1=[])
167def constant(value, dtype=None, shape=None, name="Const"):
168 """Creates a constant tensor from a tensor-like object.
170 Note: All eager `tf.Tensor` values are immutable (in contrast to
171 `tf.Variable`). There is nothing especially _constant_ about the value
172 returned from `tf.constant`. This function is not fundamentally different from
173 `tf.convert_to_tensor`. The name `tf.constant` comes from the `value` being
174 embedded in a `Const` node in the `tf.Graph`. `tf.constant` is useful
175 for asserting that the value can be embedded that way.
177 If the argument `dtype` is not specified, then the type is inferred from
178 the type of `value`.
180 >>> # Constant 1-D Tensor from a python list.
181 >>> tf.constant([1, 2, 3, 4, 5, 6])
182 <tf.Tensor: shape=(6,), dtype=int32,
183 numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
184 >>> # Or a numpy array
185 >>> a = np.array([[1, 2, 3], [4, 5, 6]])
186 >>> tf.constant(a)
187 <tf.Tensor: shape=(2, 3), dtype=int64, numpy=
188 array([[1, 2, 3],
189 [4, 5, 6]])>
191 If `dtype` is specified, the resulting tensor values are cast to the requested
192 `dtype`.
194 >>> tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float64)
195 <tf.Tensor: shape=(6,), dtype=float64,
196 numpy=array([1., 2., 3., 4., 5., 6.])>
198 If `shape` is set, the `value` is reshaped to match. Scalars are expanded to
199 fill the `shape`:
201 >>> tf.constant(0, shape=(2, 3))
202 <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
203 array([[0, 0, 0],
204 [0, 0, 0]], dtype=int32)>
205 >>> tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])
206 <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
207 array([[1, 2, 3],
208 [4, 5, 6]], dtype=int32)>
210 `tf.constant` has no effect if an eager Tensor is passed as the `value`, it
211 even transmits gradients:
213 >>> v = tf.Variable([0.0])
214 >>> with tf.GradientTape() as g:
215 ... loss = tf.constant(v + v)
216 >>> g.gradient(loss, v).numpy()
217 array([2.], dtype=float32)
219 But, since `tf.constant` embeds the value in the `tf.Graph` this fails for
220 symbolic tensors:
222 >>> with tf.compat.v1.Graph().as_default():
223 ... i = tf.compat.v1.placeholder(shape=[None, None], dtype=tf.float32)
224 ... t = tf.constant(i)
225 Traceback (most recent call last):
226 ...
227 TypeError: ...
229 `tf.constant` will create tensors on the current device. Inputs which are
230 already tensors maintain their placements unchanged.
232 Related Ops:
234 * `tf.convert_to_tensor` is similar but:
235 * It has no `shape` argument.
236 * Symbolic tensors are allowed to pass through.
238 >>> with tf.compat.v1.Graph().as_default():
239 ... i = tf.compat.v1.placeholder(shape=[None, None], dtype=tf.float32)
240 ... t = tf.convert_to_tensor(i)
242 * `tf.fill`: differs in a few ways:
243 * `tf.constant` supports arbitrary constants, not just uniform scalar
244 Tensors like `tf.fill`.
245 * `tf.fill` creates an Op in the graph that is expanded at runtime, so it
246 can efficiently represent large tensors.
247 * Since `tf.fill` does not embed the value, it can produce dynamically
248 sized outputs.
250 Args:
251 value: A constant value (or list) of output type `dtype`.
252 dtype: The type of the elements of the resulting tensor.
253 shape: Optional dimensions of resulting tensor.
254 name: Optional name for the tensor.
256 Returns:
257 A Constant Tensor.
259 Raises:
260 TypeError: if shape is incorrectly specified or unsupported.
261 ValueError: if called on a symbolic tensor.
262 """
263 return _constant_impl(value, dtype, shape, name, verify_shape=False,
264 allow_broadcast=True)
267def _constant_impl(
268 value, dtype, shape, name, verify_shape, allow_broadcast):
269 """Implementation of constant."""
270 ctx = context.context()
271 if ctx.executing_eagerly():
272 if trace.enabled:
273 with trace.Trace("tf.constant"):
274 return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
275 return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
277 const_tensor = ops._create_graph_constant( # pylint: disable=protected-access
278 value, dtype, shape, name, verify_shape, allow_broadcast
279 )
280 return const_tensor
283def _constant_eager_impl(ctx, value, dtype, shape, verify_shape):
284 """Creates a constant on the current device."""
285 t = convert_to_eager_tensor(value, ctx, dtype)
286 if shape is None:
287 return t
288 shape = tensor_shape.as_shape(shape)
289 if shape == t.shape:
290 return t
291 if verify_shape:
292 raise TypeError(f"Expected Tensor {t} (converted from {value}) with shape "
293 f"{tuple(shape)}, but got shape {tuple(t.shape)}.")
294 num_t = t.shape.num_elements()
295 # TODO(josh11b): Implement shape -> eager tensor conversion.
296 if num_t == shape.num_elements():
297 return _eager_reshape(t, shape.as_list(), ctx)
298 if num_t == 1:
299 if t.dtype == dtypes.bool:
300 # We don't have a Fill kernel for bool dtype on GPU. So we first run
301 # Fill on CPU and then copy to GPU if needed.
302 with ops.device("/device:CPU:0"):
303 x = _eager_fill(shape.as_list(), _eager_identity(t, ctx), ctx)
304 return _eager_identity(x, ctx)
305 else:
306 return _eager_fill(shape.as_list(), t, ctx)
307 raise TypeError("Eager execution of tf.constant with unsupported shape. "
308 f"Tensor {t} (converted from {value}) has {num_t:d} "
309 f"elements, but got `shape` {shape} with "
310 f"{shape.num_elements()} elements).")
313def is_constant(tensor_or_op):
314 if isinstance(tensor_or_op, ops.Tensor):
315 op = tensor_or_op.op
316 else:
317 op = tensor_or_op
318 return op.type == "Const"
321def _constant_tensor_conversion_function(v, dtype=None, name=None,
322 as_ref=False):
323 _ = as_ref
324 return constant(v, dtype=dtype, name=name)
326# Register the conversion function for the "unconvertible" types
327# as a conversion to a constant.
328tensor_conversion_registry.register_tensor_conversion_function_internal(
329 tensor_conversion_registry._CONSTANT_OP_CONVERTIBLES, # pylint: disable=protected-access
330 _constant_tensor_conversion_function,
331 0)
333tensor_conversion_registry.register_tensor_conversion_function(
334 (list, tuple), _constant_tensor_conversion_function, 100)
335tensor_conversion_registry.register_tensor_conversion_function(
336 object, _constant_tensor_conversion_function, 200)
339def _tensor_shape_tensor_conversion_function(s,
340 dtype=None,
341 name=None,
342 as_ref=False):
343 """Function to convert TensorShape to Tensor."""
344 _ = as_ref
345 if not s.is_fully_defined():
346 raise ValueError(
347 f"Cannot convert a partially known TensorShape {s} to a Tensor.")
348 s_list = s.as_list()
349 int64_value = 0
350 for dim in s_list:
351 if dim >= 2**31:
352 int64_value = dim
353 break
355 if dtype is not None:
356 if dtype not in (dtypes.int32, dtypes.int64):
357 raise TypeError(f"Cannot convert TensorShape {s} to dtype {dtype}. "
358 "Allowed dtypes are tf.int32 and tf.int64.")
359 if dtype == dtypes.int32 and int64_value:
360 raise ValueError(f"Cannot convert TensorShape {s} to dtype int32; "
361 f"a dimension is too large. Consider using tf.int64.")
362 else:
363 dtype = dtypes.int64 if int64_value else dtypes.int32
364 if name is None:
365 name = "shape_as_tensor"
366 return constant(s_list, dtype=dtype, name=name)
369tensor_conversion_registry.register_tensor_conversion_function(
370 tensor_shape.TensorShape, _tensor_shape_tensor_conversion_function, 100)
373def _dimension_tensor_conversion_function(d,
374 dtype=None,
375 name=None,
376 as_ref=False):
377 """Function to convert Dimension to Tensor."""
378 _ = as_ref
379 if d.value is None:
380 raise ValueError(f"Cannot convert unknown Dimension {d} to a Tensor.")
381 if dtype is not None:
382 if dtype not in (dtypes.int32, dtypes.int64):
383 raise TypeError(f"Cannot convert Dimension {d} to dtype {dtype}. "
384 "Allowed dtypes are tf.int32 and tf.int64.")
385 else:
386 dtype = dtypes.int32
387 if name is None:
388 name = "shape_as_tensor"
389 return constant(d.value, dtype=dtype, name=name)
392tensor_conversion_registry.register_tensor_conversion_function(
393 tensor_shape.Dimension, _dimension_tensor_conversion_function, 100)