Home | History | Annotate | Download | only in xla
      1 /* Copyright 2017 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 #include "tensorflow/compiler/xla/index_util.h"
     17 
     18 #include <algorithm>
     19 #include <string>
     20 
     21 #include "tensorflow/compiler/xla/shape_util.h"
     22 #include "tensorflow/compiler/xla/types.h"
     23 #include "tensorflow/compiler/xla/xla_data.pb.h"
     24 #include "tensorflow/core/lib/strings/str_util.h"
     25 #include "tensorflow/core/platform/logging.h"
     26 
     27 namespace xla {
     28 
     29 /* static */ int64 IndexUtil::MultidimensionalIndexToLinearIndex(
     30     const Shape& shape, tensorflow::gtl::ArraySlice<int64> multi_index) {
     31   DCHECK_EQ(shape.dimensions_size(), multi_index.size());
     32   // Padding and nested layouts not supported yet.
     33   DCHECK_EQ(0, shape.layout().padded_dimensions_size());
     34 
     35   for (size_t i = 0; i < multi_index.size(); ++i) {
     36     DCHECK_GE(multi_index[i], 0);
     37     DCHECK_LT(multi_index[i], shape.dimensions(i))
     38         << "indexing beyond extent in dimension " << i << ":"
     39         << "\n\tindex: " << tensorflow::str_util::Join(multi_index, ",")
     40         << "\n\tshape: " << ShapeUtil::HumanString(shape);
     41   }
     42 
     43   // Let the array be sized like so for dimensions i from 0 to n-1:
     44   //
     45   //   [D{n-1} x D{n-2} x .. x D{0}]
     46   //
     47   // Let the order of the dimensions in the minor_to_major field in
     48   // Layout be:
     49   //
     50   //   L(0), L(1), ... , L(n-1)
     51   //
     52   // where L(0) is the most-minor dimension and L(n-1) the most-major. The
     53   // multidimensional index:
     54   //
     55   //   [I{0}, I{1}, ... , I{n-1}]
     56   //
     57   // then corresponds to the following linear index:
     58   //
     59   // linear_index =
     60   //   (((  ... + I{L(2)}) * D{L(1)} + I{L(1)}) * D{L(0)} + I{L(0)}
     61   //
     62   // or equivalently:
     63   //
     64   // linear_index =
     65   //   I{L(n-1)} * (D{L(n-2)} * D{L(n-3)} * D{L(n-4)} *     ....    D{L(0)}) +
     66   //   I{L(n-2)} *             (D{L(n-3)} * D{L(n-4)} *     ....    D{L(0)}) +
     67   //   I{L(n-3)} *                         (D{L(n-4)} *     ....    D{L(0)}) +
     68   //                                   ...                                   +
     69   //   I{L(2)} *                                         (D{L(1)} * D{L(0)}) +
     70   //   I{L(1)} *                                                    D{L(0)}  +
     71   //   I{L(0)}
     72   //
     73   // We compute the linear index value by accumulating the terms above from
     74   // I{L(0)} up to I{L(n-1)}. Scale accumulates the product term D{L(0}} *
     75   // D{L(1)} * ...
     76 
     77   // Scale factor holding the growing product of D{L(i)} terms.
     78   int64 scale = 1;
     79   int64 linear_index = 0;
     80   bool first = true;
     81   for (auto dimension : LayoutUtil::MinorToMajor(shape)) {
     82     if (first) {
     83       // Avoid two multiplies on the first loop iteration
     84       linear_index = multi_index[dimension];
     85       scale = shape.dimensions(dimension);
     86       first = false;
     87     } else {
     88       linear_index += scale * multi_index[dimension];
     89       scale *= shape.dimensions(dimension);
     90     }
     91   }
     92   return linear_index;
     93 }
     94 
     95 /* static */ std::vector<int64> IndexUtil::LinearIndexToMultidimensionalIndex(
     96     const Shape& shape, int64 linear_index) {
     97   // Padding and nested layouts not supported yet.
     98   DCHECK_EQ(0, shape.layout().padded_dimensions_size());
     99   DCHECK_GE(linear_index, 0);
    100   DCHECK_LT(linear_index, ShapeUtil::ElementsIn(shape));
    101 
    102   // The following formula computes each element of the multidimensional index
    103   // (See comments in MultidimensionalIndexToLinearIndex for notation):
    104   //
    105   // I{L(0)} = linear_index % D{L(0)}
    106   // I{L(1)} = (linear_index / D{L(0)}) % D{L(1)}
    107   // I{L(2)} = (linear_index / (D{L(0)} * D{L(1)})) % D{L(2)}
    108   // ...
    109   std::vector<int64> multi_index(shape.dimensions_size());
    110 
    111   // Accumulated product D{L(0)} * D{L(1)} * ...
    112   int64 divisor = 1;
    113   for (auto dimension : LayoutUtil::MinorToMajor(shape)) {
    114     multi_index[dimension] =
    115         (linear_index / divisor) % shape.dimensions(dimension);
    116     divisor *= shape.dimensions(dimension);
    117   }
    118   return multi_index;
    119 }
    120 
    121 /* static */ bool IndexUtil::BumpIndices(
    122     const Shape& shape, tensorflow::gtl::MutableArraySlice<int64> indices) {
    123   for (int64 dimno = indices.size() - 1; dimno >= 0; --dimno) {
    124     int64 limit = shape.dimensions(dimno);
    125     if (indices[dimno] + 1 < limit) {
    126       indices[dimno]++;
    127       std::fill(indices.begin() + dimno + 1, indices.end(), 0);
    128       return true;
    129     }
    130   }
    131   return false;
    132 }
    133 
    134 /* static */ int64 IndexUtil::GetDimensionStride(const Shape& shape,
    135                                                  int64 dimension) {
    136   int64 pdim_size = LayoutUtil::PaddedDimensions(shape).size();
    137   int64 stride = 1;
    138   DCHECK(pdim_size == 0 || pdim_size == shape.dimensions_size());
    139   for (auto dim : LayoutUtil::MinorToMajor(shape)) {
    140     if (dim == dimension) {
    141       break;
    142     }
    143     if (pdim_size == 0) {
    144       stride *= shape.dimensions(dim);
    145     } else {
    146       stride *= LayoutUtil::PaddedDimension(shape, dim);
    147     }
    148   }
    149   return stride;
    150 }
    151 
    152 /* static */ bool IndexUtil::IndexInBounds(
    153     const Shape& shape, tensorflow::gtl::ArraySlice<int64> index) {
    154   int64 rank = ShapeUtil::Rank(shape);
    155   if (rank != index.size()) {
    156     return false;
    157   }
    158   for (int64 d = 0; d < rank; ++d) {
    159     if (index[d] >= shape.dimensions(d)) {
    160       return false;
    161     }
    162   }
    163   return true;
    164 }
    165 
    166 /* static */ int IndexUtil::CompareIndices(
    167     tensorflow::gtl::ArraySlice<int64> lhs,
    168     tensorflow::gtl::ArraySlice<int64> rhs) {
    169   int64 rank = lhs.size();
    170   CHECK_EQ(rhs.size(), rank);
    171   for (int64 dim = 0; dim < rank; ++dim) {
    172     if (lhs[dim] < rhs[dim]) {
    173       return -1;
    174     } else if (lhs[dim] > rhs[dim]) {
    175       return 1;
    176     }
    177   }
    178   return 0;
    179 }
    180 
    181 }  // namespace xla
    182