Loading...
Searching...
No Matches
dense_matrix_product.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Dense matrix products over BLAS: alpha * op(left) * op(right), with each
5 * operand taken as it stands or conjugate-transposed.
6 *
7 * A product of two column-major blocks is one call here, which is what makes
8 * the shapes checkable in one place -- an inner dimension that does not match
9 * is refused with both shapes named -- and what puts the arithmetic in zgemm
10 * rather than in a loop per caller. zgemm accumulates in its own order, so a
11 * product agrees with a hand-written triple loop to the rounding of a few
12 * operations, not to the bit.
13 */
14
15#include <stdexcept>
16#include <string>
17
22
23namespace q3 {
24
25namespace internal::dense_matrix_product {
26
27/// The rows and columns of op(matrix).
29 int rows;
31};
32
33[[nodiscard]] inline OperandShape shape_of(MatrixOperation operation,
34 const DenseMatrix<Complex>& matrix) {
35 if (operation == MatrixOperation::ConjugateTranspose) {
36 return {matrix.columns(), matrix.rows()};
37 }
38 return {matrix.rows(), matrix.columns()};
39}
40
41[[nodiscard]] inline std::string describe(MatrixOperation operation,
42 const DenseMatrix<Complex>& matrix) {
43 const std::string shape =
44 std::to_string(matrix.rows()) + " x " + std::to_string(matrix.columns());
45 return operation == MatrixOperation::ConjugateTranspose ? "the adjoint of a " + shape
46 : "a " + shape;
47}
48
49} // namespace internal::dense_matrix_product
50
51/**
52 * result = alpha * op(left) * op(right) + result. The result must already have
53 * the product's shape; an empty operand dimension leaves it as it was.
54 */
55inline void multiply_add(MatrixOperation left_operation,
56 const DenseMatrix<Complex>& left,
57 MatrixOperation right_operation,
58 const DenseMatrix<Complex>& right,
59 Complex alpha,
60 DenseMatrix<Complex>& result) {
63
64 const auto [rows, inner] = shape_of(left_operation, left);
65 const auto [inner_of_right, columns] = shape_of(right_operation, right);
66 if (inner != inner_of_right) {
67 throw std::invalid_argument("multiply: " + describe(left_operation, left) +
68 " matrix cannot multiply " + describe(right_operation, right) +
69 " one; the inner dimensions are " + std::to_string(inner) +
70 " and " + std::to_string(inner_of_right));
71 }
72 if (result.rows() != rows || result.columns() != columns) {
73 throw std::invalid_argument("multiply: the product is " + std::to_string(rows) + " x " +
74 std::to_string(columns) + " but the result given is " +
75 std::to_string(result.rows()) + " x " +
76 std::to_string(result.columns()));
77 }
78 if (rows == 0 || columns == 0 || inner == 0) {
79 return;
80 }
81
82 const int leading_left = left.rows();
83 const int leading_right = right.rows();
84 const int leading_result = result.rows();
85 const Complex beta(1.0, 0.0);
86 blas::zgemm_(blas_transpose_flag(left_operation),
87 blas_transpose_flag(right_operation),
88 &rows,
89 &columns,
90 &inner,
91 &alpha,
92 left.data(),
93 &leading_left,
94 right.data(),
95 &leading_right,
96 &beta,
97 result.data(),
98 &leading_result);
99}
100
101/// alpha * op(left) * op(right), as a new matrix of the product's shape.
102[[nodiscard]] inline DenseMatrix<Complex> multiply(MatrixOperation left_operation,
103 const DenseMatrix<Complex>& left,
104 MatrixOperation right_operation,
105 const DenseMatrix<Complex>& right,
106 Complex alpha = Complex(1.0, 0.0)) {
108 const auto [rows, inner] = shape_of(left_operation, left);
109 const auto [inner_of_right, columns] = shape_of(right_operation, right);
110 (void)inner;
111 (void)inner_of_right;
112 DenseMatrix<Complex> result(rows, columns, Complex(0.0, 0.0));
113 multiply_add(left_operation, left, right_operation, right, alpha, result);
114 return result;
115}
116
117} // namespace q3
int rows() const
T * data()
The column-major buffer: with rows() it is the (pointer, leading dimension) BLAS takes.
int columns() const
void zgemm_(const char *transa, const char *transb, const int *m, const int *n, const int *k, const std::complex< double > *alpha, const std::complex< double > *a, const int *lda, const std::complex< double > *b, const int *ldb, const std::complex< double > *beta, std::complex< double > *c, const int *ldc)
OperandShape shape_of(MatrixOperation operation, const DenseMatrix< Complex > &matrix)
std::string describe(MatrixOperation operation, const DenseMatrix< Complex > &matrix)
void multiply_add(MatrixOperation left_operation, const DenseMatrix< Complex > &left, MatrixOperation right_operation, const DenseMatrix< Complex > &right, Complex alpha, DenseMatrix< Complex > &result)
const char * blas_transpose_flag(MatrixOperation operation)
The letter BLAS takes for the operation.
std::complex< double > Complex
DenseMatrix< Complex > multiply(MatrixOperation left_operation, const DenseMatrix< Complex > &left, MatrixOperation right_operation, const DenseMatrix< Complex > &right, Complex alpha=Complex(1.0, 0.0))
alpha * op(left) * op(right), as a new matrix of the product's shape.