forked from SciSharp/TensorFlow.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patharray_ops.cs
More file actions
698 lines (611 loc) · 29.1 KB
/
array_ops.cs
File metadata and controls
698 lines (611 loc) · 29.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
/*****************************************************************************
Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using NumSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using Tensorflow.Framework;
using static Tensorflow.Binding;
namespace Tensorflow
{
public class array_ops
{
public static Tensor placeholder_with_default<T>(T input, int[] shape, string name = null)
=> gen_array_ops.placeholder_with_default(input, shape, name);
public static Tensor prevent_gradient(Tensor input, string message = "", string name = null)
=> gen_array_ops.prevent_gradient(input, message: message, name: name);
internal static Tensor constant(object value,
TF_DataType dtype = TF_DataType.DtInvalid,
int[] shape = null,
string name = "Const",
bool verify_shape = false) => constant_op._constant_impl(value,
dtype,
shape,
name,
verify_shape: verify_shape,
allow_broadcast: false);
public static Tensor zeros(TensorShape shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return tf_with(ops.name_scope(name, "zeros", shape), scope =>
{
name = scope;
switch (dtype)
{
case TF_DataType.TF_BOOL:
return _constant_if_small(false, shape, dtype, name);
case TF_DataType.TF_DOUBLE:
return _constant_if_small(0.0D, shape, dtype, name);
case TF_DataType.TF_FLOAT:
return _constant_if_small(0.0F, shape, dtype, name);
case TF_DataType.TF_INT64:
return _constant_if_small(0l, shape, dtype, name);
case TF_DataType.TF_INT32:
return _constant_if_small(0, shape, dtype, name);
case TF_DataType.TF_INT8:
return _constant_if_small<byte>(0, shape, dtype, name);
default:
throw new TypeError("can't find type for zeros");
}
});
}
public static Tensor boolean_mask<T1, T2>(T1 tensor, T2 mask, string name = "boolean_mask", int axis = 0)
{
return tf_with(ops.name_scope(name, values: new { tensor, mask }), delegate
{
var tensor_tensor = ops.convert_to_tensor(tensor, name: "tensor");
var mask_tensor = ops.convert_to_tensor(mask, name: "mask");
var shape_mask = mask_tensor.TensorShape;
var ndims_mask = shape_mask.ndim;
var shape_tensor = tensor_tensor.TensorShape;
if (ndims_mask < 1)
throw new ValueError("mask cannot be scalar.");
var leading_size = gen_math_ops.prod(shape(tensor_tensor)[$"{axis}:{axis + ndims_mask}"], new[] { 0 });
var shape1 = concat(new[]
{
shape(tensor_tensor)[$":{axis}"],
tf.expand_dims(leading_size, 0),
shape(tensor_tensor)[$"{axis + ndims_mask}:"]
}, 0);
tensor_tensor = reshape(tensor, shape1);
var first_dim = shape_tensor.dims.Skip(axis).Take(ndims_mask).First();
var s1 = tensor_shape.as_shape(shape_tensor.dims.Take(axis).ToArray());
var s2 = s1.concatenate(new[] { first_dim }).concatenate(shape_tensor.dims.Skip(axis + ndims_mask).ToArray());
tensor_tensor.set_shape(s2);
mask_tensor = reshape(mask_tensor, new[] { -1 });
return _apply_mask_1d(tensor_tensor, mask_tensor, axis);
});
}
private static Tensor _apply_mask_1d(Tensor reshaped_tensor, Tensor mask, int axis = 0)
{
var indices = squeeze(where(mask), axis: new[] { 1 });
return gather(reshaped_tensor, indices, axis: axis);
}
public static Tensor zeros(Tensor shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return tf_with(ops.name_scope(name, "zeros", shape), scope =>
{
name = scope;
switch (dtype)
{
case TF_DataType.TF_BOOL:
return gen_array_ops.fill(shape, tf.constant(false, dtype: dtype), name: name);
case TF_DataType.TF_DOUBLE:
return gen_array_ops.fill(shape, tf.constant(0.0D, dtype: dtype), name: name);
case TF_DataType.TF_FLOAT:
return gen_array_ops.fill(shape, tf.constant(0.0F, dtype: dtype), name: name);
case TF_DataType.TF_INT32:
return gen_array_ops.fill(shape, tf.constant(0, dtype: dtype), name: name);
default:
throw new TypeError("can't find type for zeros");
}
});
}
private static Tensor _constant_if_small(int value, Tensor shape)
{
return shape < 1000;
}
private static Tensor _constant_if_small<T>(T value, TensorShape shape, TF_DataType dtype, string name)
{
Tensor tShape = null;
if (shape.size < 1000)
{
return constant_op.constant(value, shape: shape, dtype: dtype, name: name);
}
else
{
tShape = constant_op._tensor_shape_tensor_conversion_function(shape);
var c = constant_op.constant(0, dtype: dtype);
return gen_array_ops.fill(tShape, c, name: name);
}
}
public static Tensor _autopacking_conversion_function(object[] v, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool as_ref = false)
{
var inferred_dtype = _get_dtype_from_nested_lists(v);
if (dtype == TF_DataType.DtInvalid)
dtype = inferred_dtype;
return _autopacking_helper(v, dtype, name == null ? "packed" : name);
}
private static TF_DataType _get_dtype_from_nested_lists(object[] list_or_tuple)
{
TF_DataType dtype = TF_DataType.DtInvalid;
foreach(var obj in list_or_tuple)
{
switch (obj)
{
case Tensor t:
dtype = t.dtype.as_base_dtype();
break;
}
if (dtype != TF_DataType.DtInvalid)
break;
}
return dtype;
}
public static Tensor _autopacking_helper(object[] list_or_tuple, TF_DataType dtype, string name)
{
var must_pack = false;
var converted_elems = new List<object>();
return tf_with(ops.name_scope(name), scope =>
{
foreach (var (i, elem) in enumerate(list_or_tuple))
{
converted_elems.Add(elem);
must_pack = true;
}
if(must_pack)
{
var elems_as_tensors = new List<Tensor>();
foreach (var (i, elem) in enumerate(converted_elems))
{
if (elem is Tensor tensor)
elems_as_tensors.Add(tensor);
else
{
var elem_tensor = constant_op.constant(elem, dtype: dtype, name: i.ToString());
elems_as_tensors.Add(elem_tensor);
}
}
return gen_array_ops.pack(elems_as_tensors.ToArray(), name: scope);
}
else
{
return tf.constant(np.array(new float[0]));
}
});
}
public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1)
=> expand_dims_v2(input, axis, name);
private static Tensor expand_dims_v2(Tensor input, int axis, string name = null)
=> gen_array_ops.expand_dims(input, axis, name);
/// <summary>
/// Returns the rank of a tensor.
/// </summary>
/// <param name="input"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor rank(Tensor input, string name = null)
=> rank_internal(input, name, optimize: true);
public static Tensor rank(Tensor[] inputs, string name = null)
{
return tf_with(ops.name_scope(name, "Rank", new { inputs }), scope =>
{
name = scope;
var input_tensor = ops.convert_to_tensor(inputs);
return constant_op.constant(input_tensor.NDims, dtype: tf.int32, name: name);
});
}
public static Tensor rank_internal(Tensor input, string name = null, bool optimize = true)
{
return tf_with(ops.name_scope(name, "Rank", new List<Tensor> { input }), scope =>
{
name = scope;
var input_tensor = ops.convert_to_tensor(input);
var input_shape = tensor_util.to_shape(input_tensor.shape);
if (optimize && input_shape.ndim > 0)
return constant_op.constant(input_shape.ndim, dtype: tf.int32, name: name);
else
return gen_array_ops.rank(input, name);
});
}
/// <summary>
/// Creates a tensor with all elements set to 1.
/// </summary>
/// <param name="tensor"></param>
/// <param name="dtype"></param>
/// <param name="name"></param>
/// <param name="optimize"></param>
/// <returns></returns>
public static Tensor ones_like<T>(T tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true)
=> ones_like_impl(tensor, dtype, name, optimize);
public static Tensor reshape<T1, T2>(T1 tensor, T2 shape, string name = null)
=> gen_array_ops.reshape(tensor, shape, null);
private static Tensor ones_like_impl<T>(T tensor, TF_DataType dtype, string name, bool optimize = true)
{
return tf_with(ops.name_scope(name, "ones_like", new { tensor }), scope =>
{
name = scope;
var tensor1 = ops.convert_to_tensor(tensor, name: "tensor");
var ones_shape = shape_internal(tensor1, optimize: optimize);
if (dtype == TF_DataType.DtInvalid)
dtype = tensor1.dtype;
var ret = ones(ones_shape, dtype: dtype, name: name);
ret.shape = tensor1.shape;
return ret;
});
}
public static Tensor ones(Tensor shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return tf_with(ops.name_scope(name, "ones", new { shape }), scope =>
{
name = scope;
var output = gen_array_ops.fill(shape, constant_op.constant(1.0f, dtype: dtype), name: name);
return output;
});
}
public static Tensor ones(Tensor[] shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return tf_with(ops.name_scope(name, "ones", new { shape }), scope =>
{
name = scope;
var output = _constant_if_small(1, shape[0]);
var shape1 = ops.convert_to_tensor(shape, dtype: TF_DataType.TF_INT32);
output = gen_array_ops.fill(shape1, constant_op.constant(1, dtype: dtype), name: name);
return output;
});
}
public static Tensor ones(int[] dims, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return tf_with(ops.name_scope(name, "ones", new { dims }), scope =>
{
name = scope;
var output = _constant_if_small(1, dims, dtype, name);
return output;
});
}
public static Tensor one_hot(Tensor indices, int depth,
Tensor on_value = null,
Tensor off_value = null,
TF_DataType dtype = TF_DataType.DtInvalid,
int axis = -1,
string name = null)
{
return tf_with(ops.name_scope(name, "one_hot", new { indices, depth, dtype }), scope =>
{
name = scope;
var on_exists = false;
var off_exists = false;
var on_dtype = TF_DataType.DtInvalid;
var off_dtype = TF_DataType.DtInvalid;
if (dtype == TF_DataType.DtInvalid)
dtype = TF_DataType.TF_FLOAT;
if(!on_exists)
{
on_value = ops.convert_to_tensor(1, dtype, name: "on_value");
on_dtype = dtype;
}
if (!off_exists)
{
off_value = ops.convert_to_tensor(0, dtype, name = "off_value");
off_dtype = dtype;
}
return gen_array_ops.one_hot(indices, depth,
on_value: on_value,
off_value: off_value,
axis: axis,
name: name);
});
}
public static (Tensor, Tensor) unique(Tensor x, TF_DataType out_idx = TF_DataType.TF_INT32, string name = null)
=> gen_array_ops.unique(x, out_idx: out_idx, name: name);
public static Tensor stack(Tensor[] values, int axis = 0, string name = "stack")
{
if (axis == 0)
{
return ops.convert_to_tensor(values, name: name);
}
var value_shape = ops.convert_to_tensor(values[0], name: name).TensorShape;
return gen_array_ops.pack(values, axis: axis, name: name);
}
public static Tensor[] unstack(Tensor value, int? num = null, int axis = 0, string name = "unstack")
{
if(num == null)
{
value = ops.convert_to_tensor(value);
var value_shape = value.TensorShape;
num = value_shape.dims[axis];
}
return gen_array_ops.unpack(value, num: num.Value, axis: axis, name: name);
}
public static Tensor where(Tensor condition, object x = null, object y = null, string name = null)
{
if( x == null && y == null)
{
return tf_with(ops.name_scope(name, "Where", new { condition }), scope =>
{
name = scope;
condition = ops.convert_to_tensor(condition, preferred_dtype: dtypes.@bool, name: "condition");
return gen_array_ops.where(condition: condition, name: name);
});
}
else if(x != null && y != null)
{
return gen_array_ops.select(condition, x, y, name);
}
else
{
throw new ValueError("x and y must both be non-None or both be None.");
}
}
/// <summary>
/// Returns the shape of a tensor.
/// </summary>
/// <param name="input">A `Tensor` or `SparseTensor`.</param>
/// <param name="name">A name for the operation (optional).</param>
/// <param name="out_type">
/// (Optional) The specified output type of the operation
/// (`int32` or `int64`). Defaults to `tf.int32`.
/// </param>
/// <returns>A `Tensor` of type `out_type`.</returns>
public static Tensor shape(Tensor input, string name = null, TF_DataType out_type = TF_DataType.TF_INT32)
=> shape_internal(input, name, optimize: true, out_type: out_type);
public static Tensor size(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
=> size_internal(input, name, optimize: optimize, out_type: out_type);
public static Tensor shape_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
{
return tf_with(ops.name_scope(name, "Shape", new { input }), scope =>
{
name = scope;
if (!tf.context.executing_eagerly())
{
var input_tensor = ops.convert_to_tensor(input);
var input_shape = tensor_util.to_shape(input_tensor.shape);
if (optimize && input_tensor.NDims > -1 && input_shape.is_fully_defined())
{
var nd = np.array(input_tensor.shape).astype(out_type.as_numpy_dtype());
return constant_op.constant(nd, name: name);
}
}
return gen_array_ops.shape(input, name: name, out_type: out_type);
});
}
private static Tensor size_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
{
return tf_with(ops.name_scope(name, "Size", new { input }), scope =>
{
name = scope;
var input_tensor = ops.convert_to_tensor(input);
var input_shape = tensor_util.to_shape(input_tensor.shape);
if (optimize)
{
if (input_shape.is_fully_defined())
{
return constant_op.constant(input_shape.size, dtype: out_type, name: name);
}
}
return gen_array_ops.size(input, name: name, out_type: out_type);
});
}
public static Tensor zeros_like(Tensor tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true)
{
return tf_with(ops.name_scope(name, "zeros_like", new Tensor[] { tensor }), scope =>
{
name = scope;
tensor = ops.convert_to_tensor(tensor, name: "tensor");
// is_fully_defined return unexpected value.
if (optimize && tensor_util.to_shape(tensor.shape).is_fully_defined() && dtype != TF_DataType.TF_VARIANT)
{
}
if(dtype != TF_DataType.DtInvalid && dtype != tensor.dtype && dtype != TF_DataType.TF_VARIANT)
{
throw new NotImplementedException("zeros_like");
// return zeros(shape_internal(tensor, optimize: optimize), dtype: dtype, name: name);
}
else
{
return gen_array_ops.zeros_like(tensor, name: name);
}
});
}
/// <summary>
/// When building ops to compute gradients, this op prevents the contribution of
/// its inputs to be taken into account.Normally, the gradient generator adds ops
/// to a graph to compute the derivatives of a specified 'loss' by recursively
/// finding out inputs that contributed to its computation.If you insert this op
/// in the graph it inputs are masked from the gradient generator. They are not
/// taken into account for computing gradients.
/// </summary>
/// <param name="input"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor stop_gradient(Tensor input, string name = null)
=> gen_array_ops.stop_gradient(input, name);
/// <summary>
/// Extracts a strided slice of a tensor (generalized python array indexing).
/// </summary>
/// <param name="input_"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <param name="strides"></param>
/// <param name="begin_mask"></param>
/// <param name="end_mask"></param>
/// <param name="ellipsis_mask"></param>
/// <param name="new_axis_mask"></param>
/// <param name="shrink_axis_mask"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor strided_slice(Tensor input_, Tensor begin, Tensor end,
Tensor strides = null,
int begin_mask = 0,
int end_mask = 0,
int ellipsis_mask = 0,
int new_axis_mask = 0,
int shrink_axis_mask = 0,
string name = null)
{
var op = gen_array_ops.strided_slice(
input: input_,
begin: begin,
end: end,
strides: strides,
begin_mask: begin_mask,
end_mask: end_mask,
ellipsis_mask: ellipsis_mask,
new_axis_mask: new_axis_mask,
shrink_axis_mask: shrink_axis_mask,
name: name);
string parent_name = name;
return op;
}
/// <summary>
/// Removes dimensions of size 1 from the shape of a tensor.
/// Given a tensor `input`, this operation returns a tensor of the same type with
/// all dimensions of size 1 removed.If you don't want to remove all size 1
/// dimensions, you can remove specific size 1 dimensions by specifying
/// `axis`.
/// </summary>
/// <param name="input"> A `Tensor`. The `input` to squeeze.</param>
/// <param name="axis"> An optional list of `ints`. Defaults to `[]`.
/// If specified, only squeezes the dimensions listed.The dimension
/// index starts at 0. It is an error to squeeze a dimension that is not 1.
/// Must be in the range `[-rank(input), rank(input))`.</param>
/// <param name="name"> A name for the operation (optional).</param>
/// <param name="squeeze_dims" >Deprecated keyword argument that is now axis.</param>
/// <returns>A `Tensor`. Has the same type as `input`.
/// Contains the same data as `input`, but has one or more dimensions of
/// size 1 removed.</returns>
public static Tensor squeeze(Tensor input, int[] axis = null, string name = null, int[] squeeze_dims = null)
=> gen_array_ops.squeeze(input, axis, name);
public static Tensor identity(Tensor input, string name = null)
=> gen_array_ops.identity(input, name);
public static Tensor invert_permutation(Tensor x, string name = null)
=> gen_array_ops.invert_permutation(x, name: name);
/// <summary>
/// Computes the shape of a broadcast given symbolic shapes.
/// When shape_x and shape_y are Tensors representing shapes(i.e.the result of
/// calling tf.shape on another Tensor) this computes a Tensor which is the shape
/// of the result of a broadcasting op applied in tensors of shapes shape_x and
/// shape_y.
/// For example, if shape_x is [1, 2, 3] and shape_y is [5, 1, 3], the result is a
/// Tensor whose value is [5, 2, 3].
/// This is useful when validating the result of a broadcasting operation when the
/// tensors do not have statically known shapes.
/// </summary>
/// <param name="shape_x"> A rank 1 integer `Tensor`, representing the shape of x.</param>
/// <param name="shape_y"> A rank 1 integer `Tensor`, representing the shape of y.</param>
/// <returns> A rank 1 integer `Tensor` representing the broadcasted shape.</returns>
public static Tensor broadcast_dynamic_shape(Tensor shape_x, Tensor shape_y)
=> gen_array_ops.broadcast_args(shape_x, shape_y);
public static Tensor broadcast_static_shape(Tensor shape_x, Tensor shape_y)
=> Framework.common_shapes.broadcast_shape(shape_x, shape_y);
/// <summary>
/// Concatenates tensors along one dimension.
/// </summary>
/// <param name="values"></param>
/// <param name="axis"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor concat(Tensor[] values, int axis, string name = "concat")
{
if(values.Length == 1) // Degenerate case of one tensor.
{
return tf_with(ops.name_scope(name), scope => {
var t = ops.convert_to_tensor(axis, name: "concat_dim", dtype: TF_DataType.TF_INT32);
return identity(values[0], name: scope);
});
}
return gen_array_ops.concat_v2(values, axis, name: name);
}
public static Tensor concat(Tensor[] values, Tensor axis, string name = "concat")
{
return gen_array_ops.concat_v2(values, axis, name: name);
}
public static Tensor concat(object[] values, int axis, string name = "concat")
{
return gen_array_ops.concat_v2(values, axis, name: name);
}
public static Tensor gather<T1, T2>(T1 @params, T2 indices, string name = null, int axis = 0)
{
if (axis != 0)
return gen_array_ops.gather_v2(@params, indices, axis, name: name);
if (@params is ResourceVariable variable &&
indices is Tensor indices_tensor)
return variable.sparse_read(indices_tensor, name);
return gen_array_ops.gather_v2(@params, indices, axis, name: name);
}
public static Tensor transpose<T1, T2>(T1 a, T2 perm, string name = "transpose", bool conjugate = false)
{
return tf_with(ops.name_scope(name, "transpose", new { a }), scope =>
{
return gen_array_ops.transpose(a, perm, name: scope);
});
}
public static Tensor[] split(Tensor value, int num_or_size_splits, Tensor axis,
string name = "split")
{
var size_splits = ops.convert_to_tensor(num_or_size_splits);
return gen_array_ops.split(axis: axis,
num_split: num_or_size_splits,
value: value,
name: name);
}
public static Tensor slice<Tb, Ts>(Tensor input, Tb begin, Ts size, string name = null)
=> gen_array_ops.slice(input, begin, size, name: name);
public static Tensor stack(object values, int axis = 0, string name = "stack")
{
if (axis == 0)
// If the input is a constant list, it can be converted to a constant op
return ops.convert_to_tensor(values, name: name);
throw new NotImplementedException("array_ops.stack");
}
public static Tensor pad(Tensor tensor, Tensor paddings, string mode = "CONSTANT", string name = null, int constant_values = 0)
{
Tensor result = null;
mode = mode.ToUpper();
if(mode == "CONSTANT")
{
if (constant_values != 0)
throw new NotImplementedException("gen_array_ops.pad_v2");
else
result = gen_array_ops.pad(tensor, paddings, name: name);
}
// Restore shape information where possible.
var paddings_constant = tensor_util.constant_value(
result.op.inputs[1], partial: true);
var input_shape = result.op.inputs[0].TensorShape;
if (input_shape.ndim > -1 &&
!result.TensorShape.is_fully_defined() &&
!(paddings_constant is null))
{
var new_shape = new List<int>();
foreach((NDArray padding, int dim) in zip(paddings_constant.GetNDArrays(), np.array(input_shape.dims).GetNDArrays()))
{
if (padding is null || dim == -1 || padding.GetData<int>().Contains(-1))
new_shape.Add(-1);
else
new_shape.Add(np.sum(padding) + dim);
}
result.set_shape(new_shape.ToArray());
}
return result;
}
public static Tensor placeholder(TF_DataType dtype)
{
throw new NotImplementedException("array_ops.placeholder");
}
}
}