/* ****************************************************************************** * * * 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 ******************************************************************************/ // // @author Yurii Shyrma (iuriish@yahoo.com) // #include #include #include #include #include #include namespace sd { ////////////////////////////////////////////////////////////////////////// LongType* ShapeUtils::evalTransposeShapeInfo(NDArray& arr, memory::Workspace* workspace, const bool setContigStrides) { LongType rank = arr.rankOf(); // note we do this because of stack allocation crashes // if the stack is used a vector's data can cause crashes when it goes out of scope LongType* dims = new LongType[rank]; for (LongType i = 0; i < rank; i++) { dims[i] = rank - 1 - i; } auto ret = evalPermShapeInfo(dims, rank, &arr, workspace, setContigStrides); delete[] dims; return ret; } // evaluate shape for array resulting from tensorDot operation, also evaluate shapes and dimensions permutations for // transposition of two input arrays std::vector ShapeUtils::evalShapeForTensorDot( LongType* aShapeInfo, LongType* bShapeInfo, const std::vector axesA, const std::vector axesB, std::vector& permutAt, std::vector& permutBt, std::vector& shapeAt, std::vector& shapeBt) { LongType axeAsize = static_cast(axesA.size()); LongType axeBsize = static_cast(axesB.size()); LongType aRank = aShapeInfo[0]; LongType bRank = bShapeInfo[0]; if (axeAsize != axeBsize) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForTensorDot method: the numbers of a axes and b axes to make dot product along must " "have identical values !\n"; errorMessage += "axesASize: "; errorMessage += std::to_string(axeAsize); errorMessage += ", axesBSize: "; errorMessage += std::to_string(axeBsize); errorMessage += "\n"; THROW_EXCEPTION(errorMessage.c_str()); } if (axeAsize > aRank || axeBsize > bRank) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForTensorDot method: the length of vector of a or b axes is larger than array rank !\n"; errorMessage += "axesASize: "; errorMessage += std::to_string(axeAsize); errorMessage += ", axesBSize: "; errorMessage += std::to_string(axeBsize); errorMessage += "\n"; errorMessage += "aRank: "; errorMessage += std::to_string(aRank); errorMessage += ", bRank: "; errorMessage += std::to_string(bRank); errorMessage += "\n"; THROW_EXCEPTION(errorMessage.c_str()); } // check whether axesA and axesB contain only unique numbers std::set uniqueElems(axesA.begin(), axesA.end()); if ((LongType)uniqueElems.size() != axeAsize) { THROW_EXCEPTION("ShapeUtils::evalShapeForTensorDot method: the vector of a axes contains duplicates !"); } uniqueElems.clear(); uniqueElems = std::set(axesB.begin(), axesB.end()); if ((LongType)uniqueElems.size() != axeBsize) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForTensorDot method: the vector of b axes contains duplicates !\n"; errorMessage += "axesBsize: "; errorMessage += std::to_string(axesB.size()); errorMessage += " uniqueElems: "; errorMessage += std::to_string(uniqueElems.size()); THROW_EXCEPTION(errorMessage.c_str()); } std::vector list_A, list_B; for (LongType i = 0; i < aRank; i++) if (std::find(axesA.begin(), axesA.end(), i) == axesA.end()) list_A.emplace_back(i); for (LongType i = 0; i < bRank; i++) if (std::find(axesB.begin(), axesB.end(), i) == axesB.end()) list_B.emplace_back(i); permutAt = list_A; permutAt.insert(permutAt.end(), axesA.begin(), axesA.end()); permutBt = axesB; permutBt.insert(permutBt.end(), list_B.begin(), list_B.end()); // if permute contains something like {0,1,2,..rank-1}, then there is no need to make permutation and we return empty // vector in this case LongType i1, i2; for (i1 = 0; i1 < aRank; ++i1) if (permutAt[i1] != i1) break; if (i1 == aRank) permutAt = {}; for (i2 = 0; i2 < bRank; ++i2) if (permutBt[i2] != i2) break; if (i2 == bRank) permutBt = {}; LongType n2 = 1; for (LongType i = 0; i < axeAsize; i++) n2 *= aShapeInfo[axesA[i] + 1]; shapeAt = {shape::length(aShapeInfo) / n2, n2}; std::vector oldShapeA; oldShapeA.resize(list_A.size()); for (size_t i = 0; i < oldShapeA.size(); ++i) oldShapeA[i] = aShapeInfo[list_A[i] + 1]; LongType n3 = 1; for (LongType i = 0; i < axeBsize; i++) n3 *= bShapeInfo[axesB[i] + 1]; shapeBt = {n3, shape::length(bShapeInfo) / n3}; std::vector oldShapeB; oldShapeB.resize(list_B.size()); for (size_t i = 0; i < oldShapeB.size(); i++) oldShapeB[i] = bShapeInfo[list_B[i] + 1]; std::vector aPlusB(oldShapeA); aPlusB.insert(aPlusB.end(), oldShapeB.begin(), oldShapeB.end()); return aPlusB; } ////////////////////////////////////////////////////////////////////////// std::vector ShapeUtils::evalShapeForTensorDot(NDArray* a, NDArray* b, const std::vector& axesA, const std::vector& axesB, std::vector& permutAt, std::vector& permutBt, std::vector& shapeAt, std::vector& shapeBt) { return evalShapeForTensorDot(a->shapeInfo(), b->shapeInfo(), axesA, axesB, permutAt, permutBt, shapeAt, shapeBt); } ////////////////////////////////////////////////////////////////////////// // evaluate output shape for reduce operation when input shape is empty LongType* ShapeUtils::evalReduceShapeInfoEmpty(const char order, std::vector* dimsToExclude, LongType* shapeInfo, const DataType dataType, const bool keepDims, memory::Workspace* workspace) { if (dimsToExclude->size() == 0) { // return copy of input shape LongType* outShapeInfo = ShapeBuilders::copyShapeInfoAndType(shapeInfo, dataType, true, workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(outShapeInfo)->primary(); return ret; } const LongType rank = shape::rank(shapeInfo); LongType* outShapeInfo = nullptr; if (static_cast(dimsToExclude->size()) == rank) { // return scalar or shape filled with unities if (!keepDims) outShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); else outShapeInfo = ShapeBuilders::createShapeInfo(dataType, order, std::vector(rank, 1), workspace); } else { shape::checkDimensions(rank, dimsToExclude); std::vector outShape; if (keepDims) { outShape.assign(shapeInfo + 1, shapeInfo + 1 + rank); for (const auto dim : *dimsToExclude) outShape[dim] = 1; } else { for (LongType i = 0, j = 0; i < rank; ++i) { if (j < static_cast(dimsToExclude->size()) && i == dimsToExclude->at(j)) ++j; else outShape.emplace_back(shapeInfo[i + 1]); } } outShapeInfo = ShapeBuilders::createShapeInfo(dataType, order, outShape, workspace); } auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(outShapeInfo)->primary(); delete[] outShapeInfo; return ret; } LongType* ShapeUtils::evalReduceShapeInfo(const char order, std::vector* dimsToExclude, NDArray& arr, const bool keepDims, const bool supportOldShapes, memory::Workspace* workspace) { return const_cast( evalReduceShapeInfo(order, dimsToExclude, arr, arr.dataType(), keepDims, supportOldShapes, workspace)); } ////////////////////////////////////////////////////////////////////////// // return new (shorter) sorted dimensions array without dimensions that are present in input vector std::vector* ShapeUtils::evalDimsToExclude(const LongType rank, const LongType dimsLen, const LongType* dimensions) { std::vector * ret = new std::vector(); // Validate input parameters if (rank <= 0) { THROW_EXCEPTION("ShapeUtils::evalDimsToExclude: rank must be positive"); } if (dimsLen < 0) { THROW_EXCEPTION("ShapeUtils::evalDimsToExclude: dimsLen cannot be negative"); } if (dimsLen > 0 && dimensions == nullptr) { THROW_EXCEPTION("ShapeUtils::evalDimsToExclude: dimensions array is null but dimsLen > 0"); } if (dimsLen == 0) { // if input vector is empty then return whole shape range ret->resize(rank); std::iota(ret->begin(), ret->end(), 0); // fill with 0, 1, ... rank-1 } else { // Validate dimensions are within bounds for (LongType j = 0; j < dimsLen; j++) { LongType dim = dimensions[j] >= 0 ? dimensions[j] : dimensions[j] + rank; if (dim < 0 || dim >= rank) { delete ret; THROW_EXCEPTION("ShapeUtils::evalDimsToExclude: dimension index is out of bounds"); } } bool isAbsent; for (LongType i = 0; i < rank; i++) { isAbsent = true; for (LongType j = 0; j < dimsLen; j++) { LongType dim = dimensions[j] >= 0 ? dimensions[j] : dimensions[j] + rank; if (i == dim) { isAbsent = false; break; } } if (isAbsent) ret->emplace_back(i); } } // Note: We keep the original behavior - if ret is empty, it means all dimensions // were excluded, which is a valid case that the caller (gather operation) should handle return ret; } ////////////////////////////////////////////////////////////////////////// // evaluate shape resulting from reduce operation LongType* ShapeUtils::evalReduceShapeInfo(const char order, std::vector* dimsToExclude, LongType* shapeInfo, const DataType dataType, const bool keepDims, const bool supportOldShapes, memory::Workspace* workspace) { if (ArrayOptions::arrayType(shapeInfo) == EMPTY) { return evalReduceShapeInfoEmpty(order, dimsToExclude, shapeInfo, dataType, keepDims, workspace); } LongType* newShapeInfo = nullptr; LongType rank = shape::rank(const_cast(shapeInfo)); if (dimsToExclude->size() == 0) { // return scalar or array with len=1 in this case if (keepDims && rank > 1) { newShapeInfo = new LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) newShapeInfo[i + 1] = 1; updateStridesAndType(newShapeInfo, shapeInfo, order); ArrayOptions::setDataType(newShapeInfo, dataType); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else if (supportOldShapes) { newShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } shape::checkDimensions(rank, dimsToExclude); LongType dimSize = dimsToExclude->size(); if (keepDims) { newShapeInfo = new LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) { if (std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[i + 1] = 1; else newShapeInfo[i + 1] = shapeInfo[i + 1]; } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } LongType newRank = rank - dimSize; if (newRank == 0 || (dimSize == 1 && dimsToExclude->at(0) == INT_MAX)) { // check whether given dimension is meant for the whole dimension if (supportOldShapes) { newShapeInfo = new LongType[shape::shapeInfoLength(2)]; shape::shapeOldScalar(ArrayOptions::dataType(shapeInfo), newShapeInfo, 'c'); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(ArrayOptions::dataType(shapeInfo), workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } newShapeInfo = new LongType[shape::shapeInfoLength(newRank)]; newShapeInfo[0] = newRank; // set rank LongType j = 1; for (LongType i = 0; i < rank; ++i) if (!std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[j++] = shapeInfo[i + 1]; // ensure whether vector has proper shape for old shape type if (newRank == 1 && supportOldShapes) { LongType oldValue = newShapeInfo[1]; delete[] newShapeInfo; newShapeInfo = new LongType[shape::shapeInfoLength(2)]; newShapeInfo[0] = 2; if (dimsToExclude->at(0) == 0) { newShapeInfo[1] = 1; newShapeInfo[2] = oldValue; } else { newShapeInfo[1] = oldValue; newShapeInfo[2] = 1; } } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } LongType* ShapeUtils::evalReduceShapeInfo(const char order, std::vector* dimsToExclude, NDArray& arr, const DataType dataType, const bool keepDims, const bool supportOldShapes, memory::Workspace* workspace) { sd::LongType *shapeInfo = arr.shapeInfo(); if (ArrayOptions::arrayType(shapeInfo) == EMPTY) return evalReduceShapeInfoEmpty(order, dimsToExclude, shapeInfo, dataType, keepDims, workspace); LongType* newShapeInfo = nullptr; LongType rank = shape::rank(const_cast(shapeInfo)); if (dimsToExclude->size() == 0) { // return scalar or array with len=1 in this case if (keepDims && rank > 1) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) newShapeInfo[i + 1] = 1; updateStridesAndType(newShapeInfo, shapeInfo, order); ArrayOptions::setDataType(newShapeInfo, dataType); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else if (supportOldShapes) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(2)]; shape::shapeOldScalar(dataType, newShapeInfo, 'c'); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } shape::checkDimensions(rank, dimsToExclude); LongType dimSize = dimsToExclude->size(); if (keepDims) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) { if (std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[i + 1] = 1; else newShapeInfo[i + 1] = shapeInfo[i + 1]; } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } LongType newRank = rank - dimSize; if (newRank == 0 || (dimSize == 1 && dimsToExclude->at(0) == INT_MAX)) { // check whether given dimension is meant for the whole dimension if (supportOldShapes) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(2)]; shape::shapeOldScalar(ArrayOptions::dataType(shapeInfo), newShapeInfo, 'c'); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(ArrayOptions::dataType(shapeInfo), workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } newShapeInfo = new sd::LongType[shape::shapeInfoLength(newRank)]; newShapeInfo[0] = newRank; // set rank LongType j = 1; for (LongType i = 0; i < rank; ++i) if (!std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[j++] = shapeInfo[i + 1]; // ensure whether vector has proper shape for old shape type if (newRank == 1 && supportOldShapes) { LongType oldValue = newShapeInfo[1]; delete[] newShapeInfo; RELEASE(newShapeInfo, workspace); newShapeInfo = new sd::LongType[shape::shapeInfoLength(2)]; ALLOCATE(newShapeInfo, workspace, shape::shapeInfoLength(2), sd::LongType); // set newRank = 2 newShapeInfo[0] = 2; if (dimsToExclude->at(0) == 0) { newShapeInfo[1] = 1; newShapeInfo[2] = oldValue; } else { newShapeInfo[1] = oldValue; newShapeInfo[2] = 1; } } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); RELEASE(newShapeInfo, workspace); return ret; } LongType* ShapeUtils::evalReduceShapeInfo(char order, std::vector* dimsToExclude, LongType* shapeInfo, const bool keepDims, bool supportOldShapes, memory::Workspace* workspace) { sd::DataType dataType = ArrayOptions::dataType(shapeInfo); if (ArrayOptions::arrayType(shapeInfo) == EMPTY) return evalReduceShapeInfoEmpty(order, dimsToExclude, shapeInfo, dataType, keepDims, workspace); LongType* newShapeInfo = nullptr; LongType rank = shape::rank(const_cast(shapeInfo)); if (dimsToExclude->size() == 0) { // return scalar or array with len=1 in this case if (keepDims && rank > 1) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) newShapeInfo[i + 1] = 1; updateStridesAndType(newShapeInfo, shapeInfo, order); ArrayOptions::setDataType(newShapeInfo, dataType); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else if (supportOldShapes) { newShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); shape::shapeOldScalar(dataType, newShapeInfo, 'c'); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(dataType, workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } shape::checkDimensions(rank, dimsToExclude); LongType dimSize = dimsToExclude->size(); if (keepDims) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(rank)]; newShapeInfo[0] = rank; for (LongType i = 0; i < rank; ++i) { if (std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[i + 1] = 1; else newShapeInfo[i + 1] = shapeInfo[i + 1]; } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } LongType newRank = rank - dimSize; if (newRank == 0 || (dimSize == 1 && dimsToExclude->at(0) == INT_MAX)) { // check whether given dimension is meant for the whole dimension if (supportOldShapes) { newShapeInfo = new sd::LongType[shape::shapeInfoLength(2)]; shape::shapeOldScalar(ArrayOptions::dataType(shapeInfo), newShapeInfo, 'c'); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } else { newShapeInfo = ShapeBuilders::createScalarShapeInfo(ArrayOptions::dataType(shapeInfo), workspace); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } } newShapeInfo = new sd::LongType[shape::shapeInfoLength(newRank)]; newShapeInfo[0] = newRank; // set rank LongType j = 1; for (LongType i = 0; i < rank; ++i) if (!std::binary_search(dimsToExclude->begin(), dimsToExclude->end(), i)) // dimsToExclude is already sorted after shape::checkDimensions() has been applied newShapeInfo[j++] = shapeInfo[i + 1]; // ensure whether vector has proper shape for old shape type if (newRank == 1 && supportOldShapes) { LongType oldValue = newShapeInfo[1]; delete[] newShapeInfo; newShapeInfo = new sd::LongType[shape::shapeInfoLength(2)]; newShapeInfo[0] = 2; if (dimsToExclude->at(0) == 0) { newShapeInfo[1] = 1; newShapeInfo[2] = oldValue; } else { newShapeInfo[1] = oldValue; newShapeInfo[2] = 1; } } updateStridesAndType(newShapeInfo, shapeInfo, order); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); delete[] newShapeInfo; return ret; } ////////////////////////////////////////////////////////////////////////// // evaluate shape for array which is result of repeat operation applied to arr std::vector ShapeUtils::evalRepeatShape(LongType axis, const std::vector& repeats, NDArray& arr) { if (axis < 0) axis += arr.rankOf(); if (repeats.size() != 1 && static_cast(repeats.size()) != arr.sizeAt(axis)) THROW_EXCEPTION( "ShapeUtils::evalRepeatShape: size of repeats vector must be 1 or equal to dimension at given axis !"); auto* shapeVec = arr.getShapeAsVector(); std::vector outShape = *shapeVec; delete shapeVec; if (repeats.size() == 1) outShape[axis] *= repeats[0]; else outShape[axis] = std::accumulate(repeats.begin(), repeats.end(), 0); return outShape; } ////////////////////////////////////////////////////////////////////////// // evaluate shapeInfo of permuted array LongType* ShapeUtils::evalPermShapeInfo(LongType* dimensions, LongType rank, NDArray* arr, memory::Workspace* workspace, const bool setContigStrides) { if (rank != arr->rankOf()) THROW_EXCEPTION("ShapeUtils::evalPermShapeInfo static method: wrong arguments: rank is not suitable!"); auto shapeInfoLength = shape::shapeInfoLength(rank); // allocate memory for new array - shapeInfo LongType* shapeInfoNew = nullptr; ALLOCATE(shapeInfoNew, workspace, shapeInfoLength, sd::LongType); // copy arr _shapeInfo into new array memcpy(shapeInfoNew, arr->shapeInfo(), shape::shapeInfoByteLength(rank)); // perform buffer permutation shape::doPermuteShapeInfo(shapeInfoNew, dimensions, rank); if (setContigStrides) { shape::updateStrides(shapeInfoNew, arr->ordering(), true); } shape::setOrder(shapeInfoNew, arr->ordering()); ArrayOptions::setDataType(shapeInfoNew, arr->dataType()); return shapeInfoNew; } ////////////////////////////////////////////////////////////////////////// bool ShapeUtils::copyVectorPart(std::vector& target, std::vector& source, LongType rank, LongType offset) { if (static_cast(source.size()) < offset + rank) return false; for (LongType e = offset; e < offset + rank; e++) target.push_back(source[e]); return true; } ////////////////////////////////////////////////////////////////////////// // check whether 2 arrays have mutually broadcastable shapes // shape comparison starts from the end bool ShapeUtils::areShapesBroadcastable(NDArray& arr1, NDArray& arr2) { return areShapesBroadcastable(arr1.shapeInfo(), arr2.shapeInfo()); } bool ShapeUtils::areShapesBroadcastable(const LongType* shapeInfo1, const LongType* shapeInfo2) { // Scalars can be broadcast with anything if (shape::isScalar(shapeInfo1) || shape::isScalar(shapeInfo2)) return true; LongType minRank = shape::rank(shapeInfo1) < shape::rank(shapeInfo2) ? shape::rank(shapeInfo1) : shape::rank(shapeInfo2); for (LongType i = -1; i >= -minRank; --i) if (shape::sizeAt(shapeInfo1, i) != shape::sizeAt(shapeInfo2, i) && shape::sizeAt(shapeInfo1, i) != 1 && shape::sizeAt(shapeInfo2, i) != 1) return false; return true; } bool ShapeUtils::areShapesBroadcastable(const std::vector& shape1, const std::vector& shape2) { const auto rank1 = shape1.size(); const auto rank2 = shape2.size(); // Scalars can be broadcast with anything if (rank1 == 0 || rank2 == 0) return true; const LongType minRank = rank1 < rank2 ? rank1 : rank2; for (LongType i = 1; i <= minRank; ++i) if (shape1[rank1 - i] != shape2[rank2 - i] && shape1[rank1 - i] != 1 && shape2[rank2 - i] != 1) return false; return true; } ////////////////////////////////////////////////////////////////////////// // check the possibility of broadcast operation, if true then return shapeInfo of resulting array // if evalMinMax == false the array with larger rank has to be passed as first argument bool ShapeUtils::evalBroadcastShapeInfo(NDArray& x, NDArray& y, const bool evalMinMax, LongType*& resultShapeInfo, memory::Workspace* workspace) { return evalBroadcastShapeInfo(x.shapeInfo(), y.shapeInfo(), evalMinMax, resultShapeInfo, workspace); } bool ShapeUtils::evalBroadcastShapeInfo( LongType* max, LongType* min, const bool evalMinMax, LongType*& resultShapeInfo, memory::Workspace* workspace) { // Scalars can be broadcast with anything - result shape is the non-scalar if (shape::isScalar(max) || shape::isScalar(min)) { if (shape::isScalar(max) && shape::isScalar(min)) { // Both scalars - use max resultShapeInfo = ConstantShapeHelper::getInstance().createFromExisting(max); } else if (shape::isScalar(max)) { // max is scalar, min is not - result is min's shape resultShapeInfo = ConstantShapeHelper::getInstance().createFromExisting(min); } else { // min is scalar, max is not - result is max's shape resultShapeInfo = ConstantShapeHelper::getInstance().createFromExisting(max); } return true; } // Handle empty arrays early - if either input has a dimension of size 0, result should be empty bool maxEmpty = shape::isEmptyConst(max); bool minEmpty = shape::isEmptyConst(min); if (shape::shapeEquals(max, min)) { const int len = shape::shapeInfoLength(shape::rank(max)); resultShapeInfo = new LongType[len]; const auto constCast = const_cast(resultShapeInfo); for (int i = 0; i < len; i++) { constCast[i] = max[i]; } resultShapeInfo = (ConstantShapeHelper::getInstance().bufferForShapeInfo(resultShapeInfo)->primary()); return true; } // sometimes we have 1 and 2d vectors if (shape::isVector(min) && shape::isVector(max) && shape::length(min) == shape::length(max)) { if (shape::rank(min) > shape::rank(max)) { resultShapeInfo = ConstantShapeHelper::getInstance().createFromExisting(min); return true; } resultShapeInfo = ConstantShapeHelper::getInstance().createFromExisting(max); return true; } // check whether broadcast operation is possible for input arrays if (!areShapesBroadcastable(max, min)) { std::string errorMessage; errorMessage += "ShapeUtils::evalBroadcastShapeInfo: shapes are not broadcastable!\n"; errorMessage += "Shape 1: "; errorMessage += ShapeUtils::shapeAsString(max); errorMessage += "\nShape 2: "; errorMessage += ShapeUtils::shapeAsString(min); errorMessage += "\n"; THROW_EXCEPTION(errorMessage.c_str()); } auto maxShapeInfo = max; auto minShapeInfo = min; if (evalMinMax && (shape::rank(max) < shape::rank(min))) { maxShapeInfo = min; minShapeInfo = max; } const auto maxRank = shape::rank(maxShapeInfo); const auto minRank = shape::rank(minShapeInfo); // evaluate shapeInfo for resulting array if (resultShapeInfo != nullptr) THROW_EXCEPTION( "std::runtime_error(ShapeUtils::evalBroadcastShapeInfo method: the input pointer on shapeInfo must be empty " "(=nullptr) !"); LongType* tmpShapeInfo = nullptr; ALLOCATE(tmpShapeInfo, workspace, shape::shapeInfoLength(maxRank), sd::LongType); memcpy(tmpShapeInfo, maxShapeInfo, shape::shapeInfoByteLength(maxRank)); // Handle dimension broadcasting - dimension size 0 should be preserved (empty arrays) // Compare dimensions from right to left (broadcasting semantics) for (LongType i = 0; i < minRank; ++i) { // Get dimensions from the end: -1 means last dim, -2 means second-to-last, etc. LongType maxDim = shape::sizeAt(maxShapeInfo, -1 - i); LongType minDim = shape::sizeAt(minShapeInfo, -1 - i); // If either dimension is 0, result should be 0 (empty array) if (maxDim == 0 || minDim == 0) { tmpShapeInfo[1 + maxRank - 1 - i] = 0; } // Otherwise follow standard broadcasting rules else if (maxDim < minDim) { tmpShapeInfo[1 + maxRank - 1 - i] = minDim; } } updateStridesAndType(tmpShapeInfo, DataTypeUtils::pickPairwiseResultType(maxShapeInfo, minShapeInfo), shape::order(maxShapeInfo)); if (maxEmpty || minEmpty) { ArrayOptions::setPropertyBit(tmpShapeInfo, ARRAY_EMPTY); memset(shape::stride(tmpShapeInfo), 0, shape::rank(tmpShapeInfo) * sizeof(LongType)); } resultShapeInfo = (ConstantShapeHelper::getInstance().bufferForShapeInfo(tmpShapeInfo)->primary()); delete[] tmpShapeInfo; return true; } ////////////////////////////////////////////////////////////////////////// // evaluate shapeInfo for resulting array from tile operation LongType* ShapeUtils::evalTileShapeInfo(NDArray& arr, const std::vector& reps, memory::Workspace* workspace) { // check whether reps contains at least one zero (then throw exception) or whether all elements in reps are unities // (then simply reshape or do nothing) LongType repsSize = reps.size(); LongType product = 1; for (const auto& item : reps) product *= item; if (product == 0) THROW_EXCEPTION("NDArray::tile method: one of the elements in reps array is zero !"); LongType rankOld = arr.rankOf(); LongType diff = rankOld - repsSize; // evaluate new shapeInfo LongType* newShapeInfo = nullptr; if (diff < 0) { ALLOCATE(newShapeInfo, workspace, shape::shapeInfoLength(repsSize), sd::LongType); newShapeInfo[0] = repsSize; // set new rank for (LongType i = 1; i <= -diff; ++i) newShapeInfo[i] = 1; // set unities to be new dimensions at left-hand side of newShapeInfo shape place memcpy(newShapeInfo + 1 - diff, arr.shapeInfo() + 1, rankOld * sizeof(LongType)); // copy old dimensions to the right-hand side of newShapeInfo shape place for (LongType i = 1; i <= repsSize; ++i) newShapeInfo[i] *= reps[i - 1]; // set new shape by multiplying old dimensions by corresponding numbers from reps } else { ALLOCATE(newShapeInfo, workspace, shape::shapeInfoLength(rankOld), sd::LongType); memcpy(newShapeInfo, arr.shapeInfo(), shape::shapeInfoByteLength(rankOld)); // copy all elements of _shapeInfo to newShapeInfo for (LongType i = 1; i <= repsSize; ++i) newShapeInfo[rankOld + 1 - i] *= reps[repsSize - i]; // set new shape by multiplying old dimensions by corresponding numbers from reps } shape::updateStrides(newShapeInfo, arr.ordering(), false); ArrayOptions::setDataType(newShapeInfo, arr.dataType()); auto ret = ConstantShapeHelper::getInstance().bufferForShapeInfo(newShapeInfo)->primary(); RELEASE(newShapeInfo, workspace); return ret; } std::vector ShapeUtils::pullShapeFromShapeInfo(const LongType* shapeInfo) { std::vector shape(shape::rank(shapeInfo)); LongType shapeSize = shape.size(); for (LongType e = 0; e < shapeSize; e++) shape[e] = shape::shapeOf(shapeInfo)[e]; return shape; } std::string ShapeUtils::shapeAsString(NDArray* array) { if (array->rankOf() == 0 && !array->isEmpty()) return "[0]"; std::string result; result.append("["); for (LongType e = 0; e < array->rankOf(); e++) { result += flatbuffers::NumToString(array->sizeAt(e)); if (e < array->rankOf() - 1) result.append(", "); } result.append("]"); return result; } std::string ShapeUtils::shapeAsString(const std::vector& shape) { std::string result; result.append("["); for (size_t e = 0; e < shape.size(); e++) { result += flatbuffers::NumToString(shape.at(e)); if (e < shape.size() - 1) result.append(", "); } result.append("]"); return result; } std::string ShapeUtils::shapeAsString(const LongType* shapeInfo) { if (shapeInfo == nullptr) THROW_EXCEPTION("ShapeUtils::shapeAsString method: input shapeInfo must not be nullptr !"); if (shapeInfo[0] < 0 || shapeInfo[0] > SD_MAX_RANK) { THROW_EXCEPTION( "Shape info appears to be corrupt. Shape info[0] is less than 0 or greater than 32. Might have been " "deallocated."); } std::string result; result.append("["); for (LongType e = 0; e < shapeInfo[0]; e++) { result += flatbuffers::NumToString(shapeInfo[e + 1]); if (e < shapeInfo[0] - 1) result.append(", "); } result.append("]"); return result; } std::string ShapeUtils::shapeInfoAsString(const LongType* shapeInfo) { if (!shapeInfo) THROW_EXCEPTION("ShapeUtils::shapeAsString method: input shapeInfo must not be nullptr !"); std::string result; // Stack allocation instead of heap LongType len = shape::shapeInfoLength(shapeInfo[0]); result.append("["); for (LongType e = 0; e < len; e++) { result.append(flatbuffers::NumToString(shapeInfo[e])); if (e < len - 1) result.append(", "); } result.append("]"); return result; // Return by value (move semantics will optimize) } std::string ShapeUtils::shapeAsString(const LongType rank, const LongType* shapeInfo) { if (!shapeInfo) THROW_EXCEPTION("ShapeUtils::shapeAsString method: input shapeInfo must not be nullptr !"); std::string result; result.append("["); for (LongType e = 0; e < rank; e++) { result += flatbuffers::NumToString(shapeInfo[e]); if (e < rank - 1) result.append(", "); } result.append("]"); return result; } ////////////////////////////////////////////////////////////////////////// std::vector ShapeUtils::shapeAsVector(const LongType* shapeInfo) { if (!shapeInfo) THROW_EXCEPTION("ShapeUtils::shapeAsVector method: input shapeInfo must not be nullptr !"); std::vector vector(shapeInfo[0]); for (LongType e = 0; e < shapeInfo[0]; e++) vector[e] = shapeInfo[e + 1]; return vector; } ////////////////////////////////////////////////////////////////////////// // evaluate shapeInfo for diagonal array which is made using input arr elements as diagonal LongType* ShapeUtils::evalDiagShapeInfo(LongType* shapeInfoConst, memory::Workspace* workspace) { auto shapeInfo = const_cast(shapeInfoConst); const auto rank = shape::rank(shapeInfo); LongType* outputShapeInfo = nullptr; if (shape::isVector(shapeInfo) || shape::isScalar(shapeInfo)) { ALLOCATE(outputShapeInfo, workspace, shape::shapeInfoLength(2), sd::LongType); outputShapeInfo[0] = 2; outputShapeInfo[1] = outputShapeInfo[2] = shape::length(shapeInfo); } else { ALLOCATE(outputShapeInfo, workspace, shape::shapeInfoLength(2 * rank), sd::LongType); outputShapeInfo[0] = 2 * rank; for (LongType i = 1; i <= rank; ++i) outputShapeInfo[i] = outputShapeInfo[i + rank] = shapeInfo[i]; } updateStridesAndType(outputShapeInfo, shapeInfo, shape::order(shapeInfo)); auto nonConstShape = const_cast(outputShapeInfo); auto result = ConstantShapeHelper::getInstance().bufferForShapeInfo(nonConstShape); RELEASE(outputShapeInfo, workspace); return result->primary(); } std::vector ShapeUtils::evalBroadcastBackwardAxis(const LongType* operand, const LongType* result) { // rRank >= oRank always !! const auto oRank = shape::rank(operand); const auto rRank = shape::rank(result); const auto diff = rRank - oRank; std::vector axis; for (LongType i = 0; i < rRank; ++i) if (i < diff || shape::sizeAt(operand, i - diff) != shape::sizeAt(result, i)) axis.push_back(i); return axis; } //////////////////////////////////////////////////////////////////////////////// LongType* ShapeUtils::matrixProductShape(LongType* theFirstShape, LongType* theSecondShape, bool shouldTranspondFirst, bool shouldTranspondSecond, DataType dtype, memory::Workspace* workspace) { auto inA = theFirstShape; auto inB = theSecondShape; LongType* shape; ALLOCATE(shape, workspace, shape::shapeInfoLength(2), sd::LongType); LongType* tmpA = ShapeBuilders::copyShapeInfo(inA, true, workspace); LongType* tmpB = ShapeBuilders::copyShapeInfo(inB, true, workspace); if (shouldTranspondFirst) shape::transposeInplace(tmpA); if (shouldTranspondSecond) shape::transposeInplace(tmpB); if (shape::rank(tmpA) == 1 && shape::isMatrix(tmpB)) { // special case here shape[0] = 1; shape[1] = tmpB[2]; LongType* newShape = ShapeBuilders::createShapeInfo(dtype, 'f', 2, shape, workspace, false); RELEASE(shape, workspace); RELEASE(tmpA, workspace); RELEASE(tmpB, workspace); return newShape; } else if (shape::isScalar(tmpA) && shape::isScalar(tmpB)) { // just scalar vs scalar shape[0] = 1; shape[1] = 1; } else if (shape::isMatrix(tmpA) && shape::isVector(tmpB)) { // gemv case if (shape::rank(tmpB) == 2) { shape[0] = tmpA[1]; shape[1] = tmpB[2]; } else { // we have new 1D shape here auto newShape = ShapeBuilders::createVectorShapeInfo(dtype, tmpA[1], workspace); RELEASE(shape, workspace); RELEASE(tmpA, workspace); RELEASE(tmpB, workspace); return newShape; } } else if ((shape::isMatrix(tmpA) && shape::isMatrix(tmpB)) || (shape::isVector(tmpA) && shape::isMatrix(tmpB)) || (shape::isColumnVector(tmpA) && shape::isVector(tmpB))) { // gemm case shape[0] = tmpA[1]; shape[1] = tmpB[2]; } else if ((shape::isVector(tmpA) && shape::isScalar(tmpB)) || (shape::isScalar(tmpA) && shape::isVector(tmpB))) { // element-wise shape[0] = 1; shape[1] = (LongType)sd::math::sd_max(shape::length(tmpA), shape::length(tmpB)); } else if (shape::isRowVector(tmpA) && shape::isRowVector(tmpB)) { // dot case shape[0] = 1; shape[1] = 1; } else if (shape::isRowVector(tmpA) && shape::isColumnVector(tmpB)) { // dot case shape[0] = 1; shape[1] = 1; } auto newShape = ConstantShapeHelper::getInstance().createShapeInfo(dtype, 'f', 2, shape, -1); RELEASE(shape, workspace); RELEASE(tmpA, workspace); RELEASE(tmpB, workspace); return newShape; } //////////////////////////////////////////////////////////////////////////////// std::vector ShapeUtils::composeShapeUsingDimsAndIdx(const std::vector& dimsAndIdx) { auto size = dimsAndIdx.size(); if (size % 2 != 0) THROW_EXCEPTION("ShapeUtils::composeShapeUsingDimsAndIdx static method: the size of input vector must be even !"); size /= 2; std::vector shape(size); LongType index; for (LongType i = 0; i < static_cast(size); ++i) { index = dimsAndIdx[i + size]; if (index > static_cast(size - 1)) THROW_EXCEPTION("ShapeUtils::composeShapeUsingDimsAndIdx static method: input index is too large !"); shape[index] = dimsAndIdx[i]; } return shape; } //////////////////////////////////////////////////////////////////////////////// std::vector ShapeUtils::evalShapeForMatmul(const LongType* xShapeInfo, const LongType* yShapeInfo, const bool transX, const bool transY) { const auto xRank = xShapeInfo[0]; const auto yRank = yShapeInfo[0]; const LongType x0Dim = transX ? xShapeInfo[xRank] : xShapeInfo[xRank - 1]; const LongType y0Dim = transY ? yShapeInfo[yRank] : yShapeInfo[yRank - 1]; const LongType x1Dim = transX ? xShapeInfo[xRank - 1] : xShapeInfo[xRank]; const LongType y1Dim = transY ? yShapeInfo[yRank - 1] : yShapeInfo[yRank]; if (xRank == 1 && yRank == 1) { // dot case, output is scalar if (xShapeInfo[1] != yShapeInfo[1]) { sd_printf( "ShapeUtils::evalShapeForMatmul method: since input arrays are vectors they must have the same length, but " "got x length = %i, y length = %i !", xShapeInfo[1], yShapeInfo[1]); THROW_EXCEPTION(""); } return std::vector({}); } if (xRank == 1 && yRank == 2) { // vector x matrix, i.e. [4] x [4,5] = [5], output is vector if (xShapeInfo[1] != y0Dim) { sd_printf( "ShapeUtils::evalShapeForMatmul method: input arrays have inconsistent shapes for vector-matrix product: x " "%s, y %s !", ShapeUtils::shapeAsString(xShapeInfo).c_str(), ShapeUtils::shapeAsString(yShapeInfo).c_str()); THROW_EXCEPTION(""); } return std::vector({y1Dim}); } if (xRank == 2 && yRank == 1) { // matrix x vector , i.e. [4,5] x [5] = [4], output is vector if (x1Dim != yShapeInfo[1]) { sd_printf( "ShapeUtils::evalShapeForMatmul method: input arrays have inconsistent shapes for vector-matrix product: x " "%s, y %s !", ShapeUtils::shapeAsString(xShapeInfo).c_str(), ShapeUtils::shapeAsString(yShapeInfo).c_str()); THROW_EXCEPTION(""); } return std::vector({x0Dim}); } // rest cases - usual 2Dx2D or batched mmul // Handle rank mismatch when one input has singleton leading dimensions // This supports ONNX Gemm patterns like [1,1,1,768] x [768,768] -> [1,1,1,768] if (xRank != yRank) { // Check if higher-rank input has all singleton leading dims that can be squeezed const LongType* higherRankInfo = xRank > yRank ? xShapeInfo : yShapeInfo; const LongType* lowerRankInfo = xRank > yRank ? yShapeInfo : xShapeInfo; const auto higherRank = xRank > yRank ? xRank : yRank; const auto lowerRank = xRank > yRank ? yRank : xRank; const auto rankDiff = higherRank - lowerRank; // Check if all leading dimensions are singletons (size 1) bool allLeadingSingleton = true; for (LongType i = 0; i < rankDiff; ++i) { if (higherRankInfo[i + 1] != 1) { allLeadingSingleton = false; break; } } if (allLeadingSingleton && lowerRank == 2) { // Can treat as 2D matmul with singleton batch dims preserved in output // For x having higher rank: x[1,1,...,M,K] @ y[K,N] -> [1,1,...,M,N] // For y having higher rank: x[M,K] @ y[1,1,...,K,N] -> [1,1,...,M,N] LongType outM, outN, xK, yK; if (xRank > yRank) { // x is higher rank [1,1,...,M,K], y is 2D [K,N] // Get M and K from x's last 2 dimensions const LongType xSecondLast = higherRankInfo[higherRank - 1]; // M (or K if transposed) const LongType xLast = higherRankInfo[higherRank]; // K (or M if transposed) // Get K and N from y const LongType yFirst = lowerRankInfo[1]; // K (or N if transposed) const LongType ySecond = lowerRankInfo[2]; // N (or K if transposed) if (transX) { outM = xLast; xK = xSecondLast; } else { outM = xSecondLast; xK = xLast; } if (transY) { yK = ySecond; outN = yFirst; } else { yK = yFirst; outN = ySecond; } } else { // y is higher rank [1,1,...,K,N], x is 2D [M,K] // Get M and K from x const LongType xFirst = lowerRankInfo[1]; // M (or K if transposed) const LongType xSecond = lowerRankInfo[2]; // K (or M if transposed) // Get K and N from y's last 2 dimensions const LongType ySecondLast = higherRankInfo[higherRank - 1]; // K (or N if transposed) const LongType yLast = higherRankInfo[higherRank]; // N (or K if transposed) if (transX) { outM = xSecond; xK = xFirst; } else { outM = xFirst; xK = xSecond; } if (transY) { yK = yLast; outN = ySecondLast; } else { yK = ySecondLast; outN = yLast; } } // Validate K dimensions match if (xK != yK) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForMatmul static method: the dimensions of arrays are inconsistent: "; errorMessage += "xShape = " + shapeAsString(xShapeInfo) + ", "; errorMessage += "yShape = " + shapeAsString(yShapeInfo); errorMessage += " (xK=" + std::to_string(xK) + ", yK=" + std::to_string(yK) + ") ! \n"; THROW_EXCEPTION(errorMessage.c_str()); } std::vector cShape; // Preserve leading singleton dimensions from the higher-rank input for (LongType i = 0; i < rankDiff; ++i) { cShape.push_back(1); } // Add the matrix dimensions [M, N] cShape.push_back(outM); cShape.push_back(outN); return cShape; } else { sd_printf( "ShapeUtils::evalShapeForMatmul static method: the ranks of arrays must be the same, but got xRank = %i and " "yRank = %i ! \n", xRank, yRank); THROW_EXCEPTION(""); } } if (x1Dim != y0Dim) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForMatmul static method: the dimensions of arrays are inconsistent: "; errorMessage += "xShape = " + shapeAsString(xShapeInfo) + ", "; errorMessage += "yShape = " + shapeAsString(yShapeInfo) + " ! \n"; THROW_EXCEPTION(errorMessage.c_str()); } for (LongType i = 0; i < xRank - 2; ++i) if (xShapeInfo[i + 1] != yShapeInfo[i + 1]) { std::string errorMessage; errorMessage += "ShapeUtils::evalShapeForMatmul static method: the dimensions of arrays are inconsistent: "; errorMessage += "xShape = " + shapeAsString(xShapeInfo) + ", "; errorMessage += "yShape = " + shapeAsString(yShapeInfo) + " ! \n"; THROW_EXCEPTION(errorMessage.c_str()); } std::vector cShape; for(int i = 0; i < xRank - 2; i++) { cShape.push_back(shape::sizeAt(xShapeInfo, i)); } cShape.push_back(x0Dim); cShape.push_back(y1Dim); return cShape; } //////////////////////////////////////////////////////////////////////////////// LongType ShapeUtils::getNumOfSubArrs(const LongType* shapeInfo, const std::vector& dimsToExclude) { LongType numOfSubArrs = 1; if (static_cast(dimsToExclude.size()) == shape::rank(shapeInfo) || dimsToExclude.size() == 0) // means there is only one sub-array and it coincides with whole array return numOfSubArrs; for (const auto& dim : dimsToExclude) numOfSubArrs *= shapeInfo[dim + 1]; return numOfSubArrs; } //////////////////////////////////////////////////////////////////////////////// void ShapeUtils::updateStridesAndType(LongType* dest, const LongType* source, const char order) { shape::updateStrides(dest, order, false); dest[2 * dest[0] + 1] = 0; // zero extra ArrayOptions::copyDataType(dest, source); } //////////////////////////////////////////////////////////////////////////////// void ShapeUtils::updateStridesAndType(LongType* dest, const DataType dtype, const char order) { shape::updateStrides(dest, order, true); ArrayOptions::setDataType(dest, dtype); } bool ShapeUtils::areShapesEqual(const LongType* shapeInfo, const std::vector& shapeOnly) { LongType rank = shape::rank(shapeInfo); if (rank != static_cast(shapeOnly.size())) { return false; } sd::LongType *inputShapeOnly = shape::shapeOf(shapeInfo); for (LongType i = 0; i < rank; ++i) { if (inputShapeOnly[i] != shapeOnly[i]) { return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// std::vector* ShapeUtils::evalDimsForReduceOp(const LongType rank, const std::vector* dimsToExclude) { std::vector* dims = evalDimsToExclude(rank, dimsToExclude->size(), dimsToExclude->data()); std::vector* output = new std::vector(*dims); LongType dimsExcludeLen = static_cast(dimsToExclude->size()); for (LongType j = 0; j < dimsExcludeLen; j++) { LongType currElement = dimsToExclude->at(j); bool contains = false; for (size_t i = 0; i < output->size(); i++) { if (output->at(i) == currElement) { contains = true; break; } else { contains = false; } } bool elementLess = currElement < rank; if (!contains && elementLess) { output->push_back(dimsToExclude->at(j)); } } delete dims; return output; } //////////////////////////////////////////////////////////////////////////////// } // namespace sd