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

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. 

16 

17See the [constants guide](https://tensorflow.org/api_guides/python/constant_op). 

18""" 

19 

20# Must be separate from array_ops to avoid a cyclic dependency. 

21 

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 

32 

33 

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 

44 

45 

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 

55 

56 

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 

63 

64 

65def convert_to_eager_tensor(value, ctx, dtype=None): 

66 """Converts the given `value` to an `EagerTensor`. 

67 

68 Note that this function could return cached copies of created constants for 

69 performance reasons. 

70 

71 Args: 

72 value: value to convert to EagerTensor. 

73 ctx: value of context.context(). 

74 dtype: optional desired dtype of the converted EagerTensor. 

75 

76 Returns: 

77 EagerTensor created from value. 

78 

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) 

99 

100 

101@tf_export(v1=["constant"]) 

102def constant_v1( 

103 value, dtype=None, shape=None, name="Const", verify_shape=False): 

104 """Creates a constant tensor. 

105 

106 The resulting tensor is populated with values of type `dtype`, as 

107 specified by arguments `value` and (optionally) `shape` (see examples 

108 below). 

109 

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. 

116 

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. 

119 

120 If the argument `dtype` is not specified, then the type is inferred from 

121 the type of `value`. 

122 

123 For example: 

124 

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] 

128 

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 ``` 

133 

134 `tf.constant` differs from `tf.fill` in a few ways: 

135 

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. 

144 

145 Args: 

146 value: A constant value (or list) of output type `dtype`. 

147 

148 dtype: The type of the elements of the resulting tensor. 

149 

150 shape: Optional dimensions of resulting tensor. 

151 

152 name: Optional name for the tensor. 

153 

154 verify_shape: Boolean that enables verification of a shape of values. 

155 

156 Returns: 

157 A Constant Tensor. 

158 

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) 

164 

165 

166@tf_export("constant", v1=[]) 

167def constant(value, dtype=None, shape=None, name="Const"): 

168 """Creates a constant tensor from a tensor-like object. 

169 

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. 

176 

177 If the argument `dtype` is not specified, then the type is inferred from 

178 the type of `value`. 

179 

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]])> 

190 

191 If `dtype` is specified, the resulting tensor values are cast to the requested 

192 `dtype`. 

193 

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.])> 

197 

198 If `shape` is set, the `value` is reshaped to match. Scalars are expanded to 

199 fill the `shape`: 

200 

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)> 

209 

210 `tf.constant` has no effect if an eager Tensor is passed as the `value`, it 

211 even transmits gradients: 

212 

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) 

218 

219 But, since `tf.constant` embeds the value in the `tf.Graph` this fails for 

220 symbolic tensors: 

221 

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: ... 

228 

229 `tf.constant` will create tensors on the current device. Inputs which are 

230 already tensors maintain their placements unchanged. 

231 

232 Related Ops: 

233 

234 * `tf.convert_to_tensor` is similar but: 

235 * It has no `shape` argument. 

236 * Symbolic tensors are allowed to pass through. 

237 

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) 

241 

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. 

249 

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. 

255 

256 Returns: 

257 A Constant Tensor. 

258 

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) 

265 

266 

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) 

276 

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 

281 

282 

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).") 

311 

312 

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" 

319 

320 

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) 

325 

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) 

332 

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) 

337 

338 

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 

354 

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) 

367 

368 

369tensor_conversion_registry.register_tensor_conversion_function( 

370 tensor_shape.TensorShape, _tensor_shape_tensor_conversion_function, 100) 

371 

372 

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) 

390 

391 

392tensor_conversion_registry.register_tensor_conversion_function( 

393 tensor_shape.Dimension, _dimension_tensor_conversion_function, 100)