//===========================================================================
/*!
*
*
* \brief Partly Precomputed version of a matrix for quadratic programming
*
*
* \par
*
*
*
* \author T. Glasmachers, A. Demircioglu
* \date 2007-2014
*
*
* \par Copyright 1995-2017 Shark Development Team
*
*
* This file is part of Shark.
*
*
* Shark is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shark is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Shark. If not, see .
*
*/
//===========================================================================
#ifndef SHARK_LINALG_PARTLYPRECOMPUTEDMATRIX_H
#define SHARK_LINALG_PARTLYPRECOMPUTEDMATRIX_H
#include
#include
#include
#include
namespace shark
{
///
/// \brief Partly Precomputed version of a matrix for quadratic programming
///
/// \par
/// The PartlyPrecomputedMatrix class computes all pairs of kernel
/// evaluations that fits the given cachesize in its constructor and
/// stores them im memory.
///
/// \par
/// Use of this class may be beneficial for certain model
/// selection strategies, where the whole matrix does not fit into
/// memory, and the LRU cache will produce too much hit rates,
/// so that at least partially caching the kernel matrix will help.
/// In particular this will help the KernelSGD/Pegasos algorithm.
///
template
class PartlyPrecomputedMatrix
{
public:
typedef typename Matrix::QpFloatType QpFloatType;
/// Constructor
/// \param[in] base matrix to be cached. it is assumed that this matrix is not precomputed,
/// but the (costy) computation takes place every time an entry is queried.
/// \param[in] cachesize size of the cache to use in bytes. the size of the cached matrix will
// depend on this value.
PartlyPrecomputedMatrix(Matrix* base, std::size_t cachesize = 0x4000000)
: m_cacheSize(cachesize)
, m_baseMatrix(base)
{
SHARK_RUNTIME_CHECK(m_baseMatrix || m_baseMatrix ->size() == 0, "Cannot cache a NULL matrix!");
// remember the original size of the matrix
m_originalNumberOfRows = m_baseMatrix -> size();
// determine how many bytes we need for a single row
size_t rowSizeBytes = m_originalNumberOfRows * sizeof(QpFloatType);
// how many rows fit into our cache?
size_t m_nRows = (size_t) m_cacheSize / rowSizeBytes;
SHARK_RUNTIME_CHECK(m_nRows, "Cache size is smaller than the size of a row!");
// if we have more space than needed, well, we do not need it.
if(m_nRows > m_originalNumberOfRows)
m_nRows = m_originalNumberOfRows ;
// resize matrix
m_cachedMatrix.resize(m_nRows, m_baseMatrix ->size());
// copy the rows
for(std::size_t r = 0; r < m_cachedMatrix.size1(); r++)
{
for(std::size_t j = 0; j < m_baseMatrix->size(); j++)
{
m_cachedMatrix(r, j) = (*m_baseMatrix)(r, j);
}
}
}
/// return, if a given row is cached
/// \param[in] k row to check
/// \return is given row in cached matrix or not?
bool isCached(std::size_t k) const
{
if(k < m_cachedMatrix.size1())
return true;
return false;
}
/// return a complete row of the matrix.
/// if the row is cached, it will be returned from there, if not, it will
/// be recomputed on-the-fly and not stored.
/// param[in] k row to compute
/// param[in] storage vector to store the row. must be the same size as a row!
void row(std::size_t k, blas::vector &storage) const
{
RANGE_CHECK(k < m_originalNumberOfRows);
RANGE_CHECK(0 <= k);
SIZE_CHECK(storage.size() == m_cachedMatrix.size2());
if(isCached(k) == true)
{
for(std::size_t j = 0; j < m_cachedMatrix.size2(); j++)
{
storage[j] = m_cachedMatrix(k, j);
}
}
else
{
for(std::size_t j = 0; j < m_cachedMatrix.size2(); j++)
{
storage[j] = (*m_baseMatrix)(k, j);
}
}
}
/// return a single matrix entry
/// param[in] i row of entry
/// param[in] j column entry
/// @return value of matrix at given position
QpFloatType operator()(std::size_t i, std::size_t j) const
{
return entry(i, j);
}
/// return a single matrix entry
/// param[in] i row of entry
/// param[in] j column entry
/// @return value of matrix at given position
QpFloatType entry(std::size_t i, std::size_t j) const
{
// check if we have to compute that or not
if(isCached(i))
return m_cachedMatrix(i, j);
// ok, need to compute that element
return (*m_baseMatrix)(i, j);
}
/// return the number of cached rows
/// @return number of rows that are cached
std::size_t size() const
{
return m_cachedMatrix.size();
}
/// return size of cached matrix in QpFloatType units
/// @return the capacity of the cached matrix in QpFloatType units
std::size_t getMaxCacheSize()
{
return m_cachedMatrix.size() * m_cachedMatrix.size2();
}
/// return the dimension of a row in the cache (as we do not shorten our
/// rows, this must be the same as the dimension of a row in the original kernel matrix).
/// @return dimension of any cached row
std::size_t getCacheRowSize() const
{
return m_cachedMatrix.size2();
}
protected:
/// container for precomputed values
blas::matrix m_cachedMatrix;
// maximal size of cache
size_t m_cacheSize;
// original kernel matrix, will be accessed if entries outsied the cache are requested
Matrix* m_baseMatrix;
// remember how big the original matrix was to prevent access errors
size_t m_originalNumberOfRows;
};
}
#endif