2015-08-05 19:23:33 +03:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "QuantizedMatrix.h"
|
2015-08-17 20:24:15 +03:00
|
|
|
#include "ColumnQuantizer.h"
|
2015-08-05 19:23:33 +03:00
|
|
|
|
|
|
|
namespace Microsoft { namespace MSR { namespace CNTK {
|
2016-01-18 11:36:14 +03:00
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType>::QuantizedMatrix(const size_t numRows, const size_t numCols, const size_t nbits, DEVICEID_TYPE deviceId, MemAllocator* allocator /* = nullptr */)
|
|
|
|
: m_numRows(numRows), m_numCols(numCols), m_numBits(nbits), m_allocator(allocator)
|
|
|
|
{
|
|
|
|
m_qColSize = QuantizedColumn<ElemType>::QuantizedColumnSize(m_numBits, m_numRows);
|
|
|
|
if (((QWordNumBits / m_numBits) * m_numBits) != QWordNumBits)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
LogicError("Quantization: 'nbits' must be a divisor of 64");
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
if (m_allocator == nullptr)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
m_quantizedData = new Matrix<char>(m_qColSize, m_numCols, deviceId);
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
else
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-30 13:51:29 +03:00
|
|
|
m_quantizedData = new Matrix<char>(m_qColSize, m_numCols, (char*)m_allocator->Malloc(m_qColSize * m_numCols), deviceId, matrixFlagDontOwnBuffer);
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType>::QuantizedMatrix(QuantizedMatrix<ElemType>&& moveFrom)
|
|
|
|
: m_quantizedData(moveFrom.m_quantizedData), m_allocator(moveFrom.m_allocator), m_numRows(moveFrom.m_numRows), m_numCols(moveFrom.m_numCols), m_numBits(moveFrom.m_numBits), m_qColSize(moveFrom.m_qColSize)
|
|
|
|
{
|
|
|
|
moveFrom.m_quantizedData = nullptr;
|
|
|
|
moveFrom.m_allocator = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType>& QuantizedMatrix<ElemType>::operator=(QuantizedMatrix<ElemType>&& moveFrom)
|
|
|
|
{
|
|
|
|
assert(this != &moveFrom);
|
|
|
|
|
|
|
|
this->m_quantizedData = moveFrom.m_quantizedData;
|
|
|
|
this->m_allocator = moveFrom.m_allocator;
|
|
|
|
this->m_numRows = moveFrom.m_numRows;
|
|
|
|
this->m_numCols = moveFrom.m_numCols;
|
|
|
|
this->m_numBits = moveFrom.m_numBits;
|
|
|
|
this->m_qColSize = moveFrom.m_qColSize;
|
|
|
|
|
|
|
|
moveFrom.m_quantizedData = nullptr;
|
|
|
|
moveFrom.m_allocator = nullptr;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType>::QuantizedMatrix(const size_t numRows, const size_t numCols, const size_t nbits, Matrix<char>* data)
|
|
|
|
: m_numRows(numRows), m_numCols(numCols), m_numBits(nbits), m_quantizedData(data), m_allocator(nullptr)
|
|
|
|
{
|
|
|
|
m_qColSize = QuantizedColumn<ElemType>::QuantizedColumnSize(m_numBits, m_numRows);
|
|
|
|
if (((QWordNumBits / m_numBits) * m_numBits) != QWordNumBits)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
LogicError("Quantization: 'nbits' must be a divisor of 64");
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
// Make sure that the data matrix has enough space
|
|
|
|
assert((m_quantizedData->GetNumRows() == m_qColSize) && (m_quantizedData->GetNumCols() >= numCols));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType>::~QuantizedMatrix()
|
|
|
|
{
|
|
|
|
if (nullptr != m_quantizedData)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
// If we used an external allocator, lets free the backing buffer of the matrix
|
|
|
|
if (m_allocator != nullptr)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
assert(!m_quantizedData->OwnBuffer());
|
|
|
|
m_allocator->Free(m_quantizedData->BufferPointer());
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
|
|
|
|
delete m_quantizedData;
|
|
|
|
m_quantizedData = nullptr;
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
int QuantizedMatrix<ElemType>::GetDeviceId() const
|
|
|
|
{
|
|
|
|
return m_quantizedData->GetDeviceId();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
size_t QuantizedMatrix<ElemType>::GetSize() const
|
|
|
|
{
|
|
|
|
return m_quantizedData->GetNumElements();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
char* QuantizedMatrix<ElemType>::GetArray() const
|
|
|
|
{
|
|
|
|
return m_quantizedData->BufferPointer();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
QuantizedMatrix<ElemType> QuantizedMatrix<ElemType>::ColumnSlice(size_t startColumn, size_t numCols) const
|
|
|
|
{
|
|
|
|
auto matrixSliceData = new Matrix<char>(m_quantizedData->ColumnSlice(startColumn, numCols));
|
|
|
|
return QuantizedMatrix<ElemType>(this->GetNumRows(), numCols, this->GetNumBits(), matrixSliceData);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class ElemType>
|
|
|
|
void QuantizedMatrix<ElemType>::Print(const char* matrixName, size_t rowStart, size_t rowEnd, size_t colStart, size_t colEnd)
|
|
|
|
{
|
|
|
|
if ((GetNumRows() == 0) || (GetNumCols() == 0))
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
LogicError("Print: QuantizedMatrix is empty.");
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
|
|
|
|
if (rowEnd >= GetNumRows() || colEnd >= GetNumCols())
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
InvalidArgument("Index out of range.");
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
|
|
|
|
DEVICEID_TYPE orgdevice = this->GetDeviceId();
|
|
|
|
CurrentDataLocation curLocation = m_quantizedData->GetCurrentMatrixLocation();
|
|
|
|
if (curLocation == CurrentDataLocation::GPU)
|
2015-08-05 19:23:33 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
m_quantizedData->_transferToDevice(CPUDEVICE, false, false);
|
2015-08-05 19:23:33 +03:00
|
|
|
}
|
2015-08-17 20:24:15 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
if (matrixName != nullptr)
|
|
|
|
fprintf(stderr, "\n###### %s (%lu, %lu) ######\n", matrixName, GetNumRows(), GetNumCols());
|
|
|
|
else
|
|
|
|
fprintf(stderr, "\n###### Unnamed Matrix (%lu, %lu) ######\n", GetNumRows(), GetNumCols());
|
2015-08-17 20:24:15 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
fprintf(stderr, "\n------ Print Range (%lu:%lu, %lu:%lu) ------\n", rowStart, rowEnd, colStart, colEnd);
|
2015-08-17 20:24:15 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
for (size_t j = colStart; j <= colEnd; j++)
|
|
|
|
{
|
|
|
|
QuantizedColumn<ElemType>* qCol = this->GetQuantizedColumn(j);
|
|
|
|
fprintf(stderr, "Lower=%.10f,Upper=%.10f\t", qCol->lower, qCol->upper);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
2015-08-17 20:24:15 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
const size_t ldNbits = ValueQuantizer<ElemType>::ld(this->GetNumBits());
|
|
|
|
size_t numQWordsPerCol = ColumnQuantizer<ElemType>::QWordsPerCol(this->GetNumRows(), this->GetNumBits());
|
|
|
|
for (size_t i = rowStart; i <= rowEnd; i++)
|
|
|
|
{
|
|
|
|
size_t qWordIdx = i % numQWordsPerCol;
|
|
|
|
size_t offsetInQWord = i / numQWordsPerCol;
|
2015-08-17 20:24:15 +03:00
|
|
|
for (size_t j = colStart; j <= colEnd; j++)
|
|
|
|
{
|
|
|
|
QuantizedColumn<ElemType>* qCol = this->GetQuantizedColumn(j);
|
2016-01-18 11:36:14 +03:00
|
|
|
ColumnQuantizer<ElemType> q(ldNbits, qCol->lower, qCol->upper);
|
|
|
|
QWord qWord = qCol->bits[qWordIdx];
|
2015-08-17 20:24:15 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
QWordVal qVal;
|
|
|
|
ElemType val;
|
|
|
|
if (this->GetNumBits() == 1)
|
2015-08-17 20:24:15 +03:00
|
|
|
{
|
2016-01-18 11:36:14 +03:00
|
|
|
ElemType val0 = q.valQ.Unquantize(0);
|
|
|
|
ElemType val1 = q.valQ.Unquantize(1);
|
|
|
|
qVal = (qWord >> offsetInQWord) & 1;
|
|
|
|
val = ValueQuantizer<ElemType>::Unquantize1(qVal != 0, val0, val1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const QWordVal bitmask = q.valQ.QuanRangeEnd() - 1;
|
|
|
|
qVal = (qWord >> (offsetInQWord * this->GetNumBits())) & bitmask;
|
|
|
|
val = q.valQ.Unquantize(qVal);
|
2015-08-17 20:24:15 +03:00
|
|
|
}
|
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
fprintf(stderr, "%10d (%.10f) \t", (int) qVal, val);
|
2015-08-17 20:24:15 +03:00
|
|
|
}
|
2016-01-18 11:36:14 +03:00
|
|
|
fprintf(stderr, "\n");
|
2015-08-17 20:24:15 +03:00
|
|
|
}
|
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
if (curLocation == CurrentDataLocation::GPU)
|
|
|
|
{
|
|
|
|
m_quantizedData->_transferToDevice(orgdevice, false, false);
|
|
|
|
}
|
|
|
|
}
|
2015-08-05 19:23:33 +03:00
|
|
|
|
2016-01-18 11:36:14 +03:00
|
|
|
// Explicit instantiation
|
|
|
|
template class QuantizedMatrix<float>;
|
|
|
|
template class QuantizedMatrix<double>;
|
|
|
|
} } }
|