/* ****************************************************************************** * * * 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, created on 16.04.2018 // #include #include #include #include #if NOT_EXCLUDED(OP_reverse) namespace sd { namespace ops { namespace helpers { template inline void swap(T* arr, sd::LongType from, sd::LongType to) { T tmp = arr[from]; arr[from] = arr[to]; arr[to] = tmp; } ///////////////////////////////////////////////////////////////////////////////////// // this legacy op is written by raver119@gmail.com template static void reverseArray(sd::LaunchContext* context, void const* vinArr, sd::LongType const* inShapeBuffer, void* voutArr, sd::LongType const* outShapeBuffer, int numOfElemsToReverse = 0) { auto inArr = reinterpret_cast(vinArr); auto outArr = reinterpret_cast(voutArr); // Cache shape information const auto inRank = shape::rank(inShapeBuffer); const auto outRank = shape::rank(outShapeBuffer); const auto* inShape = shape::shapeOf(inShapeBuffer); const auto* outShape = shape::shapeOf(outShapeBuffer); const auto* inStride = shape::stride(inShapeBuffer); const auto* outStride = shape::stride(outShapeBuffer); sd::LongType inLength = shape::length(inShapeBuffer); sd::LongType outLength = shape::length(outShapeBuffer); if (numOfElemsToReverse == 0) numOfElemsToReverse = inLength; sd::LongType sLength = numOfElemsToReverse - 1; LongType inCoords[SD_MAX_RANK]; LongType outCoords[SD_MAX_RANK]; LongType inOffset; LongType outOffset; // two step phase here if (inArr == outArr) { auto func = PRAGMA_THREADS_FOR { for (sd::LongType e = start; e < stop; e++) { INDEX2COORDS(e, inRank, inShape, inCoords); COORDS2INDEX(inRank, inStride, inCoords, inOffset); INDEX2COORDS(sLength - e, inRank, inShape, outCoords); COORDS2INDEX(inRank, inStride, outCoords, outOffset); swap(const_cast(inArr), inOffset, outOffset); } }; samediff::Threads::parallel_for(func, 0, numOfElemsToReverse / 2); } else { // single step phase here auto func = PRAGMA_THREADS_FOR { for (sd::LongType e = start; e < stop; e++) { INDEX2COORDS(e, inRank, inShape, inCoords); COORDS2INDEX(inRank, inStride, inCoords, inOffset); INDEX2COORDS(sLength - e, outRank, outShape, outCoords); COORDS2INDEX(outRank, outStride, outCoords, outOffset); outArr[outOffset] = inArr[inOffset]; } }; samediff::Threads::parallel_for(func, 0, numOfElemsToReverse); if (inLength != numOfElemsToReverse) { auto f2 = PRAGMA_THREADS_FOR { for (sd::LongType e = start; e < stop; e++) { INDEX2COORDS(e, inRank, inShape, inCoords); COORDS2INDEX(inRank, inStride, inCoords, inOffset); INDEX2COORDS(e, outRank, outShape, outCoords); COORDS2INDEX(outRank, outStride, outCoords, outOffset); outArr[outOffset] = inArr[inOffset]; } }; samediff::Threads::parallel_for(f2, numOfElemsToReverse, inLength); } } } /////////////////////////////////////////////////////////////////// template static void reverseSequence_(sd::LaunchContext* context, NDArray* input, NDArray* seqLengths, NDArray* output, int seqDim, const int batchDim) { int posOfNonUnityDim = -1; if (input->isVector() || shape::isLikeVector(input->shapeInfo(), posOfNonUnityDim)) { if ((seqDim == 0 && input->sizeAt(0) == 1) || (batchDim == posOfNonUnityDim)) output->assign(input); else helpers::reverseArray(context, const_cast(input)->buffer(), const_cast(input)->shapeInfo(), output->buffer(), output->shapeInfo(), seqLengths->e(0)); } else { if (seqDim > batchDim) --seqDim; std::vector batchDimVec = {batchDim}; std::vector *dimensions = ShapeUtils::evalDimsToExclude(input->rankOf(), 1,batchDimVec.data()); auto inSubArrsSet = input->allTensorsAlongDimension(*dimensions); auto outSubArrsSet = output->allTensorsAlongDimension(*dimensions); delete dimensions; for (int i = 0; i < inSubArrsSet.size(); ++i) { sd::LongType numOfElemsToReverse = seqLengths->e(i); if (numOfElemsToReverse == 0 || numOfElemsToReverse == 1) { outSubArrsSet.at(i)->assign(inSubArrsSet.at(i)); } else { auto inInnerSet = inSubArrsSet.at(i)->allTensorsAlongDimension({seqDim}); auto outInnerSet = outSubArrsSet.at(i)->allTensorsAlongDimension({seqDim}); for (int j = 0; j < inInnerSet.size(); ++j) helpers::reverseArray(context, inInnerSet.at(j)->buffer(), inInnerSet.at(j)->shapeInfo(), outInnerSet.at(j)->buffer(), outInnerSet.at(j)->shapeInfo(), numOfElemsToReverse); } } } } void reverseSequence(sd::LaunchContext* context, NDArray* input, NDArray* seqLengths, NDArray* output, int seqDim, const int batchDim) { BUILD_SINGLE_SELECTOR(input->dataType(), reverseSequence_, (context, input, seqLengths, output, seqDim, batchDim), SD_COMMON_TYPES); } ////////////////////////////////////////////////////////////////////////// void reverse(sd::LaunchContext* context, NDArray* input, NDArray* output, const std::vector* intArgs) { auto listOut = output->allTensorsAlongDimension(*intArgs); auto listIn = input->allTensorsAlongDimension(*intArgs); NDArray *subArrIn, *subArrOut; for (int i = 0; i < listIn.size(); ++i) { // listIn.size() = listOut.size() subArrIn = listIn.at(i); subArrOut = listOut.at(i); BUILD_SINGLE_SELECTOR( input->dataType(), helpers::reverseArray, (context, subArrIn->buffer(), subArrIn->shapeInfo(), subArrOut->buffer(), subArrOut->shapeInfo()), SD_COMMON_TYPES); } } BUILD_SINGLE_TEMPLATE( void reverseSequence_, (sd::LaunchContext * context, NDArray* input, NDArray* seqLengths, NDArray* output, int seqDim, const int batchDim), SD_COMMON_TYPES); BUILD_SINGLE_TEMPLATE( void reverseArray, (sd::LaunchContext * context, void const* inArr, sd::LongType const* inShapeBuffer, void* outArr, sd::LongType const* outShapeBuffer, int numOfElemsToReverse), SD_COMMON_TYPES); } // namespace helpers } // namespace ops } // namespace sd #endif