chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user