Home | History | Annotate | Download | only in SparseCore
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 #ifndef EIGEN_AMBIVECTOR_H
     11 #define EIGEN_AMBIVECTOR_H
     12 
     13 namespace Eigen {
     14 
     15 namespace internal {
     16 
     17 /** \internal
     18   * Hybrid sparse/dense vector class designed for intensive read-write operations.
     19   *
     20   * See BasicSparseLLT and SparseProduct for usage examples.
     21   */
     22 template<typename _Scalar, typename _StorageIndex>
     23 class AmbiVector
     24 {
     25   public:
     26     typedef _Scalar Scalar;
     27     typedef _StorageIndex StorageIndex;
     28     typedef typename NumTraits<Scalar>::Real RealScalar;
     29 
     30     explicit AmbiVector(Index size)
     31       : m_buffer(0), m_zero(0), m_size(0), m_allocatedSize(0), m_allocatedElements(0), m_mode(-1)
     32     {
     33       resize(size);
     34     }
     35 
     36     void init(double estimatedDensity);
     37     void init(int mode);
     38 
     39     Index nonZeros() const;
     40 
     41     /** Specifies a sub-vector to work on */
     42     void setBounds(Index start, Index end) { m_start = convert_index(start); m_end = convert_index(end); }
     43 
     44     void setZero();
     45 
     46     void restart();
     47     Scalar& coeffRef(Index i);
     48     Scalar& coeff(Index i);
     49 
     50     class Iterator;
     51 
     52     ~AmbiVector() { delete[] m_buffer; }
     53 
     54     void resize(Index size)
     55     {
     56       if (m_allocatedSize < size)
     57         reallocate(size);
     58       m_size = convert_index(size);
     59     }
     60 
     61     StorageIndex size() const { return m_size; }
     62 
     63   protected:
     64     StorageIndex convert_index(Index idx)
     65     {
     66       return internal::convert_index<StorageIndex>(idx);
     67     }
     68 
     69     void reallocate(Index size)
     70     {
     71       // if the size of the matrix is not too large, let's allocate a bit more than needed such
     72       // that we can handle dense vector even in sparse mode.
     73       delete[] m_buffer;
     74       if (size<1000)
     75       {
     76         Index allocSize = (size * sizeof(ListEl) + sizeof(Scalar) - 1)/sizeof(Scalar);
     77         m_allocatedElements = convert_index((allocSize*sizeof(Scalar))/sizeof(ListEl));
     78         m_buffer = new Scalar[allocSize];
     79       }
     80       else
     81       {
     82         m_allocatedElements = convert_index((size*sizeof(Scalar))/sizeof(ListEl));
     83         m_buffer = new Scalar[size];
     84       }
     85       m_size = convert_index(size);
     86       m_start = 0;
     87       m_end = m_size;
     88     }
     89 
     90     void reallocateSparse()
     91     {
     92       Index copyElements = m_allocatedElements;
     93       m_allocatedElements = (std::min)(StorageIndex(m_allocatedElements*1.5),m_size);
     94       Index allocSize = m_allocatedElements * sizeof(ListEl);
     95       allocSize = (allocSize + sizeof(Scalar) - 1)/sizeof(Scalar);
     96       Scalar* newBuffer = new Scalar[allocSize];
     97       memcpy(newBuffer,  m_buffer,  copyElements * sizeof(ListEl));
     98       delete[] m_buffer;
     99       m_buffer = newBuffer;
    100     }
    101 
    102   protected:
    103     // element type of the linked list
    104     struct ListEl
    105     {
    106       StorageIndex next;
    107       StorageIndex index;
    108       Scalar value;
    109     };
    110 
    111     // used to store data in both mode
    112     Scalar* m_buffer;
    113     Scalar m_zero;
    114     StorageIndex m_size;
    115     StorageIndex m_start;
    116     StorageIndex m_end;
    117     StorageIndex m_allocatedSize;
    118     StorageIndex m_allocatedElements;
    119     StorageIndex m_mode;
    120 
    121     // linked list mode
    122     StorageIndex m_llStart;
    123     StorageIndex m_llCurrent;
    124     StorageIndex m_llSize;
    125 };
    126 
    127 /** \returns the number of non zeros in the current sub vector */
    128 template<typename _Scalar,typename _StorageIndex>
    129 Index AmbiVector<_Scalar,_StorageIndex>::nonZeros() const
    130 {
    131   if (m_mode==IsSparse)
    132     return m_llSize;
    133   else
    134     return m_end - m_start;
    135 }
    136 
    137 template<typename _Scalar,typename _StorageIndex>
    138 void AmbiVector<_Scalar,_StorageIndex>::init(double estimatedDensity)
    139 {
    140   if (estimatedDensity>0.1)
    141     init(IsDense);
    142   else
    143     init(IsSparse);
    144 }
    145 
    146 template<typename _Scalar,typename _StorageIndex>
    147 void AmbiVector<_Scalar,_StorageIndex>::init(int mode)
    148 {
    149   m_mode = mode;
    150   if (m_mode==IsSparse)
    151   {
    152     m_llSize = 0;
    153     m_llStart = -1;
    154   }
    155 }
    156 
    157 /** Must be called whenever we might perform a write access
    158   * with an index smaller than the previous one.
    159   *
    160   * Don't worry, this function is extremely cheap.
    161   */
    162 template<typename _Scalar,typename _StorageIndex>
    163 void AmbiVector<_Scalar,_StorageIndex>::restart()
    164 {
    165   m_llCurrent = m_llStart;
    166 }
    167 
    168 /** Set all coefficients of current subvector to zero */
    169 template<typename _Scalar,typename _StorageIndex>
    170 void AmbiVector<_Scalar,_StorageIndex>::setZero()
    171 {
    172   if (m_mode==IsDense)
    173   {
    174     for (Index i=m_start; i<m_end; ++i)
    175       m_buffer[i] = Scalar(0);
    176   }
    177   else
    178   {
    179     eigen_assert(m_mode==IsSparse);
    180     m_llSize = 0;
    181     m_llStart = -1;
    182   }
    183 }
    184 
    185 template<typename _Scalar,typename _StorageIndex>
    186 _Scalar& AmbiVector<_Scalar,_StorageIndex>::coeffRef(Index i)
    187 {
    188   if (m_mode==IsDense)
    189     return m_buffer[i];
    190   else
    191   {
    192     ListEl* EIGEN_RESTRICT llElements = reinterpret_cast<ListEl*>(m_buffer);
    193     // TODO factorize the following code to reduce code generation
    194     eigen_assert(m_mode==IsSparse);
    195     if (m_llSize==0)
    196     {
    197       // this is the first element
    198       m_llStart = 0;
    199       m_llCurrent = 0;
    200       ++m_llSize;
    201       llElements[0].value = Scalar(0);
    202       llElements[0].index = convert_index(i);
    203       llElements[0].next = -1;
    204       return llElements[0].value;
    205     }
    206     else if (i<llElements[m_llStart].index)
    207     {
    208       // this is going to be the new first element of the list
    209       ListEl& el = llElements[m_llSize];
    210       el.value = Scalar(0);
    211       el.index = convert_index(i);
    212       el.next = m_llStart;
    213       m_llStart = m_llSize;
    214       ++m_llSize;
    215       m_llCurrent = m_llStart;
    216       return el.value;
    217     }
    218     else
    219     {
    220       StorageIndex nextel = llElements[m_llCurrent].next;
    221       eigen_assert(i>=llElements[m_llCurrent].index && "you must call restart() before inserting an element with lower or equal index");
    222       while (nextel >= 0 && llElements[nextel].index<=i)
    223       {
    224         m_llCurrent = nextel;
    225         nextel = llElements[nextel].next;
    226       }
    227 
    228       if (llElements[m_llCurrent].index==i)
    229       {
    230         // the coefficient already exists and we found it !
    231         return llElements[m_llCurrent].value;
    232       }
    233       else
    234       {
    235         if (m_llSize>=m_allocatedElements)
    236         {
    237           reallocateSparse();
    238           llElements = reinterpret_cast<ListEl*>(m_buffer);
    239         }
    240         eigen_internal_assert(m_llSize<m_allocatedElements && "internal error: overflow in sparse mode");
    241         // let's insert a new coefficient
    242         ListEl& el = llElements[m_llSize];
    243         el.value = Scalar(0);
    244         el.index = convert_index(i);
    245         el.next = llElements[m_llCurrent].next;
    246         llElements[m_llCurrent].next = m_llSize;
    247         ++m_llSize;
    248         return el.value;
    249       }
    250     }
    251   }
    252 }
    253 
    254 template<typename _Scalar,typename _StorageIndex>
    255 _Scalar& AmbiVector<_Scalar,_StorageIndex>::coeff(Index i)
    256 {
    257   if (m_mode==IsDense)
    258     return m_buffer[i];
    259   else
    260   {
    261     ListEl* EIGEN_RESTRICT llElements = reinterpret_cast<ListEl*>(m_buffer);
    262     eigen_assert(m_mode==IsSparse);
    263     if ((m_llSize==0) || (i<llElements[m_llStart].index))
    264     {
    265       return m_zero;
    266     }
    267     else
    268     {
    269       Index elid = m_llStart;
    270       while (elid >= 0 && llElements[elid].index<i)
    271         elid = llElements[elid].next;
    272 
    273       if (llElements[elid].index==i)
    274         return llElements[m_llCurrent].value;
    275       else
    276         return m_zero;
    277     }
    278   }
    279 }
    280 
    281 /** Iterator over the nonzero coefficients */
    282 template<typename _Scalar,typename _StorageIndex>
    283 class AmbiVector<_Scalar,_StorageIndex>::Iterator
    284 {
    285   public:
    286     typedef _Scalar Scalar;
    287     typedef typename NumTraits<Scalar>::Real RealScalar;
    288 
    289     /** Default constructor
    290       * \param vec the vector on which we iterate
    291       * \param epsilon the minimal value used to prune zero coefficients.
    292       * In practice, all coefficients having a magnitude smaller than \a epsilon
    293       * are skipped.
    294       */
    295     explicit Iterator(const AmbiVector& vec, const RealScalar& epsilon = 0)
    296       : m_vector(vec)
    297     {
    298       using std::abs;
    299       m_epsilon = epsilon;
    300       m_isDense = m_vector.m_mode==IsDense;
    301       if (m_isDense)
    302       {
    303         m_currentEl = 0;   // this is to avoid a compilation warning
    304         m_cachedValue = 0; // this is to avoid a compilation warning
    305         m_cachedIndex = m_vector.m_start-1;
    306         ++(*this);
    307       }
    308       else
    309       {
    310         ListEl* EIGEN_RESTRICT llElements = reinterpret_cast<ListEl*>(m_vector.m_buffer);
    311         m_currentEl = m_vector.m_llStart;
    312         while (m_currentEl>=0 && abs(llElements[m_currentEl].value)<=m_epsilon)
    313           m_currentEl = llElements[m_currentEl].next;
    314         if (m_currentEl<0)
    315         {
    316           m_cachedValue = 0; // this is to avoid a compilation warning
    317           m_cachedIndex = -1;
    318         }
    319         else
    320         {
    321           m_cachedIndex = llElements[m_currentEl].index;
    322           m_cachedValue = llElements[m_currentEl].value;
    323         }
    324       }
    325     }
    326 
    327     StorageIndex index() const { return m_cachedIndex; }
    328     Scalar value() const { return m_cachedValue; }
    329 
    330     operator bool() const { return m_cachedIndex>=0; }
    331 
    332     Iterator& operator++()
    333     {
    334       using std::abs;
    335       if (m_isDense)
    336       {
    337         do {
    338           ++m_cachedIndex;
    339         } while (m_cachedIndex<m_vector.m_end && abs(m_vector.m_buffer[m_cachedIndex])<=m_epsilon);
    340         if (m_cachedIndex<m_vector.m_end)
    341           m_cachedValue = m_vector.m_buffer[m_cachedIndex];
    342         else
    343           m_cachedIndex=-1;
    344       }
    345       else
    346       {
    347         ListEl* EIGEN_RESTRICT llElements = reinterpret_cast<ListEl*>(m_vector.m_buffer);
    348         do {
    349           m_currentEl = llElements[m_currentEl].next;
    350         } while (m_currentEl>=0 && abs(llElements[m_currentEl].value)<=m_epsilon);
    351         if (m_currentEl<0)
    352         {
    353           m_cachedIndex = -1;
    354         }
    355         else
    356         {
    357           m_cachedIndex = llElements[m_currentEl].index;
    358           m_cachedValue = llElements[m_currentEl].value;
    359         }
    360       }
    361       return *this;
    362     }
    363 
    364   protected:
    365     const AmbiVector& m_vector; // the target vector
    366     StorageIndex m_currentEl;   // the current element in sparse/linked-list mode
    367     RealScalar m_epsilon;       // epsilon used to prune zero coefficients
    368     StorageIndex m_cachedIndex; // current coordinate
    369     Scalar m_cachedValue;       // current value
    370     bool m_isDense;             // mode of the vector
    371 };
    372 
    373 } // end namespace internal
    374 
    375 } // end namespace Eigen
    376 
    377 #endif // EIGEN_AMBIVECTOR_H
    378