¿Cuál es la mejor manera de determinar el número de no ceros en la multiplicación de matrices dispersas?

17

Me preguntaba si existe un método rápido y eficiente para encontrar el número de no ceros de antemano por multiplicación de matrices escasa asumiendo ambas matrices están en formato CSC o RSE.

Sé que hay uno en el paquete smmp, pero necesito algo que ya esté implementado en C o C ++.

Cualquier ayuda será apreciada. Gracias por adelantado.

Recker
fuente
¿Sus matrices tienen alguna simetría o una estructura para la ubicación de sus entradas distintas de cero?
Godric vidente
@GodricSeer ... no estoy hablando sólo de matrices.Matlab escasa en general tiene nnz (A), donde A es el método de matrices dispersas para averiguar el número de zeros.I no se preguntaba si existe algún método de este tipo.
Recker
Yo personalmente no se me ocurre ninguna manera de calcular que el número que sería de orden inferior que limitarse a hacer la multiplicación de matrices reales sin explotar a cierta simetría o estructura. ¿Asumo que quieres esto para la asignación de memoria antes de hacer la multiplicación?
Godric vidente
Además, encontré este documento que describe cómo estimar el número en un producto de matriz booleana (que es idéntico a contar los elementos en cualquier producto de matriz).
Godric vidente
@ GodricSeer..Yes que tiene razón necesito el número exacto sólo para la asignación de memoria de matrix.Thanks resultantes para el enlace a papel though.That podría ayudarme a empezar en alguna dirección por un tiempo.
Recker

Respuestas:

14

Puede simular el producto matriz-matriz formando el producto de los dos patrones de dispersión, es decir, considera el patrón de dispersión (que se almacena en matrices separadas en formato CSR) como una matriz que contiene un cero o uno en cada entrada La ejecución de este producto simulado sólo requiere para formar el yopera en estos ceros y unos y, por lo tanto, es mucho más rápido que el producto matriz-matriz real; de hecho, todo lo que tiene que hacer es recorrer las filas y columnas de las dos matrices y verificar que haya al menos una entrada en un fila y la columna con la que multiplica con ambas matrices que no son cero. Esta es una operación barata, mucho más barata en cualquier caso que tener que hacer la multiplicación de coma flotante en el producto real que no solo requiere que hagas aritmética de coma flotante (costosa) sino que también leas los números de coma flotante reales de la memoria ( aún más caro, pero no es necesario que al multiplicar el patrón de escasez debido a que los que no son cero los valores de la matriz se almacenan por separado en RSE).

Wolfgang Bangerth
fuente
66
Esto se conoce como la multiplicación simbólica. No es necesariamente menos costoso que la multiplicación numérica, especialmente en paralelo, pero solo debe hacerse una vez por patrón de dispersión. Muchos algoritmos realizarán la operación varias veces con diferentes valores numéricos pero con el mismo patrón de dispersión, en cuyo caso se puede reutilizar la multiplicación simbólica.
Jed Brown
Es una buena idea, pero teniendo en cuenta los millones de transistores que están haciendo el flotador * flotante en paralelo, sólo estamos hablando de una velocidad de ahorro del 50% o menos aquí.
Evgeni Sergeev
1
@EvgeniSergeev - el punto no es el ahorro en los cálculos, pero el ahorro en la transferencia de la memoria. Dado que usted pasa el 80% o más tiempo hoy para la transferencia de la memoria para una multiplicación de matrices dispersas, es probable que el aumento de forma significativa si no tiene que leer / escribir datos de punto flotante de / a la memoria.
Wolfgang Bangerth
Expondría la complejidad de su método de forma explícita. Si es m por k, me parece que su método requiere trabajo O ( m k ) , ¿correcto? CmetrokO(metrok)
Carl Christian
@CarlChristian - Tendría que trabajar en los detalles, pero seguramente no puede ser . Se tiene que implicar el número de nonzeros por fila. Si usted tiene, en promedio, p nonzeros en cada fila, y por simplicidad si tiene m = kO(metrok)pagmetro=k , entonces me imagino que debe ser capaz de aplicar el método en algo así como o similar. Eso es mucho mejor que O ( m 2 ) . O(metropagIniciar sesiónpag)O(metro2)
Wolfgang Bangerth
13

De hecho, me escribió el código original en Matlab para A * B, A y B escasa. Pre-asignación de espacio para el resultado fue de hecho la parte interesante. Observamos lo que apunta a cabo Godric - que conocer el número de nonzeros en AB es tan costoso como el cálculo de AB.

Hicimos la implementación inicial de la escasa Matlab alrededor de 1990, antes del artículo de Edith Cohen que brindaba la primera forma práctica y rápida de estimar con precisión el tamaño de AB. Armamos un estimador de tamaño inferior, y si nos quedamos sin espacio en la mitad del cálculo, duplicamos la asignación y copiamos el resultado parcialmente calculado.

No sé qué hay en Matlab ahora.

Otra posibilidad sería la de calcular la columna uno AB a la vez. Cada columna puede almacenarse temporalmente en un acumulador de escasa (ver el artículo Matlab escaso para una explicación de estos), y el espacio asignado para mantener el tamaño exactamente conocida de la columna de resultados. El resultado sería en forma de columna dispersa comprimido dispersado - cada columna de CSC pero no contigüidad intercolumnas - utilizando 2 vectores de númeroColumnas longitud (Inicio col, col longitud), en lugar de uno, como meta-datos. Su forma un dispositivo de almacenamiento que puede ser digno de una mirada; tiene otra fortaleza: puede hacer crecer una columna sin reasignar toda la matriz.

Rob Schreiber
fuente
Bueno, para mi implementación de GPU, terminé encontrando primero la estructura distinta de cero y luego la matriz real. El rendimiento fue horrible como se esperaba. Creo que usan el método descrito en este libro para multiplicar eficientemente las dos matrices dispersas en MATLAB.
Recker
2
Realmente fresco, gracias por la perspectiva histórica, y bienvenidos a SciComp :)
Aron Ahmadia
4

Este artículo describe un algoritmo para aproximar el tamaño de una resultante de la matriz producto de dos matrices dispersas.

El problema con la búsqueda de un número exacto de los no-cero entradas en una multiplicación matriz dispersa es que cada elemento de la resultante depende de la interacción de dos vectores, ambos de los cuales son susceptibles de contener al menos un par de elementos no nulos. Por lo tanto, para calcular el número, debe evaluar las operaciones lógicas en un par de vectores para cada elemento resultante. El problema con esto es que requiere una cantidad de operaciones similares a la cantidad de operaciones necesarias para calcular el producto matriz en sí. En mis comentarios he mencionado la posibilidad de explotar ciertas estructuras en las que no son cero los elementos de las matrices originales, sin embargo esos mismos exploits podrían ser utilizados para reducir el trabajo realizado en la multiplicación de la matriz también.

Es probable sería mejor utilizar el papel por encima de a la sobre-estimar los requisitos de memoria, hacer la multiplicación y luego truncar la memoria asignada, o mover la matriz resultante a una matriz de más de tamaño apropiado. Además, los productos de matriz dispersa no son una ocurrencia rara, y casi garantizaría que este problema se haya resuelto antes. Un poco de investigación en algunas bibliotecas de matriz abierta y de código abierto debería llevarlo a los algoritmos que usan para preasignar memoria.

Vidente de Godric
fuente
0

Para que la RSE o CSC, está garantizado que su gama de elementos de la matriz ya no tiene ceros? En ese caso, es fácil de averiguar cuántos elementos no nulos existen, usando algo similar a:

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

Sin embargo, si este no es el caso (parece un poco demasiado fácil) lo que podría intentar es una reducción . Si su matriz de elementos de matriz es muy grande, esta puede ser la forma más eficiente de calcular la cantidad de elementos distintos de cero. Muchos / C ++ bibliotecas paralelo C, tales como empuje (una biblioteca CUDA) o OpenCL (que no se necesita una GPU de uso) tienen soporte para reducciones condicionales - para cada elemento, añadir el resultado de Condition(Element). Si se establece la condición de que Element != 0a continuación vamos a añadir el número de elementos no nulos. También es posible que desee eliminar los elementos de valor cero de su matriz de elementos, matriz de índices de fila / columna y ajustar sus punteros de columna / fila.

limas
fuente
gracias por su respuesta ... pero me refiero a los no ceros en A * B, donde A y B son matrices dispersas. Necesito el número de no ceros de antemano de modo que pueda asignar la cantidad exacta de memoria para almacenar la matriz resultante.
Recker
0

La forma más sencilla de implementos RSE es tratar

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

para representar a su matriz. En ese caso, realmente no se preocupará por el número de elementos distintos de cero, se accede a todos a través de

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

en cada fila. Mejor ..


fuente
2
STL, para cuando se pensaba sus rutinas de matrices dispersas no se podía hacer más lento.
Jed Brown