Files
deeplearning4j--deeplearning4j/libnd4j/include/helpers/shape.h
T
2026-07-13 12:47:05 +08:00

3250 lines
112 KiB
C++

/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* 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.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
/*
* shape.h
*
* Created on: Dec 28, 2015
* Author: agibsonccc
*
*
* Notes on this file. ALl functions here
* should be inlined.
* Inlined functions in both cpu and cuda
* allow different compilation units to embed the functions.
*
* We need these functions to be in the header in order to keep
* the functions agnostic.
*
* Note that SD_INLINE here at the time of writing (Mar 15 2024) was changed
* from always_inline from gcc.
*
*
*/
#ifndef SHAPE_H_
#define SHAPE_H_
#include <array/ArrayOptions.h>
#include <cnpy/cnpy.h>
#include <helpers/logger.h>
#include <math/templatemath.h>
#include <stdint.h>
#include <cstdio>
#include <cstring>
namespace shape {
/**
* Shape information approximating
* the information on an ndarray
*/
struct SD_LIB_EXPORT ShapeInformation {
SD_HOST_DEVICE ShapeInformation(sd::LongType *shape_ = nullptr, sd::LongType *stride_ = nullptr, char order_ = 0,
int rank_ = 0, int offset_ = 0, int elementWiseStride_ = 0, bool isEmpty_ = false)
: shape(shape_),
stride(stride_),
order(order_),
rank(rank_),
offset(offset_),
elementWiseStride(elementWiseStride_),
isEmpty(isEmpty_) {}
sd::LongType *shape;
sd::LongType *stride;
char order;
int rank;
int offset;
int elementWiseStride;
bool isEmpty;
};
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType &extra(sd::LongType *buffer) {
if(buffer == nullptr) {
THROW_EXCEPTION("extra: shapebuffer is nullptr");
}
sd::LongType rank = buffer[0];
sd::LongType idx = 0;
// rank takes up 1 element + usual elements
if (rank == 0) {
idx = 3;
} else {
// FIXME magic numbers
idx = rank + rank + 1;
}
return buffer[idx];
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType extra(const sd::LongType *buffer) {
if(buffer == nullptr) {
THROW_EXCEPTION("extra: shapebuffer is nullptr");
}
sd::LongType rank = buffer[0];
sd::LongType idx = 0;
// rank takes up 1 element + usual elements
if (rank == 0)
idx = 3;
else
// FIXME magic numbers
idx = rank + rank + 1;
return buffer[idx];
}
/**
* Returns whether the given shape
* info has the flag view set.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isViewConst(const sd::LongType *shapeInfo) {
return ((shape::extra(shapeInfo) & ARRAY_IS_VIEW) == ARRAY_IS_VIEW);
}
/**
* Returns whether the
* given shape info has an empty flag set.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isEmptyConst(const sd::LongType *shapeInfo) {
return ((shape::extra(shapeInfo) & ARRAY_EMPTY) == ARRAY_EMPTY);
}
SD_LIB_EXPORT SD_HOST_DEVICE bool shapeEquals(int shape1Rank, const sd::LongType *shape1, int shape2Rank,
const sd::LongType *shape2);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *detachShape(sd::LongType *originalShape);
SD_LIB_EXPORT SD_HOST_DEVICE bool shapeEquals(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2);
SD_LIB_EXPORT SD_HOST_DEVICE bool shapeEquals(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2,
const sd::LongType *shapeInfo3);
SD_LIB_EXPORT SD_HOST_DEVICE bool strideEquals(int shape1Rank, sd::LongType const *shape1, int shape2Rank,
sd::LongType const *shape2);
SD_LIB_EXPORT SD_HOST_DEVICE bool strideEquals(sd::LongType const *shapeInfo1, sd::LongType const *shapeInfo2);
SD_LIB_EXPORT SD_HOST_DEVICE bool equalsSoft(const sd::LongType *shapeA, const sd::LongType *shapeB);
SD_LIB_EXPORT SD_HOST_DEVICE bool equalsTypesAndShapesSoft(const sd::LongType *shapeA, const sd::LongType *shapeB);
SD_LIB_EXPORT SD_HOST_DEVICE bool equalsStrict(const sd::LongType *shapeA, const sd::LongType *shapeB);
// returns true if ranks, shapes and strides are the same
SD_LIB_EXPORT SD_HOST_DEVICE bool haveSameShapeAndStrides(const sd::LongType *shapeInfo1,
const sd::LongType *shapeInfo2);
SD_LIB_EXPORT SD_HOST_DEVICE bool haveSameShapeAndStrides(const sd::LongType *shapeInfo1,
const sd::LongType *shapeInfo2,
const sd::LongType *shapeInfo3);
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE void fill(T *buffer, T value, sd::LongType length);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType tadLength(const sd::LongType *shapeInfo, const sd::LongType *dimension,
sd::LongType dimensionLength);
/**
* Returns whether the given shape
* info has the flag view set.
*/
SD_LIB_EXPORT SD_HOST_DEVICE bool isView(sd::LongType *shapeInfo);
/**
* Returns whether the
* given shape info has an empty flag set.
*/
SD_LIB_EXPORT SD_HOST_DEVICE bool isEmpty(sd::LongType *shapeInfo);
SD_LIB_EXPORT SD_HOST_DEVICE bool reshapeC(const sd::LongType *oldShapeInfo, const char newOrder, sd::LongType newRank, const sd::LongType *newShape,
sd::LongType *newShapeInfo);
/**
* newShapeInfo contains rank, shape and order only, no strides/ews/type
*/
SD_LIB_EXPORT SD_HOST_DEVICE bool reshapeC(const sd::LongType *oldShapeInfo, sd::LongType *newShapeInfo);
/**
* Get the shape info buffer
* for the given rank and shape.
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeBuffer(sd::LongType rank, sd::DataType dtype, sd::LongType *shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeBuffer(sd::LongType rank, sd::DataType dtype, sd::LongType *shape,
sd::LongType *buffer);
SD_LIB_EXPORT SD_HOST_DEVICE void transposeInplace(sd::LongType *shapeBuffer);
/**
* Get the shape info buffer
* for the given rank and shape.
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeBufferFortran(int rank, sd::DataType dtype, sd::LongType const *shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeBufferFortran(int rank, sd::DataType dtype, sd::LongType const *shape,
sd::LongType *output);
#ifdef __CUDACC__
#ifndef __JAVACPP_HACK__
SD_DEVICE SD_LIB_EXPORT sd::LongType *cuMalloc(sd::LongType *buffer, long size);
#endif
#endif
SD_LIB_EXPORT SD_HOST_DEVICE void updateStrides(sd::LongType *shape, const char order, bool resetStridesIfView);
SD_LIB_EXPORT SD_HOST_DEVICE void updateStrides(const sd::LongType rank, const sd::LongType *shapeOnly,
sd::LongType *stridesOnly, const char order);
SD_LIB_EXPORT SD_HOST_DEVICE void doPermuteShapeInfo(sd::LongType *shapeBuffer, const sd::LongType *rearrange,
sd::LongType len = -1);
/**
* Get the ordering for the device
* @param length
* @param shape
* @param stride
* @param elementStride
* @return
*/
SD_LIB_EXPORT SD_HOST_DEVICE char getOrder(int length, sd::LongType *shape, sd::LongType *stride, int elementStride);
/**
* Ensure that every value in the re arrange
* array is unique
* @param arr
* @param shape
* @param arrLength
* @param shapeLength
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE int checkArrangeArray(T *arr, int arrLength, int shapeLength);
/**
* Permute the shape information
* @param info the shape information to permute
* @param rearrange the order to re arrange
* @param rank the rank of the rearrange array
*/
SD_LIB_EXPORT SD_HOST_DEVICE void permute(ShapeInformation **info, sd::LongType *rearrange, sd::LongType rank);
/**
* Returns whether the
* given shape is a vector or not
* @param shape the shape of the array
* @param rank the rank of cthe shape
*/
SD_LIB_EXPORT SD_HOST_DEVICE int isVector(sd::LongType const *shape, int rank);
SD_LIB_EXPORT SD_HOST_DEVICE int isVector(sd::LongType *shapeInfo);
SD_LIB_EXPORT SD_HOST_DEVICE bool isLikeVector(sd::LongType const *shapeInfo, int &posOfNonUnityDim);
SD_LIB_EXPORT SD_HOST_DEVICE bool isCommonVector(const sd::LongType *shapeInfo, sd::LongType &posOfNonUnityDim);
SD_LIB_EXPORT SD_HOST_DEVICE bool isRowVector(sd::LongType *shapeInfo);
/**
* shape - input inShape is shape only, not shapeInfo
* returns number of non-unity dimensions in inShape
*/
SD_LIB_EXPORT SD_HOST_DEVICE int numOfNonUnitDims(const int rank, const sd::LongType *inShape);
/**
* Returns whether the
* given shape is a vector or not
* @param shape the shape of the array
* @param rank the rank of the shape
*/
SD_LIB_EXPORT SD_HOST_DEVICE int isMatrix(const sd::LongType *shape, int rank);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isMatrix(const sd::LongType *shapeInfo);
/**
* Returns the shape portion of an information
* buffer
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeOf(sd::LongType *shapeInfo);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *shapeOf(const sd::LongType *shapeInfo);
/**
* Return a copy of a buffer.
* This buffer allocates memory
* that must be freed elsewhere.
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *copyOf(sd::LongType length, T const *toCopy);
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *copyOf(sd::LongType length, T const *toCopy, T *ret);
/**
* Return a copy of a buffer.
* This buffer allocates memory
* that must be freed elsewhere.
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void copyTo(sd::LongType length, T *from, T *to) {
for (sd::LongType i = 0; i < length; i++) {
to[i] = from[i];
}
}
/**
* Returns the length of the
* shape information buffer:
* rank * 2 + 3
* @param rank the rank to get the shape
* info length for
* @return rank * 2 + 4
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType shapeInfoLength(sd::LongType rank);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType shapeInfoLength(sd::LongType *shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType shapeInfoLength(const sd::LongType *shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType shapeInfoByteLength(sd::LongType rank);
SD_LIB_EXPORT SD_HOST_DEVICE size_t shapeInfoByteLength(const sd::LongType *shapeInfo);
SD_LIB_EXPORT SD_HOST_DEVICE size_t shapeInfoByteLength(const sd::LongType *shapeInfo);
/**
* Returns the rank portion of
* an information buffer
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType rank(const sd::LongType *shapeInfo);
/**
* Returns the stride portion of an information
* buffer
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *stride(sd::LongType *buffer);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *stride(const sd::LongType *buffer);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType length(const sd::LongType *shapeInfo);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType length(std::initializer_list<int> &shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType length(std::initializer_list<sd::LongType> &shape);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType &extra(sd::LongType *buffer);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType sizeAt(const sd::LongType *shapeInfo, const sd::LongType dim);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType strideAt(const sd::LongType *shapeInfo, const sd::LongType dim);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setShape(sd::LongType *shapeInfo, sd::LongType *shape);
SD_LIB_EXPORT SD_HOST_DEVICE void setStrideConst(sd::LongType *buffer, const sd::LongType *strides);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE char setOrder(sd::LongType *buffer, char c);
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setExtra(sd::LongType *buffer, sd::LongType extra);
/**
* Returns the ordering
* for this shape information buffer
*/
SD_LIB_EXPORT SD_HOST_DEVICE char order(const sd::LongType *buffer);
/**
* Returns the type
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType type(const sd::LongType *shapeInfo);
/**
* Returns whether
* the given shape info buffer
* represents a scalar shape
*/
SD_LIB_EXPORT SD_HOST_DEVICE int isScalar(const sd::LongType *info);
/**
* Returns whether
* the given shape information
* represents a scalar
* shape or not
*/
SD_LIB_EXPORT SD_HOST_DEVICE int isScalar(volatile ShapeInformation *info);
/**
* Return a copy of this array with the
* given index omitted
*
* @param data the data to copy
* @param indexes the index of the item to remove
* @param dataLength the length of the data array
* @param indexesLength the length of the data array
* @return the new array with the omitted
*
* item
*/
template <typename T1, typename T2>
SD_LIB_EXPORT SD_HOST_DEVICE void removeIndex(T1 const *data, T2 const *indexes, sd::LongType dataLength,
sd::LongType indexesLength, T1 *out);
/**
* Generate an int buffer
* up to the given length
* at the specified increment
*
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *range(int from, int to, int increment);
/**
* Range between from and two with an
* increment of 1
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *range(int from, int to);
/**
* Keep the given indexes
* in the data
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *keep(volatile sd::LongType *data, const sd::LongType *index, int indexLength,
int dataLength);
/**
*
* @param arr1
* @param arr1Length
* @param arr2
* @param arr2Length
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *concat(T const *arr1, sd::LongType const arr1Length, T const *arr2,
sd::LongType const arr2Length);
/**
*
* @param numArrays
* @param numTotalElements
* @param arr
* @param lengths
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_HOST_DEVICE T *concat(int const numArrays, int const numTotalElements, sd::LongType const **arr,
sd::LongType const *lengths);
/**
* Returns a shape buffer
* for the shape information metadata.
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *toShapeBuffer(ShapeInformation *info);
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType *toShapeBuffer(ShapeInformation *info, sd::LongType *ret);
/**
* Returns the prod of the data
* up to the given length
*/
SD_LIB_EXPORT SD_HOST_DEVICE sd::LongType prodLong(const sd::LongType *data, int length);
SD_LIB_EXPORT SD_HOST_DEVICE void printShapeInfo(const sd::LongType *shapeInfo);
// max array is outer for min array, min array is sub-array of max array
// function calculates the coordinates of min array (and saves them into minIdxs) given coordinates of max array
// (already stored in maxIdxs) dimsToExclude - should be sorted in increasing order dimsLen - length of dimsToExclude,
// if not set (= -1), then it is calculated as maxRank - minRank
SD_LIB_EXPORT SD_HOST_DEVICE void maxIndToMinInd(sd::LongType *maxIdxs, sd::LongType *minIdxs,
const sd::LongType *maxShapeInfo, const sd::LongType *minShapeInfo,
const sd::LongType *dimsToExclude = nullptr,
sd::LongType dimsLen = -1);
SD_INLINE SD_HOST SD_LIB_EXPORT sd::LongType tensorsAlongDimension(const sd::LongType* shapeInfo, sd::LongType* dimensions, sd::LongType dimensionLength) {
// Guard against null or empty dimensions
if (dimensions == nullptr || dimensionLength == 0)
THROW_EXCEPTION("Invalid input: dimensions not specified (null or length 0)");
const sd::LongType rank = shape::rank(shapeInfo);
// Single tensor case
if (dimensionLength >= rank || (dimensionLength == 1 && dimensions[0] == SD_MAX_INT))
return 1;
// Handle negative dimensions
for(int i = 0; i < dimensionLength; i++) {
if(dimensions[i] < 0)
dimensions[i] += rank;
}
// Calculate product of shape along specified dimensions
sd::LongType tensorLen = 1;
for(int i = 0; i < dimensionLength; i++) {
tensorLen *= shape::sizeAt(shapeInfo, dimensions[i]);
}
// Handle empty tensor case
if(tensorLen == 0)
return 1;
// Calculate number of tensors
sd::LongType length = shape::length(shapeInfo);
sd::LongType numTensors = length / tensorLen;
if(numTensors >= SD_MAX_INT)
THROW_EXCEPTION("Tensors along dimension cannot be >= Integer.MAX_VALUE");
return numTensors;
}
/**
* Keep the given indexes in the data
* @param data
* @param index
* @param indexLength
* @param dataLength
* @return
*/
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType *keep(volatile sd::LongType *data, const sd::LongType *index, int indexLength, int dataLength) {
sd::LongType *ret = new sd::LongType[indexLength];
int count = 0;
for (int i = 0; i < dataLength; i++) {
int contains = 0;
for (int j = 0; j < indexLength; j++) {
if (i == index[j]) {
contains = 1;
break;
}
}
if (contains) ret[count++] = data[i];
}
return ret;
}
// Optimized version with special cases for common ranks
#define INDEX2COORDS(linear_index, rank, shape, coords) \
do { \
sd::LongType idx = (linear_index); \
if ((rank) == 0) { \
/* Rank 0 is a scalar, set to 0 to avoid undefined behavior */\
(coords)[0] = 0; \
} \
else if ((rank) == 1) { \
/* Rank 1 is simple: the linear index is the coordinate */ \
(coords)[0] = idx; \
} \
else if ((rank) == 2) { \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 3) { \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 4) { \
(coords)[3] = idx % (shape)[3]; \
idx /= (shape)[3]; \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 5) { \
(coords)[4] = idx % (shape)[4]; \
idx /= (shape)[4]; \
(coords)[3] = idx % (shape)[3]; \
idx /= (shape)[3]; \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 6) { \
(coords)[5] = idx % (shape)[5]; \
idx /= (shape)[5]; \
(coords)[4] = idx % (shape)[4]; \
idx /= (shape)[4]; \
(coords)[3] = idx % (shape)[3]; \
idx /= (shape)[3]; \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 7) { \
(coords)[6] = idx % (shape)[6]; \
idx /= (shape)[6]; \
(coords)[5] = idx % (shape)[5]; \
idx /= (shape)[5]; \
(coords)[4] = idx % (shape)[4]; \
idx /= (shape)[4]; \
(coords)[3] = idx % (shape)[3]; \
idx /= (shape)[3]; \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else if ((rank) == 8) { \
(coords)[7] = idx % (shape)[7]; \
idx /= (shape)[7]; \
(coords)[6] = idx % (shape)[6]; \
idx /= (shape)[6]; \
(coords)[5] = idx % (shape)[5]; \
idx /= (shape)[5]; \
(coords)[4] = idx % (shape)[4]; \
idx /= (shape)[4]; \
(coords)[3] = idx % (shape)[3]; \
idx /= (shape)[3]; \
(coords)[2] = idx % (shape)[2]; \
idx /= (shape)[2]; \
(coords)[1] = idx % (shape)[1]; \
(coords)[0] = idx / (shape)[1]; \
} \
else { \
for (sd::LongType i10 = (rank) - 1; i10 > 0; --i10) { /* avoid variable name clashes with normal i */ \
(coords)[i10] = idx % (shape)[i10]; \
idx /= (shape)[i10]; \
} \
(coords)[0] = idx; \
} \
} while (0)
#define COORDS2INDEX(rank, strides, coords, index_var) \
do { \
if ((rank) == 0) { \
/* Rank 0 is a scalar, index is always 0 */ \
(index_var) = 0; \
} \
else if ((rank) == 1) { \
/* Rank 1 is simple: just use the stride */ \
(index_var) = (coords)[0] * (strides)[0]; \
} \
else if ((rank) == 2) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1]; \
} \
else if ((rank) == 3) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2]; \
} \
else if ((rank) == 4) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2] + \
(coords)[3] * (strides)[3]; \
} \
else if ((rank) == 5) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2] + \
(coords)[3] * (strides)[3] + \
(coords)[4] * (strides)[4]; \
} \
else if ((rank) == 6) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2] + \
(coords)[3] * (strides)[3] + \
(coords)[4] * (strides)[4] + \
(coords)[5] * (strides)[5]; \
} \
else if ((rank) == 7) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2] + \
(coords)[3] * (strides)[3] + \
(coords)[4] * (strides)[4] + \
(coords)[5] * (strides)[5] + \
(coords)[6] * (strides)[6]; \
} \
else if ((rank) == 8) { \
(index_var) = (coords)[0] * (strides)[0] + \
(coords)[1] * (strides)[1] + \
(coords)[2] * (strides)[2] + \
(coords)[3] * (strides)[3] + \
(coords)[4] * (strides)[4] + \
(coords)[5] * (strides)[5] + \
(coords)[6] * (strides)[6] + \
(coords)[7] * (strides)[7]; \
} \
else { \
(index_var) = 0; \
for (sd::LongType i10 = 0; i10 < (rank); ++i10) { \
(index_var) += (coords)[i10] * (strides)[i10]; \
} \
} \
} while (0)
/////
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void fill(T *buffer, T value, sd::LongType length) {
for (int e = 0; e < length; e++) buffer[e] = value;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType subArrayIndex(sd::LongType maxIdx, const sd::LongType *maxShapeInfo,
const sd::LongType *minShapeInfo) {
sd::LongType maxIdxs[SD_MAX_RANK];
INDEX2COORDS(maxIdx, shape::rank(maxShapeInfo), shape::shapeOf(maxShapeInfo), maxIdxs);
sd::LongType minIdxs[SD_MAX_RANK];
shape::maxIndToMinInd(maxIdxs, minIdxs, maxShapeInfo, minShapeInfo, nullptr, -1);
sd::LongType minIdx;
COORDS2INDEX(shape::rank(minShapeInfo), shape::stride(minShapeInfo), minIdxs, minIdx);
return minIdx;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool strideDescendingCAscendingF( sd::LongType *shapeBuffer) {
sd::LongType rank = shape::rank(shapeBuffer);
sd::LongType *strides = shape::stride(const_cast<sd::LongType *>(shapeBuffer));
char order = shape::order(shapeBuffer);
if (shape::isRowVector(shapeBuffer) && strides[0] == 1 && strides[1] == 1) return true;
if (order == 'c') {
for (sd::LongType i = 1; i < rank; i++)
if (strides[i - 1] <= strides[i]) return false;
return true;
} else if (order == 'f') {
for (sd::LongType i = 1; i < rank; i++)
if (strides[i - 1] >= strides[i]) return false;
return true;
} else {
return false;
}
}
SD_LIB_EXPORT SD_INLINE SD_HOST int outerArrayOffsets(sd::LongType *maxOffsets, const sd::LongType minIdx,
const sd::LongType *maxShapeInfo, const sd::LongType *minShapeInfo,
sd::LongType *memBuff, const sd::LongType *dimsToExclude) {
const auto rankMin = shape::rank(minShapeInfo);
const auto rankMax = shape::rank(maxShapeInfo);
const auto diff = rankMax - rankMin; // the size of dimsToExclude is equal to diff
sd::LongType *indices = memBuff;
sd::LongType *increment = memBuff + rankMax;
sd::LongType N, minI, maxI;
// calculate min per-dim-indices which corresponds to absolute minIdx index
INDEX2COORDS(minIdx, rankMin, shape::shapeOf(minShapeInfo), indices);
// transform storage indices to contain per-dim max indices, purpose - memory saving
// fill increment array as well
if (dimsToExclude == nullptr) { // means dimsToExclude == {0,1,2,...,diff-1}
for (minI = rankMin - 1, maxI = rankMax - 1; maxI >= diff; --maxI, --minI) {
increment[maxI] = (maxShapeInfo[maxI + 1] == minShapeInfo[minI + 1]) ? 0 : minShapeInfo[minI + 1];
indices[maxI] = indices[minI];
}
for (maxI = 0; maxI < diff; ++maxI) {
increment[maxI] = 1;
indices[maxI] = 0;
}
} else {
for (N = diff - 1, minI = rankMin - 1, maxI = rankMax - 1; maxI >= 0; --maxI) {
if (N >= 0 && dimsToExclude[N] == maxI) {
increment[maxI] = 1;
indices[maxI] = 0;
--N;
} else {
increment[maxI] = (maxShapeInfo[maxI + 1] == minShapeInfo[minI + 1]) ? 0 : minShapeInfo[minI + 1];
indices[maxI] = indices[minI--];
}
}
}
maxI = rankMax - 1;
N = 0;
int step;
sd::LongType offset;
sd::LongType coords[SD_MAX_RANK];
COORDS2INDEX(rankMax, shape::stride(maxShapeInfo), indices, offset);
maxOffsets[N++] = offset;
// nested loops - producing of absolute indices for max array
while (maxI >= 0) {
if (increment[maxI] != 0) {
indices[maxI] += increment[maxI];
if (indices[maxI] >= maxShapeInfo[maxI + 1]) {
indices[maxI] %= increment[maxI]; // restore initial value of indices[maxI]
step = -1;
} else {
COORDS2INDEX(rankMax, shape::stride(maxShapeInfo), indices, offset);
maxOffsets[N++] = offset;
step = rankMax - 1 - maxI;
}
} else if (maxI == rankMax - 1)
step = -1;
maxI += step;
}
return N;
}
// max array is outer for min array, min array is sub-array of max array
// function calculates the coordinates of min array (and saves them into minIdxs) given coordinates of max array
// (already stored in maxIdxs)
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void maxIndToMinInd(sd::LongType *maxIdxs, sd::LongType *minIdxs,
const sd::LongType *maxShapeInfo, const sd::LongType *minShapeInfo,
const sd::LongType *dimsToExclude, sd::LongType dimsLen) {
const auto maxRank = shape::rank(maxShapeInfo);
const auto minRank = shape::rank(minShapeInfo);
if (dimsLen == -1) dimsLen = maxRank - minRank; // if size is not given (= -1) then it is equal to ranks difference
if (maxRank == minRank) {
if (dimsToExclude == nullptr) { // --> means dimsToExclude == {0,1,2,...,dimsLen-1}
for (sd::LongType i = 0; i < maxRank; ++i) {
if (i < dimsLen)
minIdxs[i] = maxIdxs[i];
else {
// FIX: Ensure proper modulo for tiling with dimensions of size 1
sd::LongType dimSize = shape::shapeOf(minShapeInfo)[i];
// Only use modulo for non-zero dimension sizes to avoid division by zero
if (dimSize > 0) {
minIdxs[i] = maxIdxs[i] % dimSize;
} else {
// Handle the case where dimension size is 0 (should be rare)
minIdxs[i] = maxIdxs[i];
}
}
}
} else {
for (sd::LongType i = 0, dim = 0; i < maxRank; ++i) {
if (dim < dimsLen && dimsToExclude[dim] == i) {
minIdxs[i] = maxIdxs[i];
++dim;
continue;
}
// FIX: Ensure proper modulo for tiling with dimensions of size 1
sd::LongType dimSize = shape::shapeOf(minShapeInfo)[i];
// Only use modulo for non-zero dimension sizes to avoid division by zero
if (dimSize > 0) {
minIdxs[i] = maxIdxs[i] % dimSize;
} else {
// Handle the case where dimension size is 0 (should be rare)
minIdxs[i] = maxIdxs[i];
}
}
}
} else {
if (dimsToExclude == nullptr) { // --> means dimsToExclude == {0,1,2,...,dimsLen-1}
for (sd::LongType i = 0; i < minRank; ++i) {
// FIX: Ensure proper modulo for tiling with dimensions of size 1
sd::LongType dimSize = shape::shapeOf(minShapeInfo)[i + 1];
sd::LongType maxIdx = maxIdxs[i + dimsLen];
// Only use modulo for non-zero dimension sizes to avoid division by zero
if (dimSize > 0) {
minIdxs[i] = maxIdx % dimSize;
} else {
// Handle the case where dimension size is 0 (should be rare)
minIdxs[i] = maxIdx;
}
}
} else {
for (sd::LongType minI = 0, maxI = 0, dim = 0; maxI < maxRank; ++maxI) {
if (dim < dimsLen && dimsToExclude[dim] == maxI) {
++dim;
continue;
}
// FIX: Ensure proper modulo for tiling with dimensions of size 1
sd::LongType dimSize = shape::shapeOf(minShapeInfo)[minI + 1];
// Only use modulo for non-zero dimension sizes to avoid division by zero
if (dimSize > 0) {
minIdxs[minI] = maxIdxs[maxI] % dimSize;
} else {
// Handle the case where dimension size is 0 (should be rare)
minIdxs[minI] = maxIdxs[maxI];
}
++minI;
}
}
}
}
// calculate offsets of max-array, these offsets correspond to one minIdx index of min-array which is sub-array of
// max-array maxOffsets - will contain calculated offsets of max-array, buffer for maxOffsets should be allocated
// beforehand dimsToExclude - should be sorted in increasing order memBuff - auxiliary memory buffer (size = 2 *
// max_rank) for coordinates and increments storing, should be allocated beforehand
SD_LIB_EXPORT SD_HOST_DEVICE int outerArrayOffsets(sd::LongType *maxOffsets, const sd::LongType minIdx,
const sd::LongType *maxShapeInfo, const sd::LongType *minShapeInfo,
sd::LongType *memBuff, const sd::LongType *dimsToExclude = nullptr);
// calculates offsets for entities (elements or sub-arrays), shape in context of sub-array means dimensions excluded
// from outer array rank is equal to size of shape
SD_LIB_EXPORT void calcOffsets(const sd::LongType rank, const sd::LongType *shape, const sd::LongType *strides,
sd::LongType *offsets, const char order = 'c');
SD_LIB_EXPORT void calcOffsets(const sd::LongType *shapeInfo, sd::LongType *offsets, const char order = 'c');
SD_LIB_EXPORT SD_HOST_DEVICE void shapeOldScalar(sd::DataType dtype, sd::LongType *const buffer, const char order);
// deduce order and element-wise stride
// if array is scalar or unit length vector then ews = 1 and order is preserved
// if array is common vector then ews = stride of non-unity dimension and order is preserved
// if strides are normal/contiguous then ews = 1 and corresponding order is set, otherwise ews = 0 and order is
// preserved
SD_LIB_EXPORT SD_HOST_DEVICE void checkStridesEwsAndOrder(sd::LongType *shapeInfo, const char proposedOrder,
const sd::LongType numOfNonUnitDims,
const sd::LongType *shapeNoUnities,
const sd::LongType *stridesNoUnities);
SD_LIB_EXPORT SD_HOST_DEVICE void checkStridesEwsAndOrder(sd::LongType *shapeInfo);
/**
* processes whole set of sub-arrays
* evaluates shapeInfo of sub-arrays (all sub-arrays have the same shapeInfo) and their buffer offsets (each sub-array
* has its own unique offset from original this-buffer) arguments: wholeShapeInfo - original shapeInfo of whole array
* numOfSubArrs - number of sub-arrays, size of subArrOffsets is equal to numOfSubArrs
* dimsSize - size of dimsToExclude, if dimsSize = array rank or dimsSize = 0 it means sub-array is whole array, copy of
* wholeShapeInfo and one zero offset will be returned dimsToExclude - MUST BE SORTED, dimensions to evaluate sub-array
* along, i.e. when shape is [2,3,4,5] and dimsToExclude={0,2}, then there will be 8 sub-arrays with shape [3,5]
* subArrShapeInfo - output argument, contains shapeInfo (same for all sub-arrays)
* subArrOffsets - output argument, contains successive sub-arrays offsets from original this-buffer
* keepUnitiesInShape - if false then eliminate unities from sub-array shapeInfo, for example {1,a,1,b} -> {a,b}
*/
SD_LIB_EXPORT SD_HOST_DEVICE void calcSubArrsShapeInfoAndOffsets(
const sd::LongType *wholeShapeInfo, const sd::LongType numOfSubArrs, const sd::LongType dimsSize,
const sd::LongType *dimsToExclude, sd::LongType *subArrShapeInfo, sd::LongType *subArrOffsets,
bool keepUnitiesInShape = false);
/**
* processes only one sub-array, evaluates shapeInfo of sub-array and its buffer offset from original array
* arguments:
* idx - input argument, intervals of indexes which define the sub-array to point on,
* when isStrided = false then idx has form {dim0Start,dim0End, dim1Start,dim1End, ....} and length (2 *
* maxRank) when isStrided = true then idx has form {dim0Start,dim0End,dim0Stride, dim1Start,dim1End,dim1Stride, ....}
* and length (3 * maxRank) when (dimStart == dimEnd) then whole range will be used for current dimension maxShapeInfo -
* input argument, shapeInfo of original array minShapeInfo - output argument, shapeInfo of sub-array to be deduced
* minOffset - output argument, offset of sub-array buffer offsets from original buffer
* keepUnitiesInShape - input argument, if false then eliminate unities from sub-array shapeInfo, for example {1,a,1,b}
* -> {a,b} isStrided - input argument, if true then idx has length (3 * this->rankOf()) and contains additional stride
* numbers which correspond to stride between dimStart and dimEnd, numOfUntiesInMinShape - input argument, number of
* occurrences in idx when (dimEnd - dimStart) = 1
*/
SD_LIB_EXPORT void calcSubArrShapeInfoAndOffset(const sd::LongType *idx, const sd::LongType *maxShapeInfo,
sd::LongType *minShapeInfo, sd::LongType &minOffset,
const bool keepUnitiesInShape = false, const bool isStrided = false,
const sd::LongType numOfUntiesInMinShape = 0);
/**
* for example inShapeInfo is {3, 2,1,4, 4,4,1, 16384,1,99}
* then output shapeNoUnities will contain {2,4, 4,1} - that is only shape and strides, no rank/type/ews/order
* stridesNoUnities will point on strides in shapeNoUnities that is on {4,1}
* returns number of non-unity dimensions in inShapeInfo
* if there is no unities in inShapeInfo, then no copy procedure will be performed and shapeNoUnities/stridesNoUnities
* will point on corresponding places in inShapeInfo
*/
SD_LIB_EXPORT SD_HOST_DEVICE int excludeUnitiesFromShapeInfo(const sd::LongType *inShapeInfo,
sd::LongType *shapeNoUnities,
sd::LongType *stridesNoUnities);
/**
* for example inShapeInfo is {3, 2,1,3,1,4, 12,12,4,4,1, 16384,1,99}, dimsToExclude(points on unity dimensions) =
* {1,3}, dimsSize = 2 then outShapeInfo will contain {3, 2,3,4, 12,4,1, 16384,1,99}
*/
SD_LIB_EXPORT SD_HOST_DEVICE void excludeUnitiesFromShapeInfo(const sd::LongType *inShapeInfo,
const sd::LongType *dimsToExclude,
const sd::LongType dimsSize, sd::LongType *outShapeInfo);
/**
* get stride over contiguous axis (contiguous axis must have stride = 1)
* for example when inShapeInfo is {4, 2,5,4,3, 60,1,5,20, 16384,0,99} then output is 5 (that is smallest stride in
* inShapeInfo except those equal to 1)
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool strideEquals(int const shape1Rank, sd::LongType const *shape1, int const shape2Rank,
sd::LongType const *shape2) {
if (shape1Rank != shape2Rank) return false;
// rank not equals
for (int i = 0; i < shape1Rank; i++) {
if (shape1[i] != shape2[i]) return false;
}
return true;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool strideEquals(sd::LongType const *shapeInfo1, sd::LongType const *shapeInfo2) {
return strideEquals(rank(shapeInfo1), stride(shapeInfo1), rank(shapeInfo2), stride(shapeInfo2));
}
/**
* Computes the standard packed array strides for a given shape.
*
* @param shape the shape of a matrix:
* @param startNum the start number for the strides
* @return the strides for a matrix of n dimensions
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStridesFortran(sd::LongType const *shape, sd::LongType rank, sd::LongType startNum) {
sd::LongType dimensions = rank;
sd::LongType *stride = new sd::LongType[dimensions];
sd::LongType st = startNum;
for (sd::LongType j = 0; j < rank; j++) {
stride[j] = st;
st *= shape[j];
}
return stride;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStridesFortran(sd::LongType const *shape, int rank, int startNum, sd::LongType *ret) {
sd::LongType st = startNum;
for (sd::LongType j = 0; j < rank; j++) {
ret[j] = st;
st *= shape[j];
}
return ret;
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void excludeUnitiesFromShapeInfo(const sd::LongType *inShapeInfo, const sd::LongType *dimsToExclude,
const sd::LongType dimsSize, sd::LongType *outShapeInfo) {
outShapeInfo[0] = inShapeInfo[0] - dimsSize;
for (sd::LongType j = 0, k = 0, i = 0; i < inShapeInfo[0]; ++i) {
if (j < dimsSize && i == dimsToExclude[j]) {
++j;
continue;
}
shapeOf(outShapeInfo)[k] = shapeOf(inShapeInfo)[i];
stride(outShapeInfo)[k++] = stride(inShapeInfo)[i];
}
outShapeInfo[2 * outShapeInfo[0] + 1] = 0;
sd::ArrayOptions::copyDataType(outShapeInfo, inShapeInfo); // type
outShapeInfo[2 * outShapeInfo[0] + 3] = order(inShapeInfo); // order
}
/**
* Returns a shape buffer
* for the shape information metadata.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType *toShapeBuffer(ShapeInformation *info) {
auto ret = new sd::LongType[shapeInfoLength(info->rank)];
int count = 1;
int rank = info->rank;
ret[0] = info->rank;
for (int i = 0; i < rank; i++) {
ret[count++] = info->shape[i];
}
for (int i = 0; i < rank; i++) {
ret[count++] = info->stride[i];
}
ret[count++] = info->offset;
ret[count++] = info->elementWiseStride;
ret[count] = info->order;
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST const char *shapeInfoString(const sd::LongType *shapeInfo) {
if (shapeInfo == nullptr) return "";
std::string ret;
if (shapeInfo != nullptr) {
if (shapeInfo[0] > 32 || shapeInfo[0] < 0)
THROW_EXCEPTION("Input shape buffer is corrupt. First rank is < 0 or greater than the max rank of 32.");
}
sd::LongType rank = shape::rank(shapeInfo);
std::stringstream ss;
if (rank == 0) {
ss << "Rank " << rank << "\n";
ss << "Buffer is:";
for (int i = 0; i < shapeInfoLength(rank); i++) {
ss << " " << shapeInfo[i] << " ";
}
auto flags = sd::ArrayOptions::enumerateSetFlags(shapeInfo);
ss << flags;
ss << "\n";
ret += ss.str();
return (new std::string(ret))->c_str();
}
sd::LongType *shape = shapeOf(shapeInfo);
ss << "Rank " << rank << "\n";
ss << "Shape:\n";
for (int i = 0; i < rank; i++) {
ss << " " << (sd::LongType)shape[i] << " ";
}
ss << "\n";
sd::LongType *stride = shape::stride(shapeInfo);
ss << "Stride:\n";
for (int i = 0; i < rank; i++) {
ss << " " << (sd::LongType)stride[i] << " ";
}
ss << "\n";
ss << "Order " << order(shapeInfo) << "\n";
ss << "Buffer is:";
for (int i = 0; i < shapeInfoLength(rank); i++) {
ss << " " << (sd::LongType)shapeInfo[i] << " ";
}
auto flags = sd::ArrayOptions::enumerateSetFlags(shapeInfo);
ss << flags;
ss << "\n";
ret += ss.str();
return (new std::string(ret))->c_str();
}
SD_LIB_EXPORT SD_INLINE SD_HOST void printShapeInfo(const sd::LongType *shapeInfo) {
if (shapeInfo == nullptr) return;
if (shapeInfo != nullptr) {
if (shapeInfo[0] > 32 || shapeInfo[0] < 0)
THROW_EXCEPTION("Input shape buffer is corrupt. First rank is < 0 or greater than the max rank of 32.");
}
sd::LongType rank = shape::rank(shapeInfo);
if (rank == 0) {
printf("Rank %lld\n", rank);
printf("Buffer is:");
for (int i = 0; i < shapeInfoLength(rank); i++) {
printf(" %lld ", shapeInfo[i]);
}
auto flags = sd::ArrayOptions::enumerateSetFlags(shapeInfo);
printf("%s", flags);
printf("\n");
return;
}
sd::LongType *shape = shapeOf(shapeInfo);
printf("Rank %lld\n", rank);
printf("Shape:\n");
for (int i = 0; i < rank; i++) {
printf(" %lld ", (sd::LongType)shape[i]);
}
printf("\n");
sd::LongType *stride = shape::stride(shapeInfo);
printf("Stride:\n");
for (int i = 0; i < rank; i++) {
printf(" %lld ", (sd::LongType)stride[i]);
}
printf("\n");
printf("Order %c\n", order(shapeInfo));
printf("Buffer is:");
for (int i = 0; i < shapeInfoLength(rank); i++) {
printf(" %lld ", (sd::LongType)shapeInfo[i]);
}
auto flags = sd::ArrayOptions::enumerateSetFlags(shapeInfo);
printf("%s", flags);
printf("\n");
}
SD_LIB_EXPORT SD_INLINE SD_HOST void printShapeInfoLinear(const sd::LongType *shapeInfo) {
sd::LongType rank = shape::rank(shapeInfo);
sd::LongType lim = shapeInfoLength(rank);
printf("ShapeInfo: [");
for (sd::LongType i = 0; i < lim; i++) {
printf("%lld", shapeInfo[i]);
if (i < lim - 1) {
printf(", ");
}
}
printf("]\n");
#ifndef __CUDA_ARCH__
fflush(stdout);
#endif
}
SD_LIB_EXPORT SD_INLINE SD_HOST void printShapeInfoLinear(const char *msg, const sd::LongType *shapeInfo) {
int rank = shape::rank(shapeInfo);
int lim = shapeInfoLength(rank);
printf("%s : [", msg);
for (int i = 0; i < lim; i++) {
printf("%lld", shapeInfo[i]);
if (i < lim - 1) {
printf(", ");
}
}
printf("]\n");
#ifndef __CUDACC__
fflush(stdout);
#endif
}
SD_LIB_EXPORT SD_INLINE SD_HOST void transposeInplace(sd::LongType *shapeBuffer) {
int rank = shape::rank(shapeBuffer);
sd::LongType *shape = shapeOf(shapeBuffer);
sd::LongType *strides = stride(shapeBuffer);
// swap shape
for (int e = 0; e < rank / 2; e++) {
int idx1 = rank - e - 1;
int idx2 = e;
int tmp = shape[idx2];
shape[idx2] = shape[idx1];
shape[idx1] = tmp;
}
// swap strides
for (int e = 0; e < rank / 2; e++) {
int idx1 = rank - e - 1;
int idx2 = e;
int tmp = strides[idx2];
strides[idx2] = strides[idx1];
strides[idx1] = tmp;
}
if (order(shapeBuffer) == 'c')
shapeBuffer[shapeInfoLength(shapeBuffer) - 1] = 102;
else
shapeBuffer[shapeInfoLength(shapeBuffer) - 1] = 99;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *shapeBufferOfNpy(sd::LongType rank, sd::LongType *shape, bool fortranOrder) {
if (fortranOrder) {
sd::LongType *shapeBufferRet = shapeBufferFortran(rank, sd::FLOAT32, (sd::LongType *)shape);
return shapeBufferRet;
} else {
sd::LongType *newShape = new sd::LongType[rank];
for (int i = 0; i < rank; i++) {
newShape[i] = shape[i];
}
sd::LongType *shapeBufferRet = shapeBuffer(rank, sd::FLOAT32, newShape);
delete[] newShape;
return shapeBufferRet;
}
}
SD_INLINE SD_HOST sd::LongType *shapeBufferOfNpy(cnpy::NpyArray arr) {
return shapeBufferOfNpy(arr.shape.size(), (sd::LongType *)arr.shape.data(), arr.fortranOrder);
}
/**
* Computes the standard packed array strides for a given shape.
*
* @param shape the shape of a matrix:
* @param startNum the start number for the strides
* @return the strides for a matrix of n dimensions
*/
SD_LIB_EXPORT SD_HOST_DEVICE SD_INLINE sd::LongType *calcStrides(sd::LongType const *shape, sd::LongType rank, sd::LongType startNum) {
sd::LongType *stride = new sd::LongType[rank];
if (rank <= 1) {
if (rank < 0) {
delete[] stride;
THROW_EXCEPTION("calcStrides: Invalid rank, must be >= 0");
}
stride[0] = 1;
return stride;
}
// Validate shape array to prevent buffer overflow
for (sd::LongType i = 0; i < rank; i++) {
if (shape[i] < 0) {
delete[] stride;
std::string errorMessage = "calcStrides: Invalid shape dimension at index ";
errorMessage += std::to_string(i);
errorMessage += ", all dimensions must be >= 0, but got: ";
errorMessage += std::to_string(shape[i]);
THROW_EXCEPTION(errorMessage.c_str());
}
}
sd::LongType st = startNum;
for (sd::LongType j = rank - 1; j >= 0; j--) {
stride[j] = st;
st *= shape[j];
}
return stride;
}
SD_INLINE SD_HOST_DEVICE sd::LongType *calcStrides(sd::LongType const *shape, sd::LongType rank, sd::LongType startNum,
sd::LongType *ret) {
if (rank <= 1) {
ret[0] = 1;
return ret;
}
sd::LongType st = startNum;
for (sd::LongType j = rank - 1; j >= 0; j--) {
ret[j] = st;
st *= shape[j];
}
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStrides(sd::LongType const *shape, int rank, sd::LongType *ret) {
return calcStrides(shape, rank, 1, ret);
}
// function calculates absolute offset of min array, min is sub-array of max, offset to be returned corresponds to
// maxIdx of max array dimsToExclude - should be sorted in increasing order
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType subArrayOffset(const sd::LongType maxIdx, const sd::LongType *maxShapeInfo,
const sd::LongType *minShapeInfo,
const sd::LongType *dimsToExclude = nullptr,
const sd::LongType dimsLen = -1);
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType subArrayOffset(const sd::LongType maxIdx, const sd::LongType *maxShapeInfo,
const sd::LongType *minShapeInfo, const sd::LongType *dimsToExclude,
const sd::LongType dimsLen) {
sd::LongType maxIdxs[SD_MAX_RANK];
INDEX2COORDS(maxIdx, shape::rank(maxShapeInfo), shapeOf(maxShapeInfo), maxIdxs);
sd::LongType minIdxs[SD_MAX_RANK];
maxIndToMinInd(maxIdxs, minIdxs, maxShapeInfo, minShapeInfo, dimsToExclude, dimsLen);
sd::LongType ret;
COORDS2INDEX(shape::rank(minShapeInfo), shape::stride(minShapeInfo), minIdxs, ret);
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST const char *shapeToString(const sd::LongType *shapeInfo, const char *message) {
if (shapeInfo == nullptr) {
auto ret = new std::string("Shape info is empty");
return ret->c_str();
}
std::string shapeInfoString;
shapeInfoString += message;
shapeInfoString += " ";
sd::LongType rank = shape::rank(shapeInfo);
if (rank == 0) {
shapeInfoString += "Rank: ";
shapeInfoString += std::to_string(rank);
auto ret = new std::string(shapeInfoString.c_str());
return ret->c_str();
}
shapeInfoString += " Rank ";
shapeInfoString += std::to_string(rank);
sd::LongType *shape = shapeOf(shapeInfo);
shapeInfoString += " Shape: ";
for (int i = 0; i < rank; i++) {
shapeInfoString += std::to_string(shape[i]);
shapeInfoString += " ";
}
shapeInfoString += " ";
sd::LongType *stride = shape::stride(shapeInfo);
shapeInfoString += (" Stride: ");
for (int i = 0; i < rank; i++) {
shapeInfoString += std::to_string(stride[i]);
shapeInfoString += " ";
}
shapeInfoString += (" ");
shapeInfoString += ("Order: ");
shapeInfoString += order(shapeInfo);
shapeInfoString += " ";
shapeInfoString += " Flags extra value: ";
shapeInfoString += std::to_string(extra(const_cast<sd::LongType *>(shapeInfo)));
shapeInfoString += " ";
shapeInfoString += ("Buffer is:");
for (int i = 0; i < shapeInfoLength(rank); i++) {
shapeInfoString += std::to_string(shapeInfo[i]);
shapeInfoString += " ";
}
shapeInfoString += (" ");
auto ret = new std::string(shapeInfoString.c_str());
return ret->c_str();
}
/**
* Computes the standard packed array strides for a given shape.
*
* @param shape the shape of a matrix:
* @param startNum the start number for the strides
* @return the strides for a matrix of n dimensions
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStridesFortran(sd::LongType const *shape, sd::LongType rank) {
return calcStridesFortran(shape, rank, 1);
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStridesFortran(sd::LongType const *shape, int rank, sd::LongType *ret) {
return calcStridesFortran(shape, rank, 1, ret);
}
/**
* Computes the standard packed array strides for a given shape.
*
* @param shape the shape of a matrix:
* @param startNum the start number for the strides
* @return the strides for a matrix of n dimensions
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *calcStrides(sd::LongType const *shape, sd::LongType rank) {
return calcStrides(shape, rank, 1);
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool shapeEquals(const int shape1Rank, const sd::LongType *shape1, const int shape2Rank,
const sd::LongType *shape2) {
if (shape1Rank != shape2Rank) return false;
// rank not equals
for (int i = 0; i < shape1Rank; i++) {
if (shape1[i] != shape2[i]) return false;
}
return true;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool shapeEquals(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2) {
return shapeEquals(rank(shapeInfo1), shapeOf(const_cast<sd::LongType *>(shapeInfo1)), rank(shapeInfo2),
shapeOf(const_cast<sd::LongType *>(shapeInfo2)));
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool shapeEquals(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2,
const sd::LongType *shapeInfo3) {
return shapeEquals(shapeInfo1, shapeInfo2) && shapeEquals(shapeInfo1, shapeInfo3);
}
#if defined(__CUDACC__)
/**
* BEWARE: THIS METHOD DOES NOT CHECKS ALLOCATION BOUNDARIES
*/
SD_DEVICE SD_INLINE sd::LongType *cuMalloc(sd::LongType *buffer, long size) {
sd::LongType *ret = buffer;
ret += (threadIdx.x * size);
return ret;
}
#endif
//////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/**
* Get the ordering for the device
* @param length
* @param shape
* @param stride
* @param elementStride
* @return
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE char getOrder(int length, sd::LongType *shape, sd::LongType *stride, int elementStride) {
sd::LongType sd = 1;
int dim = -1;
int i = -1;
int cContiguous = 1;
int isFortran = 1;
for (i = length - 1; i >= 0; --i) {
dim = shape[i];
if (stride[i] != sd) {
cContiguous = 0;
break;
}
/* contiguous, if it got this far */
if (dim == 0) {
break;
}
sd *= dim;
}
/* check if fortran contiguous */
sd = elementStride;
for (i = 0; i < length; ++i) {
dim = shape[i];
if (stride[i] != sd) {
isFortran = 0;
}
if (dim == 0) {
break;
}
sd *= dim;
}
if (isFortran && cContiguous)
return 'a';
else if (isFortran && !cContiguous)
return 'f';
else if (!isFortran && !cContiguous)
return 'c';
else
return 'c';
}
/**
* Ensure that every value in the re arrange
* array is unique
* @param arr
* @param shape
* @param arrLength
* @param shapeLength
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int checkArrangeArray(T *arr, int arrLength, int shapeLength) {
if (arrLength != shapeLength) return -1;
for (int i = 0; i < arrLength; i++) {
if (arr[i] >= arrLength || arr[i] < 0) return -1;
}
for (int i = 0; i < arrLength; i++) {
for (int j = 0; j < arrLength; j++) {
if (i != j && arr[i] == arr[j]) return -1;
}
}
return 1;
}
/**
* Returns whether the
* given shape is a vector or not
* @param shape the shape of the array
* @param rank the rank of the shape
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isVector(sd::LongType const *shape, int rank) {
if (rank == 0) return 0;
if (rank == 1) return 1;
if (rank > 2) return 0;
if (rank <= 2) {
if (shape[0] == 1 || shape[1] == 1) return 1;
}
return 0;
}
SD_INLINE SD_HOST_DEVICE bool isLikeVector(sd::LongType const *shapeInfo, int &posOfNonUnityDim) {
int numOfNonUnity = 0;
for (int i = 1; i <= shapeInfo[0]; ++i) {
if (shapeInfo[i] != 1) {
++numOfNonUnity;
posOfNonUnityDim = i - 1;
}
}
return numOfNonUnity == 1 && shapeInfo[0] > 2;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isCommonVector(const sd::LongType *shapeInfo, sd::LongType &posOfNonUnityDim) {
if (rank(shapeInfo) > 0 && length(shapeInfo) == 1) {
posOfNonUnityDim = -1;
return true;
}
int numOfNonUnity = 0;
for (int i = 1; i <= shapeInfo[0]; ++i) {
if (shapeInfo[i] != 1) {
++numOfNonUnity;
posOfNonUnityDim = i - 1;
}
}
return numOfNonUnity == 1;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *detachShape(sd::LongType *originalShape) {
sd::LongType *newShape = new sd::LongType[shapeInfoLength(originalShape)];
memcpy(newShape, originalShape, shapeInfoByteLength(originalShape));
return newShape;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isVector(sd::LongType *shapeInfo) {
return isVector(shapeOf(const_cast<sd::LongType *>(shapeInfo)), rank(shapeInfo));
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isRowVector(sd::LongType *shapeInfo) {
bool isVector = shape::isVector(shapeInfo) == 1;
bool shapeFirstOne = shapeOf(const_cast<sd::LongType *>(shapeInfo))[0] == 1;
return isVector && shapeFirstOne;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isColumnVector( sd::LongType *shapeInfo) {
bool isVector = shape::isVector(shapeInfo) == 1;
bool shapeFirstOne = shapeOf(shapeInfo)[0] == 1;
return isVector && !shapeFirstOne;
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int numOfNonUnitDims(const int rank, const sd::LongType *inShape) {
int num = 0;
for (sd::LongType i = 0; i < rank; ++i)
if (inShape[i] != 1) ++num;
return num;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int oneDimEqualToLength(sd::LongType *shape, int rank) {
for (int i = 0; i < rank; i++) {
if (shape[i] == prodLong(shape, rank)) return 1;
}
return 0;
}
/**
* Returns whether the
* given shape is a vector or not
* @param shape the shape of the array
* @param rank the rank of the shape
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isMatrix(const sd::LongType *shape, int rank) {
if (rank > 2) return 0;
if (rank <= 2) {
if (shape[0] == 1 || shape[1] == 1) return 0;
}
return 1;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isMatrix(const sd::LongType *shapeInfo) {
return isMatrix(shapeOf(shapeInfo), rank(shapeInfo));
}
/**
* Returns the shape portion of an information
* buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *shapeOf(sd::LongType *shapeInfo) { return shapeInfo + 1; }
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setShape(sd::LongType *shapeInfo, sd::LongType *shape) {
sd::LongType rank = shape::rank(shapeInfo);
for (int i = 0; i < rank; i++) {
shapeInfo[i + 1] = shape[i];
if(shape[i] < 0) {
std::string errorMessage;
errorMessage += "Shape value is invalid: ";
errorMessage += std::to_string(shape[i]);
errorMessage += " at index ";
errorMessage += std::to_string(i);
errorMessage += " in shape ";
errorMessage += std::to_string(rank);
errorMessage += "\n";
THROW_EXCEPTION(errorMessage.c_str());
}
}
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *shapeOf(const sd::LongType *shapeInfo) {
return shapeOf(const_cast<sd::LongType *>(shapeInfo));
}
/**
* Return a copy of a buffer.
* This buffer allocates memory
* that must be freed elsewhere.
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T *copyOf(sd::LongType length, T const *toCopy) {
T *ret = new T[length];
return copyOf(length, toCopy, ret);
}
/**
* Returns the length of the
* shape information buffer:
* rank * 2 + 4
* A shape buffer contains:
* rank
* shape elements
* stride elements
* flags such as array type like empty and data type
* element wise stride
* offset
* ordering
* @param rank the rank to get the shape
* info length for
* @return rank * 2 + 4
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType shapeInfoLength(sd::LongType rank) {
if(rank > SD_MAX_RANK) {
#ifndef __CUDACC__
std::string errorMessage;
errorMessage += "Rank is too high: ";
errorMessage += std::to_string(rank);
errorMessage += "\n";
THROW_EXCEPTION(errorMessage.c_str());
#else
printf("Rank is too high: %lld\n", rank);
return -1;
#endif
}
// rank takes up 1 element + usual elements
if (rank < 1)
// shape of 0 (scalar) even has elements for shape and stride
return 1 * 2 + 4;
// FIXME magic numbers
return rank * 2 + 4;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType shapeInfoLength(sd::LongType *shape) {
return shapeInfoLength(shape[0]);
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType shapeInfoLength(const sd::LongType *shape) {
return shapeInfoLength(static_cast<sd::LongType>(shape[0]));
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType shapeInfoByteLength(sd::LongType rank) {
// scalar formula isn't correct
if (rank == 0) return 6 * sizeof(sd::LongType);
// FIXME magic numbers
return (rank * 2 + 4) * sizeof(sd::LongType);
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE size_t shapeInfoByteLength(const sd::LongType *shapeInfo) {
// FIXME magic numbers
return shapeInfoByteLength(shapeInfo[0]);
}
/**
* Returns the rank portion of
* an information buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType rank(const sd::LongType *buffer) {
return buffer[0];
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setStride(sd::LongType *buffer, sd::LongType *strides) {
auto stridesRet = buffer + (1 + rank(buffer));
int rank = shape::rank(buffer);
if (rank < 1) {
buffer[2] = 0;
return;
}
for (int i = 0; i < rank; i++) {
stridesRet[i] = strides[i];
}
}
/**
* Returns the stride portion of an information
* buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *stride(sd::LongType *buffer) { return buffer + (1 + rank(buffer)); }
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *stride(const sd::LongType *buffer) {
return stride(const_cast<sd::LongType *>(buffer));
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType length(std::initializer_list<int> &shape) {
sd::LongType ret = 1;
for (auto v : shape) {
ret *= v;
}
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType length(std::initializer_list<sd::LongType> &shape) {
sd::LongType ret = 1;
for (auto v : shape) {
ret *= v;
}
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setExtra(sd::LongType *buffer, sd::LongType extra) {
buffer[sd::ArrayOptions::extraIndex(buffer)] = extra;
}
/**
* Compute the length of the given shape
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType length(const sd::LongType *shapeInfo) {
const sd::LongType rank = shape::rank(shapeInfo);
if (rank == 0) {
if (isEmptyConst(shapeInfo)) return 0L;
return 1L;
}
if (rank == 1) return shapeInfo[1];
return prodLong(shapeOf(const_cast<sd::LongType *>(shapeInfo)), rank);
}
/**
* Returns the ordering
* for this shape information buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE char order(const sd::LongType *buffer) {
// order doesn't matter for scalars
if (rank(buffer) < 1) return 'c';
sd::LongType len = shapeInfoLength(buffer[0]);
auto longValidation = buffer[len - 1];
if(longValidation != 99 && longValidation != 102) {
std::string errorMessage;
errorMessage += "Invalid order from shape descriptor: ";
errorMessage += std::to_string(longValidation);
errorMessage += " Order should either be 99 (c) or 102 (f)";
THROW_EXCEPTION(errorMessage.c_str());
}
char ret = static_cast<char>(buffer[len - 1]);
if (ret != 'c' && ret != 'f') {
std::string errorMessage;
errorMessage += "Invalid order from shape descriptor: ";
errorMessage += std::to_string(ret);
errorMessage += " for buffer ";
errorMessage += shapeToString(buffer, "Buffer was:");
THROW_EXCEPTION(errorMessage.c_str());
}
return ret;
}
/**
* Returns the ordering
* for this shape information buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE char setOrder(sd::LongType *buffer, char c) {
if(shape::rank(buffer) < 1) {
buffer[5] = 'c';
return 'c';
}
// FIXME magic numbers
if (length(buffer) > 1 && c != 'c' && c != 'f') {
std::string errorMessage;
errorMessage += "Invalid order from descriptor: ";
errorMessage += std::to_string(c);
THROW_EXCEPTION(errorMessage.c_str());
}
sd::LongType len = shapeInfoLength(buffer[0]);
buffer[len - 1] = static_cast<sd::LongType>(c);
return c;
}
/**
* Returns type
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType type(const sd::LongType *shapeInfo) {
if (shapeInfo[0] < 1) return shapeInfo[2 * 1 + 1];
return shapeInfo[2 * shapeInfo[0] + 1];
}
/**
* Returns the element wise stride for this information
* buffer
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType elementWiseStride(const sd::LongType *buffer) {
return buffer[shapeInfoLength(buffer[0]) - 2];
}
/**
* Returns whether
* the given shape info buffer
* represents a scalar shape
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isScalar(const sd::LongType *info) {
if (isEmptyConst(info)) return 0;
const sd::LongType rank = shape::rank(info);
if (rank == 0 || shape::length(info) == 0 && !shape::isEmptyConst(info)) return 1;
return 0;
}
/**
* Returns whether
* the given shape information
* represents a scalar
* shape or not
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE int isScalar(volatile ShapeInformation *info) {
const sd::LongType rank = info->rank;
if (rank > 2) return 0;
if (rank == 1) return info->shape[0] == 1;
if (rank == 2) return info->shape[0] == 1 && info->shape[1] == 1;
return 0;
}
/**
* Return a copy of this array with the
* given index omitted
*
* @param data the data to copy
* @param indexes the index of the item to remove
* @param dataLength the length of the data array
* @param indexesLength the length of the data array
* @return the new array with the omitted
*
* item
*/
template <typename T1, typename T2>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T1 *removeIndex(T1 const *data, T2 const *indexes, sd::LongType dataLength,
sd::LongType indexesLength) {
auto lengthOfArr = dataLength - indexesLength;
if (lengthOfArr < 0) {
printf("Remove index call created a <= 0 length array. This was likely not intended.");
}
auto ret = new T1[lengthOfArr];
memset(ret, 0, sizeof(T1) * lengthOfArr);
removeIndex<T1, T2>(data, indexes, dataLength, indexesLength, ret);
return ret;
}
/**
* Computes the offset for accessing
* a global element given the shape information
* and the offset to be read.
*/
#if defined(__CUDACC__)
SD_LIB_EXPORT SD_INLINE SD_DEVICE int tadOffset(ShapeInformation *xInfo, int offset) {
return offset + threadIdx.x * xInfo->elementWiseStride;
}
#else
SD_LIB_EXPORT SD_INLINE SD_HOST int tadOffset(ShapeInformation *xInfo, int offset) {
return 0;
}
#endif
/**
* Returns a shape
* forces the given length to be 2.
* @param shape the shape to modify
* @param dimension the dimension (row or column)
* for the shape to be returned as
* @return the new shape
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *ensureVectorShape(sd::LongType *shape, int dimension) {
sd::LongType *ret = new sd::LongType[2];
if (dimension == 0) {
ret[0] = 1;
ret[1] = shape[0];
} else {
ret[0] = shape[0];
ret[1] = 1;
}
return ret;
}
/**
* This method does STRICT comparison for two shape buffers
*
* @param shape
* @return
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool equalsStrict(const sd::LongType *shapeA, const sd::LongType *shapeB) {
if (shapeA[0] != shapeB[0]) return false;
if (shapeA[0] == 0) return true;
// we do full comparison here
int length = shapeInfoLength(shapeA[0]);
for (int e = 1; e < length; e++)
if (shapeA[e] != shapeB[e]) return false;
return true;
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool haveSameShapeAndStrides(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2) {
if (shapeInfo1[0] != shapeInfo2[0]) return false;
if (shapeInfo1[0] == 0) return true;
for (sd::LongType e = 0; e < rank(shapeInfo1); ++e)
if (shapeOf(shapeInfo1)[e] != shapeOf(shapeInfo2)[e] || stride(shapeInfo1)[e] != stride(shapeInfo2)[e])
return false;
return true;
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool haveSameShapeAndStrides(const sd::LongType *shapeInfo1, const sd::LongType *shapeInfo2,
const sd::LongType *shapeInfo3) {
return haveSameShapeAndStrides(shapeInfo1, shapeInfo2) && haveSameShapeAndStrides(shapeInfo1, shapeInfo3);
}
#ifndef __JAVACPP_HACK__
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType sizeAt(const sd::LongType *shapeInfo, const sd::LongType dim) {
sd::LongType inputDim = dim;
if(inputDim < 0)
inputDim += rank(shapeInfo);
if (0 == shape::rank(shapeInfo)) return 1;
if (inputDim >= 0) return shapeInfo[1 + inputDim];
return shapeInfo[1 + (rank(shapeInfo) + inputDim)];
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType strideAt(const sd::LongType *shapeInfo, const sd::LongType dim) {
sd::LongType inputDim = dim;
if(inputDim < 0)
inputDim += rank(shapeInfo);
if (0 == rank(shapeInfo)) return 1;
if (dim >= 0) return shapeInfo[1 + rank(shapeInfo) + inputDim];
return shapeInfo[1 + 2 * rank(shapeInfo) + inputDim];
}
#endif
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool equalsTypesAndShapesSoft(const sd::LongType *shapeA, const sd::LongType *shapeB) {
return equalsSoft(shapeA, shapeB) && shapeA[shapeInfoLength(shapeA) - 3] == shapeB[shapeInfoLength(shapeB) - 3];
}
/**
* Generate an int buffer
* up to the given length
* at the specified increment
*
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T *range(int from, int to, int increment) {
int diff = sd::math::sd_abs<int,int>(from - to);
int retLength = diff / increment;
T *ret;
if (diff / increment < 1)
ret = new T[1];
else
ret = new T[diff / increment];
if (from < to) {
int count = 0;
for (int i = from; i < to; i += increment) {
if (count >= retLength) break;
ret[count++] = i;
}
} else if (from > to) {
int count = 0;
for (int i = from - 1; i >= to; i -= increment) {
if (count >= retLength) break;
ret[count++] = i;
}
}
return ret;
}
/**
* Generate a range
* beginning at from and ending at to
* incrementing by 1
* @param from the start
* @param to the end
* @return the int array starting at from and ending at to
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T *range(int from, int to) {
return range<T>(from, to, 1);
}
/**
*
* @param arr1
* @param arr1Length
* @param arr2
* @param arr2Length
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T *concat(T const *arr1, sd::LongType const arr1Length, T const *arr2,
sd::LongType const arr2Length) {
T *ret = new T[arr1Length + arr2Length];
std::memcpy(ret, arr1, arr1Length * sizeof(T));
std::memcpy(ret + arr1Length, arr2, arr2Length * sizeof(T));
return ret;
}
/**
*
* @param numArrays
* @param numTotalElements
* @param arr
* @param lengths
* @return
*/
template <typename T>
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE T *concat(sd::LongType const numArrays, sd::LongType const numTotalElements, T const **arr,
sd::LongType const *lengths) {
T *ret = new T[numTotalElements];
sd::LongType count = 0;
for (sd::LongType i = 0; i < numArrays; i++) {
for (sd::LongType j = 0; j < lengths[i]; j++) {
ret[count++] = arr[i][j];
}
}
return ret;
}
#if defined(__CUDACC__)
/**
* Computes the offset for accessing
* a global element given the shape information
* and the offset to be read.
*/
SD_LIB_EXPORT SD_INLINE SD_DEVICE int tadOffset(sd::LongType *xInfo, int offset) {
return offset + threadIdx.x * elementWiseStride(xInfo);
}
#else
SD_LIB_EXPORT SD_INLINE SD_HOST int tadOffset(sd::LongType *xInfo, int offset) {
return 0;
}
#endif
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *createScalarShapeInfo() {
auto shape = new sd::LongType[1];
shape[0] = 1;
auto stride = new sd::LongType[1];
stride[0] = 1;
auto shapeInformation2 = new ShapeInformation();
shapeInformation2->rank = 1;
shapeInformation2->offset = 0;
shapeInformation2->stride = stride;
shapeInformation2->shape = shape;
shapeInformation2->elementWiseStride = 1;
shapeInformation2->order = 99;
sd::LongType *ret = toShapeBuffer(shapeInformation2);
delete shapeInformation2;
delete[] shape;
delete[] stride;
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType *createScalarShapeInfo(sd::LongType *ret) {
ret[0] = 2;
ret[1] = 1;
ret[2] = 1;
ret[3] = 1;
ret[4] = 1;
ret[5] = 0;
ret[6] = 1;
ret[7] = 99;
return ret;
}
/**
* Returns the prod of the data
* up to the given length
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE sd::LongType prodLong(const sd::LongType *data, int length) {
sd::LongType prod = 1;
for (int i = 0; i < length; i++) {
prod *= data[i];
}
return prod;
}
#if defined(__CUDACC__)
SD_DEVICE SD_LIB_EXPORT SD_INLINE void sweepShapeInfoBuffer(sd::LongType *shapeInfoBuffer, sd::LongType *targetBuffer) {
// we read first element, to find out length of our shapeInfoBuffer
int rank = shapeInfoBuffer[0];
int len = shape::shapeInfoLength(rank);
for (int i = threadIdx.x; i < len; i += blockDim.x) targetBuffer[i] = shapeInfoBuffer[i];
}
#else
SD_HOST SD_LIB_EXPORT SD_INLINE void sweepShapeInfoBuffer(sd::LongType *shapeInfoBuffer, sd::LongType *targetBuffer) {
}
#endif
// this function checks the consistence of dimensions with array rank (negative dimensions, too large dimensions, too
// big number of dimensions) also it sorts input array of dimensions, this operation is also necessary for creating TAD
// object
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void checkDimensions(const sd::LongType rank, std::vector<sd::LongType> *dimensions) {
int dimSize = dimensions->size();
if (dimSize == 0) {
THROW_EXCEPTION("shape::checkDimensions method: array of dimensions is empty!");
}
// check presence of negative dimensions and if they are present transform them to positive ones -dim -> rank - |dim|
for (auto &dim : *dimensions)
if (dim < 0) dim += rank;
// sort input array of dimensions, this operation is also necessary for creating TAD object in external methods
if (dimSize > 1) {
std::sort(dimensions->begin(), dimensions->end());
// remove duplicates if they are present
dimensions->erase(std::unique(dimensions->begin(), dimensions->end()), dimensions->end());
}
// check whether number of dimensions is to big (>rank)
dimSize = dimensions->size();
if (dimSize > rank)
THROW_EXCEPTION("shape::checkDimensions method: number of input dimensions is too big ( > rank of array)!");
// check if min dimension is still negative and whether max dimension is bigger then rank-1
if (dimensions->at(0) < 0 || dimensions->back() > (rank - 1))
THROW_EXCEPTION(
"shape::checkDimensions method: the negative dimension is still present in input array after transform or the "
"too big dimension is present ( > rank of array) !");
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void shapeOldScalar(sd::DataType dataType, sd::LongType *const buffer, const char order) {
buffer[0] = 2;
buffer[1] = 1;
buffer[2] = 1;
buffer[3] = 1;
buffer[4] = 1;
buffer[6] = 1;
buffer[7] = order;
sd::ArrayOptions::setDataType(buffer, dataType);
}
/**
*
* @param length
* @param shape
* @param rearrange
* @return
*/
SD_LIB_EXPORT SD_INLINE SD_HOST void doPermuteSwap(sd::LongType length, sd::LongType *shape, sd::LongType *rearrange) {
if (length == 1) {
return;
} else {
if (prodLong(shape, length) < 2) {
return;
}
}
bool inOrder = true;
for (sd::LongType i = 0; i < length - 1; i++) {
inOrder = inOrder && rearrange[i] + 1 == rearrange[i + 1];
}
// all in order, nothing to do
if (inOrder) return;
// we know they are just reversed, dimension length of 2
if (length == 2) {
auto shapeFirst = shape[0];
auto shapeSecond = shape[1];
shape[0] = shapeSecond;
shape[1] = shapeFirst;
return;
} else if (length == 1) {
// no permute
return;
}
auto temp = new sd::LongType[length];
memcpy(temp, shape, sizeof(sd::LongType) * length);
for (sd::LongType i = 0; i < length; i++) {
shape[i] = temp[rearrange[i]];
}
delete[] temp;
}
/**
* Permute the shape information
* @param info the shape information to permute
* @param rearrange the order to re arrange
* @param rank the rank of the rearrange array
*/
SD_LIB_EXPORT SD_INLINE SD_HOST void permute(ShapeInformation **info, sd::LongType *rearrange, sd::LongType rank) {
ShapeInformation *infoDeref = *info;
checkArrangeArray(rearrange, rank, rank);
doPermuteSwap(rank, infoDeref->shape, rearrange);
doPermuteSwap(rank, infoDeref->stride, rearrange);
char order = getOrder(rank, infoDeref->shape, infoDeref->stride, infoDeref->elementWiseStride);
infoDeref->order = order;
}
/**
* Returns whether the given shape
* info has the flag view set.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isView(sd::LongType *shapeInfo) {
return shape::isViewConst(const_cast<const sd::LongType *>(shapeInfo));
}
/**
* Returns whether the
* given shape info has an empty flag set.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool isEmpty(sd::LongType *shapeInfo) {
return shape::isEmptyConst(const_cast<const sd::LongType *>(shapeInfo));
}
/**
* This method does SOFT comparison for two shape buffers, we compare only rank & shapes
*
* @param shape
* @return
*/
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE bool equalsSoft(const sd::LongType *shapeA, const sd::LongType *shapeB) {
if (shapeA[0] != shapeB[0]) {
return false;
}
if (isEmptyConst(shapeA) && isEmptyConst(shapeB)) {
return true;
}
if (shapeA[0] == 0) return true;
// we compare only shapes, and ignoring stride & ews
auto length = shapeA[0];
for (int e = 1; e <= length; e++)
if (shapeA[e] != shapeB[e]) return false;
return true;
}
SD_LIB_EXPORT SD_INLINE SD_HOST_DEVICE void setStrideConst(sd::LongType *buffer, const sd::LongType *strides) {
auto stridesRet = buffer + (1 + rank(buffer));
int rank = shape::rank(buffer);
if (rank < 1) {
buffer[2] = 0;
return;
}
for (int i = 0; i < rank; i++) {
stridesRet[i] = strides[i];
}
}
/**
* Get the shape info buffer
* for the given rank and shape.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType *shapeBuffer(sd::LongType rank, sd::DataType dtype, sd::LongType *shape) {
sd::LongType *stride = calcStrides(shape, rank);
auto shapeInfo = new ShapeInformation();
shapeInfo->shape = const_cast<sd::LongType *>(shape);
shapeInfo->stride = stride;
shapeInfo->offset = 0;
shapeInfo->rank = rank;
sd::LongType elementWiseStride = -1;
shapeInfo->order = 'c';
auto shapeInfoBuffer = toShapeBuffer(shapeInfo);
delete[] stride;
delete shapeInfo;
sd::ArrayOptions::setDataType(shapeInfoBuffer, dtype);
return shapeInfoBuffer;
}
SD_LIB_EXPORT SD_HOST SD_INLINE sd::LongType *shapeBuffer(sd::LongType rank, sd::DataType dtype, sd::LongType *shape,
sd::LongType *output) {
sd::LongType stride[SD_MAX_RANK];
calcStrides(shape, rank, stride);
ShapeInformation shapeInfo;
shapeInfo.shape = const_cast<sd::LongType *>(shape);
shapeInfo.stride = stride;
shapeInfo.offset = 0;
shapeInfo.rank = rank;
auto elementWiseStride = -1;
shapeInfo.order = 'c';
shapeInfo.elementWiseStride = elementWiseStride;
toShapeBuffer(&shapeInfo, output);
sd::ArrayOptions::setDataType(output, dtype);
return output;
}
/**
* Get the shape info buffer
* for the given rank and shape.
*/
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType *shapeBufferFortran(int rank, sd::DataType dtype, sd::LongType const *shape) {
auto stride = calcStridesFortran(shape, rank);
auto shapeInfo = new ShapeInformation();
shapeInfo->shape = const_cast<sd::LongType *>(shape);
shapeInfo->stride = stride;
shapeInfo->offset = 0;
shapeInfo->rank = rank;
sd::LongType elementWiseStride = -1;
shapeInfo->order = 'f';
shapeInfo->elementWiseStride = elementWiseStride;
auto shapeInfoBuffer = toShapeBuffer(shapeInfo);
delete[] stride;
delete shapeInfo;
sd::ArrayOptions::setDataType(shapeInfoBuffer, dtype);
return shapeInfoBuffer;
}
SD_LIB_EXPORT SD_HOST SD_INLINE sd::LongType *shapeBufferFortran(int rank, sd::DataType dtype, sd::LongType const *shape,
sd::LongType *output) {
sd::LongType stride[SD_MAX_RANK];
calcStridesFortran(shape, rank, stride);
ShapeInformation shapeInfo;
shapeInfo.shape = const_cast<sd::LongType *>(shape);
shapeInfo.stride = stride;
shapeInfo.offset = 0;
shapeInfo.rank = rank;
auto elementWiseStride = -1;
shapeInfo.order = 'f';
shapeInfo.elementWiseStride = elementWiseStride;
toShapeBuffer(&shapeInfo, output);
sd::ArrayOptions::setDataType(output, dtype);
return output;
}
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType *toShapeBuffer(ShapeInformation *info, sd::LongType *ret) {
int count = 1;
int rank = info->rank;
ret[0] = info->rank;
if (ret[0] == 0) {
ret[1] = 0;
ret[2] = 1;
ret[3] = 99;
return ret;
}
for (int i = 0; i < rank; i++) {
ret[count++] = info->shape[i];
}
for (int i = 0; i < rank; i++) {
ret[count++] = info->stride[i];
}
ret[count++] = info->offset;
ret[count++] = info->elementWiseStride;
ret[count++] = info->order;
return ret;
}
SD_LIB_EXPORT SD_HOST SD_INLINE void calcSubArrsShapeInfoAndOffsets(const sd::LongType *wholeShapeInfo, const sd::LongType numOfSubArrs,
const sd::LongType dimsSize, const sd::LongType *dimsToExclude,
sd::LongType *subArrShapeInfo, sd::LongType *subArrOffsets,
bool keepUnitiesInShape) {
const sd::LongType rank = shape::rank(wholeShapeInfo);
// Special handling for when all dimensions are excluded and we want scalar TADs
if (dimsSize == rank && !keepUnitiesInShape) {
// Create scalar TADs - each element is its own TAD
subArrShapeInfo[0] = 0; // rank = 0 for scalar
subArrShapeInfo[1] = 0; // no shape dimensions
subArrShapeInfo[2] = 1; // stride (not used for scalar)
subArrShapeInfo[3] = 0; // offset
subArrShapeInfo[4] = 1; // element-wise stride
subArrShapeInfo[5] = 99; // order 'c'
sd::ArrayOptions::copyDataType(subArrShapeInfo, wholeShapeInfo);
// Set sequential offsets for each scalar TAD
for (sd::LongType i = 0; i < numOfSubArrs; ++i) {
subArrOffsets[i] = i;
}
return;
}
if (dimsSize == rank || dimsSize == 0) { // means there is one sub-array and it coincides with whole array, return
// copy of wholeShapeInfo and one zero offset in this case
memcpy(subArrShapeInfo, wholeShapeInfo, shapeInfoLength(rank) * sizeof(sd::LongType));
*subArrOffsets = 0;
return;
}
const sd::LongType subArrRank = keepUnitiesInShape ? rank : rank - dimsSize;
subArrShapeInfo[0] = subArrRank; // rank
subArrShapeInfo[2 * subArrRank + 1] = 0; // clear (to avoid uninitialized)
sd::ArrayOptions::copyDataType(subArrShapeInfo, wholeShapeInfo); // type
subArrShapeInfo[2 * subArrRank + 3] = order(wholeShapeInfo); // order
sd::LongType *shape = new sd::LongType[dimsSize];
sd::LongType *strides = new sd::LongType[dimsSize];
for (sd::LongType k = subArrRank - 1, j = dimsSize - 1, i = rank - 1; i >= 0; --i) {
if (j >= 0 && i == dimsToExclude[j]) {
strides[j] = stride(wholeShapeInfo)[i];
shape[j--] = shapeOf(wholeShapeInfo)[i];
if (keepUnitiesInShape) {
shapeOf(subArrShapeInfo)[k] = 1;
stride(subArrShapeInfo)[k--] = stride(wholeShapeInfo)[i];
}
} else {
shapeOf(subArrShapeInfo)[k] = shapeOf(wholeShapeInfo)[i];
stride(subArrShapeInfo)[k--] = stride(wholeShapeInfo)[i];
}
}
// calculation of sub-array offsets (subArrOffsets)
calcOffsets(dimsSize, shape, strides, subArrOffsets);
// evaluate ews
checkStridesEwsAndOrder(subArrShapeInfo);
delete[] strides;
delete[] shape;
}
SD_LIB_EXPORT SD_INLINE SD_HOST void doPermuteShapeInfo(sd::LongType *shapeInfo,
const sd::LongType *rearrange,
sd::LongType len) {
if (shapeInfo == nullptr || rearrange == nullptr || rank(shapeInfo) < 1) {
return;
}
// note we used to automatically return early here but we can also permute
// shapes like 1,2,1,0 (aka empty) and the shape there can matter.
const sd::LongType rank = shape::rank(shapeInfo);
// check whether rearrange is like {0,1,2,3,...} - in this case we don't need permute as well
bool isPermuteNecessary = false;
for (sd::LongType i = 0; i < rank; i++) {
if (rearrange[i] != i) {
isPermuteNecessary = true;
break;
}
}
if (!isPermuteNecessary) {
return;
}
// check whether rearrange contains correct indexes
for (sd::LongType i = 0; i < rank; ++i) {
if (rearrange[i] >= rank || rearrange[i] < 0) {
std::string errorMessage;
errorMessage += "shape::doPermuteShapeInfo function failed: rearrange indexes are incorrect. Given permute indices must be < rank and >= 0. Rearrange at index ";
errorMessage += std::to_string(i);
errorMessage += " was ";
errorMessage += std::to_string(rearrange[i]);
errorMessage += "\n";
THROW_EXCEPTION(errorMessage.c_str());
}
}
// if everything is ok then perform permute
sd::LongType len2 = shapeInfoLength(rank);
auto temp = new sd::LongType[len2];
// note: it's obvious to do simd or something fancy
// here it actually seems to cause segfaults. Better to be careful.
for (int i = 0; i < len2; i++) {
temp[i] = shapeInfo[i];
}
for (sd::LongType i = 0; i < rank; i++) {
shapeInfo[i + 1] = temp[rearrange[i] + 1];
shapeInfo[i + 1 + rank] = temp[rearrange[i] + 1 + rank];
}
checkStridesEwsAndOrder(shapeInfo);
delete[] temp;
}
SD_LIB_EXPORT SD_INLINE SD_HOST sd::LongType tadLength(const sd::LongType *shapeInfo, const sd::LongType *dimension,
sd::LongType dimensionLength) {
if (shapeInfo == nullptr || dimension == nullptr) {
std::string errorMessage;
errorMessage += "shape info null: %d";
errorMessage += std::to_string(shapeInfo == nullptr);
errorMessage += " dimension null: %d";
errorMessage += std::to_string(dimension == nullptr);
THROW_EXCEPTION(errorMessage.c_str());
}
if (dimensionLength == 0) return 0;
if (shapeInfo[0] > SD_MAX_RANK || shapeInfo[0] < 0)
THROW_EXCEPTION("Corrupt shape information found. Potentially dellocated?");
if (dimensionLength == 1) {
if (dimension[0] > SD_MAX_RANK || dimension[0] < 0)
THROW_EXCEPTION("Corrupt dimension information found. Potentially dellocated?");
return shapeOf(shapeInfo)[dimension[0]];
} else {
sd::LongType ret = 1;
for (sd::LongType i = 0; i < rank(shapeInfo); i++) {
for (sd::LongType j = 0; j < dimensionLength; j++) {
if (i == dimension[j]) ret *= shapeOf(shapeInfo)[dimension[j]];
}
}
return ret;
}
}
SD_LIB_EXPORT SD_INLINE SD_HOST int excludeUnitiesFromShapeInfo(const sd::LongType *inShapeInfo, sd::LongType *shapeNoUnities,
sd::LongType *stridesNoUnities) {
const int rank = shape::rank(inShapeInfo);
const int numOfNonUnities = numOfNonUnitDims(rank, shapeOf(inShapeInfo));
if (numOfNonUnities == rank) { // no unities in shape, no copy procedure
shapeNoUnities = const_cast<sd::LongType *>(inShapeInfo) + 1;
stridesNoUnities = const_cast<sd::LongType *>(inShapeInfo) + 1 + rank;
return numOfNonUnities;
}
int j = 0;
for (int i = 0; i < rank; i++) {
if (shapeOf(inShapeInfo)[i] != 1) {
shapeNoUnities[j] = shapeOf(inShapeInfo)[i];
stridesNoUnities[j++] = stride(inShapeInfo)[i];
}
}
return numOfNonUnities;
}
SD_LIB_EXPORT SD_INLINE void SD_HOST checkStridesEwsAndOrder(sd::LongType *shapeInfo) {
// FIXME - indeed we don't need to allocate so large memory amount (2*SD_MAX_RANK), sufficient amount is
// (2*oldNumOfNonUnities + 2*newNumOfNonUnities)
sd::LongType tempBuffer[2 * SD_MAX_RANK];
sd::LongType *shape = tempBuffer, *strides = tempBuffer + shape::rank(shapeInfo);
// exclude unities from shapeInfo
const sd::LongType numOfNonUnities = excludeUnitiesFromShapeInfo(shapeInfo, shape, strides);
checkStridesEwsAndOrder(shapeInfo, order(shapeInfo), numOfNonUnities, shape, strides);
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE void SD_HOST checkStridesEwsAndOrder(sd::LongType *shapeInfo, const char proposedOrder,
const sd::LongType numOfNonUnities, const sd::LongType *shapeNoUnities,
const sd::LongType *stridesNoUnities) {
if (proposedOrder != 'c' && proposedOrder != 'f') {
std::string errorMessage;
errorMessage += "checkStridesEwsAndOrder: ";
errorMessage += "proposedOrder is invalid !";
errorMessage += " Expected c or f, but got ";
errorMessage += proposedOrder;
errorMessage += " instead !";
THROW_EXCEPTION(errorMessage.c_str());
}
const sd::LongType rank = shape::rank(shapeInfo);
if (length(shapeInfo) == 1) {
setOrder(shapeInfo, proposedOrder);
return;
}
if (numOfNonUnities == 1) { // case of common vector
setOrder(shapeInfo, proposedOrder);
return;
}
bool contiguous = true;
//*** check whether strides are in c contiguous order ***//
for (sd::LongType i = 0; i < numOfNonUnities - 1; ++i) {
if (stridesNoUnities[i] != shapeNoUnities[i + 1] * stridesNoUnities[i + 1]) {
contiguous = false;
break;
}
}
if (contiguous) {
setOrder(shapeInfo, 'c');
return;
}
contiguous = true;
//*** check whether strides are in f contiguous order ***//
for (sd::LongType i = 1; i < numOfNonUnities; ++i) {
if (stridesNoUnities[i] != shapeNoUnities[i - 1] * stridesNoUnities[i - 1]) {
contiguous = false;
break;
}
}
if (contiguous) {
setOrder(shapeInfo, 'f');
return;
}
setOrder(shapeInfo, proposedOrder);
}
SD_INLINE SD_LIB_EXPORT SD_HOST void calcOffsets(const sd::LongType *shapeInfo, sd::LongType *offsets, const char order) {
if (shapeInfo == nullptr) THROW_EXCEPTION("calcOffsets: shapeInfo is nullptr !");
if (offsets == nullptr) THROW_EXCEPTION("calcOffsets: offsets is nullptr !");
if (shapeInfo[0] < 0 || shapeInfo[0] > SD_MAX_RANK) THROW_EXCEPTION("calcOffsets: shapeInfo[0] is invalid !");
// firstly consider simple case when ews > 0
const sd::LongType ews = elementWiseStride(shapeInfo);
if (ews > 0) {
// set offset for first sub-array, it is equal to zero always
offsets[0] = 0;
sd::LongType e = 0;
if (order != shape::order(shapeInfo))
for (sd::LongType i = 1; i <= rank(shapeInfo); ++i)
if (shapeInfo[i] != 1) ++e; // check whether input is CommonVector
if (order == shape::order(shapeInfo) || e == 1) { // e==1 means common vector
e = 1;
sd::LongType len = length(shapeInfo);
while (e < len) {
offsets[e] = offsets[e - 1] + ews;
e++;
}
return;
}
}
calcOffsets(rank(shapeInfo), shapeOf(const_cast<sd::LongType *>(shapeInfo)),
stride(const_cast<sd::LongType *>(shapeInfo)), offsets, order);
}
SD_INLINE SD_LIB_EXPORT SD_HOST void calcOffsets(const sd::LongType rank, const sd::LongType *shape, const sd::LongType *strides, sd::LongType *offsets,
const char order) {
const sd::LongType len = prodLong(shape, rank);
// set offset for first sub-array, it is equal to zero always
offsets[0] = 0;
sd::LongType coords[SD_MAX_RANK];
memset(coords, 0, sizeof(sd::LongType) * rank);
if (order == 'c') {
for (sd::LongType i = 1; i < len; ++i) {
sd::LongType axis = rank - 1;
offsets[i] = 0;
while (coords[axis] == shape[axis] - 1) {
offsets[i] -= (shape[axis] - 1) * strides[axis];
coords[axis--] = 0;
}
++coords[axis];
offsets[i] += offsets[i - 1] + strides[axis];
}
} else {
for (sd::LongType i = 1; i < len; ++i) {
sd::LongType axis = 0;
offsets[i] = 0;
while (coords[axis] == shape[axis] - 1) {
offsets[i] -= (shape[axis] - 1) * strides[axis];
coords[axis++] = 0;
}
++coords[axis];
offsets[i] += offsets[i - 1] + strides[axis];
}
}
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_HOST SD_INLINE void calcSubArrShapeInfoAndOffset(const sd::LongType *idx, const sd::LongType *maxShapeInfo, sd::LongType *minShapeInfo,
sd::LongType &minOffset, const bool keepUnitiesInShape, const bool isStrided,
const sd::LongType numOfUntiesInMinShape) {
if (sd::ArrayOptions::dataType(maxShapeInfo) == sd::DataType::UNKNOWN) {
THROW_EXCEPTION("calcSubArrShapeInfoAndOffset: maxShapeInfo has unknown data type !");
}
const sd::LongType maxRank = rank(maxShapeInfo);
minOffset = 0;
sd::LongType first, last, stride, n(isStrided ? 3 : 2);
// Enhanced debugging - log input parameters
if (sd::Environment::getInstance().isDebug()) {
sd_print("=== calcSubArrShapeInfoAndOffset DEBUG ===\n");
sd_printf("maxRank: %lld, isStrided: %s, keepUnitiesInShape: %s\n",
maxRank, isStrided ? "true" : "false", keepUnitiesInShape ? "true" : "false");
sd_print("maxShape: [");
for (sd::LongType i = 0; i < maxRank; i++) {
sd_printf("%lld%s", sizeAt(maxShapeInfo, i), i < maxRank-1 ? ", " : "");
}
sd_print("]\n");
sd_printf("Input indices (step=%lld):\n", n);
for (sd::LongType i = 0; i < maxRank; i++) {
sd::LongType step = i * n;
if (isStrided) {
sd_printf(" Dim %lld: idx[%lld]=%lld, idx[%lld]=%lld, idx[%lld]=%lld\n",
i, step, idx[step], step+1, idx[step+1], step+2, idx[step+2]);
} else {
sd_printf(" Dim %lld: idx[%lld]=%lld, idx[%lld]=%lld\n",
i, step, idx[step], step+1, idx[step+1]);
}
}
}
minShapeInfo[0] = keepUnitiesInShape ? maxRank : maxRank - numOfUntiesInMinShape;
for (sd::LongType step = 0, j = 0, i = 0; i < maxRank; ++i, step += n) {
if (idx[step] == idx[step + 1]) { // means whole dimension
shapeOf(minShapeInfo)[j] = shapeOf(maxShapeInfo)[i];
shape::stride(minShapeInfo)[j++] = shape::stride(maxShapeInfo)[i];
if (sd::Environment::getInstance().isDebug()) {
sd_printf(" Dim %lld: whole dimension (idx[%lld]==idx[%lld]=%lld)\n",
i, step, step+1, idx[step]);
}
} else {
// Store original indices for error reporting
sd::LongType orig_first = idx[step];
sd::LongType orig_last = idx[step + 1];
sd::LongType shape_size = sizeAt(maxShapeInfo, i);
first = idx[step] >= 0 ? idx[step] : idx[step] + shape_size + 1;
last = idx[step + 1] >= 0 ? idx[step + 1] : idx[step + 1] + shape_size + 1;
// Enhanced error reporting before the check
if (last < first) {
sd_printf("ERROR: Negative range detected in dimension %lld!\n", i);
sd_printf(" Original indices: first=%lld, last=%lld\n", orig_first, orig_last);
sd_printf(" After processing: first=%lld, last=%lld\n", first, last);
sd_printf(" Shape size for dim %lld: %lld\n", i, shape_size);
sd_printf(" Processing: first = %lld >= 0 ? %lld : %lld + %lld + 1 = %lld\n",
orig_first, orig_first, orig_first, shape_size, first);
sd_printf(" Processing: last = %lld >= 0 ? %lld : %lld + %lld + 1 = %lld\n",
orig_last, orig_last, orig_last, shape_size, last);
// Print full context
sd_print(" Full maxShape: [");
for (sd::LongType k = 0; k < maxRank; k++) {
sd_printf("%lld%s", sizeAt(maxShapeInfo, k), k < maxRank-1 ? ", " : "");
}
sd_print("]\n");
sd_print(" All indices for this operation:\n");
for (sd::LongType k = 0; k < maxRank; k++) {
sd::LongType k_step = k * n;
if (isStrided) {
sd_printf(" Dim %lld: [%lld, %lld, %lld]\n", k, idx[k_step], idx[k_step+1], idx[k_step+2]);
} else {
sd_printf(" Dim %lld: [%lld, %lld]\n", k, idx[k_step], idx[k_step+1]);
}
}
std::string error_msg = "shape::calcSubArrShapeInfoAndOffset: negative range in input indexes is found for dimension ";
error_msg += std::to_string(i);
error_msg += "! Check strided_slice parameters.";
THROW_EXCEPTION(error_msg.c_str());
}
if (isStrided) {
stride = idx[step + 2];
if (stride == 0) {
sd_printf("ERROR: Zero stride detected in dimension %lld!\n", i);
std::string error_msg = "shape::calcSubArrShapeInfoAndOffset: zero stride detected in dimension ";
error_msg += std::to_string(i);
error_msg += "!";
THROW_EXCEPTION(error_msg.c_str());
}
last /*resulting sub-array axis*/ = (last - first + stride - 1) / stride; // ceil (last - first) / stride;
} else {
stride = 1;
last /*resulting sub-array axis*/ = last - first;
}
minOffset += first * shape::stride(maxShapeInfo)[i];
if (sd::Environment::getInstance().isDebug()) {
sd_printf(" Dim %lld: orig_indices=[%lld,%lld", i, orig_first, orig_last);
if (isStrided) {
sd_printf(",%lld", idx[step+2]);
}
sd_printf("], processed=[%lld,%lld], resulting_size=%lld, minOffset_contribution=%lld\n",
first, last, last, first * shape::stride(maxShapeInfo)[i]);
}
if (!keepUnitiesInShape && last == 1) continue;
shapeOf(minShapeInfo)[j] = last;
shape::stride(minShapeInfo)[j++] =
last == 1 ? shape::stride(maxShapeInfo)[i] : shape::stride(maxShapeInfo)[i] * stride;
}
}
setExtra(minShapeInfo, extra(maxShapeInfo));
setOrder(minShapeInfo, 'c'); // order
sd::ArrayOptions::setDataType(minShapeInfo, sd::ArrayOptions::dataType(maxShapeInfo)); // type
checkStridesEwsAndOrder(minShapeInfo);
if (sd::Environment::getInstance().isDebug()) {
sd_printf(" Final minOffset: %lld\n", minOffset);
sd_printf(" Final minShape rank: %lld\n", minShapeInfo[0]);
sd_print("=== End calcSubArrShapeInfoAndOffset DEBUG ===\n");
}
if (sd::ArrayOptions::dataType(minShapeInfo) == sd::DataType::UNKNOWN)
THROW_EXCEPTION("Attempted to set unknown data type for minShapeInfo !");
}
SD_LIB_EXPORT SD_HOST_DEVICE SD_INLINE void updateStrides(sd::LongType *shapeInfo, const char order,
bool resetStridesIfView) {
sd::LongType rank = shapeInfo[0];
if(rank < 0 || rank > SD_MAX_RANK) {
THROW_EXCEPTION("Invalid rank value. Ensure a rank has been assigned.");
}
sd::LongType doubleRank = 2 * rank;
if (isEmpty(shapeInfo)) {
auto strides = stride(shapeInfo);
for (int i = 0; i < rank; i++) {
strides[i] = 0;
}
}
//strides from views can be strange, we provide a knob here
//for times where we just need new strides when copying
//from a view to a standalone array. this is common when working
//with linear parameter vectors representing a full network
//but may need a reshape or permute (view creation) where a
//fresh array and buffer are created with new strides.
if(resetStridesIfView && shape::isView(shapeInfo)) {
auto strides = stride(shapeInfo);
if(order == 'c') {
shape::calcStrides(shape::shapeOf(shapeInfo), rank, strides);
} else if(order == 'f') {
shape::calcStridesFortran(shape::shapeOf(shapeInfo), rank, strides);
}
} else {
if (rank > 0) {
if (order == 'c') {
shapeInfo[doubleRank] = 1; // set unity as last stride for c order
for (sd::LongType j = 1; j < rank; j++) {
shapeInfo[doubleRank - j] = shapeInfo[doubleRank - j + 1] * shapeInfo[rank + 1 - j];
}
} else {
shapeInfo[rank + 1] = 1; // set unity as first stride for f order
for (sd::LongType j = rank + 1; j < doubleRank; j++) {
shapeInfo[j + 1] = shapeInfo[j] * shapeInfo[j - rank];
}
}
}
}
// set last 2 elements in shapeInfo
shapeInfo[doubleRank + 2] = 1;
setOrder(shapeInfo, order);
}
SD_LIB_EXPORT SD_INLINE SD_HOST void updateStrides(const sd::LongType rank, const sd::LongType *shapeOnly, sd::LongType *stridesOnly,
const char order) {
if (rank > 0) {
if (order == 'c') {
stridesOnly[rank - 1] = 1; // set unity as last stride for c order
for (sd::LongType j = 1; j < rank; ++j) stridesOnly[rank - 1 - j] = stridesOnly[rank - j] * shapeOnly[rank - j];
} else {
stridesOnly[0] = 1; // set unity as first stride for f order
for (sd::LongType j = 1; j < rank; ++j) {
stridesOnly[j] = stridesOnly[j - 1] * shapeOnly[j - 1];
}
}
}
}
/**
* @param toCopy the shape to copy
* @return a copy of the original struct
*/
SD_LIB_EXPORT SD_INLINE SD_HOST ShapeInformation *shapeCopy(ShapeInformation *toCopy) {
auto copy = new ShapeInformation;
copy->shape = new sd::LongType[toCopy->rank];
memcpy(copy->shape, toCopy->shape, toCopy->rank * sizeof(sd::LongType));
copy->stride = new sd::LongType[toCopy->rank];
for (sd::LongType i = 0; i < toCopy->rank; i++) {
copy->stride[i] = toCopy->stride[i];
}
copy->order = toCopy->order;
copy->rank = toCopy->rank;
copy->offset = toCopy->offset;
copy->elementWiseStride = toCopy->elementWiseStride;
return copy;
}
SD_LIB_EXPORT SD_INLINE SD_HOST bool reshapeC(const sd::LongType *oldShapeInfo,
const char newOrder,
const sd::LongType newRank,
const sd::LongType *newShape,
sd::LongType *newShapeInfo) {
// copy shape from newShape into newShapeInfo
newShapeInfo[0] = newRank;
memcpy(newShapeInfo + 1, newShape, newRank * sizeof(sd::LongType));
// copy order
newShapeInfo[2 * newRank + 3] = newOrder;
sd::ArrayOptions::copyDataType(newShapeInfo, oldShapeInfo);
setOrder(newShapeInfo, newOrder);
// inherit old data type
auto ret = reshapeC(oldShapeInfo, newShapeInfo);
return ret;
}
SD_LIB_EXPORT SD_INLINE SD_HOST void fillStrides(sd::LongType *shapeInfo) {
// double checks if the _rank and _shape_strides are set correctly before filling strides
auto _shape = shape::shapeOf(shapeInfo);
auto _strides = shape::stride(shapeInfo);
auto rank = shape::rank(shapeInfo);
auto order = shape::order(shapeInfo);
if (rank > 0 && !shape::isEmptyConst(shapeInfo)) {
if (order == 'c')
shape::calcStrides(_shape, rank, _strides);
else
shape::calcStridesFortran(_shape, rank, _strides);
} else {
for (int i = 0; i < rank; i++) {
_strides[i] = 0;
}
}
}
//////////////////////////////////////////////////////////////////////
SD_LIB_EXPORT SD_INLINE SD_HOST bool reshapeC(const sd::LongType *oldShapeInfo, sd::LongType *newShapeInfo) {
// newShapeInfo contains rank, shape and order; but no strides, type and ews
const sd::LongType newRank = shape::rank(newShapeInfo);
auto oldDt = sd::ArrayOptions::dataType(oldShapeInfo);
if (oldDt == sd::DataType::UNKNOWN) {
THROW_EXCEPTION("Attempting to reshape with an unknown data type");
}
// if oldShapeInfo is scalar or vector with length=1
if (shape::length(oldShapeInfo) <= 1) {
for (sd::LongType i = 0; i < newRank; ++i) shape::stride(newShapeInfo)[i] = 1;
sd::ArrayOptions::setDataType(newShapeInfo, sd::ArrayOptions::dataType(oldShapeInfo));
return true;
}
const auto oldOrder = shape::order(oldShapeInfo);
const auto newOrder = shape::order(newShapeInfo);
// Calculate new strides
sd::LongType newStride = 1;
if (newOrder == 'c') {
for (int i = newRank - 1; i >= 0; --i) {
shape::stride(newShapeInfo)[i] = newStride;
newStride *= shape::shapeOf(newShapeInfo)[i];
}
} else { // 'f' order
for (int i = 0; i < newRank; ++i) {
shape::stride(newShapeInfo)[i] = newStride;
newStride *= shape::shapeOf(newShapeInfo)[i];
}
}
// Check if the reshape is valid (total number of elements should remain the same)
sd::LongType oldLength = shape::length(oldShapeInfo);
sd::LongType newLength = shape::length(newShapeInfo);
if (oldLength != newLength) {
THROW_EXCEPTION("Invalid reshape: total number of elements must remain the same");
}
// Set ews and order
shape::checkStridesEwsAndOrder(newShapeInfo);
// setExtra() overwrites the entire extra field, so if we call setDataType() first,
// the data type flags will be lost when setExtra() overwrites them.
// The correct order is: setExtra() first (copies all flags from old), then setDataType() to ensure correct type.
sd::ArrayOptions::setExtra(newShapeInfo, sd::ArrayOptions::extra(oldShapeInfo));
sd::ArrayOptions::setDataType(newShapeInfo, oldDt);
return true;
}
} // namespace shape
#endif // SHAPE_HXX_