BLAS LDB с использованием DGEMM - PullRequest
0 голосов
/ 31 марта 2020

Я хочу умножить матрицы на D * W ', где W' - транспонированная версия W.

Пока я буду использовать DGEMM, я разберусь с помощью @ IanBu sh что LDB в этом случае должен быть числом строк матрицы W вместо числа столбцов. Код для этого случая:

  Call dgemm('n', 't', N1, M1, N1, 1.0_wp, D, N1, W, M1, 0.0_wp, c, n1)

, где n1 и m1 - размеры моих матриц

Dimensions of the matrices:
W = M1*N1
D = N1*N1

Как в официальной документации написано

      LDB is INTEGER
       On entry, LDB specifies the first dimension of B as declared
       in the calling (sub) program. When  TRANSB = 'N' or 'n' then
       LDB must be at least  max( 1, k ), otherwise  LDB must be at
       least  max( 1, n ).

Почему это случилось?

Ответы [ 2 ]

2 голосов
/ 31 марта 2020

Вызов dgemm содержит два набора целых чисел. Во-первых, определить размеры матриц, как описано в математике. Глядя на документацию для BLAS по адресу http://www.netlib.org/lapack/explore-html/d7/d2b/dgemm_8f.html, мы можем увидеть аргументы подпрограммы, определенной как (и отформатировать ее так, чтобы я помнил, как она работает):

         Subroutine dgemm (TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, &
                                                           B, LDB, & 
                                                     BETA, C, LDC)

Набор целых чисел для определения математических измерений: M, N, K В этом наборе

  • M - первое математическое измерение матрицы результатов, C
  • N - второе математическое измерение матрицы результатов C
  • K - длина точечного произведения, необходимая для создания отдельного элемента матрицы результатов C

Итак M, N, K чисто по математике задачи, больше ничего. Но мы находимся на компьютере, поэтому возникает второй вопрос - как каждая из двухмерных матриц размещается в памяти компьютера, которая по своей сути является одномерным объектом? Теперь Фортран хранит свои объекты в порядке Колонна Майор. Это означает, что в памяти, если у вас есть элемент A (i, j), следующая ячейка памяти будет содержать A (i + 1, j), если мы не являемся концом столбца, или A (1, j + 1), если мы. Таким образом, матрица построена так, что «первый индекс движется быстрее всего». Чтобы определить это расположение двумерного объекта, все, что нам нужно сказать программе, это то, сколько элементов A вам нужно перепрыгнуть на go с A (i, j) на A (i, j + 1) - и это это число, которое является «ведущим измерением», LDA, LDB, LDC в документации. И, таким образом, это просто число строк в матрице, как объявлено или выделено.

Теперь давайте посмотрим на цитируемую вами документацию

  LDB is INTEGER
   On entry, LDB specifies the first dimension of B as declared
   in the calling (sub) program. When  TRANSB = 'N' or 'n' then
   LDB must be at least  max( 1, k ), otherwise  LDB must be at
   least  max( 1, n ).

LDB - это количество строк в матрице B это объявлено. Таким образом,

  1. Если B НЕ транспонирован, каждый столбец B будет включен в точечное произведение со строкой или столбцом A, в зависимости от опции транспонирования для A. Математические измерения говорят, что точечное произведение к долго. Таким образом, число строк в B должно быть не менее k, чтобы математика была правильной, и поэтому LDB должен быть не менее k
  2. Если B IS транспонирован, то математическое первое измерение B также будет математическим вторым размерность матрицы результатов, и это задается N. Таким образом, LDB в этом случае должен быть не менее N

, так что это отвечает на большинство из них, и если вы всегда умножаете всю одну матрицу на объявленный всей второй матрицей, ведущее измерение будет просто самым первым измерением каждой матрицы, как объявлено. Но «по крайней мере» означает, что вы можете умножать биты массивов вместе. Подумайте о следующем, и, аккуратно разделив те целые числа, которые определяют математику, и те, которые определяют макет памяти, вы можете понять, почему он делает то, что делает? Обратите внимание, что (2, 2) в списке аргументов означает, что мы «запускаем» матрицу в этом элементе - теперь тщательно продумайте, что говорит вам ведущее измерение, и вы сможете разобраться, как это работает.

ian@eris:~/work/stack$ cat matmul2.f90
Program sirt

  Integer, Parameter :: wp = Kind( 1.0d0 )

  Real( wp ), Dimension( 1:5, 1:5 ) :: A
  Real( wp ), Dimension( 1:3, 1:3 ) :: B
  Real( wp ), Dimension( 1:4, 1:4 ) :: C

  Integer :: i

  A = 0.0_wp
  Do i = 1, 5
     A( i, i ) = Real( i, wp )
  End Do
  Write( *, * ) 'A = '
  Do i = 1, Size( A, Dim = 1 )
     Write( *, '( 100( f4.1, 1x ) )' ) A( i, : )
  End Do

  B = 0.0_wp
  B( 1, 1 ) = 1.0_wp
  B( 3, 2 ) = 1.0_wp
  B( 2, 3 ) = 1.0_wp
  Write( *, * ) 'B = '
  Do i = 1, Size( B, Dim = 1 )
     Write( *, '( 100( f4.1, 1x ) )' ) B( i, : )
  End Do

  ! Lazy - should really just initialise only the bits of C that are NOT touched
  ! by the dgemm
  C = 0.0_wp

  Call dgemm('N', 'N', 3, 3, 3, 1.0_wp, A( 2, 2 ), Size( A, Dim = 1 ), &
                                        B        , Size( B, Dim = 1 ), &
                                0.0_wp, C        , Size( C, Dim = 1 ) )

  Write( *, * ) 'C after dgemm'
  Write( *, * ) 'B = '
  Do i = 1, Size( C, Dim = 1 )
     Write( *, '( 100( f4.1, 1x ) )' ) C( i, : )
  End Do


End Program sirt
ian@eris:~/work/stack$ gfortran-8  -std=f2008 -fcheck=all -Wall -Wextra -pedantic -O matmul2.f90 -lblas
/usr/bin/ld: warning: libgfortran.so.4, needed by //usr/lib/x86_64-linux-gnu/libopenblas.so.0, may conflict with libgfortran.so.5
ian@eris:~/work/stack$ ./a.out
 A = 
 1.0  0.0  0.0  0.0  0.0
 0.0  2.0  0.0  0.0  0.0
 0.0  0.0  3.0  0.0  0.0
 0.0  0.0  0.0  4.0  0.0
 0.0  0.0  0.0  0.0  5.0
 B = 
 1.0  0.0  0.0
 0.0  0.0  1.0
 0.0  1.0  0.0
 C after dgemm
 B = 
 2.0  0.0  0.0  0.0
 0.0  0.0  3.0  0.0
 0.0  4.0  0.0  0.0
 0.0  0.0  0.0  0.0

Это использование для умножения части двух больших матриц на удивление распространено, особенно в алгоритмах блокированной линейной алгебры, и разделение математических и компоновочных измерений позволяет вам сделать это

2 голосов
/ 31 марта 2020

Представьте, что у вас есть большая матрица B , которая выглядит как

b b b b B B B
b b b b B B B
b b b b B B B
B B B B B B B
B B B B B B B
B B B B B B B

B - матрица 6x7. Однако в вашей программе вы не используете B в качестве матрицы, а просто как место хранения. Реальная матрица, которая вас интересует, это b , которая расположена в первых частях B . Теперь, поскольку вы сохранили его в этом двумерном массиве, представляющем B , здесь есть форма размещения памяти. Матрица b не является последовательной в памяти, поскольку матрица B является. В памяти B выглядит следующим образом:

b b b B B B b b b B B B b b b B B B b b b B B B B B B B B B B B B B B B B B B B B B 
  col 1    |  col 2    |  col 3    |  col 4    |  col 5    |  col 6    |  col 7

Если вы хотите передать матрицу b в LAPACK или BLAS, вам нужно проинформировать ее о расположении памяти , Это делается с использованием размеров N и M, но также с начальным размером LDA. Начальное измерение LDA - это общее количество строк в большей матрице, в которой оно хранится. Итак, здесь вы должны передать N=3, M=4 и LDA=6. При этом BLAS и LAPACK знают, какие части он должен пропускать из блока памяти.

...