Files
deeplearning4j--deeplearning4j/libnd4j/include/ops/declarable/generic/tensor/create_view.cpp
T
2026-07-13 12:47:05 +08:00

180 lines
6.1 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
******************************************************************************/
//
// @author Adam Gibson
//
#include <system/op_boilerplate.h>
#include <indexing/NDIndexUtils.h>
#if NOT_EXCLUDED(OP_create_view)
#include <ops/declarable/CustomOperations.h>
namespace sd {
namespace ops {
CUSTOM_OP_IMPL(create_view, -2, -1, true, 0, -2) {
auto inputBase = INPUT_VARIABLE(0);
auto numNewAxis = 0;
auto numPoint = 0;
auto indicesPerIndex = std::vector<std::vector<LongType>>();
auto indexTypes = std::vector<LongType>();
auto numIndicesPerIndex = std::vector<LongType>();
auto inclusive = std::vector<LongType>();
auto baseOffset = inputBase->offset();
auto outIdx = 0;
auto inIdx = 0;
std::vector<std::vector<LongType>> indexVectors;
//note we iterate from i + 1 for each input so we only go to block input size - 1
for (size_t i = 0; i < block.width() - 1; i++) {
//first element is the input we are creating the view from
auto inputIndex = INPUT_VARIABLE(i + 1);
auto indexVector = inputIndex->asVectorT<LongType>();
indexVectors.push_back(indexVector);
auto indexType = indexVector[0];
if(indexType == POINT_TYPE) {
numPoint++;
inclusive.push_back(1);
} else if(indexType == INTERVAL_TYPE) {
//the end indicates inclusive or not
inclusive.push_back(indexVector[indexVector.size() - 1]);
} else if(indexType == ALL_TYPE) {
inclusive.push_back(1);
} else if(indexType == NEW_AXIS) {
numNewAxis++;
inclusive.push_back(1);
}
}
auto outRank = inputBase->rankOf() + numNewAxis - numPoint;
auto outputShape = std::vector<LongType>(outRank);
auto outputStrides = std::vector<LongType>(outRank);
auto numIndices = block.width() - 1;
auto all = NDIndexUtils::createAll();
// Padding remaining dimensions with all() index if too few indices provided
if (numIndices - numNewAxis < static_cast<size_t>(inputBase->rankOf())) {
for (int e = numIndices; e < inputBase->rankOf() + numNewAxis; e++) {
indexTypes.push_back(ALL_TYPE);
indexVectors.push_back(all->asVectorT<LongType>());
}
}
for (size_t i = 0; i < indexVectors.size(); i++) {
auto indexVector = indexVectors[i];
auto indexType = indexVector[0];
auto currDimension = i;
indexTypes.push_back(indexType);
auto stride = indexVector[2];
//point should start at 3 for indices, interval is 4 (start,end)
auto indexIndices = std::vector<LongType>();
int indexOffset = 3;
//accumulate the target indices
//prevent out of bounds
for (size_t j = 0; j < indexVector.size() - indexOffset; j++) {
indexIndices.push_back(indexVector[j + indexOffset]);
}
indicesPerIndex.push_back(indexVector);
if(indexType == POINT_TYPE) { //point index
//Point indexes don't appear in output
auto pointOffset = indexIndices[i];
baseOffset += pointOffset * ( inputBase->strideAt(inIdx));
inIdx++;
} else if(indexType == ALL_TYPE) { // all index
//All index: doesn't change offset. Axis is in both in and output arrays
outputShape[outIdx] = inputBase->sizeAt(inIdx);
outputStrides[outIdx] = inputBase->strideAt(inIdx);
inIdx++;
outIdx++;
} else if(indexType == INTERVAL_TYPE) { //interval index
//Interval index: Axis is in both in and output arrays, but output might be smaller
auto start = indexIndices[0];
auto end = indexIndices[1];
auto endInc = end - (inclusive[currDimension] > 0 ? 0 : 1);
if (endInc > inputBase->sizeAt(inIdx)) {
std::string errorMessage;
errorMessage += "CREATE_VIEW: Indices are out of range: Cannot get interval index ";
errorMessage += std::to_string(endInc);
errorMessage += " on dimension ";
errorMessage += std::to_string(inputBase->sizeAt(inIdx));
THROW_EXCEPTION(errorMessage.c_str());
}
auto length = (endInc - start) / stride + 1;
baseOffset += start * inputBase->strideAt(inIdx);
outputShape[outIdx] = length;
outputStrides[outIdx] = stride * inputBase->strideAt(inIdx);
inIdx++;
outIdx++;
} else if(indexType == NEW_AXIS) {
//New axis: appends a 1 in shape. Axis not present in input, but is present in output
outputShape[outIdx] = 1;
if (outIdx > 0) { //Stride doesn't matter for 1 size axis anyway...
outputStrides[outIdx] = outputStrides[outIdx - 1];
} else {
outputStrides[outIdx] = 1;
}
outIdx++;
}
}
delete all;
auto outputLength = shape::prodLong(outputShape.data(),outRank);
auto newResult = new NDArray(inputBase->dataBuffer(),'c',outputShape,inputBase->dataType(),inputBase->getContext(),false,true,baseOffset);
//note we pass in delete false here so we don't cause a double free
//overwrite first calls push ndarray which has an option to delete the array if it's not relevant
//we also call delete later when it's removable.
if(block.isFastPath() && block.fastpath_out().size() > 0) {
OVERWRITE_RESULT_NO_DELETE(newResult);
} else if(block.isFastPath() && block.fastpath_out().size() < 1) {
STORE_RESULT(newResult);
}
return Status::OK;
}
DECLARE_SHAPE_FN(create_view) {
auto shapeInput = INPUT_VARIABLE(0);
return SHAPELIST(shapeInput->shapeInfo());
}
DECLARE_TYPES(create_view) { getOpDescriptor()->setAllowedInputTypes({ANY})->setAllowedOutputTypes(ANY); }
} // namespace ops
} // namespace sd
#endif