PALISADE Lattice Crypto Library  1.11.9
A lattice crypto library for software engineers by software engineers.
matrix.h
1 // @file matrix.h This code provide a templated matrix implementation
2 // @author TPOC: contact@palisade-crypto.org
3 //
4 // @copyright Copyright (c) 2019, New Jersey Institute of Technology (NJIT)
5 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution. THIS SOFTWARE IS
13 // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
14 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
15 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
16 // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
17 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 
24 #ifndef LBCRYPTO_MATH_MATRIX_H
25 #define LBCRYPTO_MATH_MATRIX_H
26 
27 #include <cmath>
28 #include <functional>
29 #include <iostream>
30 #include <memory>
31 #include <string>
32 #include <vector>
33 
34 #include "encoding/encodings.h"
35 #include "lattice/backend.h"
36 #include "math/backend.h"
37 #include "math/distrgen.h"
38 #include "math/nbtheory.h"
39 #include "utils/inttypes.h"
40 #include "utils/memory.h"
41 #include "utils/utilities.h"
42 using std::invalid_argument;
43 
44 namespace lbcrypto {
45 
46 template <class Element>
47 class Matrix : public Serializable {
48  public:
49  typedef vector<vector<Element>> data_t;
50  typedef vector<Element> data_row_t;
51  typedef std::function<Element(void)> alloc_func;
52 
60  Matrix(alloc_func allocZero, size_t rows, size_t cols)
61  : data(), rows(rows), cols(cols), allocZero(allocZero) {
62  data.resize(rows);
63  for (auto row = data.begin(); row != data.end(); ++row) {
64  for (size_t col = 0; col < cols; ++col) {
65  row->push_back(allocZero());
66  }
67  }
68  }
69 
70  // TODO: add Clear();
71 
83  Matrix(alloc_func allocZero, size_t rows, size_t cols, alloc_func allocGen);
84 
93  explicit Matrix(alloc_func allocZero = 0)
94  : data(), rows(0), cols(0), allocZero(allocZero) {}
95 
103  void SetSize(size_t rows, size_t cols) {
104  if (this->rows != 0 || this->cols != 0) {
105  PALISADE_THROW(not_available_error,
106  "You cannot SetSize on a non-empty matrix");
107  }
108 
109  this->rows = rows;
110  this->cols = cols;
111 
112  data.resize(rows);
113  for (auto row = data.begin(); row != data.end(); ++row) {
114  for (size_t col = 0; col < cols; ++col) {
115  row->push_back(allocZero());
116  }
117  }
118  }
119 
126  void SetAllocator(alloc_func allocZero) { this->allocZero = allocZero; }
127 
133  Matrix(const Matrix<Element>& other)
134  : data(), rows(other.rows), cols(other.cols), allocZero(other.allocZero) {
135  deepCopyData(other.data);
136  }
137 
145 
152 
153  // Macro for convenient definitions of class implementations of special
154  // functions
155 #define ONES_FOR_TYPE(T) \
156  template <> \
157  Matrix<T>& Matrix<T>::Ones() { \
158  for (size_t row = 0; row < rows; ++row) { \
159  for (size_t col = 0; col < cols; ++col) { \
160  data[row][col] = 1; \
161  } \
162  } \
163  return *this; \
164  }
165 
171  Matrix<Element>& ModEq(const Element& modulus);
172 
178  Matrix<Element>& ModSubEq(Matrix<Element> const& b, const Element& modulus);
179 
187  Matrix<Element>& Fill(const Element& val);
188 
195 
196 #define IDENTITY_FOR_TYPE(T) \
197  template <> \
198  Matrix<T>& Matrix<T>::Identity() { \
199  for (size_t row = 0; row < rows; ++row) { \
200  for (size_t col = 0; col < cols; ++col) { \
201  if (row == col) { \
202  data[row][col] = 1; \
203  } else { \
204  data[row][col] = 0; \
205  } \
206  } \
207  } \
208  return *this; \
209  }
210 
217  Matrix<Element> GadgetVector(int64_t base = 2) const;
218 
219 #define GADGET_FOR_TYPE(T) \
220  template <> \
221  Matrix<T> Matrix<T>::GadgetVector(int64_t base) const { \
222  Matrix<T> g(allocZero, rows, cols); \
223  auto base_matrix = allocZero(); \
224  size_t k = cols / rows; \
225  base_matrix = base; \
226  g(0, 0) = 1; \
227  for (size_t i = 1; i < k; i++) { \
228  g(0, i) = g(0, i - 1) * base_matrix; \
229  } \
230  for (size_t row = 1; row < rows; row++) { \
231  for (size_t i = 0; i < k; i++) { \
232  g(row, i + row * k) = g(0, i); \
233  } \
234  } \
235  return g; \
236  }
237 
238 #define GADGET_FOR_TYPE_DCRT(T) \
239  template <> \
240  Matrix<T> Matrix<T>::GadgetVector(int64_t base) const { \
241  Matrix<T> g(allocZero, rows, cols); \
242  auto base_matrix = allocZero(); \
243  base_matrix = base; \
244  size_t bk = 1; \
245  \
246  auto params = g(0, 0).GetParams()->GetParams(); \
247  \
248  uint64_t digitCount = (long)ceil( \
249  log2(params[0]->GetModulus().ConvertToDouble()) / log2(base)); \
250  \
251  for (size_t k = 0; k < digitCount; k++) { \
252  for (size_t i = 0; i < params.size(); i++) { \
253  NativePoly temp(params[i]); \
254  temp = bk; \
255  g(0, k + i * digitCount).SetElementAtIndex(i, std::move(temp)); \
256  } \
257  bk *= base; \
258  } \
259  \
260  size_t kCols = cols / rows; \
261  for (size_t row = 1; row < rows; row++) { \
262  for (size_t i = 0; i < kCols; i++) { \
263  g(row, i + row * kCols) = g(0, i); \
264  } \
265  } \
266  return g; \
267  }
268 
274  double Norm() const;
275 
281  double EuclideanNorm() const;
282 
283 
284 #define EUCLIDEANNORM_FOR_TYPE(T) \
285  template <> \
286  double Matrix<T>::EuclideanNorm() const { \
287  double retVal = 0.0; \
288  double locVal = 0.0; \
289  for (size_t row = 0; row < rows; ++row) { \
290  for (size_t col = 0; col < cols; ++col) { \
291  locVal = data[row][col].EuclideanNorm();\
292  retVal = retVal+locVal*locVal; \
293  } \
294  } \
295  return sqrt(retVal); \
296  }
297 
298 #define NORM_FOR_TYPE(T) \
299  template <> \
300  double Matrix<T>::Norm() const { \
301  double retVal = 0.0; \
302  double locVal = 0.0; \
303  for (size_t row = 0; row < rows; ++row) { \
304  for (size_t col = 0; col < cols; ++col) { \
305  locVal = data[row][col].Norm(); \
306  if (locVal > retVal) { \
307  retVal = locVal; \
308  } \
309  } \
310  } \
311  return retVal; \
312  }
313 
320  Matrix<Element> Mult(Matrix<Element> const& other) const;
321 
329  return Mult(other);
330  }
331 
338  Matrix<Element> ScalarMult(Element const& other) const {
339  Matrix<Element> result(*this);
340 #pragma omp parallel for
341  for (size_t col = 0; col < result.cols; ++col) {
342  for (size_t row = 0; row < result.rows; ++row) {
343  result.data[row][col] = result.data[row][col] * other;
344  }
345  }
346 
347  return result;
348  }
349 
356  Matrix<Element> operator*(Element const& other) const {
357  return ScalarMult(other);
358  }
359 
366  bool Equal(Matrix<Element> const& other) const {
367  if (rows != other.rows || cols != other.cols) {
368  return false;
369  }
370 
371  for (size_t i = 0; i < rows; ++i) {
372  for (size_t j = 0; j < cols; ++j) {
373  if (data[i][j] != other.data[i][j]) {
374  return false;
375  }
376  }
377  }
378  return true;
379  }
380 
387  bool operator==(Matrix<Element> const& other) const { return Equal(other); }
388 
395  bool operator!=(Matrix<Element> const& other) const { return !Equal(other); }
396 
402  const data_t& GetData() const { return data; }
403 
409  size_t GetRows() const { return rows; }
410 
416  size_t GetCols() const { return cols; }
417 
423  alloc_func GetAllocator() const { return allocZero; }
424 
432  void SetFormat(Format format);
433 
440  Matrix<Element> Add(Matrix<Element> const& other) const {
441  if (rows != other.rows || cols != other.cols) {
442  PALISADE_THROW(math_error,
443  "Addition operands have incompatible dimensions");
444  }
445  Matrix<Element> result(*this);
446 #pragma omp parallel for
447  for (size_t j = 0; j < cols; ++j) {
448  for (size_t i = 0; i < rows; ++i) {
449  result.data[i][j] += other.data[i][j];
450  }
451  }
452  return result;
453  }
454 
462  return this->Add(other);
463  }
464 
472 
479  Matrix<Element> Sub(Matrix<Element> const& other) const {
480  if (rows != other.rows || cols != other.cols) {
481  PALISADE_THROW(math_error,
482  "Subtraction operands have incompatible dimensions");
483  }
484  Matrix<Element> result(allocZero, rows, other.cols);
485 #pragma omp parallel for
486  for (size_t j = 0; j < cols; ++j) {
487  for (size_t i = 0; i < rows; ++i) {
488  result.data[i][j] = data[i][j] - other.data[i][j];
489  }
490  }
491 
492  return result;
493  }
494 
502  return this->Sub(other);
503  }
504 
512 
518  Matrix<Element> Transpose() const;
519 
520  // YSP The signature of this method needs to be changed in the future
527  void Determinant(Element* result) const;
528  // Element Determinant() const;
529 
537 
544  Matrix<Element>& VStack(Matrix<Element> const& other);
545 
552  Matrix<Element>& HStack(Matrix<Element> const& other);
553 
561  Element& operator()(size_t row, size_t col) { return data[row][col]; }
562 
570  Element const& operator()(size_t row, size_t col) const {
571  return data[row][col];
572  }
573 
580  Matrix<Element> ExtractRow(size_t row) const {
581  Matrix<Element> result(this->allocZero, 1, this->cols);
582  int i = 0;
583  for (auto& elem : this->GetData()[row]) {
584  result(0, i) = elem;
585  i++;
586  }
587  return result;
588  // return *this;
589  }
590 
597  Matrix<Element> ExtractCol(size_t col) const {
598  Matrix<Element> result(this->allocZero, this->rows, 1);
599  for (size_t i = 0; i < this->rows; i++) {
600  result(i, 0) = data[i][col];
601  }
602  return result;
603  // return *this;
604  }
605 
612  inline Matrix<Element> ExtractRows(size_t row_start, size_t row_end) const {
613  Matrix<Element> result(this->allocZero, row_end - row_start + 1,
614  this->cols);
615 
616  for (usint row = row_start; row < row_end + 1; row++) {
617  int i = 0;
618 
619  for (auto elem = this->GetData()[row].begin();
620  elem != this->GetData()[row].end(); ++elem) {
621  result(row - row_start, i) = *elem;
622  i++;
623  }
624  }
625 
626  return result;
627  }
628 
629  friend std::ostream& operator<<(std::ostream& os, const Matrix<Element>& m) {
630  os << "[ ";
631  for (size_t row = 0; row < m.GetRows(); ++row) {
632  os << "[ ";
633  for (size_t col = 0; col < m.GetCols(); ++col) {
634  os << m(row, col) << " ";
635  }
636  os << "]\n";
637  }
638  os << " ]\n";
639  return os;
640  }
641 
646  void SwitchFormat();
647 #define NOT_AN_ELEMENT_MATRIX(T) \
648  template <> \
649  void Matrix<T>::SwitchFormat() { \
650  PALISADE_THROW(not_available_error, "Not a matrix of Elements"); \
651  }
652 
653  /*
654  * Multiply the matrix by a vector whose elements are all 1's. This causes
655  * the elements of each row of the matrix to be added and placed into the
656  * corresponding position in the output vector.
657  */
658  Matrix<Element> MultByUnityVector() const;
659 
660  /*
661  * Multiply the matrix by a vector of random 1's and 0's, which is the same as
662  * adding select elements in each row together. Return a vector that is a rows
663  * x 1 matrix.
664  */
665  Matrix<Element> MultByRandomVector(std::vector<int> ranvec) const;
666 
667  template <class Archive>
668  void save(Archive& ar, std::uint32_t const version) const {
669  ar(::cereal::make_nvp("d", data));
670  ar(::cereal::make_nvp("r", rows));
671  ar(::cereal::make_nvp("c", cols));
672  }
673 
674  template <class Archive>
675  void load(Archive& ar, std::uint32_t const version) {
676  if (version > SerializedVersion()) {
677  PALISADE_THROW(deserialize_error,
678  "serialized object version " + std::to_string(version) +
679  " is from a later version of the library");
680  }
681  ar(::cereal::make_nvp("d", data));
682  ar(::cereal::make_nvp("r", rows));
683  ar(::cereal::make_nvp("c", cols));
684 
685  // users will need to SetAllocator for any newly deserialized matrix
686  }
687 
688  std::string SerializedObjectName() const { return "Matrix"; }
689  static uint32_t SerializedVersion() { return 1; }
690 
691  private:
692  data_t data;
693  uint32_t rows;
694  uint32_t cols;
695  alloc_func allocZero;
696  // mutable int NUM_THREADS = 1;
697 
698  // deep copy of data - used for copy constructor
699  void deepCopyData(data_t const& src) {
700  data.clear();
701  data.resize(src.size());
702  for (size_t row = 0; row < src.size(); ++row) {
703  for (auto elem = src[row].begin(); elem != src[row].end(); ++elem) {
704  data[row].push_back(*elem);
705  }
706  }
707  }
708 };
709 
717 template <class Element>
718 Matrix<Element> operator*(Element const& e, Matrix<Element> const& M) {
719  return M.ScalarMult(e);
720 }
721 
729 template <typename Element>
731 
740 template <typename Element>
742 
750 template <class Element>
751 std::ostream& operator<<(std::ostream& os, const Matrix<Element>& m);
752 
765 
766 void Cholesky(const Matrix<int32_t>& input, Matrix<double>& result);
767 
777  const BigInteger& modulus);
778 
788  const BigInteger& modulus);
789 
799 template <typename Element>
801  Matrix<int64_t> const& other, size_t n,
802  const shared_ptr<typename Element::Params> params);
803 
804 #define SPLIT64_FOR_TYPE(T) \
805  template <> \
806  Matrix<T> SplitInt64IntoElements( \
807  Matrix<int64_t> const& other, size_t n, \
808  const shared_ptr<typename T::Params> params) { \
809  auto zero_alloc = T::Allocator(params, Format::COEFFICIENT); \
810  size_t rows = other.GetRows() / n; \
811  Matrix<T> result(zero_alloc, rows, 1); \
812  for (size_t row = 0; row < rows; ++row) { \
813  std::vector<int64_t> values(n); \
814  for (size_t i = 0; i < n; ++i) values[i] = other(row * n + i, 0); \
815  result(row, 0) = values; \
816  } \
817  return result; \
818  }
819 
829 template <typename Element>
831  Matrix<int32_t> const& other, size_t n,
832  const shared_ptr<typename Element::Params> params);
833 
834 #define SPLIT32ALT_FOR_TYPE(T) \
835  template <> \
836  Matrix<T> SplitInt32AltIntoElements( \
837  Matrix<int32_t> const& other, size_t n, \
838  const shared_ptr<typename T::Params> params) { \
839  auto zero_alloc = T::Allocator(params, Format::COEFFICIENT); \
840  size_t rows = other.GetRows(); \
841  Matrix<T> result(zero_alloc, rows, 1); \
842  for (size_t row = 0; row < rows; ++row) { \
843  std::vector<int32_t> values(n); \
844  for (size_t i = 0; i < n; ++i) values[i] = other(row, i); \
845  result(row, 0) = values; \
846  } \
847  return result; \
848  }
849 
859 template <typename Element>
861  Matrix<int64_t> const& other, size_t n,
862  const shared_ptr<typename Element::Params> params);
863 
864 #define SPLIT64ALT_FOR_TYPE(T) \
865  template <> \
866  Matrix<T> SplitInt64AltIntoElements( \
867  Matrix<int64_t> const& other, size_t n, \
868  const shared_ptr<typename T::Params> params) { \
869  auto zero_alloc = T::Allocator(params, Format::COEFFICIENT); \
870  size_t rows = other.GetRows(); \
871  Matrix<T> result(zero_alloc, rows, 1); \
872  for (size_t row = 0; row < rows; ++row) { \
873  std::vector<int64_t> values(n); \
874  for (size_t i = 0; i < n; ++i) values[i] = other(row, i); \
875  result(row, 0) = values; \
876  } \
877  return result; \
878  }
879 
880 } // namespace lbcrypto
881 #endif // LBCRYPTO_MATH_MATRIX_H
Matrix< Element > & VStack(Matrix< Element > const &other)
Definition: matrix.cpp:240
Base class for PALISADE serialization.
Definition: serializable.h:76
Matrix< Element > CofactorMatrix() const
Definition: matrix.cpp:195
Matrix< Element > Sub(Matrix< Element > const &other) const
Definition: matrix.h:479
Matrix< Element > & HStack(Matrix< Element > const &other)
Definition: matrix.cpp:258
Element const & operator()(size_t row, size_t col) const
Definition: matrix.h:570
Matrix< Element > ExtractRows(size_t row_start, size_t row_end) const
Definition: matrix.h:612
Matrix< Element > & Identity()
Matrix< Element > ScalarMult(Element const &other) const
Definition: matrix.h:338
Matrix< Element > Mult(Matrix< Element > const &other) const
Definition: matrix.cpp:64
Matrix< Element > SplitInt32AltIntoElements(Matrix< int32_t > const &other, size_t n, const shared_ptr< typename Element::Params > params)
bool operator==(Matrix< Element > const &other) const
Definition: matrix.h:387
Matrix< Element > SplitInt64AltIntoElements(Matrix< int64_t > const &other, size_t n, const shared_ptr< typename Element::Params > params)
const data_t & GetData() const
Definition: matrix.h:402
size_t GetRows() const
Definition: matrix.h:409
Matrix(alloc_func allocZero=0)
Definition: matrix.h:93
Matrix< Element > & ModEq(const Element &modulus)
Matrix< Element > operator-(Matrix< Element > const &other) const
Definition: matrix.h:501
Definition: exception.h:147
Definition: exception.h:113
Matrix< Element > & operator=(const Matrix< Element > &other)
Definition: matrix.cpp:46
void SetAllocator(alloc_func allocZero)
Definition: matrix.h:126
Matrix< Element > & Ones()
Matrix< Element > GadgetVector(int64_t base=2) const
Matrix< Element > Add(Matrix< Element > const &other) const
Definition: matrix.h:440
Matrix< Element > Transpose() const
Definition: matrix.cpp:124
bool Equal(Matrix< Element > const &other) const
Definition: matrix.h:366
Matrix< Element > & Fill(const Element &val)
Definition: matrix.cpp:54
Matrix< Element > operator*(Element const &other) const
Definition: matrix.h:356
Matrix< Element > & operator-=(Matrix< Element > const &other)
Definition: matrix.cpp:108
Matrix(alloc_func allocZero, size_t rows, size_t cols)
Definition: matrix.h:60
Main class for big integers represented as an array of native (primitive) unsigned integers...
Definition: ubintfxd.h:219
Matrix< Element > SplitInt64IntoElements(Matrix< int64_t > const &other, size_t n, const shared_ptr< typename Element::Params > params)
Definition: matrix.h:47
alloc_func GetAllocator() const
Definition: matrix.h:423
void SetSize(size_t rows, size_t cols)
Definition: matrix.h:103
Matrix(const Matrix< Element > &other)
Definition: matrix.h:133
double EuclideanNorm() const
Matrix< typename Element::Vector > RotateVecResult(Matrix< Element > const &inMat)
Definition: matrix-lattice-impl.cpp:72
void SwitchFormat()
Definition: matrix-lattice-impl.cpp:113
double Norm() const
Matrix< typename Element::Integer > Rotate(Matrix< Element > const &inMat)
Definition: matrix-lattice-impl.cpp:39
Matrix< Element > ExtractRow(size_t row) const
Definition: matrix.h:580
void SetFormat(Format format)
Definition: matrix-lattice-impl.cpp:104
size_t GetCols() const
Definition: matrix.h:416
Matrix< double > Cholesky(const Matrix< int32_t > &input)
Matrix< Element > operator*(Matrix< Element > const &other) const
Definition: matrix.h:328
Definition: binfhecontext.h:36
Definition: exception.h:126
Matrix< Element > operator+(Matrix< Element > const &other) const
Definition: matrix.h:461
Matrix< Element > & operator+=(Matrix< Element > const &other)
Definition: matrix.cpp:92
Matrix< Element > ExtractCol(size_t col) const
Definition: matrix.h:597
Element & operator()(size_t row, size_t col)
Definition: matrix.h:561
void Determinant(Element *result) const
Definition: matrix.cpp:143
bool operator!=(Matrix< Element > const &other) const
Definition: matrix.h:395
Matrix< Element > & ModSubEq(Matrix< Element > const &b, const Element &modulus)
Matrix< int32_t > ConvertToInt32(const Matrix< BigInteger > &input, const BigInteger &modulus)
Definition: matrix-impl.cpp:197