[mathicgb] 25/393: Added a sparse matrix class in preparation for F4 along with a few tests.

Doug Torrance dtorrance-guest at moszumanska.debian.org
Fri Apr 3 15:58:26 UTC 2015


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to branch upstream
in repository mathicgb.

commit c106f4c92bf0a73fcd5ffa07b007a706bb27e7ec
Author: Bjarke Hammersholt Roune <bjarkehr.code at gmail.com>
Date:   Mon Sep 24 20:17:13 2012 +0200

    Added a sparse matrix class in preparation for F4 along with a few tests.
---
 Makefile.am                   |   6 +-
 src/mathicgb/SparseMatrix.cpp |   3 +
 src/mathicgb/SparseMatrix.hpp | 367 ++++++++++++++++++++++++++++++++++++++++++
 src/test/SparseMatrix.cpp     |  46 ++++++
 4 files changed, 420 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index f9d9882..1cd973b 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,7 +53,8 @@ libmathicgb_la_SOURCES = src/mathicgb/BjarkeGeobucket2.cpp				\
   src/mathicgb/SPairs.cpp src/mathicgb/SPairs.hpp						\
   src/mathicgb/stdinc.h src/mathicgb/TournamentReducer.cpp				\
   src/mathicgb/TournamentReducer.hpp src/mathicgb/SigSPairQueue.hpp		\
-  src/mathicgb/SigSPairQueue.cpp
+  src/mathicgb/SigSPairQueue.cpp src/mathicgb/SparseMatrix.hpp			\
+  src/mathicgb/SparseMatrix.cpp
 
 # When making a distribution file, Automake knows to include all files
 # that are necessary to build the project. EXTRA_DIST specifies files
@@ -82,4 +83,5 @@ unittest_LDADD = $(DEPS_LIBS) libmathicgb.la
 test_LIBS=
 unittest_SOURCES=src/test/FreeModuleOrderTest.cpp						\
   src/test/gtestInclude.cpp src/test/testMain.cpp src/test/gb-test.cpp	\
-  src/test/ideals.cpp src/test/poly-test.cpp src/test/ideals.hpp
+  src/test/ideals.cpp src/test/poly-test.cpp src/test/ideals.hpp		\
+  src/test/SparseMatrix.cpp
diff --git a/src/mathicgb/SparseMatrix.cpp b/src/mathicgb/SparseMatrix.cpp
new file mode 100755
index 0000000..1fac2f2
--- /dev/null
+++ b/src/mathicgb/SparseMatrix.cpp
@@ -0,0 +1,3 @@
+#include "stdinc.h"
+#include "SparseMatrix.hpp"
+
diff --git a/src/mathicgb/SparseMatrix.hpp b/src/mathicgb/SparseMatrix.hpp
new file mode 100755
index 0000000..0c38b3c
--- /dev/null
+++ b/src/mathicgb/SparseMatrix.hpp
@@ -0,0 +1,367 @@
+#ifndef MATHICGB_SPARSE_MATRIX_GUARD
+#define MATHICGB_SPARSE_MATRIX_GUARD
+
+#include "PolyRing.hpp"
+#include <vector>
+#include <ostream>
+#include <limits>
+#include <sstream>
+#include <string>
+
+/** A class that implements a sparse matrix.
+
+These are the mathematical concepts involved:
+  Sparse matrix: a sequence of sparse rows.
+  Sparse row: a seqence of entries.
+  Entry: a pair (i,s) where i is a column index and s is a scalar.
+
+You add a row by adding all entries in the row and then calling
+rowDone(). You cannot add entries to a row once it has been
+created, so in that sense this class is append-only. However, you
+are free to change the indices and the scalars in the entries that
+are already there. Entries are not automatically reordered by this
+class, so your rows will be in increasing order of index only if
+you make them like that.
+
+Adding an entry or a row can invalidate all pointers/references to
+entries in the matrix and all iterators. This is true even if the
+entry has been added but it has not been put in a new row yet by
+calling rowDone.
+
+There is no special treatment of entries whose scalar is zero. For
+example they still count as entries in relation to entryCount().
+
+Currently this is not a template class so you can get by without
+using the typedefs offered, for example using uint16 instead of
+SparseMatrix::Scalar. Please use the typedefs to make it easier to
+support a wider range of types of matrices in future.
+
+There is no need to specify the number of columns ahead of time. Any
+column index within the range of the ColIndex type can be used. The
+SparseMatrix automatically keeps track of the number of columns in the
+matrix.
+*/
+class SparseMatrix {
+ public:
+  typedef size_t RowIndex;
+  typedef uint32 ColIndex;
+  typedef uint16 Scalar;
+
+  /** Preallocate space for at least count entries. */
+  void reserveEntries(size_t count) {
+    mEntries.reserve(count);
+    mColIndices.reserve(count);
+  }
+
+  /** Preallocate space for at least count rows. */
+  void reserveRows(size_t count) {
+    mRowOffsets.reserve(count + 1);
+  }
+
+  /** Returns the index of the first entry in the given row. This is
+    the first entry that you added to the row - so not necessarily the
+    minimum column index in that row. The row in question must have at
+    least one entry. */
+  ColIndex leadCol(RowIndex row) const {
+    MATHICGB_ASSERT(row < rowCount());
+    MATHICGB_ASSERT(!emptyRow(row));
+    return mColIndices[mRowOffsets[row]];
+  }
+
+  /** Returns true if the given row has no entries. */
+  bool emptyRow(RowIndex row) const {
+    MATHICGB_ASSERT(row < rowCount());
+    return mRowOffsets[row] == mRowOffsets[row + 1];
+  }
+
+  /** Removes the leading trimThisMany columns.
+
+    The columns are removed by replacing all column indices col by col
+    - trimThisMany. No entry can have a column index less than
+    trimThisMany, even if the scalar of that entry is set to zero. */
+  void trimLeadingZeroColumns(ColIndex trimThisMany) {
+    MATHICGB_ASSERT(trimThisMany <= colCount());
+    typedef std::vector<ColIndex>::iterator Iter;
+    Iter end = mColIndices.end();
+    Iter it = mColIndices.begin();
+    for (; it != end; ++it) {
+      MATHICGB_ASSERT(*it >= trimThisMany);
+      *it -= trimThisMany;
+    }
+    mColCount -= trimThisMany;
+  }
+
+  /** Construct a new zero by zero sparse matrix. */
+  SparseMatrix(): mColCount(0) {
+    mRowOffsets.push_back(0);
+  }
+
+  /** Returns the number of entries in the given row. */
+  size_t entryCountInRow(RowIndex row) const {
+    MATHICGB_ASSERT(row < rowCount());
+    return mRowOffsets[row + 1] - mRowOffsets[row];
+  }
+
+  /** Returns the number of entries in the whole matrix. */
+  size_t entryCount() const {
+    return mEntries.size();
+  }
+
+  /** Prints the matrix in a human readable format to out. */
+  void print(std::ostream& out) const {
+    if (rowCount() == 0)
+      out << "matrix with no rows\n";
+    for (RowIndex row = 0; row < rowCount(); ++row) {
+      out << row << ':';
+      RowIterator end = rowEnd(row);
+      for (RowIterator it = rowBegin(row); it != end; ++it) {
+        MATHICGB_ASSERT(it.index() < colCount());
+        out << ' ' << it.index() << '#' << it.scalar();
+      }
+      out << '\n';
+    }
+  }
+
+  std::string toString() const {
+    std::ostringstream out;
+    print(out);
+    return out.str();
+  }
+
+  /** Adds a new row that contains all terms that have been appended
+    since the last time a row was added or the matrix was created. */
+  void rowDone() {
+    MATHICGB_ASSERT(mColIndices.size() == mEntries.size());
+
+    mRowOffsets.push_back(mColIndices.size());
+  }
+
+  /** Appends an entry to the matrix. Will not appear in the matrix
+    until rowDone is called. Do not call other methods that add rows
+    after calling this method until rowDone has been called. */
+  void appendEntry(ColIndex colIndex, Scalar scalar) {
+    MATHICGB_ASSERT(mColIndices.size() == mEntries.size());
+    MATHICGB_ASSERT(colIndex < std::numeric_limits<ColIndex>::max());
+
+    mColIndices.push_back(colIndex);
+    mEntries.push_back(scalar);
+    if (colIndex + 1 > mColCount)
+      mColCount = colIndex + 1;
+
+    MATHICGB_ASSERT(mColIndices.size() == mEntries.size());
+  }
+
+  
+  void appendRowAndNormalize(const SparseMatrix& matrix, RowIndex row, Scalar modulus) {
+    MATHICGB_ASSERT(row < matrix.rowCount());
+    RowIterator it = matrix.rowBegin(row);
+    RowIterator end = matrix.rowEnd(row);
+    if (it != end) {
+      appendEntry(it.index(), 1);
+      Scalar lead = it.scalar();
+      ++it;
+      if (it != end) {
+        Scalar inverse = modularInverse(lead, modulus);
+        do {
+          uint32 prod = static_cast<uint32>(inverse) * it.scalar();
+          uint16 prodMod = static_cast<uint16>(prod % modulus);
+          appendEntry(it.index(), prodMod);
+          ++it;
+        } while (it != end);
+      }
+    }
+    rowDone();
+  }
+
+  void setToAndTakeMemory(std::vector<ColIndex>& indices,
+                          std::vector<Scalar>& entries,
+                          std::vector<ColIndex>& sizes,
+                          ColIndex colCount) {
+    MATHICGB_ASSERT(indices.size() == entries.size());
+
+    mColCount = colCount;
+
+    // deallocate old memory
+    std::vector<ColIndex>().swap(mColIndices);
+    std::vector<Scalar>().swap(mEntries);
+    std::vector<size_t>().swap(mRowOffsets);
+
+    // take new memory
+    mColIndices.swap(indices);
+    mEntries.swap(entries);
+
+    // compute row offsets
+    const size_t newRowCount = sizes.size();
+    mRowOffsets.reserve(newRowCount + 1);
+    mRowOffsets.push_back(0);
+    for (size_t row = 0; row < newRowCount; ++row)
+      mRowOffsets.push_back(mRowOffsets.back() + sizes[row]);
+
+    MATHICGB_ASSERT(mRowOffsets.size() == sizes.size() + 1);
+    MATHICGB_ASSERT(mRowOffsets.front() == 0);
+    MATHICGB_ASSERT(mRowOffsets.back() == mColIndices.size());
+
+    std::vector<ColIndex>().swap(sizes); // deallocate old memory
+  }
+
+  void swap(SparseMatrix& matrix) {
+    mColIndices.swap(matrix.mColIndices);
+    mEntries.swap(matrix.mEntries);
+    mRowOffsets.swap(matrix.mRowOffsets);
+    std::swap(mColCount, matrix.mColCount);
+  }
+
+  void clear(ColIndex newColCount = 0) {
+    mColIndices.clear();
+    mEntries.clear();
+    mRowOffsets.clear();
+    mRowOffsets.push_back(0);
+    mColCount = newColCount;
+  }
+
+  struct RowIterator {
+    RowIterator& operator++() {
+      ++mOffset;
+      return *this;
+    }
+    bool operator==(const RowIterator& it) const {return mOffset == it.mOffset;}
+    bool operator!=(const RowIterator& it) const {return !(*this == it);}
+    Scalar scalar() {return mMatrix.scalarAtOffset(mOffset);}
+    ColIndex index() {return mMatrix.indexAtOffset(mOffset);}
+    
+  private:
+    friend class SparseMatrix;
+    RowIterator(const SparseMatrix& matrix, size_t offset): mMatrix(matrix), mOffset(offset) {}
+ 
+    const SparseMatrix& mMatrix;
+    size_t mOffset;      
+  };
+
+  RowIndex rowCount() const {
+    MATHICGB_ASSERT(!mRowOffsets.empty());
+    return mRowOffsets.size() - 1;
+  }
+
+  ColIndex colCount() const {
+    return mColCount;
+  }
+
+  RowIterator rowBegin(RowIndex row) const {
+    MATHICGB_ASSERT(row < rowCount());
+    return RowIterator(*this, mRowOffsets[row]);
+  }
+
+  RowIterator rowEnd(RowIndex row) const {
+    MATHICGB_ASSERT(row < rowCount());
+    return RowIterator(*this, mRowOffsets[row + 1]);
+  }
+
+  void ensureAtLeastThisManyColumns(ColIndex count) {
+    if (count > colCount())
+      mColCount = count;
+  }
+
+  void appendRowWithModulus(std::vector<uint64> const& v, Scalar modulus) {
+    MATHICGB_ASSERT(v.size() == colCount());
+    ColIndex count = colCount();
+    for (ColIndex col = 0; col < count; ++col) {
+      Scalar scalar = static_cast<Scalar>(v[col] % modulus);
+      if (scalar != 0)
+        appendEntry(col, scalar);
+    }
+    rowDone();
+  }
+
+  void appendRow(std::vector<uint64> const& v, size_t leadCol = 0) {
+    MATHICGB_ASSERT(v.size() == colCount());
+#ifdef MATHICGB_DEBUG
+    for (ColIndex col = leadCol; col < leadCol; ++col) {
+      MATHICGB_ASSERT(v[col] == 0);
+    }
+#endif
+
+    ColIndex count = colCount();
+    for (ColIndex col = leadCol; col < count; ++col)
+      if (v[col] != 0)
+        appendEntry(col, v[col]);
+    rowDone();
+  }
+  
+  void appendRowWithModulusNormalized(std::vector<uint64> const& v, Scalar modulus) {
+    MATHICGB_ASSERT(v.size() == colCount());
+    ColIndex count = colCount();
+    uint16 multiply = 1;
+   
+    bool first = true;
+    for (ColIndex col = 0; col < count; ++col) {
+      Scalar scalar = static_cast<Scalar>(v[col] % modulus);
+      if (scalar == 0)
+        continue;
+      if (first) {
+        multiply = modularInverse(scalar, modulus);
+        scalar = 1;
+        first = false;
+      } else {
+        uint32 prod = static_cast<uint32>(multiply) * scalar;
+        scalar = prod % modulus;
+      }
+      appendEntry(col, scalar);
+    }
+    rowDone();
+  }
+
+  // Returns true if the row was non-zero. Otherwise the row was not
+  // appended.
+  bool appendRowWithModulusIfNonZero(std::vector<uint64> const& v, Scalar modulus) {
+    appendRowWithModulus(v, modulus);
+    MATHICGB_ASSERT(mRowOffsets.size() >= 2);
+    std::vector<size_t>::const_iterator it = mRowOffsets.end();
+    --it;
+    size_t last = *it;
+    --it;
+    if (last == *it) {
+      mRowOffsets.pop_back();
+      return false;
+    } else
+      return true;
+  }
+
+  bool operator==(const SparseMatrix& mat) const {
+    return mColCount == mat.mColCount &&
+      mColIndices == mat.mColIndices &&
+      mEntries == mat.mEntries &&
+      mRowOffsets == mat.mRowOffsets;
+  }
+
+  bool operator!=(const SparseMatrix& mat) const {
+    return !(*this == mat);
+  }
+
+  const std::vector<Scalar>& entries() const {
+    return mEntries;
+  }
+
+  const std::vector<ColIndex>& colIndices() const {
+    return mColIndices;
+  }
+
+ private:
+  friend class RowIterator;
+
+  ColIndex indexAtOffset(size_t offset) const {
+    MATHICGB_ASSERT(offset < mColIndices.size());
+    return mColIndices[offset];
+  }
+
+  Scalar scalarAtOffset(size_t offset) const {
+    MATHICGB_ASSERT(offset < mEntries.size());
+    return mEntries[offset];
+  }
+
+  std::vector<ColIndex> mColIndices;
+  std::vector<Scalar> mEntries;
+  std::vector<size_t> mRowOffsets;  
+  ColIndex mColCount;
+};
+
+#endif
diff --git a/src/test/SparseMatrix.cpp b/src/test/SparseMatrix.cpp
new file mode 100755
index 0000000..dc68c62
--- /dev/null
+++ b/src/test/SparseMatrix.cpp
@@ -0,0 +1,46 @@
+#include "mathicgb/stdinc.h"
+
+#include "mathicgb/SparseMatrix.hpp"
+#include <gtest/gtest.h>
+
+TEST(SparseMatrix, NoRows) {
+  SparseMatrix mat; // test a matrix with no rows
+  ASSERT_EQ(0, mat.entryCount());
+  ASSERT_EQ(0, mat.rowCount());
+  ASSERT_EQ(0, mat.colCount());
+  ASSERT_EQ("matrix with no rows\n", mat.toString()); 
+}
+
+TEST(SparseMatrix, Simple) {
+  SparseMatrix mat;
+
+  mat.appendEntry(5, 101);
+  mat.rowDone();
+  ASSERT_EQ(1, mat.entryCount());
+  ASSERT_EQ(1, mat.rowCount());
+  ASSERT_EQ(6, mat.colCount());
+  ASSERT_EQ(5, mat.leadCol(0));
+  ASSERT_EQ(1, mat.entryCountInRow(0));
+  ASSERT_EQ("0: 5#101\n", mat.toString()); 
+  ASSERT_FALSE(mat.emptyRow(0));
+
+  mat.rowDone(); // add a row with no entries
+  ASSERT_EQ(1, mat.entryCount());
+  ASSERT_EQ(2, mat.rowCount());
+  ASSERT_EQ(6, mat.colCount());
+  ASSERT_EQ(5, mat.leadCol(0));
+  ASSERT_EQ(0, mat.entryCountInRow(1));
+  ASSERT_EQ("0: 5#101\n1:\n", mat.toString()); 
+  ASSERT_TRUE(mat.emptyRow(1));
+
+  mat.appendEntry(5, 102);
+  mat.appendEntry(2000, 0); // scalar zero
+  mat.rowDone(); // add a row with two entries
+  ASSERT_EQ(3, mat.entryCount());
+  ASSERT_EQ(3, mat.rowCount());
+  ASSERT_EQ(2001, mat.colCount());
+  ASSERT_EQ(5, mat.leadCol(2));
+  ASSERT_EQ(2, mat.entryCountInRow(2));
+  ASSERT_EQ("0: 5#101\n1:\n2: 5#102 2000#0\n", mat.toString()); 
+  ASSERT_FALSE(mat.emptyRow(2));
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/mathicgb.git



More information about the debian-science-commits mailing list