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

176 lines
5.8 KiB
C++

/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// Created by Yurii Shyrma on 11.01.2018
//
#include <helpers/hhColPivQR.h>
#include <helpers/householder.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
HHcolPivQR::HHcolPivQR(NDArray &matrix) {
_qr = matrix.dup(matrix.ordering());
std::vector<LongType> coeffsShape = {1,_diagSize};
_diagSize = math::sd_min<int>(matrix.sizeAt(0), matrix.sizeAt(1));
std::vector<LongType> permShape = {matrix.sizeAt(1), matrix.sizeAt(1)};
_coeffs = new NDArray(matrix.ordering(),coeffsShape, matrix.dataType(), matrix.getContext());
_permut = new NDArray(matrix.ordering(), permShape, matrix.dataType(), matrix.getContext());
evalData();
}
void HHcolPivQR::evalData() { BUILD_SINGLE_SELECTOR(_qr->dataType(), _evalData, (), SD_FLOAT_TYPES); }
//////////////////////////////////////////////////////////////////////////
template <typename T>
void HHcolPivQR::_evalData() {
const int rows = _qr->sizeAt(0);
const int cols = _qr->sizeAt(1);
std::vector<LongType> colsShape = {cols};
NDArray transp(_qr->ordering(), colsShape, _qr->dataType(), _qr->getContext());
NDArray normsUpd(_qr->ordering(), colsShape , _qr->dataType(), _qr->getContext());
NDArray normsDir(_qr->ordering(),colsShape , _qr->dataType(), _qr->getContext());
auto qRDeRef = *_qr;
for (int k = 0; k < cols; ++k) {
NDArray *colViewPtr = qRDeRef({0, 0, k, k + 1});
auto norm = colViewPtr->reduceNumber(reduce::Norm2);
normsDir.r<T>(k) = normsUpd.r<T>(k) = norm->t<T>(0);
delete norm;
delete colViewPtr;
}
auto max = (normsUpd.reduceNumber(reduce::Max));
T normScaled = max->t<T>(0) * DataTypeUtils::eps<T>();
T threshold1 = normScaled * normScaled / (T)rows;
T threshold2 = math::sd_sqrt<T, T>(DataTypeUtils::eps<T>());
T nonZeroPivots = static_cast<T>(_diagSize);
T maxPivot = static_cast<T>(0.);
delete max;
for (int k = 0; k < _diagSize; ++k) {
NDArray *normsUpdViewPtr = normsUpd({k, -1});
NDArray *indexNum = normsUpdViewPtr->indexReduceNumber(indexreduce::IndexMax);
int biggestColIndex = indexNum->e<int>(0);
auto max2 = normsUpdViewPtr->reduceNumber(reduce::Max);
T biggestColNorm = max2->t<T>(0);
delete normsUpdViewPtr;
delete max2;
T biggestColSqNorm = biggestColNorm * biggestColNorm;
biggestColIndex += k;
if (nonZeroPivots == (T)_diagSize && biggestColSqNorm < threshold1 * (T)(rows - k)) nonZeroPivots = k;
transp.r<T>(k) = (T)biggestColIndex;
if (k != biggestColIndex) {
NDArray *temp1Ptr = qRDeRef({0, 0, k, k + 1});
NDArray temp1 = *temp1Ptr;
delete temp1Ptr;
NDArray *temp2Ptr = qRDeRef({0, 0, biggestColIndex, biggestColIndex + 1});
NDArray temp2 = *temp2Ptr;
delete temp2Ptr;
temp1.swapUnsafe(temp2);
math::sd_swap<T>(normsUpd.r<T>(k), normsUpd.r<T>(biggestColIndex));
math::sd_swap<T>(normsDir.r<T>(k), normsDir.r<T>(biggestColIndex));
}
T normX, c;
NDArray *qrBlockPtr = qRDeRef({k, rows, k, k + 1});
NDArray qrBlock = *qrBlockPtr;
delete qrBlockPtr;
Householder<T>::evalHHmatrixDataI(qrBlock, c, normX);
_coeffs->r<T>(k) = c;
_qr->r<T>(k, k) = normX;
T max = math::sd_abs<T,T>(normX);
if (max > maxPivot) maxPivot = max;
if (k < rows && (k + 1) < cols) {
NDArray *qrBlock2Ptr = qRDeRef({k, rows, k + 1, cols}, true);
NDArray qrBlock2 = *qrBlock2Ptr;
delete qrBlock2Ptr;
NDArray *tailPtr = qRDeRef({k + 1, rows, k, k + 1}, true);
NDArray tail = *tailPtr;
delete tailPtr;
Householder<T>::mulLeft(qrBlock2, tail, _coeffs->t<T>(k));
}
for (int j = k + 1; j < cols; ++j) {
if (normsUpd.t<T>(j) != (T)0.f) {
T temp = math::sd_abs<T,T>(_qr->t<T>(k, j)) / normsUpd.t<T>(j);
temp = ((T)1. + temp) * ((T)1. - temp);
temp = temp < (T)0. ? (T)0. : temp;
T temp2 = temp * normsUpd.t<T>(j) * normsUpd.t<T>(j) / (normsDir.t<T>(j) * normsDir.t<T>(j));
if (temp2 <= threshold2) {
if (k + 1 < rows && j < cols) {
NDArray *normViewPtr = qRDeRef({k + 1, rows, j, j + 1});
auto reduce = normViewPtr->reduceNumber(reduce::Norm2);
normsDir.r<T>(j) = reduce->t<T>(0);
delete normViewPtr;
delete reduce;
}
normsUpd.r<T>(j) = normsDir.t<T>(j);
} else
normsUpd.r<T>(j) = normsUpd.t<T>(j) * math::sd_sqrt<T, T>(temp);
}
}
delete indexNum;
}
_permut->setIdentity();
auto permuteRef = *_permut;
for (int k = 0; k < _diagSize; ++k) {
int idx = transp.e<int>(k);
NDArray *temp1Ptr = permuteRef({0, 0, k, k + 1});
NDArray temp1 = *temp1Ptr;
delete temp1Ptr;
NDArray *temp2Ptr = permuteRef({0, 0, idx, idx + 1});
NDArray temp2 = *temp2Ptr;
delete temp2Ptr;
temp1.swapUnsafe(temp2);
}
}
BUILD_SINGLE_TEMPLATE( void HHcolPivQR::_evalData, (), SD_FLOAT_TYPES);
} // namespace helpers
} // namespace ops
} // namespace sd