chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1,145 @@
/*
* ******************************************************************************
* *
* *
* * 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 raver119@gmail.com
//
#ifndef SD_CUDA
#include <array/PrimaryPointerDeallocator.h>
#include <execution/AffinityManager.h>
#include <helpers/ConstantHelper.h>
#include <loops/type_conversions.h>
#include <system/type_boilerplate.h>
#include <types/types.h>
#include <cstring>
#include <system/selective_rendering.h>
namespace sd {
ConstantHelper::ConstantHelper() {
int numDevices = getNumberOfDevices();
_cache.resize(numDevices);
_counters.resize(numDevices);
for (int e = 0; e < numDevices; e++) {
SD_MAP_IMPL<ConstantDescriptor, ConstantHolder *> map;
_cache[e] = map;
_counters[e] = 0L;
}
}
ConstantHelper::~ConstantHelper() {
for (const auto &v : _cache) {
for (const auto &c : v) {
delete c.second;
}
}
}
ConstantHelper &ConstantHelper::getInstance() {
static ConstantHelper instance;
return instance;
}
void *ConstantHelper::replicatePointer(void *src, size_t numBytes, memory::Workspace *workspace) {
if (workspace == nullptr) {
auto deviceId = getCurrentDevice();
_counters[deviceId] += numBytes;
}
int8_t *ptr = nullptr;
ALLOCATE(ptr, workspace, numBytes, int8_t);
std::memcpy(ptr, src, numBytes);
return ptr;
}
int ConstantHelper::getCurrentDevice() { return AffinityManager::currentDeviceId(); }
int ConstantHelper::getNumberOfDevices() { return AffinityManager::numberOfDevices(); }
ConstantDataBuffer *ConstantHelper::constantBuffer(const ConstantDescriptor &descriptor, sd::DataType dataType) {
const auto deviceId = getCurrentDevice();
// we're locking away cache modification
_mutexHolder.lock();
if (_cache[deviceId].count(descriptor) == 0) {
_cache[deviceId][descriptor] = new ConstantHolder();
}
auto holder = _cache[deviceId][descriptor];
// releasing cache lock
_mutexHolder.unlock();
ConstantDataBuffer *result;
// access to this holder instance is synchronous
holder->mutex()->lock();
if (holder->hasBuffer(dataType))
result = holder->getConstantDataBuffer(dataType);
else {
auto size = descriptor.length() * DataTypeUtils::sizeOf(dataType);
auto cbuff = std::make_shared<PointerWrapper>(new int8_t[size], std::make_shared<PrimaryPointerDeallocator>());
_counters[deviceId] += size;
// create buffer with this dtype
if (descriptor.isFloat()) {
auto doubleDtype = sd::DataType::DOUBLE;
BUILD_DOUBLE_SELECTOR(
DOUBLE, dataType, sd::TypeCast::convertGeneric,
(nullptr, const_cast<double *>(descriptor.floatValues().data()), descriptor.length(), cbuff->pointer()),
(DOUBLE, double), SD_COMMON_TYPES);
} else if (descriptor.isInteger()) {
auto int64DType = INT64;
BUILD_DOUBLE_SELECTOR(INT64, dataType, sd::TypeCast::convertGeneric,
(nullptr, const_cast<sd::LongType *>(descriptor.integerValues().data()),
descriptor.length(), cbuff->pointer()),
(INT64, LongType), SD_COMMON_TYPES);
}
ConstantDataBuffer dataBuffer(cbuff, descriptor.length(), dataType);
holder->addBuffer(dataBuffer, dataType);
result = holder->getConstantDataBuffer(dataType);
}
holder->mutex()->unlock();
return result;
}
void * ConstantHelper::getConstantSpace() {
THROW_EXCEPTION("NO CONSTANT SPACE FOUND FOR CPU;");
}
sd::LongType ConstantHelper::getCachedAmount(int deviceId) {
int numDevices = getNumberOfDevices();
if (deviceId > numDevices || deviceId < 0)
return 0L;
else
return _counters[deviceId];
}
} // namespace sd
#endif
@@ -0,0 +1,93 @@
/*
* ******************************************************************************
* *
* *
* * 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
* *****************************************************************************
*/
#include <helpers/cpu/CpuShapeBufferCreator.h>
#include <array/PrimaryPointerDeallocator.h>
#if defined(SD_GCC_FUNCTRACE)
#include <array/ShapeCacheLifecycleTracker.h>
#endif
namespace sd {
ConstantShapeBuffer* CpuShapeBufferCreator::create(const LongType* shapeInfo, int rank) {
if (shapeInfo == nullptr) {
THROW_EXCEPTION("CpuShapeBufferCreator::create: shapeInfo cannot be nullptr");
}
if (rank < 0 || rank > SD_MAX_RANK) {
std::string msg = "CpuShapeBufferCreator::create: invalid rank: " + std::to_string(rank);
THROW_EXCEPTION(msg.c_str());
}
const int shapeInfoLength = shape::shapeInfoLength(rank);
LongType* shapeCopy = new LongType[shapeInfoLength];
if (shapeCopy == nullptr) {
THROW_EXCEPTION("CpuShapeBufferCreator::create: failed to allocate memory for shapeCopy");
}
std::memcpy(shapeCopy, shapeInfo, shapeInfoLength * sizeof(LongType));
// Previously used PointerDeallocator (no-op) which leaked memory
auto deallocator = std::shared_ptr<PrimaryPointerDeallocator>(
new PrimaryPointerDeallocator(),
[] (PrimaryPointerDeallocator* ptr) { delete ptr; }
);
auto hPtr = new PointerWrapper(shapeCopy, deallocator);
if (hPtr == nullptr) {
delete[] shapeCopy;
THROW_EXCEPTION("CpuShapeBufferCreator::create: failed to create PointerWrapper");
}
auto buffer = new ConstantShapeBuffer(hPtr);
if (buffer == nullptr) {
delete hPtr;
THROW_EXCEPTION("CpuShapeBufferCreator::create: failed to create ConstantShapeBuffer");
}
#if defined(SD_GCC_FUNCTRACE)
// Track shape cache allocation
sd::array::ShapeCacheLifecycleTracker::getInstance().recordAllocation(shapeCopy);
#endif
// Session #977: Validate buffer before returning.
// primary() will now throw an exception if the buffer is invalid,
// so we just call it to trigger validation.
#ifdef __cpp_exceptions
try {
(void)buffer->primary(); // Trigger validation - will throw if invalid
} catch (...) {
delete buffer;
throw; // Re-throw the exception
}
#else
// Exceptions disabled - direct validation call without try/catch
(void)buffer->primary(); // Trigger validation
#endif
return buffer;
}
CpuShapeBufferCreator& CpuShapeBufferCreator::getInstance() {
static CpuShapeBufferCreator instance;
return instance;
}
} // namespace sd
@@ -0,0 +1,49 @@
/*
* ******************************************************************************
* *
* *
* * 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
* *****************************************************************************
*/
#ifndef LIBND4J_CPUSHAPEBUFFERCREATOR_H
#define LIBND4J_CPUSHAPEBUFFERCREATOR_H
#include <helpers/ShapeBufferCreator.h>
#include <helpers/shape.h>
#include <memory>
namespace sd {
/**
* CPU implementation of the ShapeBufferCreator.
*/
class CpuShapeBufferCreator : public ShapeBufferCreator {
public:
/**
* Create a ConstantShapeBuffer for CPU usage
*/
ConstantShapeBuffer* create(const LongType* shapeInfo, int rank) override;
// Singleton pattern implementation
static CpuShapeBufferCreator& getInstance();
private:
CpuShapeBufferCreator() = default;
};
} // namespace sd
#endif // LIBND4J_CPUSHAPEBUFFERCREATOR_H
+663
View File
@@ -0,0 +1,663 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
// @author Yurii Shyrma (iuriish@yahoo.com)
//
#include "../MmulHelper.h"
#include <array/NDArrayFactory.h>
#include <exceptions/datatype_exception.h>
#include <execution/Threads.h>
#include <helpers/BlasHelper.h>
#include <helpers/ShapeUtils.h>
namespace sd {
//////////////////////////////////////////////////////////////////////////////
// MXK x KxN = MxN -> actual sequence of axes doesn't matter
template <typename T1, typename T2, typename T3>
static void usualGemm(NDArray* vA, NDArray* vB, NDArray* vC, const int aMaxis, const int aKaxis,
const int bKaxis, const int bNaxis, const int cMaxis, const int cNaxis, const double alpha,
const double beta) {
T1* A = vA->bufferAsT<T1>();
T2* B = vB->bufferAsT<T2>();
T3* C = vC->bufferAsT<T3>();
if (A == nullptr) {
THROW_EXCEPTION("usualGemm: A is nullptr");
}
if (B == nullptr) {
THROW_EXCEPTION("usualGemm: B is nullptr");
}
if (C == nullptr) {
THROW_EXCEPTION("usualGemm: C is nullptr");
}
const T3 alphaZ = static_cast<T3> (alpha);
const T3 betaZ = static_cast<T3>(beta);
const bool betaPresent = beta;
const sd::LongType* aShapeInfo = vA->shapeInfo();
const sd::LongType* bShapeInfo = vB->shapeInfo();
const sd::LongType* cShapeInfo = vC->shapeInfo();
const int aRank = vA->rankOf();
const int bRank = vB->rankOf();
const int cRank = vC->rankOf();
const sd::LongType cLen = vC->lengthOf();
const int K = vA->sizeAt(aKaxis);
sd::LongType *cShape = shape::shapeOf(cShapeInfo);
sd::LongType *aShape = shape::shapeOf(aShapeInfo);
sd::LongType *bShape = shape::shapeOf(bShapeInfo);
sd::LongType *aStride = shape::stride(aShapeInfo);
sd::LongType *bStride = shape::stride(bShapeInfo);
sd::LongType *cStride = shape::stride(cShapeInfo);
auto func = PRAGMA_THREADS_FOR {
std::vector<sd::LongType> aCoords(aRank), bCoords(bRank), cCoords(cRank);
for (auto i = start; i < stop; i++) {
// evaluate C coordinates
INDEX2COORDS(i, cRank, shape::shapeOf(cShapeInfo), cCoords.data());
// evaluate A coordinates
aCoords[aMaxis] = cCoords[cMaxis];
aCoords[aKaxis] = 0;
// evaluate B coordinates
bCoords[bKaxis] = 0;
bCoords[bNaxis] = cCoords[cNaxis];
sd::LongType aOffset, bOffset, cOffset;
COORDS2INDEX(aRank, aStride, aCoords.data(), aOffset);
COORDS2INDEX(bRank, bStride, bCoords.data(), bOffset);
T3 val = A[aOffset] * B[bOffset]; // first iteration
for (int j = 1; j < K; j++) { // rest iterations
aOffset += aStride[aKaxis];
bOffset += bStride[bKaxis];
val += A[aOffset] * B[bOffset];
}
COORDS2INDEX(cRank, cStride, cCoords.data(), cOffset);
if (betaPresent) {
C[cOffset] = alphaZ * val + betaZ * C[cOffset];
} else {
C[cOffset] = alphaZ * val;
}
}
};
samediff::Threads::parallel_tad(func, 0, cLen);
}
//////////////////////////////////////////////////////////////////////////////
// MXN x N = M -> actual sequence of {M,N} axes doesn't matter
template <typename T1, typename T2, typename T3>
static void usualGemv( NDArray* vA, NDArray* vX, NDArray* vY, const int incx, const int incy,
const int aMaxis, const double alpha, const double beta) {
T1* A = vA->bufferAsT<T1>();
T2* X = vX->bufferAsT<T2>();
T3* Y = vY->bufferAsT<T3>();
const T3 alphaZ = static_cast<T3>(alpha);
const T3 betaZ = static_cast<T3>(beta);
const bool betaPersent = beta;
const sd::LongType* aShapeInfo = vA->shapeInfo();
const sd::LongType* xShapeInfo = vX->shapeInfo();
const sd::LongType* yShapeInfo = vY->shapeInfo();
const int N = vX->lengthOf();
const int M = vY->lengthOf();
const auto aMstride = vA->strideAt(aMaxis);
const auto aNstride = vA->strideAt(aMaxis == 0 ? 1 : 0);
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; ++i) {
// evaluate offsets
auto aOffset = i * aMstride;
auto xOffset = 0;
T3 val = A[aOffset] * X[xOffset]; // first iteration
for (int j = 1; j < N; ++j) { // rest iterations
aOffset += aNstride;
xOffset += incx;
val = val + A[aOffset] * X[xOffset];
}
auto yOffset = i * incy;
if (betaPersent)
Y[yOffset] = alphaZ * val + betaZ * Y[yOffset];
else
Y[yOffset] = alphaZ * val;
}
};
samediff::Threads::parallel_tad(func, 0, M);
}
//////////////////////////////////////////////////////////////////////////////
// (X*Y) = Z[0]
template <typename T1, typename T2, typename T3>
static void usualDot(const sd::LongType length, const double alpha, const void* vX, const sd::LongType incx,
const void* vY, const sd::LongType incy, const double beta, void* vZ) {
T1* X = reinterpret_cast<T1*>(const_cast<void*>(vX));
T2* Y = reinterpret_cast<T2*>(const_cast<void*>(vY));
T3* Z = reinterpret_cast<T3*>(vZ);
T3 alphaZ(alpha), betaZ(beta);
const bool betaPersent = beta;
T3 sum = static_cast<T3>(0);
auto func = PRAGMA_THREADS_FOR {
for (sd::LongType i = start; i < stop; ++i) {
sum += X[i * incx] * Y[i * incy];
}
};
samediff::Threads::parallel_for(func, 0, length);
if (betaPersent)
*Z = alphaZ * sum + betaZ * *Z;
else
*Z = alphaZ * sum;
}
//////////////////////////////////////////////////////////////////////////////
// MXK x KxN = MxN
NDArray* MmulHelper::mmulMxM( NDArray* A, NDArray* B, NDArray* C, const double alpha, const double beta,
const char outOrder) {
auto M = A->sizeAt(0);
auto K = A->sizeAt(1);
auto N = B->sizeAt(1);
if (C != nullptr && C->rankOf() != 2) {
std::string errorMessage = "MmulHelper::mmulMxM: rank of C array should be equal to 2, but got " +
std::to_string(C->rankOf()) + ". ";
errorMessage += "C datatype: " + DataTypeUtils::asString(C->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
if (B->sizeAt(0) != K) {
std::string errorMessage = "MmulHelper::mmulMxM: B array should have the same number of rows as A has columns. ";
errorMessage += "A columns: " + std::to_string(K) + ", ";
errorMessage += "B rows: " + std::to_string(B->sizeAt(0));
THROW_EXCEPTION(errorMessage.c_str());
}
if (C != nullptr && C->sizeAt(0) != M) {
std::string errorMessage = "MmulHelper::mmulMxM: C array should have the same number of rows as A. ";
errorMessage += "A rows: " + std::to_string(M) + ", ";
errorMessage += "C rows: " + std::to_string(C->sizeAt(0));
THROW_EXCEPTION(errorMessage.c_str());}
if (C != nullptr && C->sizeAt(1) != N) {
std::string errorMessage = "MmulHelper::mmulMxM: C array should have the same number of columns as B. ";
errorMessage += "B columns: " + std::to_string(N) + ", ";
errorMessage += "C columns: " + std::to_string(C->sizeAt(1));
THROW_EXCEPTION(errorMessage.c_str());
}
if (C == nullptr) {
std::vector<sd::LongType> shape = {M,N};
C = new NDArray(outOrder, shape, DataTypeUtils::pickPairwiseResultType(A->dataType(), B->dataType()),
A->getContext());
}
if (C->isEmpty()) return C;
const auto aType = A->dataType();
const auto bType = B->dataType();
const auto cType = C->dataType();
const bool AB(aType == bType), AC(aType == cType), ABC(AB && AC);
const bool hasGemm = BlasHelper::getInstance().hasGEMM(aType);
const bool typeDouble = hasGemm && ABC && aType == DataType::DOUBLE;
const bool typeFloat = hasGemm && ABC && aType == DataType::FLOAT32;
if ((!typeFloat && !typeDouble) || !Environment::getInstance().isEnableBlas()) {
BUILD_SINGLE_SELECTOR_THRICE(aType, usualGemm, (A, B, C, 0, 1, 0, 1, 0, 1, alpha, beta), SD_NUMERIC_TYPES);
} else {
std::vector<NDArray*> toDelete;
NDArray *pA(const_cast<NDArray*>(A)), *pB(const_cast<NDArray*>(B)), *pC(const_cast<NDArray*>(C));
bool aMcont = M == 1 || A->strideAt(0) == 1;
bool aKcont = K == 1 || A->strideAt(1) == 1;
bool bKcont = K == 1 || B->strideAt(0) == 1;
bool bNcont = N == 1 || B->strideAt(1) == 1;
bool cMcont = M == 1 || C->strideAt(0) == 1;
bool cNcont = N == 1 || C->strideAt(1) == 1;
if (!aMcont && !aKcont) {
pA = A->dup('f', false);
toDelete.push_back(pA);
aMcont = true;
}
if (!bKcont && !bNcont) {
pB = B->dup('f', false);
toDelete.push_back(pB);
bKcont = true;
}
if (!cMcont && !cNcont) {
pC = C->dup('f', false);
toDelete.push_back(pC);
cMcont = true;
}
const CBLAS_ORDER blasOrder = cMcont ? CblasColMajor : CblasRowMajor;
const bool transA = (!aMcont && cMcont) || (aMcont && !cMcont);
const bool transB = (!bKcont && cMcont) || (bKcont && !cMcont);
const CBLAS_TRANSPOSE transAblas = transA ? CblasTrans : CblasNoTrans;
const CBLAS_TRANSPOSE transBblas = transB ? CblasTrans : CblasNoTrans;
const int lda = (aMcont && aKcont) ? M : !aMcont ? pA->strideAt(0) : pA->strideAt(1);
const int ldb = (bKcont && bNcont) ? K : !bKcont ? pB->strideAt(0) : pB->strideAt(1);
const int ldc = (cMcont && cNcont) ? M : !cMcont ? pC->strideAt(0) : pC->strideAt(1);
// Acquire BLAS lock to prevent OpenBLAS TLS corruption and race conditions
// This serializes external BLAS calls while allowing OpenBLAS to use multiple threads internally
auto blasLock = BlasHelper::getInstance().lockBlas();
if (typeFloat) {
BlasHelper::getInstance().sgemm()(blasOrder, transAblas, transBblas, M, N, K, (float)alpha,
pA->bufferAsT<float>(), lda, pB->bufferAsT<float>(), ldb, (float)beta,
pC->bufferAsT<float>(), ldc);
} else if (typeDouble) {
BlasHelper::getInstance().dgemm()(blasOrder, transAblas, transBblas, M, N, K, (double)alpha,
pA->bufferAsT<double>(), lda, pB->bufferAsT<double>(), ldb, (double)beta,
pC->bufferAsT<double>(), ldc);
}
if (pC != C) {
C->assign(pC);
}
for (auto* arr : toDelete) {
delete arr;
}
}
return C;
}
////////////////////////////////////////////////////////////////////////////
// MXN x N = M
NDArray* MmulHelper::mmulMxV( NDArray* A, NDArray* X, sd::NDArray* Y, const double alpha, const double beta,
const char outOrder) {
if (X->dataType() != A->dataType()) {
std::string errorMessage;
errorMessage = "mmulMxV expects all data types to be the same";
errorMessage += "A: " + DataTypeUtils::asString(A->dataType());
errorMessage += "X: " + DataTypeUtils::asString(X->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
if (Y != nullptr && X->dataType() != Y->dataType()) {
std::string errorMessage;
errorMessage = "mmulMxV expects all data types to be the same";
errorMessage += "X: " + DataTypeUtils::asString(X->dataType());
errorMessage += "Y: " + DataTypeUtils::asString(Y->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
sd::LongType xLenDim, yLenDim(0);
if (A->rankOf() != 2) THROW_EXCEPTION("MmulHelper::mmulMxV: rank of A array is not equal 2 !");
if (!shape::isCommonVector(X->shapeInfo(), xLenDim))
THROW_EXCEPTION("MmulHelper::mmulMxV: X array must be vector !");
const auto M = A->sizeAt(0);
const auto N = A->sizeAt(1);
if (Y != nullptr && !shape::isCommonVector(Y->shapeInfo(), yLenDim))
THROW_EXCEPTION("MmulHelper::mmulMxV: Y array must be vector !");
if (X->lengthOf() != N) THROW_EXCEPTION("MmulHelper::mmulMxV: X vector has wrong length !");
if (Y != nullptr && Y->lengthOf() != M) THROW_EXCEPTION("MmulHelper::mmulMxV: Y array has wrong length !");
if (Y == nullptr) {
std::vector<sd::LongType> shape = {M};
Y = new NDArray(outOrder,shape, DataTypeUtils::pickPairwiseResultType(A->dataType(), X->dataType()),
A->getContext());
}
if (Y->isEmpty()) return Y;
const int incx = X->stridesOf()[xLenDim];
const int incy = Y->stridesOf()[yLenDim];
const auto aType = A->dataType();
const auto xType = X->dataType();
const auto yType = Y->dataType();
const bool AX(aType == xType), AY(aType == yType), AXY(AX && AY);
const bool hasGemv = BlasHelper::getInstance().hasGEMV(aType);
const bool typeDouble = hasGemv && AXY && aType == DataType::DOUBLE;
const bool typeFloat = hasGemv && AXY && aType == DataType::FLOAT32;
if ((!typeDouble && !typeFloat) || !Environment::getInstance().isEnableBlas()) {
BUILD_SINGLE_SELECTOR_THRICE(aType, usualGemv, (A, X, Y, incx, incy, 0, alpha, beta), SD_NUMERIC_TYPES);
} else {
NDArray* pA(const_cast<NDArray*>(A));
bool aMcont = M == 1 || A->strideAt(0) == 1;
bool aNcont = N == 1 || A->strideAt(1) == 1;
if (!aMcont && !aNcont) {
pA = A->dup('f', false); // dup() already returns NDArray*, no need for new
aMcont = true;
}
const CBLAS_ORDER blasOrder = aMcont ? CblasColMajor : CblasRowMajor;
const int lda = (aMcont && aNcont) ? M : !aMcont ? pA->strideAt(0) : pA->strideAt(1);
// Acquire BLAS lock to prevent OpenBLAS TLS corruption and race conditions
auto blasLock = BlasHelper::getInstance().lockBlas();
// choose appropriate cuda gemm api depending on data types
if (typeDouble) {
BlasHelper::getInstance().dgemv()(blasOrder, CblasNoTrans, M, N, alpha, (double*)pA->buffer(), lda,
(double*)X->buffer(), incx, beta, (double*)Y->buffer(), incy);
} else if (typeFloat) {
BlasHelper::getInstance().sgemv()(blasOrder, CblasNoTrans, M, N, (float)alpha, (float*)pA->buffer(), lda,
(float*)X->buffer(), incx, (float)beta, (float*)Y->buffer(), incy);
}
// Clean up duplicated array
if (pA != A) {
delete pA;
}
}
return Y;
}
////////////////////////////////////////////////////////////////////////////
// (X * Y) = Z[0]
NDArray* MmulHelper::dot(NDArray* X, NDArray* Y, sd::NDArray* Z, const double alpha, const double beta) {
if (X->dataType() != Y->dataType()) {
std::string errorMessage = "Dot expects all data types to be the same. ";
errorMessage += "X datatype: " + DataTypeUtils::asString(X->dataType()) + ", ";
errorMessage += "Y datatype: " + DataTypeUtils::asString(Y->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
if (Z != nullptr && X->dataType() != Z->dataType()) {
std::string errorMessage = "Dot expects all data types to be the same. ";
errorMessage += "X datatype: " + DataTypeUtils::asString(X->dataType()) + ", ";
errorMessage += "Z datatype: " + DataTypeUtils::asString(Z->dataType());
THROW_EXCEPTION(errorMessage.c_str());
}
sd::LongType xLenDim(0), yLenDim(0);
if (!shape::isCommonVector(X->shapeInfo(), xLenDim)) {
std::string errorMessage = "MmulHelper::dot: X array must be a vector, but its shape is: ";
for (int i = 0; i < X->rankOf(); ++i) {
errorMessage += std::to_string(X->sizeAt(i));
if (i < X->rankOf() - 1) errorMessage += "x";
}
THROW_EXCEPTION(errorMessage.c_str());
}
if (!shape::isCommonVector(Y->shapeInfo(), yLenDim)) {
std::string errorMessage = "MmulHelper::dot: Y array must be a vector, but its shape is: ";
for (int i = 0; i < Y->rankOf(); ++i) {
errorMessage += std::to_string(Y->sizeAt(i));
if (i < Y->rankOf() - 1) errorMessage += "x";
}
THROW_EXCEPTION(errorMessage.c_str());
}
if (Z != nullptr && Z->lengthOf() > 1) {
std::string errorMessage = "MmulHelper::dot: Z array must be a scalar, but it has length " + std::to_string(Z->lengthOf());
THROW_EXCEPTION(errorMessage.c_str());
}
const auto length = X->lengthOf();
if (Y->lengthOf() != length) {
std::string errorMessage = "MmulHelper::dot: lengths of input vectors are different! ";
errorMessage += "X length: " + std::to_string(X->lengthOf()) + ", ";
errorMessage += "Y length: " + std::to_string(Y->lengthOf());
THROW_EXCEPTION(errorMessage.c_str());
}
if (Z == nullptr)
Z = new NDArray(DataTypeUtils::pickPairwiseResultType(X->dataType(), Y->dataType()), X->getContext());
const sd::LongType incx = X->stridesOf()[xLenDim];
const sd::LongType incy = Y->stridesOf()[yLenDim];
const auto xType = X->dataType();
const auto yType = Y->dataType();
const auto zType = Z->dataType();
BUILD_SINGLE_SELECTOR_THRICE(
xType, usualDot, (length, alpha, X->buffer(), incx, Y->buffer(), incy, beta, Z->buffer()), SD_NUMERIC_TYPES);
return Z;
}
//////////////////////////////////////////////////////////////////////////////
// [bS,M,K] x [bS,K,N] = [bS,M,N]
// [bS,M,K] x [K,N] = [bS,M,N]
// [M,K] x [bS,K,N] = [bS,M,N]
// bS could stand for several axes
template <typename T1, typename T2, typename T3>
static void batchedGemm(NDArray* vA, NDArray* vB, NDArray* vC, const sd::LongType* aBatchDims,
const sd::LongType* bBatchDims, const sd::LongType* cBatchDims, sd::LongType aMaxis, sd::LongType aKaxis,
sd::LongType bKaxis, sd::LongType bNaxis, sd::LongType cMaxis, sd::LongType cNaxis, const double alpha, const double beta) {
T1* A = vA->bufferAsT<T1>();
T2* B = vB->bufferAsT<T2>();
T3* C = vC->bufferAsT<T3>();
const T3 alphaZ = static_cast<T3>(alpha);
const T3 betaZ = static_cast<T3>(beta);
const bool betaPersent = beta;
const sd::LongType* aShapeInfo = vA->shapeInfo();
const sd::LongType* bShapeInfo = vB->shapeInfo();
const sd::LongType* cShapeInfo = vC->shapeInfo();
const sd::LongType aRank = vA->rankOf();
const sd::LongType bRank = vB->rankOf();
const sd::LongType cRank = vC->rankOf();
const sd::LongType cLen = vC->lengthOf();
const sd::LongType K = vA->sizeAt(aKaxis);
sd::LongType *cShape = shape::shapeOf(cShapeInfo);
sd::LongType *aShape = shape::shapeOf(aShapeInfo);
sd::LongType *bShape = shape::shapeOf(bShapeInfo);
sd::LongType *aStride = shape::stride(aShapeInfo);
sd::LongType *bStride = shape::stride(bShapeInfo);
sd::LongType *cStride = shape::stride(cShapeInfo);
auto func = PRAGMA_THREADS_FOR {
std::vector<sd::LongType> aCoords(aRank), bCoords(bRank), cCoords(cRank);
for (sd::LongType i = start; i < stop; ++i) {
// evaluate C coordinates
INDEX2COORDS(i, cRank,cShape, cCoords.data());
// calculate index of current batch
sd::LongType batchInd;
if (cRank > 2) COORDS2INDEX(cRank, cStride, cCoords.data(), batchInd);
// evaluate A coordinates
if (aRank > 2) INDEX2COORDS(batchInd, aRank, aShape, aCoords.data());
aCoords[aMaxis] = cCoords[cMaxis];
aCoords[aKaxis] = 0;
// evaluate B coordinates
if (bRank > 2) INDEX2COORDS(batchInd, bRank, bShape, bCoords.data());
bCoords[bKaxis] = 0;
bCoords[bNaxis] = cCoords[cNaxis];
sd::LongType aOffset, bOffset, cOffset;
COORDS2INDEX(aRank, aStride, aCoords.data(), aOffset);
COORDS2INDEX(bRank, bStride, bCoords.data(), bOffset);
T3 val = A[aOffset] * B[bOffset]; // first iteration
for (int j = 1; j < K; ++j) { // rest iterations
aOffset += aStride[aKaxis];
bOffset += bStride[bKaxis];
val = val + A[aOffset] * B[bOffset];
}
COORDS2INDEX(cRank, cStride, cCoords.data(), cOffset);
if (betaPersent)
C[cOffset] = alphaZ * val + betaZ * C[cOffset];
else
C[cOffset] = alphaZ * val;
}
};
samediff::Threads::parallel_tad(func, 0, cLen);
}
//////////////////////////////////////////////////////////////////////////
NDArray* MmulHelper::mmulNxN( NDArray* A, NDArray* B, NDArray* C, const double alpha, const double beta,
const char outOrder) {
const sd::LongType aRank = A->rankOf();
const sd::LongType bRank = B->rankOf();
auto shapeToString = []( NDArray* arr) {
std::string shape = "[";
for (int i = 0; i < arr->rankOf(); ++i) {
shape += std::to_string(arr->sizeAt(i));
if (i < arr->rankOf() - 1) shape += ",";
}
shape += "]";
return shape;
};
// input ranks validation
if (aRank > bRank && bRank != 2) {
std::string errorMessage = "MmulHelper::mmulNxN: rank of B array should be equal 2, but got " + std::to_string(bRank) +
"! A shape: " + shapeToString(A) + ", B shape: " + shapeToString(B);
THROW_EXCEPTION(errorMessage.c_str());
} else if (bRank > aRank && aRank != 2) {
std::string errorMessage = "MmulHelper::mmulNxN: rank of A array should be equal 2, but got " + std::to_string(aRank) +
"! A shape: " + shapeToString(A) + ", B shape: " + shapeToString(B);
THROW_EXCEPTION(errorMessage.c_str());
} else if (aRank == bRank) {
for (int i = 0; i < aRank - 2; ++i)
if (A->sizeAt(i) != B->sizeAt(i)) {
std::string errorMessage = "MmulHelper::mmulNxN: shapes of A and B arrays are not suitable for matrix multiplication! "
"Mismatch at dimension " + std::to_string(i) + ": A[" + std::to_string(i) + "] = " +
std::to_string(A->sizeAt(i)) + ", B[" + std::to_string(i) + "] = " + std::to_string(B->sizeAt(i)) +
". Full shapes: A " + shapeToString(A) + ", B " + shapeToString(B);
THROW_EXCEPTION(errorMessage.c_str());
}
}
if (A->sizeAt(-1) != B->sizeAt(-2)) {
std::string errorMessage = "MmulHelper::mmulNxN: shapes of A and B arrays are not suitable for matrix multiplication! "
"A's last dimension (" + std::to_string(A->sizeAt(-1)) + ") must match B's second-to-last dimension (" +
std::to_string(B->sizeAt(-2)) + "). Full shapes: A " + shapeToString(A) + ", B " + shapeToString(B);
THROW_EXCEPTION(errorMessage.c_str());
}
// validation of C array
std::vector<sd::LongType> *cExpectedShape = aRank > bRank ? A->getShapeAsVector() : B->getShapeAsVector();
(*cExpectedShape)[cExpectedShape->size() - 2] = A->sizeAt(-2);
(*cExpectedShape)[cExpectedShape->size() - 1] = B->sizeAt(-1);
if (C != nullptr) {
if (!C->isSameShape(*cExpectedShape)) {
std::string errorMessage = "MmulHelper::mmulNxN: shape of C array is not suitable for AxB matrix multiplication! "
"Expected shape: [";
for (size_t i = 0; i < cExpectedShape->size(); ++i) {
errorMessage += std::to_string((*cExpectedShape)[i]);
if (i < cExpectedShape->size() - 1) errorMessage += ",";
}
errorMessage += "], but got: " + shapeToString(C) + ". A shape: " + shapeToString(A) + ", B shape: " + shapeToString(B);
delete cExpectedShape;
THROW_EXCEPTION(errorMessage.c_str());
}
} else {
C = new NDArray(outOrder, *cExpectedShape, B->dataType());
}
if (C->isEmpty()) {
delete cExpectedShape;
return C;
}
const sd::LongType cRank = C->rankOf();
const sd::LongType aMaxis(aRank - 2), aKaxis(aRank - 1), bKaxis(bRank - 2), bNaxis(bRank - 1), cMaxis(cRank - 2),
cNaxis(cRank - 1);
std::vector<sd::LongType> *aBatchDims, *bBatchDims, *cBatchDims;
if (aRank > 2) {
sd::LongType aaxes[2];
aaxes[0] = aMaxis;
aaxes[1] = aKaxis;
aBatchDims = ShapeUtils::evalDimsToExclude(aRank,2,aaxes);
} else {
aBatchDims = new std::vector<sd::LongType>();
}
if (bRank > 2) {
sd::LongType baxes[2];
baxes[0] = bKaxis;
baxes[1] = bNaxis;
bBatchDims = ShapeUtils::evalDimsToExclude(bRank, 2,baxes);
} else {
bBatchDims = new std::vector<sd::LongType>();
}
if (cRank > 2) {
sd::LongType caxes[2];
caxes[0] = cMaxis;
caxes[1] = cNaxis;
cBatchDims = ShapeUtils::evalDimsToExclude(cRank, 2,caxes);
} else {
cBatchDims = new std::vector<sd::LongType>();
}
BUILD_SINGLE_SELECTOR_THRICE(A->dataType(), batchedGemm,
(A, B, C, aBatchDims->data(), bBatchDims->data(), cBatchDims->data(), aMaxis, aKaxis,
bKaxis, bNaxis, cMaxis
, cNaxis, alpha, beta),
SD_NUMERIC_TYPES);
if(aBatchDims != nullptr)
delete aBatchDims;
if(bBatchDims != nullptr)
delete bBatchDims;
if(cBatchDims != nullptr)
delete cBatchDims;
delete cExpectedShape;
return C;
}
} // namespace sd
@@ -0,0 +1,58 @@
/* ******************************************************************************
*
*
* 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), created on 06.02.2019
//
#ifndef SD_CUDA
#include <exceptions/cuda_exception.h>
#include <helpers/PointersManager.h>
#include <helpers/logger.h>
#include <memory/Workspace.h>
namespace sd {
//////////////////////////////////////////////////////////////////////////
PointersManager::PointersManager(const sd::LaunchContext* context, const std::string& funcName) {
_context = const_cast<sd::LaunchContext*>(context);
_funcName = funcName;
}
//////////////////////////////////////////////////////////////////////////
void* PointersManager::allocateDevMem(const size_t sizeInBytes) {
// no-op
}
//////////////////////////////////////////////////////////////////////////
void* PointersManager::replicatePointer(const void* src, const size_t numberOfBytes) {
// no-op
return const_cast<void*>(src);
}
//////////////////////////////////////////////////////////////////////////
void PointersManager::synchronize() const {
// no-op
}
//////////////////////////////////////////////////////////////////////////
PointersManager::~PointersManager() {
// no-op
}
} // namespace sd
#endif
@@ -0,0 +1,111 @@
#include <array/TadCalculator.h>
#include <helpers/ShapeUtils.h>
#include <helpers/ConstantHelper.h>
#include <helpers/ConstantShapeHelper.h>
namespace sd {
TadCalculator::TadCalculator(LongType* originalShape)
: _originalShape(originalShape), _numTads(0), _tadShape(nullptr), _tadOffsets(nullptr) {}
TadCalculator::~TadCalculator() {
// Only delete _tadOffsets if we still own it (hasn't been released)
if (_tadOffsets != nullptr) {
delete _tadOffsets;
_tadOffsets = nullptr;
}
// _tadShape comes from ConstantShapeHelper cache - DON'T delete
}
void TadCalculator::createTadPack(const std::vector<LongType>& dimensions) {
if (!_originalShape) {
THROW_EXCEPTION("Original shape is null");
}
// Check for empty array
if (shape::isEmptyConst(_originalShape)) {
THROW_EXCEPTION("Cannot create TADs for empty array");
}
auto shapeInfo = ConstantShapeHelper::getInstance().createFromExisting(_originalShape);
const LongType rank = shape::rank(shapeInfo);
// Check for zero-sized dimensions
for (LongType i = 0; i < rank; i++) {
if (shape::sizeAt(shapeInfo, i) == 0) {
THROW_EXCEPTION("Cannot create TADs for array with zero-sized dimensions");
}
}
const std::vector<LongType>* dimsToExclude = ShapeUtils::evalDimsToExclude(rank, dimensions.size(), dimensions.data());
if (!dimsToExclude) {
THROW_EXCEPTION("Failed to evaluate dimensions to exclude");
}
if (dimsToExclude->size() == 0 || dimsToExclude->size() == rank) {
const LongType totalElements = shape::length(shapeInfo);
auto scalarShapeInfo = ConstantShapeHelper::getInstance().scalarShapeInfo(ArrayOptions::dataType(shapeInfo));
auto scalarShapeBuffer = ConstantShapeHelper::getInstance().bufferForShapeInfo(scalarShapeInfo);
auto oPtr = std::make_shared<PointerWrapper>(new LongType[totalElements]);
LongType* offsets = oPtr->pointerAsT<LongType>();
for (LongType i = 0; i < totalElements; ++i) {
offsets[i] = i;
}
_tadShape = scalarShapeBuffer;
_tadOffsets = new ConstantOffsetsBuffer(oPtr);
_numTads = totalElements;
delete dimsToExclude;
return;
}
const LongType numOfSubArrs = ShapeUtils::getNumOfSubArrs(shapeInfo, *dimsToExclude);
if (numOfSubArrs > 0) {
const LongType subArrRank = (static_cast<size_t>(rank) == dimsToExclude->size() || false) ? rank : rank - dimsToExclude->size();
auto sPtr = std::make_shared<PointerWrapper>(new LongType[shape::shapeInfoLength(subArrRank)]);
auto oPtr = std::make_shared<PointerWrapper>(new LongType[numOfSubArrs]);
shape::calcSubArrsShapeInfoAndOffsets(
shapeInfo,
numOfSubArrs,
dimsToExclude->size(),
dimsToExclude->data(),
sPtr->pointerAsT<LongType>(),
oPtr->pointerAsT<LongType>(),
false);
auto shapesBuffer = ConstantShapeHelper::getInstance().bufferForShapeInfo(sPtr->pointerAsT<LongType>());
_tadOffsets = new ConstantOffsetsBuffer(oPtr);
_tadShape = shapesBuffer;
_numTads = numOfSubArrs;
} else {
const LongType subArrRank = rank;
auto sPtr = std::make_shared<PointerWrapper>(new LongType[shape::shapeInfoLength(subArrRank)]);
LongType* shapeInfo2 = sPtr->pointerAsT<LongType>();
auto nonConstant = const_cast<LongType*>(shapeInfo);
auto nonConst2 = const_cast<LongType*>(shapeInfo2);
shape::copyTo<LongType>(shape::shapeInfoLength(subArrRank), nonConstant, nonConst2);
LongType* baseOffset = new LongType[1];
baseOffset[0] = 0;
auto oPtr = std::make_shared<PointerWrapper>(baseOffset);
auto shapesBuffer = ConstantShapeHelper::getInstance().bufferForShapeInfo(sPtr->pointerAsT<LongType>());
_tadOffsets = new ConstantOffsetsBuffer(oPtr);
_tadShape = shapesBuffer;
_numTads = 1;
}
delete dimsToExclude;
}
} // namespace sd
@@ -0,0 +1,43 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include "../cublasHelper.h"
namespace sd {
static void* handle_() { return nullptr; }
static void destroyHandle_(void* handle) {}
CublasHelper::CublasHelper() {}
CublasHelper::~CublasHelper() {}
CublasHelper& CublasHelper::getInstance() {
static CublasHelper instance;
return instance;
}
void* CublasHelper::handle() { return nullptr; }
void* CublasHelper::solver() { return nullptr; }
void* CublasHelper::handle(int deviceId) { return nullptr; }
} // namespace sd
@@ -0,0 +1,244 @@
/* ******************************************************************************
*
*
* 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), created on 14.03.2019
//
#include <helpers/Loops.h>
using namespace simdOps;
// Helper function to safely convert index to output type
template<typename Z>
SD_INLINE SD_HOST_DEVICE Z convertIndexToZ(sd::LongType index) {
if constexpr (any_my_string_v<Z>) {
// For string types, we can't meaningfully store an index
// You might want to convert to string representation or use default
return Z{}; // Default construct empty string
// Alternative: return Z(std::to_string(index)); if you want string representation
} else {
return static_cast<Z>(index);
}
}
//////////////////////////////////////////////////////////////////////////////
template <typename X, typename Z>
template <typename OpType>
SD_LIB_EXPORT void sd::IndexReductionLoops<X, Z>::loopIndexReduce( X* x, const LongType* xShapeInfo, Z* z,
const LongType* zShapeInfo,
const LongType* tadShapeInfo,
const LongType* tadOffsets, void* vextraParams) {
sd::LoopKind::Kind kindOfLoop = sd::LoopKind::deduceKindOfLoopTadXZ(xShapeInfo, zShapeInfo, tadShapeInfo);
auto extraParams = reinterpret_cast<X*>(vextraParams);
const sd::LongType zLen = shape::length(zShapeInfo);
const sd::LongType tadLen = shape::length(tadShapeInfo);
const sd::LongType* tadShape = shape::shapeOf(const_cast<sd::LongType*>(tadShapeInfo));
const sd::LongType* tadStride = shape::stride(const_cast<sd::LongType*>(tadShapeInfo));
switch (kindOfLoop) {
//*********************************************//
//*********************************************//
//*********************************************//
case sd::LoopKind::RANK1: {
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType i0 = 0; i0 < tadLen; ++i0) {
functions::indexreduce::IndexValue<X> comp(tad[i0 * tadStride[0]], i0);
indexValue = OpType::update(indexValue, comp, extraParams);
}
z[i] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
} break;
//*********************************************//
case sd::LoopKind::RANK2: {
sd::LongType newStride[2];
shape::updateStrides(2, tadShape, newStride, 'c');
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType i0 = 0; i0 < tadShape[0]; ++i0) {
for (sd::LongType i1 = 0; i1 < tadShape[1]; ++i1) {
const auto tadOffset = i0 * tadStride[0] + i1 * tadStride[1];
const auto tadIndex = i0 * newStride[0] + i1;
functions::indexreduce::IndexValue<X> comp(tad[tadOffset], tadIndex);
indexValue = OpType::update(indexValue, comp, extraParams);
}
}
z[i] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
} break;
//*********************************************//
case sd::LoopKind::RANK3: {
sd::LongType newStride[3];
shape::updateStrides(3, tadShape, newStride, 'c');
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType i0 = 0; i0 < tadShape[0]; ++i0) {
for (sd::LongType i1 = 0; i1 < tadShape[1]; ++i1) {
for (sd::LongType i2 = 0; i2 < tadShape[2]; ++i2) {
const auto tadOffset = i0 * tadStride[0] + i1 * tadStride[1] + i2 * tadStride[2];
const auto tadIndex = i0 * newStride[0] + i1 * newStride[1] + i2;
functions::indexreduce::IndexValue<X> comp(tad[tadOffset], tadIndex);
indexValue = OpType::update(indexValue, comp, extraParams);
}
}
}
z[i] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
} break;
//*********************************************//
case sd::LoopKind::RANK4: {
sd::LongType newStride[4];
shape::updateStrides(4, tadShape, newStride, 'c');
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType i0 = 0; i0 < tadShape[0]; ++i0) {
for (sd::LongType i1 = 0; i1 < tadShape[1]; ++i1) {
for (sd::LongType i2 = 0; i2 < tadShape[2]; ++i2) {
for (sd::LongType i3 = 0; i3 < tadShape[3]; ++i3) {
const auto tadOffset = i0 * tadStride[0] + i1 * tadStride[1] + i2 * tadStride[2] + i3 * tadStride[3];
const auto tadIndex = i0 * newStride[0] + i1 * newStride[1] + i2 * newStride[2] + i3;
functions::indexreduce::IndexValue<X> comp(tad[tadOffset], tadIndex);
indexValue = OpType::update(indexValue, comp, extraParams);
}
}
}
}
z[i] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
} break;
//*********************************************//
case sd::LoopKind::RANK5: {
sd::LongType newStride[5];
shape::updateStrides(5, tadShape, newStride, 'c');
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType i0 = 0; i0 < tadShape[0]; ++i0) {
for (sd::LongType i1 = 0; i1 < tadShape[1]; ++i1) {
for (sd::LongType i2 = 0; i2 < tadShape[2]; ++i2) {
for (sd::LongType i3 = 0; i3 < tadShape[3]; ++i3) {
for (sd::LongType i4 = 0; i4 < tadShape[4]; ++i4) {
const auto tadOffset = i0 * tadStride[0] + i1 * tadStride[1] + i2 * tadStride[2] +
i3 * tadStride[3] + i4 * tadStride[4];
const auto tadIndex =
i0 * newStride[0] + i1 * newStride[1] + i2 * newStride[2] + i3 * newStride[3] + i4;
functions::indexreduce::IndexValue<X> comp(tad[tadOffset], tadIndex);
indexValue = OpType::update(indexValue, comp, extraParams);
}
}
}
}
}
z[i] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
} break;
//*********************************************//
//*********************************************//
default: {
sd::LongType tadRank = shape::rank(tadShapeInfo);
sd::LongType *tadShape = shape::shapeOf(tadShapeInfo);
sd::LongType *tadStride = shape::stride(tadShapeInfo);
sd::LongType zRank = shape::rank(zShapeInfo);
sd::LongType *zShape = shape::shapeOf(zShapeInfo);
sd::LongType *zStride = shape::stride(zShapeInfo);
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) {
auto tad = const_cast<X*>(x) + tadOffsets[i];
auto indexValue = OpType::startingIndexValue(tad);
for (sd::LongType j = 0; j < tadLen; j++) {
LongType coords[SD_MAX_RANK];
INDEX2COORDS(j, tadRank, tadShape, coords);
LongType tadOffset;
COORDS2INDEX(tadRank, tadStride, coords, tadOffset);
functions::indexreduce::IndexValue<X> comp(tad[tadOffset], j);
indexValue = OpType::update(indexValue, comp, extraParams);
}
LongType coords[SD_MAX_RANK];
INDEX2COORDS(i, zRank, zShape, coords);
LongType zOffset;
COORDS2INDEX(zRank, zStride, coords, zOffset);
z[zOffset] = convertIndexToZ<Z>(indexValue.index);
}
};
samediff::Threads::parallel_tad(func, 0, zLen);
}
}
}
template <typename X, typename Z>
SD_LIB_HIDDEN void sd::IndexReductionLoops<X, Z>::wrapIndexReduce(const int opNum, const void* vx,
const sd::LongType* xShapeInfo, void* vz,
const sd::LongType* zShapeInfo,
const sd::LongType* tadShapeInfo,
const sd::LongType* tadOffsets, void* vextraParams) {
auto x = reinterpret_cast<X*>(const_cast<void *>(vx));
auto z = reinterpret_cast<Z*>(vz);
DISPATCH_BY_OPNUM_TT(loopIndexReduce, PARAMS(x, xShapeInfo, z, zShapeInfo, tadShapeInfo, tadOffsets, vextraParams),
INDEX_REDUCE_OPS);
}
@@ -0,0 +1,28 @@
/*
* ******************************************************************************
* *
* *
* * 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 <helpers/cpu/loops/IndexReductionLoops.hpp>
#cmakedefine SD_COMMON_TYPES_GEN
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@) && defined(SD_INDEXING_TYPES_0)
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void sd::IndexReductionLoops, ::wrapIndexReduce(const int opNum, const void* vx, const sd::LongType* xShapeInfo, void* z, const sd::LongType* zShapeInfo, const sd::LongType* tadShapeInfo, const sd::LongType* tadOffsets, void* vextraParams), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES_0 );
#endif
@@ -0,0 +1,28 @@
/*
* ******************************************************************************
* *
* *
* * 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 <helpers/cpu/loops/IndexReductionLoops.hpp>
#cmakedefine SD_COMMON_TYPES_GEN
#if defined(SD_COMMON_TYPES_GEN) && defined(SD_COMMON_TYPES_@FL_TYPE_INDEX@) && defined(SD_INDEXING_TYPES_1)
BUILD_DOUBLE_TEMPLATE( SD_LIB_HIDDEN void sd::IndexReductionLoops, ::wrapIndexReduce(const int opNum, const void* vx, const sd::LongType* xShapeInfo, void* z, const sd::LongType* zShapeInfo, const sd::LongType* tadShapeInfo, const sd::LongType* tadOffsets, void* vextraParams), SD_COMMON_TYPES_@FL_TYPE_INDEX@, SD_INDEXING_TYPES_1 );
#endif
@@ -0,0 +1,29 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include <helpers/cpu/loops/Reduction3Loops.hpp>
#cmakedefine SD_FLOAT_TYPES_GEN
#if defined(SD_FLOAT_TYPES_GEN) && defined(SD_FLOAT_TYPES_@FL_TYPE_INDEX@)
namespace sd {
BUILD_DOUBLE_TEMPLATE( class SD_LIB_HIDDEN Reduction3Loops, , SD_COMMON_TYPES, SD_FLOAT_TYPES_@FL_TYPE_INDEX@);
}
#endif
@@ -0,0 +1,82 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include <helpers/Loops.h>
#include <types/types.h>
using namespace simdOps;
namespace sd {
template <typename X, typename Z>
template <typename OpType>
SD_LIB_HIDDEN void Reduction3Loops<X, Z>::innerloopReduce3(const X* x, const sd::LongType* xShapeInfo, const X* y,
const sd::LongType* yShapeInfo, Z* z,
const sd::LongType* zShapeInfo, LongType* dims, int dimsLen,
Z* extraParams, int64_t start, int64_t stop) {
#ifndef SD_LOOPS_INLINED
Reduction3Loops<X, Z>::template loopReduce3<OpType>(x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, dims, dimsLen,
extraParams, start, stop);
#endif
}
template <typename X, typename Z>
template <typename OpType>
SD_LIB_HIDDEN void Reduction3Loops<X, Z>::innerloopReduce3All(
const X* x, const sd::LongType* xShapeInfo, const X* y, const sd::LongType* yShapeInfo, Z* z,
const sd::LongType* zShapeInfo, const sd::LongType* xTadShapeInfo, const sd::LongType* xTadOffsets,
const sd::LongType* yTadShapeInfo, const sd::LongType* yTadOffsets, Z* extraParams, int64_t start, int64_t stop) {
#ifndef SD_LOOPS_INLINED
Reduction3Loops<X, Z>::template loopReduce3All<OpType>(x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, xTadShapeInfo,
xTadOffsets, yTadShapeInfo, yTadOffsets, extraParams, start,
stop);
#endif
}
template <typename X, typename Y>
SD_LIB_HIDDEN void Reduction3Loops<X, Y>::wrapper(int opNum, const X* x, const sd::LongType* xShapeInfo,
const X* y, const sd::LongType* yShapeInfo, Y* z,
const sd::LongType* zShapeInfo,
LongType* dims, int dimsLen,
Y* extraParams, int64_t start, int64_t stop) {
#ifndef SD_LOOPS_INLINED
DISPATCH_BY_OPNUM_TT(innerloopReduce3,
PARAMS(x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, dims, dimsLen, extraParams, start, stop),
REDUCE3_OPS);
#endif
}
template <typename X, typename Y>
SD_LIB_HIDDEN void Reduction3Loops<X, Y>::wrapperAll(const int opNum, const X* x, const sd::LongType* xShapeInfo,
const X* y, const sd::LongType* yShapeInfo, Y* z,
const sd::LongType* zShapeInfo, const sd::LongType* xTadShapeInfo,
const sd::LongType* xTadOffsets, const sd::LongType* yTadShapeInfo,
const sd::LongType* yTadOffsets, Y* extraParams, int64_t start,
int64_t stop) {
#ifndef SD_LOOPS_INLINED
DISPATCH_BY_OPNUM_TT(innerloopReduce3All,
PARAMS(x, xShapeInfo, y, yShapeInfo, z, zShapeInfo, xTadShapeInfo, xTadOffsets, yTadShapeInfo,
yTadOffsets, extraParams, start, stop),
REDUCE3_OPS);
#endif
}
} // namespace sd
@@ -0,0 +1,23 @@
/* ******************************************************************************
*
*
* 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), created on 14.03.2019
//
#include <helpers/Loops.h>
#include <system/op_boilerplate.h>
@@ -0,0 +1,50 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include "ReductionLoops.hpp"
using namespace simdOps;
namespace sd {
template <typename X, typename Z>
template <typename OpType>
void ReductionBoolLoops<X, Z>::innerloopReduce(sd::memory::Workspace* workspace, const X* x,
const sd::LongType* xShapeInfo, Z* z, const sd::LongType* zShapeInfo,
const LongType* dims, X* extraParams) {
#ifndef SD_LOOPS_INLINED
ReductionLoops<X, Z, X>::template loopReduce<OpType>(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams);
#endif
}
template <typename X, typename Z>
void ReductionBoolLoops<X, Z>::wrapper(int opNum, memory::Workspace* workspace, const X* x, const LongType* xShapeInfo, Z* z,
const LongType* zShapeInfo, const LongType* dims, X* extraParams) {
#ifndef SD_LOOPS_INLINED
DISPATCH_BY_OPNUM_TT(innerloopReduce, PARAMS(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams),
REDUCE_BOOL_OPS);
#endif
}
// NOTE: Template instantiations are handled by TemplateProcessing.cmake
// Removed BUILD_DOUBLE_TEMPLATE to avoid duplicate instantiations
} // namespace sd
@@ -0,0 +1,31 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include <helpers/cpu/loops/ReductionLoops_float.hpp>
#cmakedefine SD_FLOAT_TYPES_GEN
#if defined(SD_FLOAT_TYPES_GEN) && defined(SD_FLOAT_TYPES_@FL_TYPE_INDEX@)
namespace sd {
BUILD_DOUBLE_TEMPLATE( class SD_LIB_HIDDEN ReductionFloatLoops, , SD_COMMON_TYPES, SD_FLOAT_TYPES_@FL_TYPE_INDEX@);
}
#endif
@@ -0,0 +1,51 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include <types/types.h>
#include "ReductionLoops.hpp"
using namespace simdOps;
namespace sd {
template <typename X, typename Z>
template <typename OpType>
SD_LIB_HIDDEN void ReductionFloatLoops<X, Z>::innerloopReduce(sd::memory::Workspace* workspace, const X* x,
const sd::LongType* xShapeInfo, Z* z,
const sd::LongType* zShapeInfo, const LongType* dims,
Z* extraParams) {
#ifndef SD_LOOPS_INLINED
ReductionLoops<X, Z, Z>::template loopReduce<OpType>(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams);
#endif
}
template <typename X, typename Y>
SD_LIB_HIDDEN void ReductionFloatLoops<X, Y>::wrapper(int opNum, sd::memory::Workspace* workspace, const X* x,
const sd::LongType* xShapeInfo, Y* z,
const sd::LongType* zShapeInfo, const LongType* dims, Y* extraParams) {
#ifndef SD_LOOPS_INLINED
DISPATCH_BY_OPNUM_TT(innerloopReduce, PARAMS(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams),
REDUCE_FLOAT_OPS);
#endif
}
} // namespace sd
@@ -0,0 +1,61 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#ifndef LIBND4J_REDUCTIONLOOPS_LONG_CPP
#define LIBND4J_REDUCTIONLOOPS_LONG_CPP
#include "ReductionLoops.hpp"
using namespace simdOps;
#include <types/types.h>
namespace sd {
template <typename X, typename Z>
template <typename OpType>
void ReductionLongLoops<X, Z>::innerloopReduce(sd::memory::Workspace* workspace, const X* x,
const sd::LongType* xShapeInfo, Z* z, const sd::LongType* zShapeInfo,
const LongType* dims, X* extraParams) {
#ifndef SD_LOOPS_INLINED
ReductionLoops<X, Z, X>::template loopReduce<OpType>(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams);
#endif
}
template <typename X, typename Z>
void ReductionLongLoops<X, Z>::wrapper(int opNum, sd::memory::Workspace* workspace, const X* x,
const sd::LongType* xShapeInfo, Z* z, const sd::LongType* zShapeInfo,
const LongType* dims, X* extraParams) {
#ifndef SD_LOOPS_INLINED
DISPATCH_BY_OPNUM_TT(innerloopReduce, PARAMS(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams),
REDUCE_LONG_OPS);
#endif
}
// Template instantiations for ReductionLongLoops are generated by TemplateProcessing.cmake
// This includes both class instantiations and member function templates (innerloopReduce<OpType>)
// for all type combinations in SD_COMMON_TYPES × SD_LONG_TYPES.
// No manual BUILD_DOUBLE_TEMPLATE needed here to avoid duplicate instantiation errors.
} // namespace sd
#endif // LIBND4J_REDUCTIONLOOPS_LONG_CPP
@@ -0,0 +1,55 @@
/* ******************************************************************************
*
*
* 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 raver119@gmail.com
//
#include "ReductionLoops.hpp"
using namespace simdOps;
namespace sd {
template <typename X>
template <typename OpType>
void ReductionSameLoops<X>::innerloopReduce(sd::memory::Workspace *workspace, const X *x,
const sd::LongType *xShapeInfo, X *z, const sd::LongType *zShapeInfo,
const LongType *dims, X *extraParams) {
#ifndef SD_LOOPS_INLINED
ReductionLoops<X, X, X>::template loopReduce<OpType>(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams);
#endif
}
template <typename X>
void ReductionSameLoops<X>::wrapper(int opNum, sd::memory::Workspace *workspace, const X *vx,
const sd::LongType *xShapeInfo, X *z, const sd::LongType *zShapeInfo,
const LongType *dims, X *vextraParams) {
#ifndef SD_LOOPS_INLINED
auto x = reinterpret_cast<X *>(vx);
auto z = reinterpret_cast<X *>(vz);
auto extraParams = reinterpret_cast<X *>(vextraParams);
DISPATCH_BY_OPNUM_T(innerloopReduce, PARAMS(workspace, x, xShapeInfo, z, zShapeInfo, dims, extraParams),
REDUCE_SAME_OPS);
#endif
}
// NOTE: Template instantiations are handled by TemplateProcessing.cmake
// Removed BUILD_SINGLE_TEMPLATE to avoid duplicate instantiations
} // namespace sd
+998
View File
@@ -0,0 +1,998 @@
/* ******************************************************************************
*
*
* 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), created on 03.01.2018
//
#include <array/ResultSet.h>
#include <helpers/biDiagonalUp.h>
#include <helpers/jacobiSVD.h>
#include <helpers/svd.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
template <typename T>
SVD<T>::SVD(NDArray& matrix, const int switchSize, const bool calcU, const bool calcV, const bool fullUV)
: _m(matrix.dataType(), matrix.getContext(), true),
_s(matrix.dataType(), matrix.getContext(), true),
_u(matrix.dataType(), matrix.getContext(), true),
_v(matrix.dataType(), matrix.getContext(), true) {
if (matrix.rankOf() != 2 || matrix.isScalar())
THROW_EXCEPTION("ops::helpers::SVD constructor: input array must be 2D matrix !");
const int rows = matrix.sizeAt(0);
const int cols = matrix.sizeAt(1);
if (cols > rows) {
_transp = true;
_diagSize = rows;
} else {
_transp = false;
_diagSize = cols;
}
_switchSize = switchSize;
_calcU = calcU;
_calcV = calcV;
_fullUV = fullUV;
if (_transp) math::sd_swap<bool>(_calcU, _calcV);
std::vector<sd::LongType> sShape = {_diagSize, 1};
std::vector<sd::LongType> mShape = {_diagSize + 1, _diagSize};
_s = NDArray(matrix.ordering(), sShape, matrix.dataType(), matrix.getContext());
_m = NDArray(matrix.ordering(), mShape, matrix.dataType(), matrix.getContext());
std::vector<sd::LongType> uShapeOne = {_diagSize + 1, _diagSize + 1};
std::vector<sd::LongType> uShapeTwo = {2, _diagSize + 1};
if (_calcU)
_u = NDArray(matrix.ordering(), uShapeOne, matrix.dataType(), matrix.getContext());
else
_u = NDArray(matrix.ordering(), uShapeTwo, matrix.dataType(), matrix.getContext());
if (_calcV) {
std::vector<sd::LongType> vShape = {_diagSize, _diagSize};
_v = NDArray(matrix.ordering(),vShape, matrix.dataType(), matrix.getContext());
}
evalData(matrix);
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
SVD<T>::SVD(NDArray& matrix, const int switchSize, const bool calcU, const bool calcV, const bool fullUV,
const char t)
: _m(matrix.dataType(), matrix.getContext(), true),
_s(matrix.dataType(), matrix.getContext(), true),
_u(matrix.dataType(), matrix.getContext(), true),
_v(matrix.dataType(), matrix.getContext(), true) {
if (matrix.rankOf() != 2 || matrix.isScalar())
THROW_EXCEPTION("ops::helpers::SVD constructor: input array must be 2D matrix !");
const int rows = matrix.sizeAt(0);
const int cols = matrix.sizeAt(1);
if (cols > rows) {
_transp = true;
_diagSize = rows;
} else {
_transp = false;
_diagSize = cols;
}
_switchSize = switchSize;
_calcU = calcU;
_calcV = calcV;
_fullUV = fullUV;
if (_transp) math::sd_swap<bool>(_calcU, _calcV);
std::vector<sd::LongType> sShape = {_diagSize, 1};
std::vector<sd::LongType> mShape = {_diagSize + 1, _diagSize};
_s = NDArray(matrix.ordering(), sShape, matrix.dataType(), matrix.getContext());
_m = NDArray(matrix.ordering(), mShape, matrix.dataType(), matrix.getContext());
std::vector<sd::LongType> uShapeOne = {_diagSize + 1, _diagSize + 1};
std::vector<sd::LongType> uShapeTwo = {2, _diagSize + 1};
if (_calcU)
_u = NDArray(matrix.ordering(), uShapeOne, matrix.dataType(), matrix.getContext());
else
_u = NDArray(matrix.ordering(), uShapeTwo, matrix.dataType(), matrix.getContext());
if (_calcV) {
std::vector<sd::LongType> vShape = {_diagSize, _diagSize};
_v = NDArray(matrix.ordering(),vShape, matrix.dataType(), matrix.getContext());
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::deflation1(int col1, int shift, int ind, int size) {
if (ind <= 0)
THROW_EXCEPTION("ops::helpers::SVD::deflation1 method: input int must satisfy condition ind > 0 !");
int first = col1 + shift;
T cos = _m.t<T>(first, first);
T sin = _m.t<T>(first + ind, first);
T denom = math::sd_sqrt<T, T>(cos * cos + sin * sin);
if (denom == (T)0.) {
_m.template r<T>(first + ind, first + ind) = (T)0;
return;
}
cos /= denom;
sin /= denom;
_m.template r<T>(first, first) = denom;
_m.template r<T>(first + ind, first) = (T)0;
_m.template r<T>(first + ind, first + ind) = (T)0;
std::vector<sd::LongType> rotShape = {2, 2};
NDArray rotation(_m.ordering(), rotShape, _m.dataType(), _m.getContext());
rotation.template r<T>(0, 0) = rotation.template r<T>(1, 1) = cos;
rotation.template r<T>(0, 1) = -sin;
rotation.template r<T>(1, 0) = sin;
if (_calcU) {
NDArray *temp = _u({col1, col1 + size + 1, 0, 0}, true);
JacobiSVD<T>::mulRotationOnRight(col1, col1 + ind, *temp, rotation);
delete temp;
} else
JacobiSVD<T>::mulRotationOnRight(col1, col1 + ind, _u, rotation);
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::deflation2(int col1U, int col1M, int row1W, int col1W, int ind1, int ind2, int size) {
if (ind1 >= ind2)
THROW_EXCEPTION("ops::helpers::SVD::deflation2 method: input intes must satisfy condition ind1 < ind2 !");
if (size <= 0)
THROW_EXCEPTION("ops::helpers::SVD::deflation2 method: input size must satisfy condition size > 0 !");
T cos = _m.t<T>(col1M + ind1, col1M);
T sin = _m.t<T>(col1M + ind2, col1M);
T denom = math::sd_sqrt<T, T>(cos * cos + sin * sin);
if (denom == (T)0.) {
_m.template r<T>(col1M + ind1, col1M + ind1) = _m.t<T>(col1M + ind2, col1M + ind2);
return;
}
cos /= denom;
sin /= denom;
_m.template r<T>(col1M + ind1, col1M) = denom;
_m.template r<T>(col1M + ind2, col1M + ind2) = _m.t<T>(col1M + ind1, col1M + ind1);
_m.template r<T>(col1M + ind2, col1M) = (T)0;
std::vector<sd::LongType> rotShape = {2, 2};
NDArray rotation(_m.ordering(), rotShape, _m.dataType(), _m.getContext());
rotation.template r<T>(0, 0) = rotation.template r<T>(1, 1) = cos;
rotation.template r<T>(0, 1) = -sin;
rotation.template r<T>(1, 0) = sin;
if (_calcU) {
NDArray *temp = _u({col1U, col1U + size + 1, 0, 0}, true);
JacobiSVD<T>::mulRotationOnRight(col1U + ind1, col1U + ind2, *temp, rotation);
delete temp;
} else
JacobiSVD<T>::mulRotationOnRight(col1U + ind1, col1U + ind2, _u, rotation);
if (_calcV) {
NDArray *temp = _v({row1W, row1W + size, 0, 0}, true);
JacobiSVD<T>::mulRotationOnRight(col1W + ind1, col1W + ind2, *temp, rotation);
delete temp;
}
}
//////////////////////////////////////////////////////////////////////////
// has effect on block from (col1+shift, col1+shift) to (col2+shift, col2+shift) inclusively
template <typename T>
void SVD<T>::deflation(int col1, int col2, int ind, int row1W, int col1W, int shift) {
const int len = col2 + 1 - col1;
NDArray *colVec0Ptr = _m({col1 + shift, col1 + shift + len, col1 + shift, col1 + shift + 1}, true);
NDArray colVec0 = *colVec0Ptr;
delete colVec0Ptr;
NDArray *viewPtr = _m({col1 + shift, col1 + shift + len, col1 + shift, col1 + shift + len}, true);
NDArray diagInterval = viewPtr->diagonal('c');
delete viewPtr;
const T almostZero = DataTypeUtils::min_positive<T>();
T maxElem;
if (len == 1)
maxElem = math::sd_abs<T,T>(diagInterval.template t<T>(0));
else {
NDArray *diagIntervalSubPtr = diagInterval({1, -1, 0, 0}, true);
auto reduce = diagIntervalSubPtr->reduceNumber(reduce::AMax);
maxElem = reduce->template t<T>(0);
delete reduce;
delete diagIntervalSubPtr;
}
auto reduce = colVec0.reduceNumber(reduce::AMax);
T maxElem0 = reduce->template t<T>(0);
delete reduce;
T eps = math::sd_max<T>(almostZero, DataTypeUtils::eps<T>() * maxElem);
T epsBig = (T)8. * DataTypeUtils::eps<T>() * math::sd_max<T>(maxElem0, maxElem);
if (diagInterval.template t<T>(0) < epsBig) diagInterval.template r<T>(0) = epsBig;
for (int i = 1; i < len; ++i)
if (math::sd_abs<T,T>(colVec0.template t<T>(i)) < eps) colVec0.template r<T>(i) = (T)0;
for (int i = 1; i < len; i++)
if (diagInterval.template t<T>(i) < epsBig) {
deflation1(col1, shift, i, len);
for (int j = 0; j < len; ++j) diagInterval.template r<T>(j) = _m.t<T>(col1 + shift + j, col1 + shift + j);
}
{
bool totDefl = true;
for (int i = 1; i < len; i++)
if (colVec0.template t<T>(i) >= almostZero) {
totDefl = false;
break;
}
int* permut = nullptr;
ALLOCATE(permut, _m.getContext()->getWorkspace(), 3 * _diagSize, int);
{
permut[0] = 0;
int p = 1;
for (int i = 1; i < len; ++i)
if (math::sd_abs<T,T>(diagInterval.template t<T>(i)) < almostZero) permut[p++] = i;
int k = 1, m = ind + 1;
for (; p < len; ++p) {
if (k > ind)
permut[p] = m++;
else if (m >= len)
permut[p] = k++;
else if (diagInterval.template t<T>(k) < diagInterval.template t<T>(m))
permut[p] = m++;
else
permut[p] = k++;
}
}
if (totDefl) {
for (int i = 1; i < len; ++i) {
int ki = permut[i];
if (math::sd_abs<T,T>(diagInterval.template t<T>(ki)) < almostZero ||
diagInterval.template t<T>(0) < diagInterval.template t<T>(ki))
permut[i - 1] = permut[i];
else {
permut[i - 1] = 0;
break;
}
}
}
int* tInd = permut + len;
int* tCol = permut + 2 * len;
for (int m = 0; m < len; m++) {
tCol[m] = m;
tInd[m] = m;
}
for (int i = totDefl ? 0 : 1; i < len; i++) {
const int ki = permut[len - (totDefl ? i + 1 : i)];
const int jac = tCol[ki];
math::sd_swap<T>(diagInterval.template r<T>(i), diagInterval.template r<T>(jac));
if (i != 0 && jac != 0) math::sd_swap<T>(colVec0.template r<T>(i), colVec0.template r<T>(jac));
if (_calcU) {
NDArray *temp1 = _u({col1, col1 + len + 1, col1 + i, col1 + i + 1});
NDArray *temp2 = _u({col1, col1 + len + 1, col1 + jac, col1 + jac + 1});
temp1->swapUnsafe(*temp2);
delete temp1;
delete temp2;
} else {
NDArray *temp1 = _u({0, 2, col1 + i, col1 + i + 1});
NDArray *temp2 = _u({0, 2, col1 + jac, col1 + jac + 1});
temp1->swapUnsafe(*temp2);
delete temp1;
delete temp2;
}
if (_calcV) {
NDArray *temp1 = _v({row1W, row1W + len, col1W + i, col1W + i + 1});
NDArray *temp2 = _v({row1W, row1W + len, col1W + jac, col1W + jac + 1});
temp1->swapUnsafe(*temp2);
delete temp1;
delete temp2;
}
const int tI = tInd[i];
tCol[tI] = jac;
tCol[ki] = i;
tInd[jac] = tI;
tInd[i] = ki;
}
RELEASE(permut, _m.getContext()->getWorkspace());
}
{
int i = len - 1;
while (i > 0 && (math::sd_abs<T,T>(diagInterval.template t<T>(i)) < almostZero ||
math::sd_abs<T,T>(colVec0.template t<T>(i)) < almostZero))
--i;
for (; i > 1; --i) {
if ((diagInterval.template t<T>(i) - diagInterval.template t<T>(i - 1)) < DataTypeUtils::eps<T>() * maxElem) {
if (math::sd_abs<T,T>(diagInterval.template t<T>(i) - diagInterval.template t<T>(i - 1)) >= epsBig)
THROW_EXCEPTION("ops::helpers::SVD::deflation: diagonal elements are not properly sorted !");
deflation2(col1, col1 + shift, row1W, col1W, i - 1, i, len);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
T SVD<T>::secularEq(const T diff, NDArray& col0, NDArray& diag, NDArray permut,
NDArray& diagShifted, const T shift) {
auto len = permut.lengthOf();
T res = static_cast<T>(1.);
T item;
for (int i = 0; i < len; ++i) {
int j = (int)permut.t<T>(i);
item = col0.t<T>(j) / ((diagShifted.t<T>(j) - diff) * (diag.t<T>(j) + shift + diff));
res += item * col0.t<T>(j);
}
return res;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::calcSingVals(NDArray col0, NDArray& diag, NDArray& permut, NDArray& singVals,
NDArray& shifts, NDArray& mus) {
auto len = col0.lengthOf();
auto curLen = len;
while (curLen > 1 && col0.t<T>(curLen - 1) == (T)0.f) --curLen;
for (sd::LongType k = 0; k < len; ++k) {
if (col0.t<T>(k) == (T)0.f || curLen == 1) {
singVals.template r<T>(k) = k == 0 ? col0.t<T>(0) : diag.t<T>(k);
mus.template r<T>(k) = (T)0;
shifts.template r<T>(k) = k == 0 ? col0.t<T>(0) : diag.t<T>(k);
continue;
}
T left = diag.t<T>(k);
T right;
if (k == curLen - 1) {
auto reduce = col0.reduceNumber(reduce::Norm2);
right = diag.t<T>(curLen - 1) + reduce->t<T>(0);
delete reduce;
} else {
int l = k + 1;
while (col0.t<T>(l) == (T)0.f) {
++l;
if (l >= curLen) THROW_EXCEPTION("ops::helpers::SVD::calcSingVals method: l >= curLen !");
}
right = diag.t<T>(l);
}
T mid = left + (right - left) / (T)2.;
T fMid = secularEq(mid, col0, diag, permut, diag, static_cast<T>(0.));
T shift = (k == curLen - 1 || fMid > (T)0.) ? left : right;
auto diagShifted = diag - shift;
T muPrev, muCur;
if (shift == left) {
muPrev = (right - left) * 0.1;
if (k == curLen - 1)
muCur = right - left;
else
muCur = (right - left) * 0.5;
} else {
muPrev = -(right - left) * 0.1;
muCur = -(right - left) * 0.5;
}
T fPrev = secularEq(muPrev, col0, diag, permut, *diagShifted, shift);
T fCur = secularEq(muCur, col0, diag, permut, *diagShifted, shift);
if (math::sd_abs<T,T>(fPrev) < math::sd_abs<T,T>(fCur)) {
math::sd_swap<T>(fPrev, fCur);
math::sd_swap<T>(muPrev, muCur);
}
bool useBisection = fPrev * fCur > (T)0.;
while (fCur != (T).0 &&
math::sd_abs<T,T>(muCur - muPrev) >
(T)8. * DataTypeUtils::eps<T>() * math::sd_max<T>(math::sd_abs<T,T>(muCur), math::sd_abs<T,T>(muPrev)) &&
math::sd_abs<T,T>(fCur - fPrev) > DataTypeUtils::eps<T>() && !useBisection) {
T a = (fCur - fPrev) / ((T)1. / muCur - (T)1. / muPrev);
T jac = fCur - a / muCur;
T muZero = -a / jac;
T fZero = secularEq(muZero, col0, diag, permut, *diagShifted, shift);
muPrev = muCur;
fPrev = fCur;
muCur = muZero;
fCur = fZero;
if (shift == left && (muCur < (T)0. || muCur > right - left))
useBisection = true;
else if (shift == right && (muCur < -(right - left) || muCur > (T)0.))
useBisection = true;
else if (math::sd_abs<T,T>(fCur) > math::sd_abs<T,T>(fPrev) &&
math::sd_abs<T,T>(fCur - fPrev) > (T)16. * DataTypeUtils::eps<T>())
useBisection = true;
}
if (useBisection) {
T leftShifted, rightShifted;
if (shift == left) {
leftShifted = DataTypeUtils::min_positive<T>();
rightShifted = (k == curLen - 1) ? right : ((right - left) * (T)0.6);
} else {
leftShifted = -(right - left) * (T)0.6;
rightShifted = -DataTypeUtils::min_positive<T>();
}
T fLeft = secularEq(leftShifted, col0, diag, permut, *diagShifted, shift);
while (rightShifted - leftShifted >
(T)2.f * DataTypeUtils::eps<T>() *
math::sd_max<T>(math::sd_abs<T,T>(leftShifted), math::sd_abs<T,T>(rightShifted))) {
T midShifted = (leftShifted + rightShifted) / (T)2.;
fMid = secularEq(midShifted, col0, diag, permut, *diagShifted, shift);
if (fLeft * fMid < (T)0.)
rightShifted = midShifted;
else {
leftShifted = midShifted;
fLeft = fMid;
}
}
muCur = (leftShifted + rightShifted) / (T)2.;
}
singVals.template r<T>(k) = shift + muCur;
shifts.template r<T>(k) = shift;
mus.template r<T>(k) = muCur;
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::perturb(NDArray col0, NDArray& diag, NDArray permut, NDArray& singVals,
NDArray& shifts, NDArray& mus, NDArray& zhat) {
int n = col0.lengthOf();
int m = permut.lengthOf();
if (m == 0) {
zhat.nullify();
return;
}
int last = permut.t<T>(m - 1);
for (int k = 0; k < n; ++k) {
if (col0.t<T>(k) == (T)0.f)
zhat.template r<T>(k) = (T)0;
else {
T dk = diag.t<T>(k);
T prod = (singVals.t<T>(last) + dk) * (mus.t<T>(last) + (shifts.t<T>(last) - dk));
for (int l = 0; l < m; ++l) {
int i = (int)permut.t<T>(l);
if (i != k) {
int j = i < k ? i : (int)permut.t<T>(l - 1);
prod *= ((singVals.t<T>(j) + dk) / ((diag.t<T>(i) + dk))) *
((mus.t<T>(j) + (shifts.t<T>(j) - dk)) / ((diag.t<T>(i) - dk)));
}
}
T tmp = math::sd_sqrt<T, T>(prod);
zhat.template r<T>(k) = col0.t<T>(k) > (T)0 ? tmp : -tmp;
}
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::calcSingVecs(NDArray zhat, NDArray& diag, NDArray perm, NDArray& singVals,
NDArray& shifts, NDArray& mus, NDArray& U, NDArray& V) {
int n = zhat.lengthOf();
int m = perm.lengthOf();
for (int k = 0; k < n; ++k) {
NDArray *colUPtr = U({0, 0, k, k + 1});
NDArray colU = *colUPtr;
delete colUPtr;
colU.nullify();
// Initialize colV as a scalar placeholder (will be reassigned if _calcV is true)
NDArray colV(_m.dataType(), _m.getContext(), true);
if (_calcV) {
NDArray *colVPtr = V({0, 0, k, k + 1});
colV = *colVPtr;
delete colVPtr;
colV.nullify();
}
if (zhat.t<T>(k) == (T)0.f) {
colU.template r<T>(k) = (T)1;
if (_calcV) colV.template r<T>(k) = (T)1;
} else {
for (int l = 0; l < m; ++l) {
int i = (int)perm.t<T>(l);
U.template r<T>(i, k) =
zhat.t<T>(i) / (((diag.t<T>(i) - shifts.t<T>(k)) - mus.t<T>(k))) / ((diag.t<T>(i) + singVals.t<T>(k)));
}
U.template r<T>(n, k) = (T)0;
auto reduce = colU.reduceNumber(reduce::Norm2);
colU /= *reduce;
delete reduce;
if (_calcV) {
for (int l = 1; l < m; ++l) {
int i = perm.t<T>(l);
V.template r<T>(i, k) = diag.t<T>(i) * zhat.t<T>(i) / (((diag.t<T>(i) - shifts.t<T>(k)) - mus.t<T>(k))) /
((diag.t<T>(i) + singVals.t<T>(k)));
}
V.template r<T>(0, k) = (T)-1;
auto reduce = colV.reduceNumber(reduce::Norm2);
colV /= *reduce;
delete reduce;
}
}
}
NDArray *colUPtr = U({0, 0, n, n + 1});
NDArray colU = *colUPtr;
delete colUPtr;
colU.nullify();
colU.template r<T>(n) = (T)1;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::calcBlockSVD(int col1, int size, NDArray& U, NDArray& singVals, NDArray& V) {
const T almostZero = DataTypeUtils::min_positive<T>();
int end = col1 + size;
NDArray *col0Ptr = _m({col1, end, col1, col1 + 1}, true);
NDArray col0 = *col0Ptr;
delete col0Ptr;
NDArray *viewPtr = _m({col1, end, col1, end}, true);
NDArray diag = viewPtr->diagonal('c');
delete viewPtr;
diag.template r<T>(0) = (T)0;
std::vector<sd::LongType> shape2 = {size, 1};
std::vector<sd::LongType> shape3 = {size + 1, size + 1};
singVals = NDArray(_m.ordering(), shape2, _m.dataType(), _m.getContext());
U = NDArray(_u.ordering(), shape3, _u.dataType(), _u.getContext());
std::vector<sd::LongType> sizeShape = {size, size};
if (_calcV) V = NDArray(_v.ordering(), sizeShape, _v.dataType(), _v.getContext());
int curSize = size;
while (curSize > 1 && diag.template t<T>(curSize - 1) == (T)0.f) --curSize;
int m = 0;
std::vector<int> indices;
for (int k = 0; k < curSize; ++k)
if (math::sd_abs<T,T>(col0.template t<T>(k)) > almostZero) indices.push_back(k);
std::vector<sd::LongType> permutShape = {(int)indices.size()};
NDArray permut(_m.ordering(), permutShape, _m.dataType(), _m.getContext());
for (size_t k = 0; k < indices.size(); ++k) permut.template r<T>(k) = (T)indices[k];
std::vector<sd::LongType> shape = {size,1};
NDArray shifts(_m.ordering(), shape, _m.dataType(), _m.getContext());
NDArray mus(_m.ordering(), shape, _m.dataType(), _m.getContext());
NDArray zhat(_m.ordering(),shape, _m.dataType(), _m.getContext());
calcSingVals(col0, diag, permut, singVals, shifts, mus);
perturb(col0, diag, permut, singVals, shifts, mus, zhat);
calcSingVecs(zhat, diag, permut, singVals, shifts, mus, U, V);
for (int i = 0; i < curSize - 1; ++i) {
if (singVals.t<T>(i) > singVals.t<T>(i + 1)) {
math::sd_swap<T>(singVals.template r<T>(i), singVals.template r<T>(i + 1));
NDArray *temp1 = U({0, 0, i, i + 1});
NDArray *temp2 = U({0, 0, i + 1, i + 2});
temp1->swapUnsafe(*temp2);
delete temp1;
delete temp2;
if (_calcV) {
NDArray *temp1V = V({0, 0, i, i + 1});
NDArray *temp2V = V({0, 0, i + 1, i + 2});
temp1V->swapUnsafe(*temp2V);
delete temp1V;
delete temp2V;
}
}
}
NDArray *temp1Ptr = singVals({0, curSize, 0, 0});
NDArray temp1 = *temp1Ptr;
delete temp1Ptr;
for (int e = 0; e < curSize / 2; ++e) math::sd_swap<T>(temp1.template r<T>(e), temp1.template r<T>(curSize - 1 - e));
NDArray *temp2Ptr = U({0, 0, 0, curSize}, true);
NDArray temp2 = *temp2Ptr;
delete temp2Ptr;
for (int i = 0; i < curSize / 2; ++i) {
NDArray *temp3 = temp2({0, 0, i, i + 1});
NDArray *temp4 = temp2({0, 0, curSize - 1 - i, curSize - i});
temp3->swapUnsafe(*temp4);
delete temp3;
delete temp4;
}
if (_calcV) {
NDArray *temp2VPtr = V({0, 0, 0, curSize}, true);
NDArray temp2V = *temp2VPtr;
delete temp2VPtr;
for (int i = 0; i < curSize / 2; ++i) {
NDArray *temp3 = temp2V({0, 0, i, i + 1});
NDArray *temp4 = temp2V({0, 0, curSize - 1 - i, curSize - i});
temp3->swapUnsafe(*temp4);
delete temp3;
delete temp4;
}
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::DivideAndConquer(int col1, int col2, int row1W, int col1W, int shift) {
// requires rows = cols + 1;
const int n = col2 - col1 + 1;
const int k = n / 2;
const T almostZero = DataTypeUtils::min_positive<T>();
T alphaK, betaK, r0, lambda, phi, c0, s0;
std::vector<sd::LongType> lShape = {1, k};
std::vector<sd::LongType> fShape = {1, n - k - 1};
NDArray l(_u.ordering(),lShape, _u.dataType(), _u.getContext());
NDArray f(_u.ordering(), fShape, _u.dataType(), _u.getContext());
if (n < _switchSize) {
NDArray *mViewPtr = _m({col1, col1 + n + 1, col1, col1 + n}, true);
JacobiSVD<T> jac(*mViewPtr, _calcU, _calcV, _fullUV);
delete mViewPtr;
if (_calcU) {
NDArray *uViewPtr = _u({col1, col1 + n + 1, col1, col1 + n + 1}, true);
uViewPtr->assign(&jac._u);
delete uViewPtr;
} else {
NDArray *uView1Ptr = _u({0, 1, col1, col1 + n + 1}, true);
NDArray *jacUView1Ptr = jac._u({0, 1, 0, 0}, true);
uView1Ptr->assign(jacUView1Ptr);
delete uView1Ptr;
delete jacUView1Ptr;
NDArray *uView2Ptr = _u({1, 2, col1, col1 + n + 1}, true);
NDArray *jacUView2Ptr = jac._u({n, n + 1, 0, 0}, true);
uView2Ptr->assign(jacUView2Ptr);
delete uView2Ptr;
delete jacUView2Ptr;
}
if (_calcV) {
NDArray *vViewPtr = _v({row1W, row1W + n, col1W, col1W + n}, true);
vViewPtr->assign(&jac._v);
delete vViewPtr;
}
NDArray *mNullifyPtr = _m({col1 + shift, col1 + shift + n + 1, col1 + shift, col1 + shift + n}, true);
mNullifyPtr->nullify();
delete mNullifyPtr;
auto diag = _m.diagonal('c');
NDArray *firstPtr = diag({col1 + shift, col1 + shift + n, 0, 0}, true);
NDArray first = *firstPtr;
delete firstPtr;
NDArray *secondPtr = jac._s({0, n, 0, 0}, true);
NDArray second = *secondPtr;
delete secondPtr;
first.assign(&second);
return;
}
alphaK = _m.t<T>(col1 + k, col1 + k);
betaK = _m.t<T>(col1 + k + 1, col1 + k);
DivideAndConquer(k + 1 + col1, col2, k + 1 + row1W, k + 1 + col1W, shift);
DivideAndConquer(col1, k - 1 + col1, row1W, col1W + 1, shift + 1);
if (_calcU) {
lambda = _u.t<T>(col1 + k, col1 + k);
phi = _u.t<T>(col1 + k + 1, col2 + 1);
} else {
lambda = _u.t<T>(1, col1 + k);
phi = _u.t<T>(0, col2 + 1);
}
r0 = math::sd_sqrt<T, T>((math::sd_abs<T,T>(alphaK * lambda) * math::sd_abs<T,T>(alphaK * lambda)) +
math::sd_abs<T,T>(betaK * phi) * math::sd_abs<T,T>(betaK * phi));
if (_calcU) {
NDArray *lAssignPtr = _u({col1 + k, col1 + k + 1, col1, col1 + k}, true);
l.assign(lAssignPtr);
delete lAssignPtr;
NDArray *fAssignPtr = _u({col1 + k + 1, col1 + k + 2, col1 + k + 1, col1 + n}, true);
f.assign(fAssignPtr);
delete fAssignPtr;
} else {
NDArray *lAssignPtr = _u({1, 2, col1, col1 + k}, true);
l.assign(lAssignPtr);
delete lAssignPtr;
NDArray *fAssignPtr = _u({0, 1, col1 + k + 1, col1 + n}, true);
f.assign(fAssignPtr);
delete fAssignPtr;
}
if (_calcV) _v.template r<T>(row1W + k, col1W) = (T)1;
if (r0 < almostZero) {
c0 = 1.;
s0 = 0.;
} else {
c0 = alphaK * lambda / r0;
s0 = betaK * phi / r0;
}
if (_calcU) {
NDArray *q1Ptr = _u({col1, col1 + k + 1, col1 + k, col1 + k + 1}, true);
NDArray *q1 = q1Ptr->dup();
delete q1Ptr;
NDArray *uAssignOne = *q1 * c0;
NDArray *uAssignTwo = *q1 * (-s0);
for (int i = col1 + k - 1; i >= col1; --i) {
NDArray *uSrcPtr = _u({col1, col1 + k + 1, i, i + 1}, true);
NDArray *uDstPtr = _u({col1, col1 + k + 1, i + 1, i + 2}, true);
uDstPtr->assign(uSrcPtr);
delete uSrcPtr;
delete uDstPtr;
}
NDArray *temp1Ptr = _u({col1 + k + 1, col1 + n + 1, col2 + 1, col2 + 2}, true);
NDArray temp1 = *temp1Ptr;
delete temp1Ptr;
NDArray *uAssignThree = temp1 * s0;
NDArray *uAssign1Ptr = _u({col1, col1 + k + 1, col1, col1 + 1}, true);
uAssign1Ptr->assign(uAssignOne);
delete uAssign1Ptr;
NDArray *uAssign2Ptr = _u({col1, col1 + k + 1, col2 + 1, col2 + 2}, true);
uAssign2Ptr->assign(uAssignTwo);
delete uAssign2Ptr;
delete uAssignOne;
delete uAssignTwo;
NDArray *uAssign3Ptr = _u({col1 + k + 1, col1 + n + 1, col1, col1 + 1}, true);
uAssign3Ptr->assign(uAssignThree);
delete uAssign3Ptr;
delete uAssignThree;
temp1 *= c0;
delete q1;
} else {
T q1 = _u.t<T>(0, col1 + k);
for (int i = col1 + k - 1; i >= col1; --i) _u.template r<T>(0, i + 1) = _u.template r<T>(0, i);
_u.template r<T>(0, col1) = q1 * c0;
_u.template r<T>(0, col2 + 1) = -q1 * s0;
_u.template r<T>(1, col1) = _u.t<T>(1, col2 + 1) * s0;
_u.template r<T>(1, col2 + 1) = _u.t<T>(1, col2 + 1) * c0;
NDArray *uNullify1Ptr = _u({1, 2, col1 + 1, col1 + k + 1});
uNullify1Ptr->nullify();
delete uNullify1Ptr;
NDArray *uNullify2Ptr = _u({0, 1, col1 + k + 1, col1 + n});
uNullify2Ptr->nullify();
delete uNullify2Ptr;
}
_m.template r<T>(col1 + shift, col1 + shift) = r0;
NDArray *assignOne = l * alphaK;
NDArray *assignTwo = f * betaK;
NDArray *mAssign1Ptr = _m({col1 + shift + 1, col1 + shift + k + 1, col1 + shift, col1 + shift + 1}, true);
mAssign1Ptr->assign(assignOne);
delete mAssign1Ptr;
NDArray *mAssign2Ptr = _m({col1 + shift + k + 1, col1 + shift + n, col1 + shift, col1 + shift + 1}, true);
mAssign2Ptr->assign(assignTwo);
delete mAssign2Ptr;
delete assignOne;
delete assignTwo;
deflation(col1, col2, k, row1W, col1W, shift);
// Initialize as scalar placeholders (will be reassigned by calcBlockSVD)
NDArray UofSVD(_u.dataType(), _u.getContext(), true);
NDArray VofSVD(_v.dataType(), _v.getContext(), true);
NDArray singVals(_m.dataType(), _m.getContext(), true);
calcBlockSVD(col1 + shift, n, UofSVD, singVals, VofSVD);
if (_calcU) {
NDArray *tempPtr = _u({col1, col1 + n + 1, col1, col1 + n + 1}, true);
NDArray temp = *tempPtr;
delete tempPtr;
NDArray *assign2 = mmul(temp, UofSVD);
temp.assign(assign2);
delete assign2;
} else {
NDArray *tempPtr = _u({0, 0, col1, col1 + n + 1}, true);
NDArray temp = *tempPtr;
delete tempPtr;
NDArray *assign2 = mmul(temp, UofSVD);
temp.assign(assign2);
delete assign2;
}
if (_calcV) {
NDArray *tempPtr = _v({row1W, row1W + n, row1W, row1W + n}, true);
NDArray temp = *tempPtr;
delete tempPtr;
NDArray *assign2 = mmul(temp, VofSVD);
temp.assign(assign2);
delete assign2;
}
NDArray *blockMPtr = _m({col1 + shift, col1 + shift + n, col1 + shift, col1 + shift + n}, true);
NDArray blockM = *blockMPtr;
delete blockMPtr;
blockM.nullify();
blockM.diagonal('c').assign(&singVals);
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::exchangeUV(HHsequence& hhU, HHsequence& hhV, NDArray& U, NDArray& V) {
if (_calcU) {
int colsU = _fullUV ? hhU.rows() : _diagSize;
std::vector<sd::LongType> tempShape = {hhU.rows(), colsU};
NDArray temp1(_u.ordering(), tempShape, _u.dataType(), _u.getContext());
temp1.setIdentity();
_u = temp1;
NDArray *uViewPtr = _u({0, _diagSize, 0, _diagSize}, true);
NDArray *vViewPtr = V({0, _diagSize, 0, _diagSize}, true);
uViewPtr->assign(vViewPtr);
delete uViewPtr;
delete vViewPtr;
const_cast<HHsequence&>(hhU).mulLeft(&_u);
}
if (_calcV) {
int colsV = _fullUV ? hhV.rows() : _diagSize;
std::vector<sd::LongType> tempShape = {hhV.rows(), colsV};
NDArray temp1(_v.ordering(), tempShape, _v.dataType(), _v.getContext());
temp1.setIdentity();
_v = temp1;
NDArray *assignPtr = U({0, _diagSize, 0, _diagSize}, true);
NDArray assign = *assignPtr;
delete assignPtr;
NDArray *vViewPtr = _v({0, _diagSize, 0, _diagSize}, true);
vViewPtr->assign(&assign);
delete vViewPtr;
const_cast<HHsequence&>(hhV).mulLeft(&_v);
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
void SVD<T>::evalData(NDArray& matrix) {
const T almostZero = DataTypeUtils::min_positive<T>();
if (matrix.sizeAt(1) < _switchSize) {
JacobiSVD<T> jac(matrix, _calcU, _calcV, _fullUV);
if (_calcU) _u = jac._u;
if (_calcV) _v = jac._v;
_s.assign(&jac._s);
return;
}
auto reduce = matrix.reduceNumber(reduce::AMax);
T scale = reduce->t<T>(0);
delete reduce;
if (scale == (T)0.) scale = 1.;
NDArray *input = _transp ? matrix.transpose() : new NDArray((matrix / scale));
BiDiagonalUp biDiag(*input);
_u.nullify();
_v.nullify();
NDArray *assign1 = biDiag._HHbidiag.transpose();
NDArray *mViewPtr = _m({0, _diagSize, 0, 0}, true);
mViewPtr->assign(assign1);
delete mViewPtr;
delete assign1;
NDArray *mNullifyPtr = _m({_m.sizeAt(0) - 1, _m.sizeAt(0), 0, 0});
mNullifyPtr->nullify();
delete mNullifyPtr;
DivideAndConquer(0, _diagSize - 1, 0, 0, 0);
for (int i = 0; i < _diagSize; ++i) {
T a = math::sd_abs<T,T>(_m.t<T>(i, i));
_s.template r<T>(i) = a * scale;
if (a < almostZero) {
NDArray *sNullifyPtr = _s({i + 1, _diagSize, 0, 0});
sNullifyPtr->nullify();
delete sNullifyPtr;
break;
} else if (i == _diagSize - 1)
break;
}
HHsequence hhV = biDiag.makeHHsequence('v');
HHsequence hhU = biDiag.makeHHsequence('u');
if (_transp)
exchangeUV(hhV, hhU, _v, _u);
else
exchangeUV(hhU, hhV, _u, _v);
delete input;
}
BUILD_SINGLE_TEMPLATE( class SVD, , SD_FLOAT_TYPES);
} // namespace helpers
} // namespace ops
} // namespace sd