// matrix/compressed-matrix.h // Copyright 2012 Johns Hopkins University (author: Daniel Povey) // Frantisek Skala, Wei Shi // See ../../COPYING for clarification regarding multiple authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, // MERCHANTABLITY OR NON-INFRINGEMENT. // See the Apache 2 License for the specific language governing permissions and // limitations under the License. #ifndef KALDI_MATRIX_COMPRESSED_MATRIX_H_ #define KALDI_MATRIX_COMPRESSED_MATRIX_H_ 1 #include "kaldi-matrix.h" namespace kaldi { /// \addtogroup matrix_group /// @{ /// This class does lossy compression of a matrix. It only /// supports copying to-from a KaldiMatrix. For large matrices, /// each element is compressed into about one byte, but there /// is a little overhead on top of that (globally, and also per /// column). /// The basic idea is for each column (in the normal configuration) /// we work out the values at the 0th, 25th, 50th and 100th percentiles /// and store them as 16-bit integers; we then encode each value in /// the column as a single byte, in 3 separate ranges with different /// linear encodings (0-25th, 25-50th, 50th-100th). /// If the matrix has 8 rows or fewer, we simply store all values as /// uint16. class CompressedMatrix { public: CompressedMatrix(): data_(NULL) { } ~CompressedMatrix() { Destroy(); } template CompressedMatrix(const MatrixBase &mat): data_(NULL) { CopyFromMat(mat); } /// Initializer that can be used to select part of an existing /// CompressedMatrix without un-compressing and re-compressing (note: unlike /// similar initializers for class Matrix, it doesn't point to the same memory /// location). CompressedMatrix(const CompressedMatrix &mat, const MatrixIndexT row_offset, const MatrixIndexT num_rows, const MatrixIndexT col_offset, const MatrixIndexT num_cols); void *Data() const { return this->data_; } /// This will resize *this and copy the contents of mat to *this. template void CopyFromMat(const MatrixBase &mat); CompressedMatrix(const CompressedMatrix &mat); CompressedMatrix &operator = (const CompressedMatrix &mat); // assignment operator. template CompressedMatrix &operator = (const MatrixBase &mat); // assignment operator. /// Copies contents to matrix. Note: mat must have the correct size, /// CopyToMat no longer attempts to resize it. template void CopyToMat(MatrixBase *mat) const; void Write(std::ostream &os, bool binary) const; void Read(std::istream &is, bool binary); /// Returns number of rows (or zero for emtpy matrix). inline MatrixIndexT NumRows() const { return (data_ == NULL) ? 0 : (*reinterpret_cast(data_)).num_rows; } /// Returns number of columns (or zero for emtpy matrix). inline MatrixIndexT NumCols() const { return (data_ == NULL) ? 0 : (*reinterpret_cast(data_)).num_cols; } /// Copies row #row of the matrix into vector v. /// Note: v must have same size as #cols. template void CopyRowToVec(MatrixIndexT row, VectorBase *v) const; /// Copies column #col of the matrix into vector v. /// Note: v must have same size as #rows. template void CopyColToVec(MatrixIndexT col, VectorBase *v) const; /// Copies submatrix of compressed matrix into matrix dest. /// Submatrix starts at row row_offset and column column_offset and its size /// is defined by size of provided matrix dest template void CopyToMat(int32 row_offset, int32 column_offset, MatrixBase *dest) const; void Swap(CompressedMatrix *other) { std::swap(data_, other->data_); } friend class Matrix; friend class Matrix; private: // allocates data using new [], ensures byte alignment // sufficient for float. static void *AllocateData(int32 num_bytes); // the "format" will be 1 for the original format where each column has a // PerColHeader, and 2 for the format now used for matrices with 8 or fewer // rows, where everything is represented as 16-bit integers. struct GlobalHeader { int32 format; float min_value; float range; int32 num_rows; int32 num_cols; }; static MatrixIndexT DataSize(const GlobalHeader &header); struct PerColHeader { uint16 percentile_0; uint16 percentile_25; uint16 percentile_75; uint16 percentile_100; }; template static void CompressColumn(const GlobalHeader &global_header, const Real *data, MatrixIndexT stride, int32 num_rows, PerColHeader *header, unsigned char *byte_data); template static void ComputeColHeader(const GlobalHeader &global_header, const Real *data, MatrixIndexT stride, int32 num_rows, PerColHeader *header); static inline uint16 FloatToUint16(const GlobalHeader &global_header, float value); static inline float Uint16ToFloat(const GlobalHeader &global_header, uint16 value); static inline unsigned char FloatToChar(float p0, float p25, float p75, float p100, float value); static inline float CharToFloat(float p0, float p25, float p75, float p100, unsigned char value); void Destroy(); void *data_; // first GlobalHeader, then PerColHeader (repeated), then // the byte data for each column (repeated). Note: don't intersperse // the byte data with the PerColHeaders, because of alignment issues. }; /// @} end of \addtogroup matrix_group } // namespace kaldi #endif // KALDI_MATRIX_COMPRESSED_MATRIX_H_