Repository navigation
Expand file tree
/
Copy pathpattern-utils.h
More file actions
886 lines (708 loc) · 35.2 KB
/
Copy pathpattern-utils.h
File metadata and controls
886 lines (708 loc) · 35.2 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
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
// tensor/pattern-utils.h
// Copyright 2019 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// 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
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_TENSOR_TENSOR_PATTERN_UTILS_H_
#define KALDI_TENSOR_TENSOR_PATTERN_UTILS_H_ 1
#include "tensor/tensor-common.h"
#include "tensor/pattern.h"
#include "tensor/array-ref.h"
// This header includes various functions operating on Patterns.
// See also pattern-extra-utils.h which contains the
// more obscure and less user-facing functions.
namespace kaldi {
namespace tensor {
// Returns true if the pattern code indicates that the pattern contains a
// negative stride.
inline bool ContainsNegativeStride(int32 pattern_code) {
// 2048 is 1 << 11; 11th bit in code is set if code indicates negative stride.
return (pattern_code | 2048) != 0;
}
/**
This function converts an eaxis-index into an raxis-index, with no error
checking (you would normally check afterward that the raxis-index is in the
correct range). Find "Eaxis-index:" and "Raxis-index:" in pattern.h,
but basically and eaxis-index is an axis-index in the public numbering where
we allow negative values to mean offsets from the end.
*/
inline int32 EaxisToRaxis(int32 eaxis, int32 num_axes) {
return (eaxis < 0 ? 1 - eaxis : num_axes - 1 - eaxis);
}
/**
Returns true if the pattern code indicates that the pattern contains a
negative stride. Caution: will return true if pattern_code was -1, so if you
call this on a code on a valid Pattern where the code might be -1, all it
means is that the Pattern "might" contain a negative stride.
@param [in] pattern The input pattern. Must be valid;
return status is undefined otherwise.
@return Returns true if either the pattern's code was
-1 (meaning: not known), or if the code
indicates that a negative stride was present.
*/
inline bool PatternMightContainNegativeStride(
const Pattern &pattern) {
// 2048 is 1 << 11; 11th bit in code is set if code indicates negative stride.
return (pattern.code | 2048) != 0;
}
/**
Returns true if the pattern contains a negative stride.
See pattern-utils-inl.h for implementation.
@param [in] pattern Input pattern. Must be valid;
return status is undefined otherwise.
TODO: if we need this to work for, e.g.
valid- or valid-- patterns, find
the exact conditions.
@return Returns true if the pattern contained at
least one negative stride, false otherwise.
*/
inline bool ContainsNegativeStride(const Pattern &pattern);
// Returns true if the pattern code indicates that the raxis
// numbered 'raxis' (the r refers to the backwards numbering used
// in 'pattern') is 'trivial' (meaning: dim=1, stride=0).
inline bool AxisIsTrivial(int32 pattern_code, int32 raxis) {
return (pattern_code | 1 << raxis) == 0;
}
/**
This function copies pattern_in to pattern_out while removing
trivial axes (i.e. axes with dim=1), reducing num_axes appropriately.
@param [in] pattern_in Input pattern.
@param [out] pattern_out Output pattern; may not point to pattern_in.
At exit it will be the same as pattern_in except any
axes with dim=1 will have been removed and the num_axes
reduced. Will be valid at output if pattern_in was
valid-1 at input.
*/
void RemoveTrivialAxes(const Pattern &pattern_in,
Pattern *pattern_out);
/**
This function removes trivial axes (i.e. axes with dim=1) from 'pattern'.
This version works in-place.
@param [in,out] pattern Pattern to be modified. Any axes with dim=1
will be removed and the num_axes reduced. Will be
valid at output if it was valid-1 at input.
*/
void RemoveTrivialAxes(Pattern *pattern);
/**
This function returns a code that compactly represents information about
which axes had dim != 1; which axis, if any, had stride == 1; and
whether any axis had stride < 0.
Let
n = 0 if no axis had stride=1, otherwise:
n = 1 + the raxis index which had stride=1.
(raxis is the axis index when accessing the axes in reversed order, as
stored in pattern.dims and pattern.strides).
For example if the strides were [10,3,1] we would have
n = 1; i if the strides were [10,1,3] we would have n = 2.
IMPORTANT NOTE ON ORDERING: lists of dims or strides in square
brackets, like [1,2], are in the non-reversed ordering as exposed
by the Tensor API.
The value 'n' occupies the bits starting from 8 in the returned code,
i.e. bits 8,9,10 (counting from the right, i.e. from the least to
most significant).
Bit 11 is 1 if any of the strides were negative, and zero otherwise.
(None of the example bit-patterns below have this bit set.) The
underlying BLAS in most cases does not support negative strides so
we deal with it by copying the data to a temporary with positive
strides.
The low-order KALDI_TENSOR_MAX_DIM bits have a 1 corresponding to
axes where dim != 1, and a 0 if dim == 1 for that axis. Axis zero
in the private numbering (equal to the highest-numbered axis in the
public numbering) is the rightmost/lowest-order of these bits.
The explanation below will use c++14 binary literals (like 0b010101), although the code
doesn't use them as we compile as c++11; we show the corresponding hex codes which
are used in the code (and anyway easier to parse).
In the notation below, in dims vectors, x or X is a stand-in for 'any number
not equal to 1', and upper-case X indicates that the axis has stride=1. In
the example `dims` vectors below, we don't put any leading `dim=1` axes,
because they would not affect the code generated. The list of numbers
in square brackets [] below may be interpreted as the sequence of dims for the
Tensor, in the non-reversed ordering that the Tensor API exposes.
The ' at the 8th bit is to make the bit-string easier to parse.
0b000'00000000 0x000 dims=[], a scalar
0b000'00000001 0x001 dims=[x], a vector with a stride
0b001'00000001 0x101 dims=[X], a vector
0b000'00000010 0x002 dims=[x,1], a vector with a stride
0b010'00000010 0x202 dims=[X,1], a vector
0b000'00000011 0x003 dims=[x,x], a matrix with a stride
0b001'00000011 0x103 dims=[x,X], a matrix
0b010'00000011 0x203 dims=[X,x], a transposed matrix
0b000'00000100 0x008 dims=[x,1,1], a vector with a stride
0b011'00000100 0x308 dims=[X,1,1], a vector
0b010'00000110 0x20B dims=[x,X,1], a matrix
0b011'00000110 0x30B dims=[X,x,1], a transposed matrix
0b000'00000110 0x10B dims=[x,x,1], a matrix with column stride
0b001'00000101 0x109 dims=[x,1,X], a matrix
0b011'00000101 0x309 dims=[X,1,x], a transposed matrix
0b000'00000101 0x009 dims=[x,1,x], a matrix with column stride
...
*/
int32 ComputePatternCode(const Pattern &pattern);
inline int32 CombineCodes(int32 code1, int32 code2) {
return (code1 << 12) | code2;
}
inline int64 CombineCodes(int32 code1, int32 code2, int32 code3) {
return (static_cast<int64>(code1) << 24) |
static_cast<int64>(code2 << 12) |
static_cast<int64>(code3);
}
/**
Copies a Pattern from `src` to `dest` while modifying it by inserting
an axis with (dim=1,stride=0) at position `raxis` (specified in the
private numbering).
@param [in] raxis The index at which the extra axis is to appear.
We require 0 <= raxis <= p->num_axes.
@param [in] src The source pattern. Must be valid and have
NumAxes() < KALDI_TENSOR_MAX_DIM.
@param [out] dest The destination pattern. Is allowed to be the same
object as `src`. Will be valid at exit if src
was valid at entry (which this function may not
check).
*/
void UnsqueezeR(int32 raxis, const Pattern &src, Pattern *dest);
/**
Modifies 'p' in-place by inserting an axis with (dim=1,stride=0) at the
specified axis-index (numbered in the public numbering).
Equivalent to PyTorch's unsqueeze(), including its behavior with
negative axis indexes (axis < 0 is interpreted as to num_axes + 1 - axis).
Showing just the dims in the pattern, in the non-reversed order as
exported by the API, some examples are:
\verbatim
Unsqueeze([6,5], 0) -> [1,6,5]
Unsqueeze([3,4], 1) -> [3,1,4]
Unsqueeze([9,10], 2) -> [9,10,1]
Unsqueeze([9,10], -1) -> [9,10,1]
\endverbatim
@param [in] eaxis The axis-index at which the extra axis is to appear,
with negatives allowed (see: "Eaxis-index" in glossary
in pattern.h).
@param [in,out] p The pattern to which we are adding an axis.
Will have its num_axes increased by 1
at exit, possibly its dims and strides
arrays changed, and its code updated.
*/
inline void Unsqueeze(int32 eaxis, Pattern *p) {
UnsqueezeR(EaxisToRaxis(eaxis, p->num_axes));
}
/**
Modifies 'p' in-place by removing an axis with dim=1 from the specified
position (in the reversed numbering physically used in the pattern). Updates
p->code. It is an error if 'p' did not, on entry, contain an axis with dim=1
as position 'raxis' in the array.
Modifies 'p' in-place by removing an axis with dim=1 from the
specified position specified in the reversed numbering physically used in the
pattern. Updates p->code. It is an error if 'p' did not initially contain
an axis with dim=1 at position 'raxis' in the array.
This function updates p->code.
In the example below we show the dims in the order they appear in the
physical array:
\verbatim
SqueezeR(0, {1,3,4}) -> {3,4}
SqueezeR(1, {5,1,7}) -> {5,7}
SqueezeR(2, {8,1,9}) -> [error]
\endverbatim
@param [in] raxis The reversed-order axis to be squeezed.
We require 0 <= raxis < p->num_axes and
p->dims[raxis] == 1.
@param [in,out] p The pattern from which we are removing an
axis. Will have its num_axes reduced by 1
at exit, possibly its dims and strides
arrays changed, and its 'code' updated.
*/
void SqueezeR(int32 raxis, Pattern *p);
/**
Modifies 'p' in-place by removing an axis with dim=1 (hence stride=0)
located at the specified axis (as numbered in the public numbering).
Equivalent to PyTorch's squeeze(), including its behavior with
negative axis indexes; axis < 0 is interpreted as to num_axes - axis,
i.e. the last axis. It is an error if 'p' did not, on entry,
contain an axis with dim=1 at position 'axis' (in the public numbering).
Showing just the dims in the pattern, in the non-reversed order as
exported by the API, some examples are:
\verbatim
Squeeze([1,6,5], 0) -> [6,5]
Squeeze([3,1,4], 1) -> [3,4]
Squeeze([9,1,10], 2) -> error
Squeeze([7,1], -1) -> [7]
\endverbatim
@param [in] axis The index at which the extra axis is to appear.
We require -p->num_axes <= axis < p->num_axes
(negative axes are permitted, interpreted
as an offset from p->num_axes).
We require that the specified axis have
dim=1.
@param [in,out] p The pattern from which we are removing an
axis. Will have its num_axes reduced by 1
at exit, possibly its dims and strides
arrays changed, and its 'code' updated.
*/
inline void Squeeze(int32 axis, Pattern *p) {
if (axis < 0) SqueezeR(1 - axis, p);
else SqueezeR(p->num_axes - 1 - axis, p);
}
/** Transpose the two specified axes (specified in the private/reversed
numbering) of a Pattern.
@param [in] raxis1 First axis to be transposed; must be >=0,
and if increase_num_axes is false, must be
less than p->num_axes.
@param [in] raxis2 Second axis to be transposed; must be >=0,
and if increase_num_axes is false, must be
less than p->num_axes.
If identical to axis1, nothing will be done.
@param [in,out] p Pattern whose axes are to be transposed.
@param [in] increase_num_axes If this is true, we allow
raxis1 and/or raxis2 to be >= p->num_axes;
we will increase p->num_axes as necessary
if this operation results in any
raxis >= p->num_axes becoming non-trivial.
*/
void TransposeR(int32 raxis1, int32 raxis2, Pattern *p,
bool increase_num_axes = false);
/** Transpose the two specified axes (specified in the private/reversed
numbering) of a Pattern.
@param [in] axis1 First axis to be transposed; must be in range
`[-p->num_axes, p->num_axes - 1]`,
with negative axis being interpreted as an offset
from p->num_axes. This axis-index is in the
public numbering, not the reversed numbering
physically used in 'pattern'.
@param [in] axis2 Second axis to be transposed; must be in range
`[-p->num_axes, t->num_axes - 1]`.
If identical to axis1, nothing will be done.
@param [in,out] p Pattern whose axes are to be transposed.
p->code is updated.
*/
void TransposeR(int32 raxis1, int32 raxis2, Pattern *p);
/**
Modifies 'p' in-place by removing an axis with dim=1 (hence stride=0)
located at the specified axis (as numbered in the public numbering).
Equivalent to PyTorch's squeeze(), including its behavior with
negative axis indexes; axis < 0 is interpreted as to num_axes - axis,
i.e. the last axis. It is an error if 'p' did not, on entry,
contain an axis with dim=1 at position 'axis' (in the public numbering).
Showing just the dims in the pattern, in the non-reversed order as
exported by the API, some examples are:
\verbatim
Squeeze([1,6,5], 0) -> [6,5]
Squeeze([3,1,4], 1) -> [3,4]
Squeeze([9,1,10], 2) -> error
Squeeze([7,1], -1) -> [7]
\endverbatim
@param [in] axis The index at which the extra axis is to appear.
We require -p->num_axes <= axis < p->num_axes
(negative axes are permitted, interpreted
as an offset from p->num_axes).
We require that the specified axis have
dim=1.
@param [in,out] p The pattern from which we are removing an
axis. Will have its num_axes reduced by 1
at exit, possibly its dims and strides
arrays changed, and its 'code' updated.
*/
inline void Squeeze(int32 axis, Pattern *p) {
if (axis < 0) SqueezeR(1 - axis, p);
else SqueezeR(p->num_axes - 1 - axis, p);
}
/** This function returns true if the dimensions of tensor patterns
a, b and c are broadcastable in the PyTorch sense (meaning;
after padding their dims on the left with ones to make them
have the same num-axes, corresponding dimensions are either
identical or 1). The previous sentence is written in terms
of the public numbering; in the private numbering it just means:
for each index `raxis` into the dims vector,
either `a.dims[raxis] == b.dims[raxis]`, or one of them is 1.
@param [in] a The pattern of the first Tensor
@param [in] b The pattern of the second Tensor
@param [in] b_not_smaller If true, then we do not allow a dim of
b to be 1 while corresponding dim of a is >1.
*/
bool Broadcastable(const Pattern &a, const Pattern &b);
/** This function returns true if the dimensions of tensor patterns
a, b and c are broadcastable in the PyTorch sense, which is
the same as
`Broadcastable(a, b) && Broadcastable(b, c) && Broadcastable(a, c)`.
See the 2-argument version of Broadcastable for more information.
@param [in] a The pattern of the first Tensor
@param [in] b The pattern of the second Tensor
@param [in] c The pattern of the third Tensor
@return Returns true if a, b and c are broadcastable
*/
bool Broadcastable(const Pattern &a, const Pattern &b,
const Pattern &c);
/**
Returns true if for each raxis, a.dims[raxis] >= b.dims[raxis].
*/
bool DimsGeq(const Pattern &a, const Pattern &b);
/**
Returns true if the shapes of a and b (see "Shape of a Pattern" in pattern.h)
are the same after adding 1's on the left (padding) as for broadcasting. See
definition of "Dims-vector of a Pattern" in pattern.h, and the entry for
"Padding". What this means in terms of the physical storage of the patterns
is that a->dims and b->dims contain the same elements, without requiring the
num_axes to be the same.
This is a stronger condition than Broadcastable(a, b).
@param [in] a The first pattern. Must be valid.
@param [in] b The second pattern. Must be valid.
@return Return true if the shapes of
a and b are the same after padding.
See also the 3-arg version of SamePaddedDims(), and SameDims().
This is a stronger condition than Broadcastable(a, b).
*/
bool SamePaddedDims(const Pattern &a, const Pattern &b);
/**
Returns true if the 'dims' vectors of a, b and c are all the same
after padding with 1's on the left (in the public numbering) to
make the dims the same. Equivalent to
SamePaddedDims(a, b) && SamePaddedDims(b, c).
This is a stronger condition than Broadcastable(a, b, c).
*/
bool SamePaddedDims(const Pattern &a, const Pattern &b,
const Pattern &c);
/**
Return true if the two provided patterns have the same dims-vectors
(meaning, effectively the same num_axes and the same dim for each
axis; see "Dims-vector" in pattern.h).
@param [in] a The first pattern. Must be valid.
@param [in] b The second pattern. Must be valid.
@return Returns true if a.num_axes == b.num_axes and
the elements of their 'dims' members are the same.
See also: SamePaddedDims().
*/
bool SameDims(const Pattern &a, const Pattern &b);
/**
Returns true if pattern1 and pattern2 have the same num_axes and strides.
(i.e. the strides, viewed as a vector of dim num_axes, are identical).
@param [in] a The first pattern. Must be valid-2
@param [in] b The second pattern. Must be valid-2
@return Returns true if a.num_axes == b.num_axes and
the elements of their 'strides' members are the same.
*/
bool SameStrides(const Pattern &a,
const Pattern &b);
/**
Reduces a Pattern by removing or combining as many axes as possible.
This version is suitable for operations that do not rely on any kind of
structure, such as zeroing or nonlinearities; the only equivalence maintained
is equivalence of the set of memory locations covered (the memory-index-set).
The order of the (dim,stride) pairs in the input does not affect the
output. The output (dim,stride) pairs will be ordered from
greatest to least stride (note: all output strides will be positive).
@param [in,out] pattern The pattern to be reduced
Examples are below, where we write a Pattern as
`{{dim1,dim2,..}, {stride1,stride2,..} [,offset] }`
(the offset is written only if nonzero).
(the curly braces in our notation imply that we are referring to the reversed
ordering physically used in 'pattern', but actually this doesn't affect
anything since the order of axes does not matter here as long as it is constent.
\verbatim
Input pattern Output pattern
{{10},{1}} {{10},{1}}
{{3,4},{4,1}} {{12},{1}}
{{4,3},{1,4}} {{12},{1}}
{{9},{-1},8} {{9},{1}} // offset reduced by 8.
{{2,3,4},{100,4,1}} {{2,12},{100,1}}
\endverbatim
*/
void ReduceOnePattern(Pattern *pattern);
/**
Sorts the axes in 'pattern' from most negative to most positive stride
in private numbering, equivalent to sorting from most positive to
most negative stride in public numbering.
TODO: decide whether to change this to sort on abs(stride), or
maybe create another version that does sort on abs(stride), if there
are situations where this turns out to be useful.
@param [in,out] The pattern whose axes are to be sorted
from most negative to most positive stride (in the
physical ordering).
*/
void SortAxes(Pattern *pattern);
/**
Returns the raxis with the smallest value of abs(stride[raxis]),
taking the lowest-numbered raxis in case of ties (which could only
happen in the case of stride == 0). Requires Valid(pattern).
*/
int32 RaxisWithSmallestAbsStride(const Pattern &pattern);
// TODO: document this.
inline void CanonicalizePattern(Pattern *pattern) {
ReduceOnePattern(pattern);
SortAxes(pattern);
}
// TODO: document this. This will later be replaced with
// a more efficient version.
inline void CanonicalizePattern(contst Pattern &pattern_in,
Pattern *pattern_out) {
*pattern_out = pattern_in;
CanonicalizePattern(pattern_out);
}
/**
This pattern checks that 'pattern' is valid and in canonical form (see
glossary for the meaning). CanonicalizePattern() will modify a valid pattern
to put it in canonical form.
*/
bool IsCanonical(const Pattern &pattern);
/**
Returns the number of elements in the pattern, computed as the
product of the dims. ('pattern' is expected to either be valid or
to at least satisfy the uniqueness property for this to actually give
the number of elements, but this is not checked).
*/
int64 NumElements(const Pattern &pattern);
/**
Multiplies all strides and the offset in 'pattern' by 'scale', which must be >
0. For now, will just crash if this causes integer overflow.
This function is used in the memory-locking code if the same storage location
is accessed using different dtypes (which is unlikely).
*/
void ScaleStridesAndOffset(int32 scale, Pattern *pattern);
/// Hashing object, used when we need an unordered_map containing Pattern.
class PatternHasher {
size_t operator () (const Pattern &pattern) const;
};
/*
ReduceTwoPatterns() is a special case of ReducePatterns() where there
are exactly two patterns to be jointly reduced. See documentation of
ReducePatterns() for explanation.
*/
void ReduceTwoPatterns(Pattern *a,
Pattern *b);
/**
Reduces a Pattern by removing or combining as many axes as possible,
while preserving the memory-index-set of the pattern (see glossary for
explanation), and also while respecting certain invariances that are relevant
when constructing 'views' ('view' is PyTorch terminology; the NumPy
equivalent is 'reshape'). The "C" in the function name refers to C-style
arrays. Basically what this function does is a highly restricted subset
of what ReduceOnePattern() does.
This function removes axes with dim=1.
This function combines successive axes if the relationship of their
dims and strides is what you would expect in a "C"-style array
when the axes are listed in their non-reversed ordering (i.e.
as exposed by class Tensor).
Suppose that in pattern 'p' we had two successive axes physically numbered
raxis, raxis+1, with p->dims[raxis] > 1 and p->dims[raxis+1] > 1
and p->strides[raxis + 1] == p->strides[raxis] * p->dims[raxis],
then this function will merge them into a single axis whose dimension
is the product of the dimensions of the two original axes.
(However, they won't be merged if it would
result in a dimension exceeding the range of int32).
TODO... finish this if it turns out to be needed for something.
I'm not sure if it will be.
The output pattern 'dest' is what you get if you keep applying the
rules above until no further change is made.
Examples are below, where we write a Pattern as
` {{dim1,dim2,..}, {stride1,stride2,..}}`.
\verbatim
Input pattern Output pattern
{{10},{1}} {{10},{1}}
{{5,1},{1,1}} {{5},{1}}
{{9},{-1}} {{9},{-1}}
{2,3,4},{100,4,1} {{2,12},{100,1}}
{2,3,4},{100,-4,-1} {{2,12},{100,-1}}
\endverbatim
*/
void ReducePatternC(Pattern *p);
/**
Creates a Pattern corresponding to a requested 'view' of the matrix.
('view' is PyTorch terminology; the NumPy equivalent is 'reshape').
The PyTorch/NumPy semantics are (I believe) as follows: Firstly, a view
can/should only be created for a tensor whose layout in memory is as for a
"C" array; suppose that the shape of array a is (9, 8), a "C" layout would
imply strides of (8, 1). A 'view' of this array simply implies interpreting
the same block of memory as a "C" array with some other sequence of
dimensions, say (3, 3, 8) or (8, 9) or (1, 72); any sequence whose product
matches the number of elements in "a".
Our semantics of "view" is the same as that of PyTorch/NumPy except that we
impose fewer constraints on what strides the input Tensor cmay have. Let the
'view' of the array 'a' be 'b'. As long as it is possible to find a tensor
pattern for 'b' that would lead to the same relationship between the elements
of 'a' and 'b' as what you would get by asking for the same "view" in
PyTorch/NumPy assuming 'a' had had "C"-style strides (viewed in terms of
indexed elements of and b, without regard to the physical memory layout), we
allow it.
Notes on implementation (glossing over ones in 'dims' which are easy to
handle as a special case): we would first call ReducePattern on
'pattern_in'. Then we would attempt to find a correspondence with
the dimensions of this reduced pattern and a partition of the
sequence 'dims'. For example, suppose the reduced pattern
is (100, 9) and dims is (50, 2, 3, 3), then the partition would
be (50, 2), (3, 3). If this is not possible (e.g. if dims
had been (30,10,3) instead), we return false.
@param [in] pattern_in The input pattern for which we are trying to
find an alternative view
@param [in] dims The sequence of dimensions corresponding to the
desired view. Its product must be the same as the
product of pattern_in.dims.
@param [out] pattern_out The output pattern, if we were
successful (otherwise undefined). Its 'dims'
will be the same as 'dims'.
@return Returns true on success (i.e. such a view could be
created), and false otherwise. This function will
never return false if 'pattern_in' had strides as
for a "C" array (i.e., if HasCStrides(pettern_in)
returns true).
*/
bool CreateViewPattern(const Pattern &pattern_in,
ArrayRef<int32> dims,
Pattern *pattern_out);
/**
This is like PyTorch's slice() / narrow() functions.
It selects a range of dimensions on one of the axes. It is similar to
indexing with a range in Python, like A[10:20].
@param [in] eaxis Eaxis-index (see glossary in pattern.h) on which
to possibly reduce the dimensionality.
@param [in] start Starting index; must be in range [0, t->Dim(eaxis) - 1]
@param [in] end Ending index; must be in the range [start + 1, t->Dim(eaxis)]
@param [in,out] pattern Pattern to be modified. Will be valid at
exit if it was valid at entry.
See also: the other overloaded version of Slice() which accepts the 'step'
parameter; and Select(), which is similar but also reduces the num-axes.
*/
void Slice(int32 eaxis, int32 start, int32 end, Pattern *pattern);
/**
Copy one Pattern to another while modifying it by by selecting one index from
a specified axis (specified in the public numbering), of a TensorImpl `t`,
reducing the num_axes by one.
@param [in] eaxis Eaxis-index (see glossary in pattern.h) on which
to possibly reduce the dimensionality.
@param [in] index Index to select; must be in range
[0, t->Dim(eaxis) - 1].
@param [in,out] src Pattern which is to be copied; must be valid,
but we don't guarantee to check this.
@param [out] dest Pattern which we are copying to and modifying.
It is allowed to be the same object as 'src'.
Will be valid if src was valid.
*/
void Select(int32 eaxis, int32 index,
const Pattern &src, Pattern *dest);
/**
Infer an index-tuple from a memory-index m and a pattern p.
That is: find the index-tuple i such that src[i] = p[m]
and i[r] = 0 in all axes such that p.dims[r] == 1.
There is at most one such index-tuple, by the uniqueness property.
The numbering used here is by raxis (i.e. the private numbering).
@param [in] p Input pattern. Required to be valid.
@param [in] m The memory-index we are querying.
@param [out] index_tuple On success, the index-tuple will be
written to here (it will have size equal to p.num_axes).
Note: the indexing is by raxis (that is why there
is "R" in the function name).
On failure, the value at exit is undefined.
@return Returns true on success, false if no
such index-tuple existed.
*/
bool GetIndexTupleR(const Pattern &p,
int64 m,
std::vector<int32> *index_tuple);
/**
Convert a memory-index from one pattern to another. Specifically, it finds
an index-tuple i such that a[i] = mindex_a, and returns mindex_b = b[i], if
exactly one such mindex_b exists; otherwise it crashes. (The caveat about
"if exactly one such index exists" has to do with the possibility that there
is an raxis r that is trivial for a but not for b).
These memory-indexes include the 'offset' members of a and b. For a version
that does not include the offset (i.e. is invariant to the offset members),
see ConvertMindexDifference.
@param [in] a Source Pattern, from which mindex_a is derived
@param [out] b Destination Pattern, which we index to get
the returned mindex
@return Returns the memory-index mindex_b such that
there exists an index-tuple i in the
index-tuple-set of the tuple (a, b) satisfying
a[i] == mindex_a and b[i] == mindex_b. If it is
not the case that exactly one such memory-index
exists, it is an error and this function may
crash.
*/
int64 ConvertMindex(const Pattern &a,
const Pattern &b,
int64 mindex_a);
/**
Convert a difference between memory-indexes from one pattern to another.
This is equivalent to setting the 'offset' values of a and b to zero and
calling ConvertMindex() with the modified args, in cases where that inner
call does not crash. (But this function also generalizes to 'out-of-range'
or negative memory-indexes, like a linear continuation of the function).
See documentation for ConvertMindex() for more explanation.
*/
int64 ConvertMindexDifference(const Pattern &a,
const Pattern &b,
int64 mindex_a);
/**
This function returns true if 'pattern' has the same strides
as 'C' array with the same dimensions would have. (Note:
we are referring here to the public numbering of the axes).
For example, an array of dims [3, 4, 5], if it were
a "C" array, would have strides of [20, 5, 1]. As a special
case, since our Patterns use stride=0 for axes with dim=1,
we treat that zero as a wildcard; that is, if there
is a stride value for which the array would have "C" strides
then we'll return true.
@param [in] pattern The pattern we are checking. It is expected
to satisfy Valid(pattern), but this function does not
check this.
@return Returns true if this pattern has 'C' strides, and
false otherwise. (See note above about axes
with dim=1).
*/
void HasCStrides(const Pattern &pattern);
/**
Returns true if there is overlap between pattern1 and pattern2,
meaning that pattern1's memory-index-set and pattern2's
memory-index-set have nonempty intersection.
*/
bool PatternsOverlap(const Pattern &pattern1,
const Pattern &pattern2);
/**
Returns true if this is a valid pattern-tuple (see "Valid pattern-tuple"
in pattern.h)
*/
bool IsValidPatternTuple(ArrayRef<Pattern*> patterns);
/**
Returns true if the memory-index-set of this pattern forms a contiguous
range, otherwise false. (Note: this is not the same as PyTorch's notion of
contiguous; see HasCStrides()). Caution: the interface may later be changed
to allow caching of this property in the 'properties' field.
*/
bool IsCompact(const Pattern &pattern);
/**
Returns true if the lowest memory-index of 'pattern' is zero (see
"Justified" in glossary in pattern.h.
(see also: ComputeMinAndMaxMindex()).
*/
bool IsJustified(const Pattern &pattern);
/**
This is the same is IsCompact(pattern) &&
IsJustified(pattern).
*/
bool IsCompactAndJustified(const Pattern &pattern);
/**
Returns true if 'pattern' has normalized strides as defined in
pattern.h (i.e.: strides are nonnegative and the nonzero ones are in
strictly increasing order in the private numbering / decreasing in the
public).
*/
bool HasNormalizedStrides(const Pattern &pattern);
/**
Returns true if the strides in 'pattern' are all positive and are in strictly
increasing order in the private numbering / decreasing in the public.
*/
bool HasNormalizedPostiveStrides(const Pattern &pattern);
/**
Returns true if all the stides in 'pattern' are nonnegative.
*/
bool HasNonnegativeStrides(const Pattern &pattern);
} // namespace tensor
} // namespace kaldi
#include "tensor/pattern-utils-inl.h"
#endif KALDI_TENSOR_TENSOR_PATTERN_UTILS_H_