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
16 #ifndef TENSORFLOW_CORE_KERNELS_TENSOR_ARRAY_H_
17 #define TENSORFLOW_CORE_KERNELS_TENSOR_ARRAY_H_
18
19 #include <limits.h>
20 #include <vector>
21
22 #include "tensorflow/core/framework/op_kernel.h"
23 #include "tensorflow/core/framework/partial_tensor_shape.h"
24 #include "tensorflow/core/framework/register_types.h"
25 #include "tensorflow/core/framework/resource_mgr.h"
26 #include "tensorflow/core/framework/tensor.h"
27 #include "tensorflow/core/framework/tensor_shape.h"
28 #include "tensorflow/core/framework/types.h"
29 #include "tensorflow/core/kernels/aggregate_ops.h"
30 #include "tensorflow/core/kernels/fill_functor.h"
31 #include "tensorflow/core/lib/core/errors.h"
32 #include "tensorflow/core/platform/logging.h"
33 #include "tensorflow/core/platform/types.h"
34
35 namespace tensorflow {
36
37 typedef Eigen::ThreadPoolDevice CPUDevice;
38 typedef Eigen::GpuDevice GPUDevice;
39
40 namespace tensor_array {
41
42 // Full implementations are in tensor_array.cc
43 template <typename Device, typename T>
AddToTensor(OpKernelContext * ctx,Tensor * sum,const Tensor * current,const Tensor * add)44 Status AddToTensor(OpKernelContext* ctx, Tensor* sum, const Tensor* current,
45 const Tensor* add) {
46 return errors::InvalidArgument(
47 "tensor_array::AddToTensor type not supported: ",
48 DataTypeString(DataTypeToEnum<T>::value));
49 }
50
51 #define TENSOR_ARRAY_WRITE_OR_ADD(Device, T) \
52 template <> \
53 Status AddToTensor<Device, T>(OpKernelContext * ctx, Tensor * sum, \
54 const Tensor* current, const Tensor* add);
55
56 #define TENSOR_ARRAY_WRITE_OR_ADD_CPU(T) TENSOR_ARRAY_WRITE_OR_ADD(CPUDevice, T)
57 TF_CALL_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_CPU)
58 #undef TENSOR_ARRAY_WRITE_OR_ADD_CPU
59
60 #if GOOGLE_CUDA || TENSORFLOW_USE_ROCM
61
62 #define TENSOR_ARRAY_WRITE_OR_ADD_GPU(T) TENSOR_ARRAY_WRITE_OR_ADD(GPUDevice, T)
63 TF_CALL_GPU_NUMBER_TYPES(TENSOR_ARRAY_WRITE_OR_ADD_GPU);
64 TF_CALL_complex64(TENSOR_ARRAY_WRITE_OR_ADD_GPU);
65 TF_CALL_complex128(TENSOR_ARRAY_WRITE_OR_ADD_GPU);
66 #undef TENSOR_ARRAY_WRITE_OR_ADD_GPU
67
68 #endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM
69
70 #undef TENSOR_ARRAY_WRITE_OR_ADD
71
72 template <typename Device, typename T>
TensorSetZero(OpKernelContext * ctx,Tensor * value)73 Status TensorSetZero(OpKernelContext* ctx, Tensor* value) {
74 return errors::InvalidArgument(
75 "tensor_array::TensorSetZero type not supported: ",
76 DataTypeString(DataTypeToEnum<T>::value));
77 }
78
79 #define TENSOR_ARRAY_SET_ZERO(Device, T) \
80 template <> \
81 Status TensorSetZero<Device, T>(OpKernelContext * ctx, Tensor * value);
82
83 #define TENSOR_ARRAY_SET_ZERO_CPU(T) TENSOR_ARRAY_SET_ZERO(CPUDevice, T)
84 TF_CALL_NUMBER_TYPES(TENSOR_ARRAY_SET_ZERO_CPU);
85 TF_CALL_bool(TENSOR_ARRAY_SET_ZERO_CPU);
86 #undef TENSOR_ARRAY_SET_ZERO_CPU
87
88 #if GOOGLE_CUDA || TENSORFLOW_USE_ROCM
89
90 #define TENSOR_ARRAY_SET_ZERO_GPU(T) TENSOR_ARRAY_SET_ZERO(GPUDevice, T)
91 TF_CALL_GPU_NUMBER_TYPES(TENSOR_ARRAY_SET_ZERO_GPU);
92 TF_CALL_complex64(TENSOR_ARRAY_SET_ZERO_GPU);
93 TF_CALL_complex128(TENSOR_ARRAY_SET_ZERO_GPU);
94 #undef TENSOR_ARRAY_SET_ZERO_GPU
95
96 #endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM
97
98 #undef TENSOR_ARRAY_SET_ZERO
99
100 } // namespace tensor_array
101
102 // The TensorArray object keeps an array of PersistentTensors. It
103 // allows reading from the array and writing to the array.
104 //
105 // Important properties:
106 // * Usually, writing to a particular index in the TensorArray is allowed at
107 // most once per index. In a special case, writes with the flag
108 // multiple_writes_aggregate allow multiple writes to the same
109 // index. In this case, the writes are summed.
110 // * Multiple reads are supported.
111 // * Deep copies of PersistentTensors are rarely made. The only
112 // time they are made is when WriteOrAggregate is called at least twice
113 // on the same index with the flag multiple_writes_aggregate = True.
114 // * Reading and Writing to the array is protected by a mutex.
115 // All operations on a TensorArray are thread-safe.
116 // * A TensorArray may be preemptively closed, which releases all
117 // memory associated with it.
118 //
119 // These properties together allow the TensorArray to work as a
120 // functional object and makes gradient computation easy. For
121 // example:
122 // * Write-Once semantics mean the gradient of a TensorArray Read never has to
123 // worry which of multiple writes to that index the gradient value
124 // is meant for.
125 // * Read-Many semantics (when using clear_after_read=false) allow the
126 // TensorArray to be read, packed, or concatenated multiple times;
127 // and the gradient operations use the multiple_writes_aggregate
128 // flag to aggregate the backprop writes. Multiple backprop writes to
129 // the same index are partial gradients corresponding to the
130 // multiple reads of that index in the forward phase.
131 //
132 class TensorArray : public ResourceBase {
133 public:
134 static std::atomic<int64> tensor_array_counter;
135
136 // Construct a TensorArray for holding Tensors of type 'dtype' with
137 // 'N' elements. While the underlying storage is a std::vector and
138 // can hold more than MAX_INT entries, in practice we do not expect
139 // users to construct this many Tensors for storage in a TensorArray.
TensorArray(const string & key,const DataType & dtype,const Tensor & handle,int32 N,const PartialTensorShape & element_shape,bool identical_element_shapes,bool dynamic_size,bool multiple_writes_aggregate,bool is_grad,int32 marked_size,bool clear_after_read)140 TensorArray(const string& key, const DataType& dtype, const Tensor& handle,
141 int32 N, const PartialTensorShape& element_shape,
142 bool identical_element_shapes, bool dynamic_size,
143 bool multiple_writes_aggregate, bool is_grad, int32 marked_size,
144 bool clear_after_read)
145 : key_(key),
146 dtype_(dtype),
147 handle_(handle),
148 closed_(false),
149 dynamic_size_(dynamic_size),
150 multiple_writes_aggregate_(multiple_writes_aggregate),
151 gradients_disallowed_(false),
152 clear_after_read_(clear_after_read),
153 is_grad_(is_grad),
154 marked_size_(marked_size),
155 element_shape_(element_shape),
156 identical_element_shapes_(identical_element_shapes),
157 tensors_(N) {}
158
159 // Write PersistentTensor 'value' to index 'index'.
160 //
161 // Preconditions:
162 // * The TensorArray is not closed
163 // * If the array has dynamic size:
164 // The index is >= 0
165 // Otherwise:
166 // The index is in [0, N) where N == Size()
167 // * The dtype of the Tensor in 'value' matches the TensorArray's dtype.
168 // * If multiple_writes_aggregate is false:
169 // The Tensor at 'index' has not yet been written to.
170 // * If multiple_writes_aggregate is true:
171 // The Tensor at 'index' has the same shape as value.
172 //
173 // Side effects:
174 // * On the first write to 'index':
175 // - The underlying Tensor in 'value' has a new reference to it.
176 // - The index 'index' is marked as written.
177 // * If multiple_writes_aggregate is false, subsequent writes to 'index'
178 // raise an InvalidArgument error.
179 // * If multiple_writes_aggregate is true, subsequent writes to 'index':
180 // - The underlying Tensors in 'value' and from the first write
181 // are released and a local PersistentTensor is created.
182 // - Index 'index' is also marked as local_copy.
183 // - The gradients_disallowed flag is set true (GradientsAllowed()
184 // will now return false).
185 //
186 // Note, value is passed as a pointer because we its underlying
187 // Tensor's shape is accessed. Otherwise it is not modified.
188 template <typename Device, typename T>
WriteOrAggregate(OpKernelContext * ctx,const int32 index,PersistentTensor * value)189 Status WriteOrAggregate(OpKernelContext* ctx, const int32 index,
190 PersistentTensor* value) {
191 mutex_lock l(mu_);
192 return LockedWriteOrAggregate<Device, T>(ctx, index, value);
193 }
194
195 template <typename Device, typename T>
WriteOrAggregateMany(OpKernelContext * ctx,const std::vector<int32> & indices,std::vector<PersistentTensor> * values)196 Status WriteOrAggregateMany(OpKernelContext* ctx,
197 const std::vector<int32>& indices,
198 std::vector<PersistentTensor>* values) {
199 mutex_lock l(mu_);
200 int32 i = 0;
201 for (const int32 ix : indices) {
202 Status s = LockedWriteOrAggregate<Device, T>(ctx, ix, &(*values)[i]);
203 ++i;
204 TF_RETURN_IF_ERROR(s);
205 }
206 return Status::OK();
207 }
208
209 // Read from index 'index' into PersistentTensor 'value'.
210 //
211 // Preconditions:
212 // * The TensorArray is not closed
213 // * The index is in [0, N)
214 // * The Tensor at 'index' has been written to.
215 // * The Tensor at 'index' has not been read from with flag
216 // clear_after_read = true.
217 //
218 // Side effects:
219 // * If clear_after_read is true, the reference to the underlying
220 // Tensor is deleted.
221 // * The reference to the underlying Tensor at 'index' is copied to
222 // the returned '*value'.
223 // * The index is marked as read (it cannot be rewritten to).
224 template <typename Device, typename T>
Read(OpKernelContext * ctx,const int32 index,PersistentTensor * value)225 Status Read(OpKernelContext* ctx, const int32 index,
226 PersistentTensor* value) {
227 mutex_lock l(mu_);
228 return LockedRead<Device, T>(ctx, index, value);
229 }
230
231 template <typename Device, typename T>
ReadMany(OpKernelContext * ctx,const std::vector<int32> & indices,std::vector<PersistentTensor> * values)232 Status ReadMany(OpKernelContext* ctx, const std::vector<int32>& indices,
233 std::vector<PersistentTensor>* values) {
234 mutex_lock l(mu_);
235 values->clear();
236 values->resize(indices.size());
237 int32 i = 0;
238 for (const int32 ix : indices) {
239 Status s = LockedRead<Device, T>(ctx, ix, &(*values)[i]);
240 ++i;
241 if (!s.ok()) return s;
242 }
243 return Status::OK();
244 }
245
ElemType()246 DataType ElemType() const { return dtype_; }
247
ElemShape()248 PartialTensorShape ElemShape() {
249 mutex_lock l(mu_);
250 return element_shape_;
251 }
252
SetElemShape(const PartialTensorShape & candidate)253 Status SetElemShape(const PartialTensorShape& candidate) {
254 mutex_lock l(mu_);
255 PartialTensorShape new_element_shape_;
256 Status s = element_shape_.MergeWith(candidate, &new_element_shape_);
257 if (!s.ok()) {
258 return s;
259 }
260 element_shape_ = new_element_shape_;
261 return Status::OK();
262 }
263
DebugString()264 string DebugString() const override {
265 mutex_lock l(mu_);
266 CHECK(!closed_);
267 return strings::StrCat("TensorArray[", tensors_.size(), "]");
268 }
269
IsClosed()270 bool IsClosed() {
271 mutex_lock l(mu_);
272 return closed_;
273 }
274
275 // Return the size of the TensorArray.
Size(int32 * size)276 Status Size(int32* size) {
277 mutex_lock l(mu_);
278 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
279 *size = tensors_.size();
280 return Status::OK();
281 }
282
283 // Record the size of the TensorArray after an unpack or split.
SetMarkedSize(int32 size)284 Status SetMarkedSize(int32 size) {
285 mutex_lock l(mu_);
286 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
287 if (!is_grad_) {
288 marked_size_ = size;
289 }
290 return Status::OK();
291 }
292
293 // Return the marked size of the TensorArray.
MarkedSize(int32 * size)294 Status MarkedSize(int32* size) {
295 mutex_lock l(mu_);
296 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
297 *size = marked_size_;
298 return Status::OK();
299 }
300
301 // Return the size that should be used by pack or concat op.
PackOrConcatSize(int32 * size)302 Status PackOrConcatSize(int32* size) {
303 mutex_lock l(mu_);
304 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
305 *size = is_grad_ ? marked_size_ : tensors_.size();
306 return Status::OK();
307 }
308
309 // Once a TensorArray is being used for gradient calculations, it
310 // should be marked as no longer resizeable.
DisableDynamicSize()311 void DisableDynamicSize() {
312 mutex_lock l(mu_);
313 dynamic_size_ = false;
314 }
315
HasDynamicSize()316 bool HasDynamicSize() {
317 mutex_lock l(mu_);
318 return dynamic_size_;
319 }
320
GradientsAllowed()321 bool GradientsAllowed() {
322 mutex_lock l(mu_);
323 return !gradients_disallowed_;
324 }
325
HasIdenticalElementShapes()326 bool HasIdenticalElementShapes() const { return identical_element_shapes_; }
327
328 // Copy the TensorShapes from another TensorArray into this one.
329 // If `shapes_to_prepend` is set, expands the rank of the copied shape by
330 // prepending the passed in shape prefix to the shape values in `rhs`.
331 // The sizes of the two TensorArrays must match and this one
332 // may not have any entries filled in. This performs a "soft copy",
333 // essentially filling the current TensorArray with virtual
334 // zero-tensors, which will be replaced by future aggregate writes,
335 // or instantiated by future reads. Requires a non-const pointer
336 // to the rhs to access its mutex.
337 Status CopyShapesFrom(TensorArray* rhs, const TensorShape* shape_to_prepend);
338
339 // Clear the TensorArray, including any Tensor references, and mark as closed.
ClearAndMarkClosed()340 void ClearAndMarkClosed() {
341 mutex_lock l(mu_);
342 tensors_.clear();
343 closed_ = true;
344 }
345
mu()346 mutex* mu() { return &mu_; }
handle()347 Tensor* handle() { return &handle_; }
348
resource_handle(OpKernelContext * ctx)349 ResourceHandle resource_handle(OpKernelContext* ctx) {
350 return ctx->step_container()->MakeResourceHandle<TensorArray>(
351 key_, *ctx->device());
352 }
353
354 private:
355 Status LockedWrite(OpKernelContext* ctx, const int32 index,
356 PersistentTensor* value) EXCLUSIVE_LOCKS_REQUIRED(mu_);
357
358 template <typename Device, typename T>
359 Status LockedWriteOrAggregate(OpKernelContext* ctx, const int32 index,
360 PersistentTensor* value)
361 EXCLUSIVE_LOCKS_REQUIRED(mu_);
362
363 template <typename Device, typename T>
364 Status LockedRead(OpKernelContext* ctx, const int32 index,
365 PersistentTensor* value) EXCLUSIVE_LOCKS_REQUIRED(mu_);
366
LockedReturnIfClosed()367 Status LockedReturnIfClosed() const EXCLUSIVE_LOCKS_REQUIRED(mu_) {
368 if (closed_) {
369 return errors::InvalidArgument("TensorArray ", handle_.vec<tstring>()(1),
370 " has already been closed.");
371 }
372 return Status::OK();
373 }
374
375 const string key_;
376
377 const DataType dtype_;
378 Tensor handle_;
379
380 mutable mutex mu_;
381
382 // Marks that the tensor_array_ has been cleared.
383 bool closed_ GUARDED_BY(mu_);
384
385 // Writes are allowed to grow the array.
386 bool dynamic_size_;
387
388 // Multiple writes to the same index will result in summation of the
389 // values (used by backprop)
390 const bool multiple_writes_aggregate_;
391
392 // If multiple Writes were attempted (e.g. via attribute
393 // multiple_writes_aggregate), then gradients are disallowed.
394 bool gradients_disallowed_ GUARDED_BY(mu_);
395
396 // After a read at an index, clear away its PersistentTensor to
397 // release memory.
398 const bool clear_after_read_;
399
400 // True iff this is a gradient tensor array.
401 const bool is_grad_;
402
403 // The size of the TensorArray after a (legacy) unpack or split is performed.
404 // -1 if there has been no unpack or split performed on the TensorArray.
405 int32 marked_size_;
406
407 // The shape of each element in the TensorArray, may be partially known or not
408 // known at all.
409 PartialTensorShape element_shape_ GUARDED_BY(mu_);
410
411 // Whether all elements in the TensorArray have identical shapes.
412 // This allows certain behaviors, like dynamically checking for
413 // consistent shapes on write, and being able to fill in properly
414 // shaped zero tensors on stack -- even if the initial element_shape
415 // was not fully defined.
416 const bool identical_element_shapes_;
417
418 // TensorAndState is used to keep track of the PersistentTensors
419 // stored in the TensorArray, along with their shapes, and a boolean
420 // that determines whether they have already been read or not.
421 struct TensorAndState {
TensorAndStateTensorAndState422 TensorAndState()
423 : written(false), read(false), cleared(false), local_copy(false) {}
424 PersistentTensor tensor;
425 TensorShape shape;
426 bool written; // True if a Tensor has been written to the index.
427 bool read; // True if a Tensor has been written to and read from the index.
428 bool cleared; // True if a tensor has been read with
429 // clear_after_read = true;
430
431 // Used by writes when multiple_writes_aggregate is true. In this
432 // case, the first time a value is written, it is a shallow copy.
433 // The second time a value is written, it is aggregated. However,
434 // in this case a new Tensor must be constructed to hold the
435 // aggregated value. This flag marks that such a Tensor is being
436 // used. All future writes will aggregate to the existing local Tensor.
437 bool local_copy;
438 };
439 // The list of underlying PersistentTensors and states.
440 std::vector<TensorAndState> tensors_ GUARDED_BY(mu_);
441 };
442
443 template <typename Device, typename T>
LockedWriteOrAggregate(OpKernelContext * ctx,const int32 index,PersistentTensor * value)444 Status TensorArray::LockedWriteOrAggregate(OpKernelContext* ctx,
445 const int32 index,
446 PersistentTensor* value) {
447 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
448 size_t index_size = static_cast<size_t>(index);
449 if (index < 0 || (!dynamic_size_ && index_size >= tensors_.size())) {
450 return errors::InvalidArgument(
451 "TensorArray ", handle_.vec<tstring>()(1), ": Tried to write to index ",
452 index, " but array is not resizeable and size is: ", tensors_.size());
453 }
454 if (dynamic_size_) {
455 // We must grow the internal TensorArray
456 if (index_size >= tensors_.capacity()) {
457 tensors_.reserve(2 * (index_size + 1));
458 }
459 if (index_size >= tensors_.size()) {
460 tensors_.resize(index_size + 1);
461 }
462 }
463 TensorAndState& t = tensors_[index];
464
465 Tensor* value_t = value->AccessTensor(ctx);
466 if (value_t->dtype() != dtype_) {
467 return errors::InvalidArgument(
468 "TensorArray ", handle_.vec<tstring>()(1),
469 ": Could not write to TensorArray index ", index,
470 " because the value dtype is ", DataTypeString(value_t->dtype()),
471 " but TensorArray dtype is ", DataTypeString(dtype_), ".");
472 }
473 if (!element_shape_.IsCompatibleWith(value_t->shape())) {
474 return errors::InvalidArgument(
475 "TensorArray ", handle_.vec<tstring>()(1),
476 ": Could not write to TensorArray index ", index,
477 " because the value shape is ", value_t->shape().DebugString(),
478 " which is incompatible with the TensorArray's inferred element "
479 "shape: ",
480 element_shape_.DebugString(), " (consider setting infer_shape=False).");
481 } else if (identical_element_shapes_ && !element_shape_.IsFullyDefined()) {
482 element_shape_ = PartialTensorShape(value_t->shape().dim_sizes());
483 }
484
485 if (t.read) {
486 return errors::InvalidArgument("TensorArray ", handle_.vec<tstring>()(1),
487 ": Could not write to TensorArray index ",
488 index, " because it has already been read.");
489 }
490
491 if (!multiple_writes_aggregate_ && t.written) {
492 return errors::InvalidArgument("TensorArray ", handle_.vec<tstring>()(1),
493 ": Could not write to TensorArray index ",
494 index,
495 " because it has already been written to.");
496 }
497
498 if (t.written) {
499 DCHECK(multiple_writes_aggregate_);
500
501 // Check that value_t shape matches t.shape
502 if (value_t->shape() != t.shape) {
503 return errors::InvalidArgument(
504 "TensorArray ", handle_.vec<tstring>()(1),
505 ": Could not aggregate to TensorArray index ", index,
506 " because the existing shape is ", t.shape.DebugString(),
507 " but the new input shape is ", value_t->shape().DebugString(), ".");
508 }
509
510 if (!t.tensor.IsInitialized() || t.tensor.NumElements() == 0) {
511 // If existing_t == nullptr but written == true, then what was stored
512 // was just a shape, which just means zeros. So all we must do in this
513 // case is copy the reference over and return early.
514 t.tensor = *value;
515 return Status::OK();
516 }
517
518 Tensor* existing_t = t.tensor.AccessTensor(ctx);
519
520 if (t.local_copy) {
521 Status s = tensor_array::AddToTensor<Device, T>(ctx, existing_t,
522 existing_t, value_t);
523 TF_RETURN_IF_ERROR(s);
524 } else {
525 PersistentTensor local_tensor;
526 Tensor* local_tensor_t;
527 TF_RETURN_IF_ERROR(ctx->allocate_persistent(
528 dtype_, existing_t->shape(), &local_tensor, &local_tensor_t));
529 Status s = tensor_array::AddToTensor<Device, T>(ctx, local_tensor_t,
530 existing_t, value_t);
531 TF_RETURN_IF_ERROR(s);
532 t.tensor = local_tensor;
533 t.local_copy = true;
534 }
535
536 // We've aggregated the values, so disallow backprop on this
537 // TensorArray.
538 gradients_disallowed_ = true;
539 } else {
540 t.tensor = *value;
541 t.shape = value_t->shape();
542 t.written = true;
543 }
544 return Status::OK();
545 }
546
547 template <typename Device, typename T>
LockedRead(OpKernelContext * ctx,const int32 index,PersistentTensor * value)548 Status TensorArray::LockedRead(OpKernelContext* ctx, const int32 index,
549 PersistentTensor* value) {
550 TF_RETURN_IF_ERROR(LockedReturnIfClosed());
551 if ((index < 0) ||
552 (!is_grad_ && (static_cast<size_t>(index) >= tensors_.size()))) {
553 return errors::InvalidArgument("Tried to read from index ", index,
554 " but array size is: ", tensors_.size());
555 }
556 size_t index_t = static_cast<size_t>(index);
557 if ((is_grad_ && (index_t >= tensors_.size() || !tensors_[index].written)) ||
558 (!is_grad_ && (index_t < tensors_.size() && !tensors_[index].written))) {
559 // Special case returning zeros if this is a gradient read that happens
560 // after a stop_gradients call with dynamic forward TensorArrays.
561 // There is sometimes a race condition where the gradient is not
562 // written due to stop_gradients, but is later read.
563 TensorShape element_shape;
564 if (is_grad_ && index_t < tensors_.size() &&
565 tensors_[index].shape.dims() > 0) {
566 // A gradient TensorArray has more specific gradient information
567 // available for each entry. A forward TensorArray must rely on
568 // the global element_shape_ to fill in zeros on read.
569 element_shape = tensors_[index].shape;
570 } else if (!element_shape_.IsFullyDefined()) {
571 return errors::InvalidArgument(
572 "TensorArray ", handle_.vec<tstring>()(1),
573 ": Could not read from TensorArray index ", index,
574 ". Furthermore, the element shape is not fully defined: ",
575 element_shape_.DebugString(),
576 ". It is possible you are working with a resizeable TensorArray and "
577 "stop_gradients is not allowing the gradients to be written. If you "
578 "set the full "
579 "element_shape property on the forward TensorArray, the proper "
580 "all-zeros tensor "
581 "will be returned instead of incurring this error.");
582 } else {
583 element_shape_.AsTensorShape(&element_shape); // Always succeeds.
584 }
585 if (index_t >= tensors_.size()) {
586 // Fill in tensors_ up to index to have known shape.
587 size_t old_tensors_size = tensors_.size();
588 tensors_.resize(index + 1);
589 for (size_t i = old_tensors_size; i < index + 1; ++i) {
590 tensors_[i].shape = element_shape;
591 tensors_[i].written = true;
592 }
593 } else {
594 tensors_[index].shape = element_shape;
595 tensors_[index].written = true;
596 }
597 }
598
599 TensorAndState& t = tensors_[index];
600
601 if (t.cleared) {
602 return errors::InvalidArgument("TensorArray ", handle_.vec<tstring>()(1),
603 ": Could not read index ", index,
604 " twice because it was cleared after a "
605 "previous read (perhaps try setting "
606 "clear_after_read = false?).");
607 }
608
609 if (!t.tensor.IsInitialized() || t.tensor.NumElements() == 0) {
610 // We stored just a shape, but no value. This means create and
611 // return zeros of the appropriate shape.
612 Tensor* tensor_t;
613 TF_RETURN_IF_ERROR(
614 ctx->allocate_persistent(dtype_, t.shape, &t.tensor, &tensor_t));
615 if (t.shape.num_elements() > 0) {
616 Status s = tensor_array::TensorSetZero<Device, T>(ctx, tensor_t);
617 if (!s.ok()) return s;
618 }
619 }
620
621 // Data is available inside the tensor, copy the reference over.
622 *value = t.tensor;
623
624 if (clear_after_read_) {
625 t.tensor = PersistentTensor();
626 t.cleared = true;
627 }
628 t.read = true;
629 return Status::OK();
630 }
631
632 } // namespace tensorflow
633
634 #endif // TENSORFLOW_CORE_KERNELS_TENSOR_ARRAY_H_
635