Який найкращий спосіб визначити кількість ненулів у розмноженому матричному множенні?


17

Мені було цікаво, чи існує швидкий та ефективний метод заздалегідь знайти кількість ненульових значень для операції розмноження матричної матриці, якщо обидві матриці знаходяться у форматі CSC або CSR.

Я знаю, що в пакеті smmp є такий, але мені потрібно щось, що вже реалізовано в C або C ++.

Будь-яка допомога буде вдячна. Заздалегідь спасибі.


чи є у ваших матриць якась симетрія чи структура щодо розташування їх ненульових записів?
Годрік Провид

@GodricSeer ... ні, я просто говорю про загальні розріджені матриці. У Matlab є nnz (A), де A є розрідженим матричним методом, щоб дізнатися кількість не нулів. Мені було цікаво, чи існує такий метод.
Recker

Я особисто не можу придумати будь-який спосіб обчислити це число, яке було б меншим порядком, ніж просто робити фактичне множення матриці, не використовуючи деякої симетрії чи структури. Я припускаю, що ви хочете цього для розподілу пам'яті перед тим, як робити множення?
Годрік Провид

Крім того, я знайшов цей документ, в якому описано, як оцінити число на бульовому матричному добутку (що ідентично підрахунку елементів у будь-якому матричному творі).
Годрік Провид

@ GodricSeer .. Так, ти маєш рацію, мені потрібне точне число лише для розподілу пам’яті в результуючій матриці. Хоча за посилання на папір. Це може на деякий час почати мене в деякому напрямку.
Recker

Відповіді:


14

Ви можете просто імітувати матрично-матричний добуток, сформувавши добуток двох шаблонів розрідженості - тобто, ви розглядаєте шаблон sparsity (який зберігається в окремих масивах у форматі CSR) як матрицю, що містить або нуль, або один у кожен запис. Виконання цього модельованого продукту вимагає лише формування таробота над цими нулями та одиницями і, таким чином, набагато швидша, ніж фактичний матричний матричний продукт - насправді, все, що вам потрібно зробити, - це пройти через рядки та стовпці двох матриць і переконатися, що є щонайменше один запис у рядок і стовпець, який ви помножуєте з тим, де обидві матриці не нульові. Це дешева операція - набагато дешевша в будь-якому випадку, ніж насправді робити множення плаваючої точки у фактичному продукті, що не тільки вимагає від вас арифметики з плаваючою комою (дорого), але і зчитування з фактичних цифр з плаваючою комою з пам'яті ( навіть дорожче, але вам це не потрібно при множенні шаблону обмеженості, оскільки ненульові значення матриці зберігаються окремо в CSR).


6
Це називається символічним множенням. Це не обов’язково менш дорого, ніж числове множення, особливо паралельно, але це потрібно робити лише один раз за шаблоном зрідженості. Багато алгоритмів виконують операцію кілька разів з різними числовими значеннями, але однаковим шаблоном розрідження, і в цьому випадку символьне множення можна повторно використовувати.
Джед Браун

Це гарна ідея, але враховуючи мільйони транзисторів, які роблять плаваючий * плаваючи паралельно, ми говоримо лише про економію швидкості на 50% або тут.
Євгеній Сергєєв

1
@EvgeniSergeev - справа не в економії обчислень, а в економії на передачі пам'яті. Оскільки ви сьогодні витрачаєте 80% або більше часу на передачу пам’яті для розрідженого множення матриць, ви, ймовірно, значно виграєте, якщо вам не доведеться читати / записувати дані з плаваючою комою з / в пам’ять.
Вольфганг Бангерт

Ви б чітко заявили про складність свого методу. Якщо є m по k , мені здається, що ваш метод вимагає O ( m k )CmkO(mk) , правильно?
Карл Крістіан

@CarlChristian - Мені доведеться розробити деталі, але це, звичайно, не може бути . Для цього потрібно включати кількість ненульових значень у рядку. Якщо у вас в середньому є p нулі в кожному рядку, а для простоти, якщо у вас m = k , то я думаю, ви повинні мати можливість реалізувати метод у чомусь на зразок O ( m p log p ) або подібному. Це набагато краще, ніж O (O(mk)pm=kO(mplogp) . O(m2)
Вольфганг Бангерт

13

Я фактично написав оригінальний код у Matlab для A * B, як A, так і B розріджених. Попередньо виділення місця для результату справді було цікавою частиною. Ми спостерігали, що вказує Годрік, - що знати кількість ненулів в АВ так само дорого, як і обчислення АВ.

Ми здійснили первинну реалізацію розрідженого Matlab близько 1990 року, перш ніж документ Едіт Коен дав перший практичний, швидкий спосіб точно оцінити розмір AB. Ми зібрали оцінку неповноцінного розміру, і якщо нам не вистачало місця в середині обчислення, подвоїли розподіл і скопіювали частково обчислений результат.

Я не знаю, що зараз у Matlab.

Іншою можливістю було б обчислити AB один стовпчик одночасно. Кожен стовпець може тимчасово зберігатися в розрідженому акумуляторі (для пояснення цього див. Розріджений папір Matlab) та простір, призначений для розміщення точно відомого розміру стовпця результатів. Результатом буде розсіяна стисла розріджена форма стовпців - кожен стовпець у CSC, але без суміжності між стовпцями - з використанням 2-х векторів нуклеолів довжини (стовпчик стовпців, довжина стовпчика), а не один, як метаданих. Його форма зберігання, яку, можливо, варто переглянути; це має ще одну силу - ви можете виростити стовпчик, не перерозподіляючи всю матрицю.


Щодо моєї реалізації GPU, я в кінцевому підсумку знайшов спочатку нульову структуру, а потім знайшов фактичну матрицю. Діяльність була жахливою, як очікувалося. Я думаю, що вони використовують метод, описаний у цій книзі, для ефективного множення двох розріджених матриць на MATLAB.
Recker

2
Дуже здорово, дякую за історичну перспективу, і ласкаво просимо до scicomp :)
Арон Ахмадія

4

У цій роботі описаний алгоритм для наближення розміру результуючого до матричного добутку двох розріджених матриць.

Проблема з знаходженням точної кількості ненульових записів у розрідженому множенні матриці полягає в тому, що кожен елемент у результаті залежить від взаємодії двох векторів, обидва з яких, ймовірно, містять принаймні кілька ненульових елементів. Тому для обчислення числа потрібно оцінити логічні операції на парі векторів для кожного елемента в результуючому. Проблема в цьому полягає в тому, що для розрахунку самого матричного продукту йому потрібен ряд операцій, аналогічних кількості операцій. У своєму коментарі я згадав про можливість використання певних структур у ненульових елементах оригінальних матриць, однак ці самі подвиги можуть бути використані і для зменшення роботи, виконаної при множенні матриці.

Вам, ймовірно, буде краще використовувати вищевказаний папір для завищеної оцінки потреб у пам’яті, виконання множення та обрізання виділеної пам’яті або переміщення отриманої матриці в масив більш відповідного розміру. Крім того, продукти з рідкою матрицею не є рідкісним явищем, і я майже гарантую, що ця проблема була вирішена раніше. Трохи заглибившись у деякі відкриті джерела, розріджені бібліотеки матриць повинні привести вас до алгоритмів, які вони використовують для попереднього розміщення пам'яті.


0

Що стосується CSR або CSC, чи гарантовано ви, що у вашому масиві елементів матриці вже немає нулів? У цьому випадку легко зрозуміти, скільки існує ненульових елементів, використовуючи щось подібне до:

int nnz = sizeof(My_Array)/sizeof(long int);

Однак якщо це не так (здається трохи надто простим), ви можете спробувати зменшення . Якщо ваш масив елементів матриці дуже великий, це може бути найефективнішим способом обчислення кількості ненульових елементів. Багато паралельних бібліотек C / C ++, таких як Thrust (бібліотека CUDA) або OpenCL (для використання яких вам не потрібен GPU), мають підтримку умовних скорочень - для кожного елемента додайте результат Condition(Element). Якщо ви встановите умову, Element != 0ви додасте кількість ненульових елементів. Ви також можете видалити нульові елементи з масиву елементів, масив індексів рядків / стовпців та відкоригувати покажчики на колонку / рядок.


дякую за вашу відповідь ... але я мав на увазі не нулі в A * B, де A і B - розріджені матриці. Мені потрібна кількість не нулів заздалегідь, щоб я міг виділити точний об'єм пам'яті для зберігання отриманої матриці.
Recker

0

Найпростіший спосіб реалізації CSR - це спробувати

std::vector< std::map<int, complex<float>> > 

представляти вашу матрицю. У такому випадку ви не будете дуже турбуватися про кількість ненульових елементів, до яких можна отримати доступ

std::map< int, complex<float> >::iterator

на кожному ряду. Кращий ..


2
STL, тому що коли ви думали, що ваші розріджені матричні процедури не можна робити повільніше.
Джед Браун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.