1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ARRAY_UTIL_GEMM_H
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ARRAY_UTIL_GEMM_H
6#include "BufferManager.h"
13#include <molpro/Options.h>
14#include <molpro/Profiler.h>
15#include <molpro/cblas.h>
16#include <molpro/linalg/array/DistrArrayDisk.h>
17#include <molpro/linalg/array/type_traits.h>
18#include <molpro/linalg/itsolv/subspace/Matrix.h>
19#include <molpro/linalg/itsolv/wrap.h>
20#include <molpro/linalg/itsolv/wrap_util.h>
21#include <molpro/linalg/options.h>
38inline constexpr bool is_disk_array_v = std::is_base_of_v<DistrArrayDisk, std::decay_t<A>>;
43 return a.distribution().range(
BufferManager<std::decay_t<A>>::rank_in(a));
54 const auto stride = p[1] - p[0];
55 for (
size_t j = 1; j + 1 < p.size(); ++j)
56 if (p[j + 1] - p[j] != stride)
58 return stride >= std::ptrdiff_t(n) and stride > 0 ? size_t(stride) : 0;
63std::vector<T*>
offset_pointers(
const std::vector<T*>& bases,
size_t lo,
size_t g) {
71template <
class T,
class A>
74 for (
const auto& a : aa)
75 p.push_back(
const_cast<T*
>(a.get().local_buffer()->data()));
80template <
class A,
class Range>
81void check_local_ranges(
const std::vector<std::reference_wrapper<A>>& aa,
const Range& range,
const std::string& name) {
82 for (
const auto& a : aa)
84 throw std::out_of_range(name +
": the distributed arrays are distributed differently");
92template <
class T,
class R,
class A>
94 const std::string& name) {
97 for (
const auto& r : rr) {
98 if (
size_t(std::size(r.get())) != size_t(q.size()))
99 throw std::out_of_range(name +
": contiguous and distributed arrays are of different size: " +
100 std::to_string(std::size(r.get())) +
" " + std::to_string(q.size()));
101 p.push_back(
const_cast<T*
>(std::data(r.get())) + lo);
117void gemm_chunk(
gemm_type type,
size_t n,
const T* x,
size_t ldx,
size_t nx, T* alphadata,
const std::vector<T*>& y) {
118 const size_t ny = y.size();
119 if (n == 0 or nx == 0 or ny == 0)
122 if (y_stride == 0 and ny > 1) {
125 thread_local std::vector<T> packed;
126 packed.resize(n * ny);
127 std::vector<T*> p(ny);
128 for (
size_t k = 0; k < ny; ++k) {
129 p[k] = packed.data() + k * n;
130 std::copy_n(y[k], n, p[k]);
132 gemm_chunk(type, n, x, ldx, nx, alphadata, p);
134 for (
size_t k = 0; k < ny; ++k)
135 std::copy_n(p[k], n, y[k]);
138 const auto dims = std::to_string(nx) +
", " + std::to_string(ny) +
", " + std::to_string(n);
142 cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, n, ny, nx, 1, x, ldx, alphadata, ny, 1, y[0], y_stride);
145 for (
size_t k = 0; k < ny; ++k)
146 cblas_dgemv(CblasColMajor, CblasNoTrans, n, nx, 1, x, ldx, alphadata + k, ny, 1, y[k], 1);
151 cblas_dgemm(CblasColMajor, CblasTrans, CblasNoTrans, nx, ny, n, 1, x, ldx, y[0], y_stride, 1, alphadata, nx);
154 for (
size_t k = 0; k < ny; ++k)
155 cblas_dgemv(CblasColMajor, CblasTrans, n, nx, 1, x, ldx, y[k], 1, 1, alphadata + k * nx, 1);
169template <
class AD,
class T =
typename AD::value_type>
171 const std::type_identity_t<std::function<std::vector<T*>(
size_t g,
size_t n)>>& y_pointers,
172 const std::function<
void(
size_t g,
size_t n)>& y_done,
gemm_type type) {
176 const auto [lo, hi] =
local_range(xx.front().get());
180 auto number_of_buffers =
options->parameter(
"GEMM_BUFFERS", 2);
184 const int buf_size = std::min(
int(hi - lo),
options->parameter(
"GEMM_PAGESIZE", 8192));
188 for (
auto buffer_iterator = buffer.
begin(); buffer_iterator != buffer.
end(); ++buffer_iterator) {
202template <
class AD,
class T =
typename AD::value_type>
205 const std::type_identity_t<std::function<std::vector<T*>(
size_t g,
size_t n)>>& y_pointers,
206 const std::function<
void(
size_t g,
size_t n)>& y_done = {}) {
208 auto alphas =
Matrix<T>({ny, xx.size()});
210 if (ny == 0 or xx.empty())
212 const auto [lo, hi] =
local_range(xx.front().get());
213 prof += xx.size() * ny * (hi - lo) * 2;
214 auto alphadata =
const_cast<T*
>(alphas.data().data());
218 MPI_Allreduce(MPI_IN_PLACE, alphadata, alphas.size(), MPI_DOUBLE, MPI_SUM, xx.front().get().communicator());
226 if (alphas.
rows() != nx)
227 throw std::out_of_range(name +
": dimensions of xx and alphas are different: " + std::to_string(alphas.
rows()) +
228 " " + std::to_string(nx));
229 if (alphas.
cols() != ny)
230 throw std::out_of_range(name +
": dimensions of yy and alphas are different: " + std::to_string(alphas.
cols()) +
231 " " + std::to_string(ny));
250template <
class AD,
class T =
typename AD::value_type>
252 if (x_bases.empty() or yy.empty())
256 const auto [lo, hi] =
local_range(yy.front().get());
258 prof += x_bases.size() * yy.size() * (hi - lo) * 2;
259 const auto nx = x_bases.size();
260 const auto ny = yy.size();
261 const bool yy_on_disk = yy.front().get().disk_page_size() > 0;
262 const size_t page = yy_on_disk ? yy.front().get().disk_page_size()
264 std::vector<T*> y_bases;
266 y_bases = local_buffer_pointers<T>(yy);
267 std::vector<T> y_page, x_page;
268 for (
size_t g = lo; g < hi; g += page) {
269 const size_t n = std::min(page, hi - g);
272 y_page.resize(n * ny);
273 for (
size_t k = 0; k < ny; ++k) {
274 y.push_back(y_page.data() + k * n);
275 yy[k].get().get(g, g + n, y.back());
281 const T* x_data = x[0];
283 x_page.resize(n * nx);
284 for (
size_t i = 0; i < nx; ++i)
285 std::copy_n(x[i], n, x_page.data() + i * n);
286 x_data = x_page.data();
291 for (
size_t k = 0; k < ny; ++k)
292 yy[k].get().put(g, g + n, y[k]);
299template <
class AL,
class T =
typename array::mapped_or_value_type_t<AL>>
302 if (not
yy.empty() and
yy.front().get().disk_page_size() == 0) {
304 bases = local_buffer_pointers<T>(
yy);
308 if (not
bases.empty())
313 for (
size_t j = 0; j <
yy.size(); ++j) {
314 p.push_back(
chunk.data() + j * n);
315 yy[j].get().get(g, g + n, p.back());
319 void done(
size_t g,
size_t n) {
321 for (
size_t j = 0; j <
yy.size(); ++j)
322 yy[j].get().put(g, g + n,
chunk.data() + j * n);
325 return [
this](
size_t g,
size_t n) {
return pointers(g, n); };
328 return [
this](
size_t g,
size_t n) {
done(g, n); };
330 const VecRef<AL>&
yy;
337template <
class AL,
class AD, std::enable_if_t<is_disk_array_v<AD>,
int> = 0>
339 const CVecRef<AD>& xx) {
345template <
class AD,
class AL, std::enable_if_t<is_disk_array_v<AD> and not is_disk_array_v<AL>,
int> = 0>
347 const CVecRef<AL>& yy) {
352template <
class AL,
class AD, std::enable_if_t<is_disk_array_v<AD>,
int> = 0>
354 const VecRef<AL>& yy) {
355 if (yy.empty() or xx.empty())
369template <
class R,
class AD>
371 using T =
typename AD::value_type;
372 if (rr.empty() or qq.empty())
373 return Matrix<T>({rr.size(), qq.size()});
374 const auto& q = qq.front().get();
377 const auto bases = contiguous_pointers<T>(rr, q,
"gemm_inner_contiguous_distr");
378 return gemm_inner_buffered(rr.size(), qq, [&bases, lo](
size_t g,
size_t) { return offset_pointers(bases, lo, g); });
382template <
class AD,
class R>
388template <
class AD,
class R>
390 const VecRef<R>& rr) {
391 using T =
typename AD::value_type;
392 if (rr.empty() or qq.empty())
396 const auto& q = qq.front().get();
399 prof += qq.size() * rr.size() * (
local_range(q).second - lo) * 2;
400 const auto bases = contiguous_pointers<T>(rr, q,
"gemm_outer_distr_contiguous");
402 const_cast<T*
>(alphas.
data().data()), qq,
403 [&bases, lo](
size_t g,
size_t) { return offset_pointers(bases, lo, g); }, {},
gemm_type::outer);
404 for (
const auto& r : rr)
405 gather_all(q.distribution(), q.communicator(), std::data(r.get()));
409template <
class R,
class AD>
411 const VecRef<AD>& qq) {
412 using T =
typename AD::value_type;
413 if (rr.empty() or qq.empty())
415 gemm_outer_to_distr(alphas, contiguous_pointers<T>(rr, qq.front().get(),
"gemm_outer_contiguous_distr"), qq);
420template <
class AL,
class AR = AL, std::enable_if_t<not is_disk_array_v<AL> and not is_disk_array_v<AR>,
int> = 0>
422 const CVecRef<AR>& yy) {
426 if (xx.size() == 0 || yy.size() == 0)
430 prof += mat.
cols() * mat.rows() * xx.at(0).get().local_buffer()->size() * 2;
431 for (
size_t j = 0; j < mat.cols(); ++j) {
432 auto loc_y = yy.at(j).get().local_buffer();
433 for (
size_t i = 0; i < mat.rows(); ++i) {
434 auto loc_x = xx.at(i).get().local_buffer();
435 mat(i, j) = std::inner_product(
begin(*loc_x),
end(*loc_x),
begin(*loc_y), (value_type)0);
440 MPI_Allreduce(MPI_IN_PLACE,
const_cast<value_type*
>(mat.data().data()), mat.size(), MPI_DOUBLE, MPI_SUM,
441 xx.at(0).get().communicator());
446template <
class AL,
class AR = AL, std::enable_if_t<not is_disk_array_v<AR>,
int> = 0>
448 const VecRef<AL>& yy) {
449 if constexpr (is_disk_array_v<AL>) {
451 if (xx.empty() or yy.empty())
459 prof += alphas.rows() * alphas.cols() * yy[0].get().local_buffer()->size() * 2;
460 for (
size_t ii = 0; ii < alphas.rows(); ++ii) {
461 auto loc_x = xx.at(ii).get().local_buffer();
462 for (
size_t jj = 0; jj < alphas.cols(); ++jj) {
463 auto loc_y = yy[jj].get().local_buffer();
464 for (
size_t i = 0; i < loc_y->size(); ++i)
465 (*loc_y)[i] += alphas(ii, jj) * (*loc_x)[i];
472template <
class AL,
class AR = AL>
474 const VecRef<AL>& yy) {
476 for (
size_t ii = 0; ii < alphas.cols(); ++ii) {
477 auto combined = std::decay_t<AR>{};
478 for (
size_t jj = 0; jj < alphas.rows(); ++jj)
479 for (
const auto& [i, v] : xx.at(jj).get())
480 combined[i] += alphas(jj, ii) * v;
481 yy[ii].get().axpy(1, combined);
485template <
class AL,
class AR = AL>
487 const CVecRef<AR>& yy) {
490 if (xx.size() == 0 || yy.size() == 0)
493 for (
size_t i = 0; i < mat.rows(); ++i) {
494 const auto& x = xx.at(i).get();
496 const bool on_disk = x.disk_page_size() > 0;
497 decltype(x.local_buffer()) loc_x;
499 loc_x = x.local_buffer();
500 for (
size_t j = 0; j < mat.cols(); ++j) {
502 for (
auto it = yy.at(j).get().lower_bound(lo); it != yy.at(j).get().end() and it->first < hi; ++it) {
505 x.get(it->first, it->first + 1, &xk);
507 xk = (*loc_x)[it->first - lo];
508 mat(i, j) += xk * it->second;
513 MPI_Allreduce(MPI_IN_PLACE,
const_cast<value_type*
>(mat.data().data()), mat.size(), MPI_DOUBLE, MPI_SUM,
514 xx.at(0).get().communicator());
521template <
class Handler,
class AL,
class AR = AL>
523 const VecRef<AL>& yy) {
524 for (
size_t ii = 0; ii < alphas.
rows(); ++ii) {
525 for (
size_t jj = 0; jj < alphas.
cols(); ++jj) {
526 handler.axpy(alphas(ii, jj), xx.at(ii).get(), yy[jj].get());
531template <
class Handler,
class AL,
class AR = AL>
533 const CVecRef<AR>& yy) {
535 if (xx.size() == 0 || yy.size() == 0)
537 for (
size_t ii = 0; ii < mat.rows(); ++ii) {
538 for (
size_t jj = 0; jj < mat.cols(); ++jj) {
539 mat(ii, jj) = handler.dot(xx.at(ii).get(), yy.at(jj).get());
BufferManager provides single-buffered or asynchronous double-buffered read access to the data in a c...
Definition BufferManager.h:20
size_t buffer_offset() const
Definition BufferManager.h:77
size_t buffer_size() const
Definition BufferManager.h:67
size_t buffer_stride() const
Definition BufferManager.h:72
Iterator end()
Definition BufferManager.h:142
Iterator begin()
Definition BufferManager.h:140
Matrix container that allows simple data access, slicing, copying and resizing without loosing data.
Definition Matrix.h:32
const std::vector< T > & data() const &
Access the underlying data buffer.
Definition Matrix.h:69
size_t size() const
Definition Matrix.h:172
void fill(T value)
Sets all elements of matrix to value.
Definition Matrix.h:93
index_type rows() const
Definition Matrix.h:169
index_type cols() const
Definition Matrix.h:170
static std::shared_ptr< Profiler > single()
auto begin(Span< T > &x)
Definition Span.h:90
auto end(Span< T > &x)
Definition Span.h:100
Definition ArrayHandler.h:24
void check_local_ranges(const std::vector< std::reference_wrapper< A > > &aa, const Range &range, const std::string &name)
Checks that the distributed arrays aa all have the local section range, so that they can be processed...
Definition gemm.h:81
Matrix< typename AD::value_type > gemm_inner_contiguous_distr(const CVecRef< R > &rr, const CVecRef< AD > &qq)
The overlap matrix (rr.size(), qq.size()) between contiguous arrays rr and distributed arrays qq....
Definition gemm.h:370
constexpr bool is_disk_array_v
Whether arrays of type A are held on disk, and so must be read through buffers rather than local_buff...
Definition gemm.h:38
void gemm_outer_to_distr(const Matrix< T > &alphas, const std::vector< T * > &x_bases, const VecRef< AD > &yy)
yy += alphas^T xx, for arrays xx held in memory and distributed arrays yy held anywhere.
Definition gemm.h:251
Matrix< typename array::mapped_or_value_type_t< AL > > gemm_inner_distr_sparse(const CVecRef< AL > &xx, const CVecRef< AR > &yy)
Definition gemm.h:486
void gemm_buffered(T *alphadata, const CVecRef< AD > &xx, const std::type_identity_t< std::function< std::vector< T * >(size_t g, size_t n)> > &y_pointers, const std::function< void(size_t g, size_t n)> &y_done, gemm_type type)
gemm between distributed arrays xx, read through a BufferManager, and arrays yy, which can be held an...
Definition gemm.h:170
void gather_all(const Distribution< size_t > &distr, MPI_Comm commun, double *first_elem)
Replicate data of a full container on all the processes based on distributed pieces.
Definition gather_all.h:18
Matrix< T > conjugate_transpose(const Matrix< T > &m)
The transpose of an overlap matrix, which for the hermitian inner product is its conjugate transpose.
Definition gemm.h:236
size_t constant_stride(const std::vector< T * > &p, size_t n)
Definition gemm.h:51
void check_gemm_outer_dimensions(const Matrix< T > &alphas, size_t nx, size_t ny, const std::string &name)
Checks the dimensions of the coefficient matrix of gemm_outer.
Definition gemm.h:225
Matrix< typename array::mapped_or_value_type_t< AL > > gemm_inner_distr_distr(const CVecRef< AL > &yy, const CVecRef< AD > &xx)
Definition gemm.h:338
void gemm_outer_distr_sparse(const Matrix< typename array::mapped_or_value_type_t< AL > > alphas, const CVecRef< AR > &xx, const VecRef< AL > &yy)
Definition gemm.h:473
auto local_range(const A &a)
The section [lo, hi) of a distributed array held by this process.
Definition gemm.h:42
void gemm_chunk(gemm_type type, size_t n, const T *x, size_t ldx, size_t nx, T *alphadata, const std::vector< T * > &y)
One chunk of n elements of a gemm between the arrays xx, which are the nx columns of x with leading d...
Definition gemm.h:117
Matrix< T > gemm_inner_buffered(size_t ny, const CVecRef< AD > &xx, const std::type_identity_t< std::function< std::vector< T * >(size_t g, size_t n)> > &y_pointers, const std::function< void(size_t g, size_t n)> &y_done={})
The overlap matrix between arrays yy, accessed as described in gemm_buffered(), and distributed array...
Definition gemm.h:204
void gemm_outer_default(Handler &handler, const Matrix< typename Handler::value_type > alphas, const CVecRef< AR > &xx, const VecRef< AL > &yy)
Definition gemm.h:522
void gemm_outer_contiguous_distr(const Matrix< typename AD::value_type > &alphas, const CVecRef< R > &rr, const VecRef< AD > &qq)
qq += alphas^T rr for contiguous arrays rr and distributed arrays qq. Local.
Definition gemm.h:410
std::vector< T * > offset_pointers(const std::vector< T * > &bases, size_t lo, size_t g)
Pointers to elements [g, ...) of arrays whose local sections, starting at global index lo,...
Definition gemm.h:63
void gemm_outer_distr_contiguous(const Matrix< typename AD::value_type > &alphas, const CVecRef< AD > &qq, const VecRef< R > &rr)
rr += alphas^T qq for contiguous arrays rr and distributed arrays qq. Collective.
Definition gemm.h:389
std::vector< T * > contiguous_pointers(const std::vector< std::reference_wrapper< R > > &rr, const A &q, const std::string &name)
Pointers to the elements of contiguous arrays rr, offset to the start of the local section of the dis...
Definition gemm.h:93
std::vector< T * > local_buffer_pointers(const std::vector< std::reference_wrapper< A > > &aa)
Pointers to the start of the local sections of distributed arrays held in memory.
Definition gemm.h:72
gemm_type
Definition gemm.h:34
@ outer
Definition gemm.h:34
@ inner
Definition gemm.h:34
Matrix< typename AD::value_type > gemm_inner_distr_contiguous(const CVecRef< AD > &qq, const CVecRef< R > &rr)
The overlap matrix (qq.size(), rr.size()) between distributed arrays qq and contiguous arrays rr....
Definition gemm.h:383
void gemm_outer_distr_distr(const Matrix< typename array::mapped_or_value_type_t< AL > > alphas, const CVecRef< AD > &xx, const VecRef< AL > &yy)
Definition gemm.h:353
Matrix< typename Handler::value_type > gemm_inner_default(Handler &handler, const CVecRef< AL > &xx, const CVecRef< AR > &yy)
Definition gemm.h:532
typename mapped_or_value_type< A >::type mapped_or_value_type_t
Definition type_traits.h:38
void conjugate_transpose_copy(ML &&ml, const MR &mr)
Copies the conjugate transpose of mr into ml.
Definition Matrix.h:299
std::vector< std::reference_wrapper< const A > > CVecRef
Definition wrap.h:14
auto const_cast_wrap(ForwardIt begin, ForwardIt end)
Takes a begin and end iterators and returns a vector of const-casted references to each element.
Definition wrap.h:42
std::vector< std::reference_wrapper< A > > VecRef
Definition wrap.h:11
const std::shared_ptr< const molpro::Options > options()
Get the Options object associated with iterative-solver.
Definition linalg_options.cpp:4
The pointers to, and on completion the write-back of, chunks of distributed arrays yy for gemm_buffer...
Definition gemm.h:300
std::vector< T * > pointers(size_t g, size_t n)
Definition gemm.h:307
std::function< std::vector< T * >(size_t, size_t)> pointers_function()
Definition gemm.h:324
std::vector< T * > bases
Definition gemm.h:333
std::vector< T > chunk
Definition gemm.h:334
std::function< void(size_t, size_t)> done_function()
Definition gemm.h:327
size_t lo
Definition gemm.h:332
const VecRef< AL > & yy
Definition gemm.h:330
void done(size_t g, size_t n)
Definition gemm.h:319
const gemm_type type
Definition gemm.h:331
DistrChunks(const VecRef< AL > &yy, gemm_type type)
Definition gemm.h:301