Files
2026-07-13 12:47:05 +08:00

147 lines
3.7 KiB
C++

//
// Created by agibsonccc on 8/30/24.
//
#ifndef LIBND4J_RESHAPENOCOPY_H
#define LIBND4J_RESHAPENOCOPY_H
#include <system/op_boilerplate.h>
#include <helpers/reshapeNoCopy.h>
#include <helpers/shape.h>
#include <array/ArrayOptions.hXX>
namespace sd {
namespace ops {
namespace helpers {
bool reshapeNoAlloc(const sd::LongType* inShape,
const std::vector<sd::LongType>& newShape,
char order,
sd::LongType* outShape) {
LongType oldnd = shape::rank(inShape);
std::vector<sd::LongType> olddims(oldnd);
std::vector<sd::LongType> oldstrides(oldnd);
sd::LongType np, op, last_stride;
int oi, oj, ok, ni, nj, nk;
std::vector<sd::LongType> newStrides(newShape.size());
int newnd = newShape.size();
bool isFOrder = order == 'f';
// FIX: Set data type early, before any return statements
// This ensures data type is preserved even for empty arrays
if(ArrayOptions::numDataTypesSet(ArrayOptions::extra(outShape)) < 1) {
ArrayOptions::setDataType(outShape, ArrayOptions::dataType(inShape));
}
// Remove axes with dimension 1 from the old array
int actual_oldnd = 0;
for (oi = 0; oi < oldnd; oi++) {
if (shape::shapeOf(inShape)[oi] != 1) {
olddims[actual_oldnd] = shape::shapeOf(inShape)[oi];
oldstrides[actual_oldnd] = shape::stride(inShape)[oi];
actual_oldnd++;
}
}
oldnd = actual_oldnd;
np = 1;
for (ni = 0; ni < newnd; ni++) {
np *= newShape[ni];
}
op = 1;
for (oi = 0; oi < oldnd; oi++) {
op *= olddims[oi];
}
if (np != op) {
return false; // total sizes must match
}
if (np == 0) {
// FIX: Data type has already been set above, so empty arrays will have correct type
return false; // don't support empty arrays
}
// oi to oj and ni to nj give the axis ranges currently worked with
oi = 0;
oj = 1;
ni = 0;
nj = 1;
while (ni < newnd && oi < oldnd) {
np = newShape[ni];
op = olddims[oi];
while (np != op) {
if (np < op) {
np *= newShape[nj++];
} else {
op *= olddims[oj++];
}
}
// Check whether the original axes can be combined
for (ok = oi; ok < oj - 1; ok++) {
if (isFOrder) {
if (oldstrides[ok + 1] != olddims[ok] * oldstrides[ok]) {
return false; // not contiguous enough
}
} else {
// C order
if (oldstrides[ok] != olddims[ok + 1] * oldstrides[ok + 1]) {
return false; // not contiguous enough
}
}
}
// Calculate new strides for all axes currently worked with
if (isFOrder) {
newStrides[ni] = oldstrides[oi];
for (nk = ni + 1; nk < nj; nk++) {
newStrides[nk] = newStrides[nk - 1] * newShape[nk - 1];
}
} else {
// C order
newStrides[nj - 1] = oldstrides[oj - 1];
for (nk = nj - 1; nk > ni; nk--) {
newStrides[nk - 1] = newStrides[nk] * newShape[nk];
}
}
ni = nj++;
oi = oj++;
}
// Set strides corresponding to trailing 1s of the new shape
if (ni >= 1) {
last_stride = newStrides[ni - 1];
} else {
last_stride = 1;
}
if (isFOrder && ni >= 1) {
last_stride *= newShape[ni - 1];
}
for (nk = ni; nk < newnd; nk++) {
newStrides[nk] = last_stride;
}
// Update the output shape info
outShape[0] = newnd; // Set rank
shape::setShape(outShape, const_cast<sd::LongType*>(newShape.data()));
shape::setStride(outShape, newStrides.data());
// Set order first
shape::setOrder(outShape, order);
// Data type was set early (lines 28-32) but we set it again here as a defensive measure
// to ensure it's preserved even if other shape operations modified the extra field
ArrayOptions::setDataType(outShape, ArrayOptions::dataType(inShape));
return true;
}
}
}
}
#endif