ALP User Documentation  0.8.preview
Algebraic Programming User Documentation
matrix_factory.hpp
Go to the documentation of this file.
1 
2 /*
3  * Copyright 2023 Huawei Technologies Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
27 #ifndef _H_GRB_MATRIX_FACTORY
28 #define _H_GRB_MATRIX_FACTORY
29 
30 #include <random>
31 #include <iostream>
32 #include <algorithm>
33 
36 
37 #include <graphblas.hpp>
38 
39 
40 namespace grb::algorithms {
41 
144  template<
145  typename D,
146  grb::IOMode mode = grb::PARALLEL,
147  grb::Backend backend = grb::config::default_backend,
148  typename RIT = grb::config::RowIndexType,
149  typename CIT = grb::config::ColIndexType,
150  typename NIT = grb::config::NonzeroIndexType
151  >
152  class matrices {
153 
154  friend class matrices< void, mode, backend >;
155 
156  private:
157 
160 
170  static size_t getP() {
171  return mode == grb::SEQUENTIAL ? 1 : grb::spmd<backend>::nprocs();
172  }
173 
179  static size_t getPID() {
180  return mode == grb::SEQUENTIAL ? 0 : grb::spmd<backend>::pid();
181  }
182 
193  static size_t compute_diag_length(
194  const size_t m, const size_t n,
195  const long k
196  ) {
197  constexpr const long zero = static_cast< long >( 0 );
198  const size_t k_abs = static_cast< size_t >(
199  (k < zero) ? -k : k );
200  // catch out-of-bounds offsets
201  if( k < zero && k_abs >= m ) {
202  return 0;
203  } else if( k > zero && k_abs >= n ) {
204  return 0;
205  }
206  return std::min( std::min( m, n ), k < zero
207  ? m - k_abs
208  : n - k_abs
209  );
210  }
211 
244  template< class IteratorV >
245  static MatrixType createIdentity_generic(
246  const size_t m, const size_t n, const long k,
247  const IteratorV V_iter, const IteratorV V_end
248  ) {
249 #ifdef _DEBUG
250  std::cout << "createIdentity_generic called with m = " << m << ", n = "
251  << n << ", k = " << k << " (non-void variant)\n";
252 #endif
253  // some useful scalars
254  constexpr const long s_zero = static_cast< long >( 0 );
255  constexpr const size_t u_zero = static_cast< size_t >( 0 );
256  const size_t diag_length = compute_diag_length( m, n, k );
257 #ifdef NDEBUG
258  (void) V_end;
259 #endif
260 #ifdef _DEBUG
261  std::cout << "Computed diag_length = " << diag_length << "\n";
262 #endif
263 
264  // declare matrix-to-be-returned
265  MatrixType matrix( m, n, diag_length );
266 
267  // get row- and column-wise starting positions
268  const RIT k_i_incr = static_cast< RIT >(
269  (k < s_zero) ? std::abs( k ) : u_zero );
270  const CIT k_j_incr = static_cast< CIT >(
271  (k < s_zero) ? u_zero : std::abs( k ) );
272 
273  // translate it to a range so we can get iterators
274  grb::utils::containers::Range< RIT > I( k_i_incr, diag_length + k_i_incr );
275  grb::utils::containers::Range< CIT > J( k_j_incr, diag_length + k_j_incr );
276 
277  // construct the matrix from the given iterators
278  const size_t s = getPID();
279  const size_t P = getP();
280  assert( static_cast< size_t >(std::distance( V_iter, V_end )) ==
281  static_cast< size_t >(std::distance( I.begin( s, P ), I.end( s, P ) )) );
282  assert( static_cast< size_t >(std::distance( V_iter, V_end )) ==
283  static_cast< size_t >(std::distance( J.begin( s, P ), J.end( s, P ) )) );
284  const RC rc = buildMatrixUnique(
285  matrix,
286  I.begin( s, P ), I.end( s, P ),
287  J.begin( s, P ), J.end( s, P ),
288  V_iter, V_end,
289  mode
290  );
291 
292  if( rc != SUCCESS ) {
293  throw std::runtime_error(
294  "Error: createIdentity_generic failed: rc = " + grb::toString( rc )
295  );
296  }
297  return matrix;
298  }
299 
300 
301  public:
302 
312  const size_t m, const size_t n
313  ) {
314  return MatrixType( m, n, 0 );
315  }
316 
355  template< class ValueIterator >
356  static MatrixType diag(
357  const size_t m, const size_t n,
358  const ValueIterator values, const ValueIterator valEnd,
359  const long k = static_cast< long >( 0 )
360  ) {
361  // static sanity checks
362  static_assert( mode == grb::SEQUENTIAL,
363  "matrices<>::diag is currently only supported with sequential I/O" );
364  static_assert(
365  std::is_convertible<
366  typename std::iterator_traits< ValueIterator >::value_type,
367  D
368  >::value,
369  "grb::factory::diag called with value types that are not convertible to the "
370  "matrix nonzero type."
371  );
372 
373  // check trivial dispatch
374  if( m == 0 || n == 0 ) {
375  return empty( n, n );
376  }
377 
378  // call generic implementation with given iterator
379  return createIdentity_generic( m, n, k, values, valEnd );
380  }
381 
402  static MatrixType eye(
403  const size_t m,
404  const size_t n,
405  const D value = static_cast< D >( 1 ),
406  const long k = static_cast< long >( 0 )
407  ) {
408  // check for possible trivial dispatch
409  if( m == 0 || n == 0 ) {
410  return empty( m, n );
411  }
412 
413  // we are in the non-pattern case, so we need a repeated iterator for the
414  // values-- determine worst-case length (cheaper than computing the actual
415  // diagonal length)
416  const size_t diag_length = compute_diag_length( m, n, k );
418  diag_length );
419 
420  // call generic implementation
421  const size_t s = getPID();
422  const size_t P = getP();
423  return createIdentity_generic( m, n, k, V.cbegin( s, P ), V.cend( s, P ) );
424  }
425 
435  static MatrixType eye( const size_t n ) {
436  return eye( n, n );
437  }
438 
458  template<
459  typename Semiring = grb::Semiring<
462  >
463  >
465  const size_t n,
466  const Semiring &ring = Semiring()
467  ) {
468  const D identity_value = ring.template getOne< D >();
469  return eye( n, n, identity_value, 0 );
470  }
471 
488  static MatrixType full(
489  const size_t m, const size_t n,
490  const D value
491  ) {
492  if( m == 0 || n == 0 ) {
493  return empty( m, n );
494  }
495 
496  const size_t nz = m * n;
497  if( nz / m != n ) {
498  throw std::runtime_error( "Requested dense matrix overflows in number of "
499  "nonzeroes." );
500  }
501 
502  const size_t s = getPID();
503  const size_t P = getP();
504 
505  // Initialise rows indices container with a range from 0 to nrows,
506  // each value repeated ncols times.
507  grb::utils::containers::Range< RIT > I( 0, m, 1, n );
508 
509  // Initialise columns values container with a range from 0 to ncols
510  // repeated nrows times. There are two ways of doing this:
511  // 1) using InterleavedIterators, or
512  // 2) using the iterator::Adapter.
513  // We select here way #2, and disable way #1:
514 #if 0
515  grb::utils::containers::InterleavedIteratorsVector<
517  > J( m );
518  for( size_t i = 0; i < m; ++i ) {
519  J.emplace_back( grb::utils::containers::Range< CIT >( 0, n ) );
520  }
521 #endif
522  const grb::utils::containers::Range< size_t > J_raw( 0, nz );
523  const auto entryInd2colInd = [&m] (const CIT k) -> CIT {
524  return k / m;
525  };
527  J_raw.cbegin( s, P ), J_raw.cend( s, P ), entryInd2colInd );
529  J_raw.cend( s, P ), J_raw.cend( s, P ), entryInd2colInd );
530 
531  // Initialise values container with the given value.
532  const size_t local_nz = std::distance( I.begin( s, P ), I.end( s, P ) );
533 #ifndef NDEBUG
534  const size_t Isz = local_nz;
535  const size_t Jsz = std::distance( J_begin, J_end );
536  assert( Isz == Jsz );
537 #endif
538  grb::utils::containers::ConstantVector< D > V( value, local_nz );
539 #ifndef NDEBUG
540  const size_t Vsz = std::distance( V.begin(), V.end() );
541  assert( Isz == Vsz );
542 #endif
543 
544  // allocate and build
545  MatrixType matrix( m, n, nz );
546  const RC rc = buildMatrixUnique(
547  matrix,
548  I.begin( s, P ), I.end( s, P ),
549  J_begin, J_end,
550  V.begin(), V.end(),
551  mode
552  );
553 
554  if( rc != SUCCESS ) {
555  throw std::runtime_error(
556  "Error: factory::full<void> failed: rc = " + grb::toString( rc )
557  );
558  }
559 
560  return matrix;
561  }
562 
576  const size_t m, const size_t n, const D value
577  ) {
578  return full( m, n, value );
579  }
580 
607  template<
608  typename Semiring = grb::Semiring<
611  >
612  >
614  const MatrixType &A,
615  const Semiring &ring = Semiring()
616  ) {
617  const size_t m = grb::nrows( A );
618  const size_t n = grb::ncols( A );
619  if( n == 0 || m == 0 ) {
620  return empty( m, n );
621  }
622 
623  if( grb::nnz( A ) == 0 ) {
624  return zeros( m, n, ring );
625  }
626 
627  const size_t nz = m * n;
628  if( nz / m != n ) {
629  throw std::runtime_error( "Requested dense matrix overflows in number of "
630  "nonzeroes." );
631  }
632 
633  const auto addMon = ring.getAdditiveMonoid();
634  const D zero = ring.template getZero< D >();
635  MatrixType matrix( m, n, nz );
636  grb::RC rc = grb::set( matrix, zero );
637  rc = rc ? rc : grb::foldl( matrix, A, addMon );
638 
639  if( rc != grb::SUCCESS) {
640  throw std::runtime_error( "Could not promote input matrix to a dense one: "
641  + grb::toString( rc ) );
642  }
643 
644  return matrix;
645  }
646 
665  template<
666  typename Semiring = grb::Semiring<
669  >
670  >
672  const size_t m, const size_t n,
673  const Semiring &ring = Semiring()
674  ) {
675  const D zero = ring.template getZero< D >();
676  return full( m, n, static_cast< D >( zero ) );
677  }
678 
697  template<
698  typename Semiring = grb::Semiring<
701  >
702  >
703  static MatrixType ones(
704  const size_t m, const size_t n,
705  const Semiring &ring = Semiring()
706  ) {
707  const D one = ring.template getOne< D >();
708  return full( m, n, static_cast< D >( one ) );
709  }
710 
711  }; // end class matrices
712 
719  template<
720  grb::IOMode mode, grb::Backend backend,
721  typename RIT, typename CIT, typename NIT
722  >
723  class matrices< void, mode, backend, RIT, CIT, NIT > {
724 
725  private:
726 
729 
735 
745  static MatrixType createIdentity_generic(
746  const size_t m, const size_t n, const long k
747  ) {
748  // pattern matrix variant of the above
749  constexpr const long s_zero = static_cast< long >( 0 );
750  constexpr const size_t u_zero = static_cast< size_t >( 0 );
751  const size_t diag_length = BaseType::compute_diag_length( m, n, k );
752  MatrixType matrix( m, n, diag_length );
753  const RIT k_i_incr = static_cast< RIT >(
754  (k < s_zero) ? std::abs( k ) : u_zero );
755  const CIT k_j_incr = static_cast< CIT >(
756  (k < s_zero) ? u_zero : std::abs( k ) );
757  grb::utils::containers::Range< RIT > I( k_i_incr, diag_length + k_i_incr );
758  grb::utils::containers::Range< CIT > J( k_j_incr, diag_length + k_j_incr );
759  const size_t s = BaseType::getPID();
760  const size_t P = BaseType::getP();
761  const RC rc = buildMatrixUnique(
762  matrix,
763  I.begin( s, P ), I.end( s, P ),
764  J.begin( s, P ), J.end( s, P ),
765  mode
766  );
767  if( rc != SUCCESS ) {
768  throw std::runtime_error(
769  "Error: createIdentity_generic<void> failed: rc = " + grb::toString( rc )
770  );
771  }
772  return matrix;
773  }
774 
775 
776  public:
777 
787  const size_t m, const size_t n
788  ) {
789  return MatrixType( m, n, 0 );
790  }
791 
812  const size_t m, const size_t n,
813  const long k = static_cast< long >( 0 )
814  ) {
815  // check trivial dispatch
816  if( m == 0 || n == 0 ) {
817  return empty( n, n );
818  }
819 
820  // call generic implementation with given iterator
821  return createIdentity_generic( m, n, k );
822  }
823 
843  static MatrixType eye(
844  const size_t m,
845  const size_t n,
846  const long k = static_cast< long >( 0 )
847  ) {
848  // check trivial case
849  if( m == 0 || n == 0 ) {
850  return empty( m, n );
851  }
852 
853  // dispatch to generic function
854  return createIdentity_generic( m, n, k );
855  }
856 
866  static MatrixType eye( const size_t n ) {
867  return eye( n, n );
868  }
869 
884  const size_t n
885  ) {
886  return eye( n, n, 0 );
887  }
888 
906  static MatrixType full( const size_t m, const size_t n ) {
907  if( m == 0 || n == 0 ) {
908  return empty( m, n );
909  }
910 
911  const size_t nz = m * n;
912  if( nz / m != n ) {
913  throw std::runtime_error( "Requested dense matrix overflows in number of "
914  "nonzeroes." );
915  }
916 
917  const size_t s = BaseType::getPID();
918  const size_t P = BaseType::getP();
919 
920  // Initialise rows indices container with a range from 0 to nrows,
921  // each value repeated ncols times.
922  grb::utils::containers::Range< RIT > I( 0, m, 1, n );
923 
924  // Initialise columns values container with a range from 0 to ncols
925  // repeated nrows times. As mentioned above, there are two ways to provide
926  // iterators, we disable the first way (via InterleavedIterators) and enable
927  // the iterators::adaptor way:
928 #if 0
929  grb::utils::containers::InterleavedIteratorsVector<
931  > J( m );
932  for( size_t i = 0; i < m; ++i ) {
933  J.emplace_back( grb::utils::containers::Range< CIT >( 0, n ) );
934  }
935 #endif
936  const grb::utils::containers::Range< size_t > J_raw( 0, nz );
937  const auto nonzeroInd2colInd = [&m] (const size_t k) -> size_t {
938  return k / m;
939  };
941  J_raw.cbegin( s, P ), J_raw.cend( s, P ), nonzeroInd2colInd );
943  J_raw.cend( s, P ), J_raw.cend( s, P ), nonzeroInd2colInd );
944 
945  assert( std::distance( I.begin( s, P ), I.end( s, P ) ) ==
946  std::distance( J_begin, J_end ) );
947 
948  // construct and populate matrix
949  MatrixType matrix( m, n, nz );
950  const RC rc = buildMatrixUnique(
951  matrix,
952  I.begin( s, P ), I.end( s, P ),
953  J_begin, J_end,
954  mode
955  );
956 
957  if( rc != SUCCESS ) {
958  throw std::runtime_error(
959  "Error: factory::full<void> failed: rc = " + grb::toString( rc )
960  );
961  }
962 
963  return matrix;
964  }
965 
979  static MatrixType dense( const size_t m, const size_t n ) {
980  return full( m, n );
981  }
982 
997  static MatrixType dense( const MatrixType &A ) {
998  (void) A;
999  return full( grb::nrows( A ), grb::ncols( A ) );
1000  }
1001 
1019  static MatrixType zeros( const size_t m, const size_t n ) {
1020  return full( m, n );
1021  }
1022 
1040  static MatrixType ones( const size_t m, const size_t n ) {
1041  return full( m, n );
1042  }
1043 
1044  }; // end class matrices (pattern specialisation)
1045 
1046 } // namespace grb::algorithms
1047 
1048 #endif // end _H_GRB_MATRIX_FACTORY
1049 
static MatrixType diag(const size_t m, const size_t n, const ValueIterator values, const ValueIterator valEnd, const long k=static_cast< long >(0))
Builds a diagonal matrix with the given values.
Definition: matrix_factory.hpp:356
The namespace for ALP/GraphBLAS algorithms.
Definition: graphblas.hpp:482
static MatrixType eye(const size_t m, const size_t n, const D value=static_cast< D >(1), const long k=static_cast< long >(0))
Builds a diagonal matrix.
Definition: matrix_factory.hpp:402
const_iterator cbegin(const size_t s=0, const size_t P=1) const
Returns a const-iterator at start position to this container.
Definition: regular.hpp:1319
RC set(Vector< DataType, backend, Coords > &x, const T val, const Phase &phase=EXECUTE, const typename std::enable_if< !grb::is_object< DataType >::value &&!grb::is_object< T >::value, void >::type *const =nullptr) noexcept
Sets all elements of a vector to the given value.
Definition: io.hpp:858
unsigned int RowIndexType
What data type should be used to store row indices.
Definition: base/config.hpp:436
Standard identity for numerical addition.
Definition: identities.hpp:57
Defines an iterator that adapts the values returned by a sub-iterator according to some user-defined ...
An ALP/GraphBLAS matrix.
Definition: matrix.hpp:72
static size_t nprocs() noexcept
Definition: spmd.hpp:56
RC
Return codes of ALP primitives.
Definition: rc.hpp:47
static MatrixType dense(const MatrixType &A)
Builds a dense pattern matrix from a given matrix.
Definition: matrix_factory.hpp:997
Standard identity for numerical multiplication.
Definition: identities.hpp:79
static MatrixType identity(const size_t n)
Builds an identity pattern matrix.
Definition: matrix_factory.hpp:883
const_iterator cbegin(const size_t s=0, const size_t P=1) const
Returns a const-iterator at start position to this container.
Definition: regular.hpp:1136
iterator begin(const size_t s=0, const size_t P=1) const
Returns a const-iterator at start position to this container.
Definition: regular.hpp:1273
static MatrixType empty(const size_t m, const size_t n)
Builds an empty matrix, without any values.
Definition: matrix_factory.hpp:311
static MatrixType empty(const size_t m, const size_t n)
Builds an empty matrix, without any values.
Definition: matrix_factory.hpp:786
Sequential mode IO.
Definition: iomode.hpp:75
static MatrixType zeros(const size_t m, const size_t n)
Builds a matrix filled with zeros.
Definition: matrix_factory.hpp:1019
iterator end(const size_t s=0, const size_t P=1) const
Returns a const-iterator at end position to this container.
Definition: regular.hpp:1307
iterator begin(const size_t s=0, const size_t P=1) const
Returns a const-iterator at start position to this container.
Definition: regular.hpp:1092
static MatrixType eye(const size_t n)
Builds a diagonal pattern matrix.
Definition: matrix_factory.hpp:866
static Adapter< SubIterT > make_adapter_iterator(const SubIterT start, const SubIterT end, const std::function< typename SubIterT::value_type(const typename SubIterT::value_type) > func)
Creates an adapter of a given iterator.
Definition: adapter.hpp:349
size_t nnz(const Vector< DataType, backend, Coords > &x) noexcept
Request the number of nonzeroes in a given vector.
Definition: io.hpp:479
This operator multiplies the two input parameters and writes the result to the output variable.
Definition: ops.hpp:208
static MatrixType ones(const size_t m, const size_t n, const Semiring &ring=Semiring())
Builds a matrix filled with ones.
Definition: matrix_factory.hpp:703
size_t nrows(const Matrix< InputType, backend, RIT, CIT, NIT > &A) noexcept
Requests the row size of a given matrix.
Definition: io.hpp:286
unsigned int ColIndexType
What data type should be used to store column indices.
Definition: base/config.hpp:449
A set of iterators that mimic containers with regular structure.
iterator end(const size_t s=0, const size_t P=1) const
Returns a const-iterator at end position to this container.
Definition: regular.hpp:1125
RC buildMatrixUnique(Matrix< InputType, implementation, RIT, CIT, NIT > &A, fwd_iterator1 I, const fwd_iterator1 I_end, fwd_iterator2 J, const fwd_iterator2 J_end, fwd_iterator3 V, const fwd_iterator3 V_end, const IOMode mode)
Assigns nonzeroes to the matrix from a coordinate format.
Definition: io.hpp:1340
size_t ncols(const Matrix< InputType, backend, RIT, CIT, NIT > &A) noexcept
Requests the column size of a given matrix.
Definition: io.hpp:339
static MatrixType identity(const size_t n, const Semiring &ring=Semiring())
Builds an identity matrix.
Definition: matrix_factory.hpp:464
RC foldl(IOType &x, const Vector< InputType, backend, Coords > &y, const Vector< MaskType, backend, Coords > &mask, const Monoid &monoid=Monoid(), const typename std::enable_if< !grb::is_object< IOType >::value &&!grb::is_object< InputType >::value &&!grb::is_object< MaskType >::value &&grb::is_monoid< Monoid >::value, void >::type *const =nullptr)
Reduces, or folds, a vector into a scalar.
Definition: blas1.hpp:3840
static size_t pid() noexcept
Definition: spmd.hpp:61
A (dense) vector of a given size that holds the same constant value at each entry.
Definition: regular.hpp:1026
Factories for creating ALP/GraphBLAS matrices with standard patterns such as identity matrices.
Definition: matrix_factory.hpp:152
Backend
A collection of all backends.
Definition: backends.hpp:49
static MatrixType full(const size_t m, const size_t n)
Build a dense pattern matrix.
Definition: matrix_factory.hpp:906
static MatrixType dense(const size_t m, const size_t n, const D value)
Builds a dense matrix filled with a given value.
Definition: matrix_factory.hpp:575
MatrixType diag(const size_t m, const size_t n, const long k=static_cast< long >(0))
Builds a diagonal pattern matrix.
Definition: matrix_factory.hpp:811
This operator takes the sum of the two input parameters and writes it to the output variable.
Definition: ops.hpp:175
IOMode
The GraphBLAS input and output functionalities can either be used in a sequential or parallel fashion...
Definition: iomode.hpp:67
static MatrixType eye(const size_t m, const size_t n, const long k=static_cast< long >(0))
Builds a diagonal pattern matrix.
Definition: matrix_factory.hpp:843
static MatrixType dense(const MatrixType &A, const Semiring &ring=Semiring())
Builds a dense matrix from a given matrix.
Definition: matrix_factory.hpp:613
const_iterator cend(const size_t s=0, const size_t P=1) const
Returns a const-iterator at end position to this container.
Definition: regular.hpp:1330
static MatrixType full(const size_t m, const size_t n, const D value)
Builds a dense matrix filled with a given value.
Definition: matrix_factory.hpp:488
The main header to include in order to use the ALP/GraphBLAS API.
A container that contains a sequence of numbers with a given stride, and optionally a given number of...
Definition: regular.hpp:1163
Parallel mode IO.
Definition: iomode.hpp:92
iterator const_iterator
The const-iterator type.
Definition: regular.hpp:1194
const_iterator cend(const size_t s=0, const size_t P=1) const
Returns a const-iterator at end position to this container.
Definition: regular.hpp:1146
size_t NonzeroIndexType
What data type should be used to refer to an array containing nonzeroes.
Definition: base/config.hpp:462
Indicates the primitive has executed successfully.
Definition: rc.hpp:54
A generalised semiring.
Definition: semiring.hpp:190
static MatrixType eye(const size_t n)
Builds a diagonal matrix.
Definition: matrix_factory.hpp:435
static MatrixType ones(const size_t m, const size_t n)
Builds a dense pattern matrix filled with ones.
Definition: matrix_factory.hpp:1040
static MatrixType dense(const size_t m, const size_t n)
Builds a dense pattern matrix.
Definition: matrix_factory.hpp:979
static MatrixType zeros(const size_t m, const size_t n, const Semiring &ring=Semiring())
Builds a matrix filled with zeros.
Definition: matrix_factory.hpp:671
std::string toString(const RC code)