Articulo de referencia

Algoritmo de Strassen

En álgebra lineal , el algoritmo de Strassen , que lleva el nombre de Volker Strassen , es un algoritmo para la multiplicación de matrices . Es más rápido que el algoritmo están...

En álgebra lineal , el algoritmo de Strassen , que lleva el nombre de Volker Strassen , es un algoritmo para la multiplicación de matrices . Es más rápido que el algoritmo estándar de multiplicación de matrices para matrices grandes, con una mejor complejidad asintótica (O(norteregistro27){\displaystyle O(n^{\log _{2}7})}versusO(norte3){\displaystyle O(n^{3})}), aunque el algoritmo ingenuo suele ser mejor para matrices pequeñas. El algoritmo de Strassen es más lento que los algoritmos más rápidos conocidos para matrices extremadamente grandes, pero estos algoritmos galácticos no son útiles en la práctica, ya que son mucho más lentos para matrices de tamaño práctico. Para matrices pequeñas existen algoritmos aún más rápidos.

El algoritmo de Strassen funciona para cualquier anillo , como plus/multiply, pero no para todos los semianillos , como min-plus o el álgebra booleana , donde el algoritmo ingenuo todavía funciona, y la llamada multiplicación combinatoria de matrices .

Historia

Volker Strassen publicó por primera vez este algoritmo en 1969 y, por lo tanto, demostró que elnorte3{\displaystyle n^{3}}El algoritmo general de multiplicación de matrices no era óptimo. [ 1 ] La publicación del algoritmo de Strassen dio lugar a más investigaciones sobre la multiplicación de matrices que condujeron tanto a cotas inferiores asintóticas como a cotas superiores computacionales mejoradas.

Algoritmo

La columna izquierda muestra los cálculos necesarios para determinar el resultado de una multiplicación de matrices de 2x2 . La multiplicación simple de matrices requiere una multiplicación por cada "1" de la columna izquierda. Cada una de las demás columnas (M1-M7) representa una de las siete multiplicaciones del algoritmo de Strassen. La suma de las columnas M1-M7 da el mismo resultado que la multiplicación completa de matrices de la izquierda.

DejarA{\displaystyle A},B{\displaystyle B}sean dos matrices cuadradas sobre un anilloR{\displaystyle {\mathcal {R}}}Por ejemplo, matrices cuyas entradas son números enteros o números reales. El objetivo de la multiplicación de matrices es calcular el producto de la matriz.do=AB{\displaystyle C=AB}. La siguiente exposición del algoritmo supone que todas estas matrices tienen tamaños que son potencias de dos (es decir,A,B,doMatr2norte×2norte(R){\displaystyle A,\,B,\,C\in \operatorname {Matr} _{2^{n}\times 2^{n}}({\mathcal {R}})}), pero esto solo es conceptualmente necesario, si las matricesA{\displaystyle A},B{\displaystyle B}no son de tipo2norte×2norte{\displaystyle 2^{n}\times 2^{n}}Las filas y columnas "faltantes" se pueden rellenar con ceros para obtener matrices con tamaños que son potencias de dos, aunque las implementaciones reales del algoritmo no hacen esto en la práctica.

Las particiones del algoritmo de StrassenA{\displaystyle A},B{\displaystyle B}ydo{\displaystyle C}en matrices de bloques de igual tamaño

A=[A11A12A21A22],B=[B11B12B21B22],do=[do11do12do21do22],{\displaystyle A={\begin{bmatrix}A_{11}&A_{12}\\A_{21}&A_{22}\end{bmatrix}},\quad B={\begin{bmatrix}B_{11}&B_{12}\\B_{21}&B_{22}\end{bmatrix}},\quad C={\begin{bmatrix}C_{11}&C_{12}\\C_{21}&C_{22}\end{bmatrix}},\quad }

conAij,Bij,doijEstera2norte1×2norte1(R){\displaystyle A_{ij},B_{ij},C_{ij}\in \operatorname {Mat} _{2^{n-1}\times 2^{n-1}}({\mathcal {R}})}El algoritmo ingenuo sería:

[do11do12do21do22]=[A11×B11+A12×B21A11×B12+A12×B22A21×B11+A22×B21A21×B12+A22×B22].{\displaystyle {\begin{bmatrix}C_{11}&C_{12}\\C_{21}&C_{22}\end{bmatrix}}={\begin{bmatrix}A_{11}{\color {red}\times }B_{11}+A_{12}{\color {red}\times }B_{21}\quad &A_{11}{\color {red}\times }B_{12}+A_{12}{\color {red}\times }B_{22}\\A_{21}{\color {red}\times }B_{11}+A_{22}{\color {red}\times }B_{21}\quad &A_{21}{\color {red}\times }B_{12}+A_{22}{\color {red}\times }B_{22}\end{bmatrix}}.}

Esta construcción no reduce el número de multiplicaciones: todavía se necesitan 8 multiplicaciones de bloques de matriz para calcular eldoij{\displaystyle C_{ij}}matrices, el mismo número de multiplicaciones necesarias que cuando se utiliza la multiplicación de matrices estándar.

El algoritmo de Strassen define nuevos valores:

METRO1=(A11+A22)×(B11+B22);METRO2=(A21+A22)×B11;METRO3=A11×(B12B22);METRO4=A22×(B21B11);METRO5=(A11+A12)×B22;METRO6=(A21A11)×(B11+B12);METRO7=(A12A22)×(B21+B22),{\displaystyle {\begin{aligned}M_{1}&=(A_{11}+A_{22}){\color {red}\times }(B_{11}+B_{22});\\M_{2}&=(A_{21}+A_{22}){\color {red}\times }B_{11};\\M_{3}&=A_{11}{\color {red}\times }(B_{12}-B_{22});\\M_{4}&=A_{22}{\color {red}\times }(B_{21}-B_{11});\\M_{5}&=(A_{11}+A_{12}){\color {red}\times }B_{22};\\M_{6}&=(A_{21}-A_{11}){\color {red}\times }(B_{11}+B_{12});\\M_{7}&=(A_{12}-A_{22}){\color {red}\times }(B_{21}+B_{22}),\\\end{aligned}}}

utilizando solo 7 multiplicaciones (una por cadaMETROk{\displaystyle M_{k}}) en lugar de 8. Ahora podemos expresar eldoij{\displaystyle C_{ij}}en términos deMETROk{\displaystyle M_{k}}:

[do11do12do21do22]=[METRO1+METRO4METRO5+METRO7METRO3+METRO5METRO2+METRO4METRO1METRO2+METRO3+METRO6].{\displaystyle {\begin{bmatrix}C_{11}&C_{12}\\C_{21}&C_{22}\end{bmatrix}}={\begin{bmatrix}M_{1}+M_{4}-M_{5}+M_{7}\quad &M_{3}+M_{5}\\M_{2}+M_{4}\quad &M_{1}-M_{2}+M_{3}+M_{6}\end{bmatrix}}.}

Iteramos recursivamente este proceso de división hasta que las submatrices degeneran en números (elementos del anillo).R{\displaystyle {\mathcal {R}}}). Si, como se mencionó anteriormente, la matriz original tenía un tamaño que no era una potencia de 2, entonces el producto resultante tendrá filas y columnas cero, al igual queA{\displaystyle A}yB{\displaystyle B}y estos serán eliminados en este punto para obtener la matriz (más pequeña)do{\displaystyle C}Realmente lo queríamos.

Las implementaciones prácticas del algoritmo de Strassen cambian a métodos estándar de multiplicación de matrices para submatrices suficientemente pequeñas, para las cuales esos algoritmos son más eficientes. El punto de cruce particular para el cual el algoritmo de Strassen es más eficiente depende de la implementación y el hardware específicos. Autores anteriores habían estimado que el algoritmo de Strassen es más rápido para matrices con anchos de 32 a 128 para implementaciones optimizadas. [ 2 ] Sin embargo, se ha observado que este punto de cruce ha ido aumentando en los últimos años, y un estudio de 2010 encontró que incluso un solo paso del algoritmo de Strassen a menudo no es beneficioso en las arquitecturas actuales, en comparación con una multiplicación tradicional altamente optimizada, hasta que los tamaños de matriz superan los 1000 o más, e incluso para tamaños de matriz de varios miles el beneficio suele ser marginal en el mejor de los casos (alrededor del 10% o menos). [ 3 ] Un estudio más reciente (2016) observó beneficios para matrices tan pequeñas como 512 y un beneficio de alrededor del 20%. [ 4 ]

Mejoras en el algoritmo de Strassen

Es posible reducir el número de sumas de matrices utilizando en su lugar la siguiente forma descubierta por Winograd en 1971: [ 5 ]

[abdod][AdoBD]=[t+b×Bw+v+(a+bdod)×Dw++d×(B+doAD)w++v]{\displaystyle {\begin{bmatrix}a&b\\c&d\end{bmatrix}}{\begin{bmatrix}A&C\\B&D\end{bmatrix}}={\begin{bmatrix}t+b{\color {red}\times }B&w+v+(a+bcd){\color {red}\times }D\\w+u+d{\color {red}\times }(B+CAD)&w+u+v\end{bmatrix}}}

dóndet=a×A,=(doa)×(doD),v=(do+d)×(doA),w=t+(do+da)×(A+Ddo){\displaystyle t=a{\color {red}\times }A,\;u=(ca){\color {red}\times }(CD),\;v=(c+d){\color {red}\times }(CA),\;w=t+(c+da){\color {red}\times }(A+DC)}.

Esto reduce el número de sumas y restas de matrices de 18 a 15. El número de multiplicaciones de matrices sigue siendo 7, y la complejidad asintótica es la misma. [ 6 ]

El algoritmo se optimizó aún más en 2017 utilizando una base alternativa, [ 7 ] reduciendo el número de sumas de matrices por paso bilineal a 12 mientras se mantiene el número de multiplicaciones de matrices, y de nuevo en 2023: [ 8 ]

A22=A12A21+A22;B22=B12B21+B22,{\displaystyle {\begin{aligned}A_{22}&=A_{12}-A_{21}+A_{22};\\B_{22}&=B_{12}-B_{21}+B_{22},\end{aligned}}}

do12=do12do22;do21=do22do21,{\displaystyle {\begin{aligned}C_{12}&=C_{12}-C_{22};\\C_{21}&=C_{22}-C_{21},\end{aligned}}}

Complejidad asintótica

El esquema del algoritmo anterior mostró que se puede prescindir de las 7 multiplicaciones de matrices tradicionales para los subbloques de la matriz, en lugar de las 8. Por otro lado, es necesario realizar sumas y restas de bloques, aunque esto no afecta la complejidad general: Sumar matrices de tamañonorte/2{\displaystyle N/2}requiere solo(norte/2)2{\displaystyle (N/2)^{2}}operaciones mientras que la multiplicación es sustancialmente más costosa (tradicionalmente2(norte/2)3{\displaystyle 2(N/2)^{3}}operaciones de suma o multiplicación).

La pregunta entonces es cuántas operaciones se necesitan exactamente para los algoritmos de Strassen, y cómo se compara esto con la multiplicación de matrices estándar que toma aproximadamente2norte3{\displaystyle 2N^{3}}(dóndenorte=2norte{\displaystyle N=2^{n}}operaciones aritméticas, es decir, una complejidad asintóticaΘ(norte3){\displaystyle \Theta (N^{3})}.

El número de sumas y multiplicaciones requeridas en el algoritmo de Strassen se puede calcular de la siguiente manera: seaF(norte){\displaystyle f(n)}sea ​​el número de operaciones para una2norte×2norte{\displaystyle 2^{n}\times 2^{n}}matriz. Luego, mediante la aplicación recursiva del algoritmo de Strassen, vemos queF(norte)=7F(norte1)+l4norte{\displaystyle f(n)=7f(n-1)+l4^{n}}, por alguna constantel{\displaystyle l}Eso depende del número de sumas realizadas en cada aplicación del algoritmo. Por lo tantoF(norte)=(7+o(1))norte{\displaystyle f(n)=(7+o(1))^{n}}, es decir, la complejidad asintótica para multiplicar matrices de tamañonorte=2norte{\displaystyle N=2^{n}}usar el algoritmo de Strassen es O([7+o(1)]norte)=O(norteregistro27+o(1))O(norte2.8074){\displaystyle O([7+o(1)]^{n})=O(N^{\log _{2}7+o(1)})\approx O(N^{2.8074})}Sin embargo, la reducción en el número de operaciones aritméticas conlleva una menor estabilidad numérica [ 9 ] , y el algoritmo también requiere mucha más memoria que el algoritmo ingenuo. Ambas matrices iniciales deben tener sus dimensiones expandidas a la siguiente potencia de 2, lo que implica almacenar hasta cuatro veces más elementos, y las siete matrices auxiliares contienen cada una una cuarta parte de los elementos de las matrices expandidas.

El algoritmo de Strassen debe compararse con la forma "ingenua" de realizar la multiplicación de matrices, que requeriría 8 multiplicaciones de subbloques en lugar de 7. Esto daría lugar a la complejidad que se espera del enfoque estándar:O(8norte)=O(norteregistro28)=O(norte3){\displaystyle O(8^{n})=O(N^{\log _{2}8})=O(N^{3})}La comparación de estos dos algoritmos muestra que asintóticamente , el algoritmo de Strassen es más rápido: existe un tamañonortelímite{\displaystyle N_{\text{threshold}}}De modo que las matrices más grandes se multiplican de forma más eficiente con el algoritmo de Strassen que con el método "tradicional". Sin embargo, la afirmación asintótica no implica que el algoritmo de Strassen sea siempre más rápido, incluso para matrices pequeñas, y en la práctica esto no es así: para matrices pequeñas, el coste de las sumas adicionales de bloques de matriz supera el ahorro en el número de multiplicaciones. También existen otros factores no contemplados en el análisis anterior, como la diferencia de coste en el hardware actual entre cargar datos de la memoria a los procesadores y el coste de realizar operaciones con estos datos. Como consecuencia de estas consideraciones, el algoritmo de Strassen se suele utilizar solo en matrices "grandes". Este efecto es aún más pronunciado con algoritmos alternativos como el de Coppersmith y Winograd : aunque asintóticamente es incluso más rápido, el punto de inflexiónnortelímite{\displaystyle N_{\text{threshold}}}es tan grande que el algoritmo no se suele utilizar en matrices que se encuentran en la práctica.

Complejidad de rango o bilineal

La complejidad bilineal o rango de una aplicación bilineal es un concepto importante en la complejidad asintótica de la multiplicación de matrices. El rango de una aplicación bilinealϕ:A×Bdo{\displaystyle \phi :\mathbf {A} \times \mathbf {B} \rightarrow \mathbf {C} } sobre un campo F se define como (en cierto modo un abuso de notación )

R(ϕ/F)=min{r|FiA,gramoiB,wido,aA,bB,ϕ(a,b)=i=1rFi(a)gramoi(b)wi}{\displaystyle R(\phi /\mathbf {F} )=\min \left\{r\left|\exists f_{i}\in \mathbf {A} ^{*},g_{i}\in \mathbf {B} ^{*},w_{i}\in \mathbf {C} ,\forall \mathbf {a} \in \mathbf {A} ,\mathbf {b} \in \mathbf {B} ,\phi (\mathbf {a} ,\mathbf {b} )=\sum _{i=1}^{r}f_{i}(\mathbf {a} )g_{i}(\mathbf {b} )w_{i}\right.\right\}}

En otras palabras, el rango de una aplicación bilineal es la longitud de su cálculo bilineal más corto. [ 10 ] La existencia del algoritmo de Strassen muestra que el rango de2×2{\displaystyle 2\times 2}La multiplicación de matrices no supera los siete términos. Para comprobarlo, expresemos este algoritmo (junto con el algoritmo estándar) como un cálculo bilineal. En el caso de las matrices, los espacios duales A * y B * consisten en aplicaciones al cuerpo F inducidas por un producto escalar doble (es decir, en este caso, la suma de todas las entradas de un producto de Hadamard ).

Se puede demostrar que el número total de multiplicaciones elementalesL{\displaystyle L}El valor requerido para la multiplicación de matrices está fuertemente ligado asintóticamente al rango.R{\displaystyle R}, es decirL=Θ(R){\displaystyle L=\Theta (R)}, o más específicamente, puesto que las constantes son conocidas,R/2LR{\displaystyle R/2\leq L\leq R}. Una propiedad útil del rango es que es submultiplicativo para productos tensoriales , y esto permite demostrar que2norte×2norte×2norte{\displaystyle 2^{n}\times 2^{n}\times 2^{n}}La multiplicación de matrices se puede realizar con no más de7norte{\displaystyle 7^{n}}multiplicaciones elementales para cualquiernorte{\displaystyle n}. (Estenorte{\displaystyle n}-producto tensorial de pliegue del2×2×2{\displaystyle 2\times 2\times 2}Mapa de multiplicación de matrices consigo mismo : unnorte{\displaystyle n}La potencia tensorial -ésima se obtiene mediante el paso recursivo del algoritmo mostrado.

Comportamiento de la caché

El algoritmo de Strassen es ajeno a la caché . El análisis de su comportamiento en la caché ha demostrado que incurre en

Θ(1+norte2b+norteregistro27bMETRO){\displaystyle \Theta \left(1+{\frac {n^{2}}{b}}+{\frac {n^{\log _{2}7}}{b{\sqrt {M}}}}\right)}

fallos de caché durante su ejecución, suponiendo una caché idealizada de tamañoMETRO{\displaystyle M}(es decir conMETRO/b{\displaystyle M/b}líneas de longitudb{\displaystyle b}). [ 11 ] : 13

Consideraciones para la implementación

La descripción anterior indica que las matrices son cuadradas, su tamaño es una potencia de dos y que se debe usar relleno si es necesario. Esta restricción permite dividir las matrices por la mitad, recursivamente, hasta alcanzar el límite de la multiplicación escalar . La restricción simplifica la explicación y el análisis de la complejidad, pero en realidad no es necesaria; [ 12 ] y, de hecho, rellenar la matriz como se describe aumentará el tiempo de cálculo y puede anular fácilmente el ahorro de tiempo relativamente pequeño obtenido al usar el método inicialmente.

Una buena implementación observará lo siguiente:

  • No es necesario ni deseable utilizar el algoritmo de Strassen hasta el límite de escalares. En comparación con la multiplicación de matrices convencional, el algoritmo añade un coste considerable.O(norte2){\displaystyle O(n^{2})}carga de trabajo en sumas/restas; por lo tanto, por debajo de cierto tamaño, será mejor usar la multiplicación convencional. Así, por ejemplo, una1600×1600{\displaystyle 1600\times 1600}no necesita ser acolchado para2048×2048{\displaystyle 2048\times 2048}, ya que podría subdividirse hasta25×25{\displaystyle 25\times 25}A ese nivel se pueden utilizar matrices y la multiplicación convencional.
  • El método se puede aplicar a matrices cuadradas de cualquier dimensión. [ 3 ] Si la dimensión es par, se dividen por la mitad como se describe. Si la dimensión es impar, primero se aplica un relleno de ceros de una fila y una columna. Este relleno se puede aplicar sobre la marcha y de forma diferida, y las filas y columnas adicionales se descartan a medida que se forma el resultado. Por ejemplo, supongamos que las matrices son199×199{\displaystyle 199\times 199}. Se pueden dividir de manera que la parte superior izquierda quede100×100{\displaystyle 100\times 100}y la parte inferior derecha es99×99{\displaystyle 99\times 99}. Dondequiera que las operaciones lo requieran, dimensiones de99{\displaystyle 99}se rellenan con ceros para100{\displaystyle 100}Primero. Nótese, por ejemplo, que el productoMETRO2{\displaystyle M_{2}}se utiliza únicamente en la fila inferior de la salida, por lo que solo se requiere que sea99{\displaystyle 99}filas de altura; y por lo tanto el factor izquierdoA21+A22{\displaystyle A_{21}+A_{22}}utilizado para generarlo solo necesita ser99{\displaystyle 99}filas de altura; por consiguiente, no hay necesidad de rellenar esa suma para100{\displaystyle 100}filas; solo es necesario rellenarA22{\displaystyle A_{22}}a100{\displaystyle 100}columnas para coincidirA21{\displaystyle A_{21}}.

Además, no es necesario que las matrices sean cuadradas. Las matrices no cuadradas se pueden dividir por la mitad utilizando los mismos métodos, obteniendo matrices no cuadradas más pequeñas. Si las matrices son suficientemente no cuadradas, valdrá la pena reducir la operación inicial a más productos cuadrados, utilizando métodos sencillos que son esencialmente O(norte2){\displaystyle O(n^{2})}, por ejemplo:

  • Un producto de tamaño[2norte×norte][norte×10norte]{\displaystyle [2N\times N]\ast [N\times 10N]}se puede hacer como 20 separados[norte×norte][norte×norte]{\displaystyle [N\times N]\ast [N\times N]}operaciones, dispuestas para formar el resultado;
  • Un producto de tamaño[norte×10norte][10norte×norte]{\displaystyle [N\times 10N]\ast [10N\times N]}se puede hacer como 10 por separado[norte×norte][norte×norte]{\displaystyle [N\times N]\ast [N\times N]}operaciones, sumadas para formar el resultado.

Estas técnicas harán que la implementación sea más complicada, en comparación con simplemente rellenar hasta un cuadrado que sea potencia de dos; sin embargo, es razonable suponer que cualquiera que emprenda una implementación de Strassen, en lugar de la multiplicación convencional, dará mayor prioridad a la eficiencia computacional que a la simplicidad de la implementación.

En la práctica, el algoritmo de Strassen se puede implementar para lograr un mejor rendimiento que la multiplicación convencional incluso para matrices tan pequeñas como500×500{\displaystyle 500\times 500}, para matrices que no son cuadradas en absoluto, y sin requerir espacio de trabajo más allá de los búferes que ya se necesitan para una multiplicación convencional de alto rendimiento. [ 4 ]

Véase también

Referencias

  1. Strassen, Volker (1969). "La eliminación gaussiana no es óptima". Numer. Math . 13 (4): 354– 356. doi : 10.1007/BF02165411 . S2CID 121656251 . 
  2. Skiena, Steven S. (1998), "§8.2.3 Multiplicación de matrices", The Algorithm Design Manual , Berlín, Nueva York: Springer-Verlag , ISBN 978-0-387-94860-7.
  3. 1 2 D'Alberto, Paolo; Nicolau, Alexandru (2005). Uso de la recursión para mejorar el rendimiento de ATLAS (PDF) . Sexto Simposio Internacional sobre Computación de Alto Rendimiento.
  4. 1 2 Huang, Jianyu; Smith, Tyler M.; Henry, Greg M.; van de Geijn, Robert A. (13 de noviembre de 2016). El algoritmo de Strassen recargado . SC16: Conferencia internacional sobre computación de alto rendimiento, redes, almacenamiento y análisis . IEEE Press. págs. 690–701 . doi : 10.1109/SC.2016.58 . ISBN  9781467388153Consultado el 1 de noviembre de 2022 .
  5. Winograd, S. (octubre de 1971). "Sobre la multiplicación de matrices de 2 × 2" . Álgebra lineal y sus aplicaciones . 4 (4): 381– 388. doi : 10.1016/0024-3795(71)90009-7 .
  6. Knuth (1997) , pág. 500.
  7. Karstadt, Elaye; Schwartz, Oded (24 de julio de 2017). «Multiplicación de matrices, un poco más rápida» . Actas del 29.º Simposio ACM sobre paralelismo en algoritmos y arquitecturas . ACM. págs. 101–110 . doi : 10.1145/3087556.3087579 . ISBN  978-1-4503-4593-4.
  8. Schwartz, Oded; Vaknin, Noa (31 de diciembre de 2023). "Juego de pebbling y base alternativa para la multiplicación de matrices de alto rendimiento" . SIAM Journal on Scientific Computing . 45 (6): C277– C303. Bibcode : 2023SJSC...45C.277S . doi : 10.1137/22M1502719 . ISSN 1064-8275 . 
  9. Webb, Miller (1975). "Complejidad computacional y estabilidad numérica". SIAM J. Comput . 4 (2): 97– 107. doi : 10.1137/0204009 .
  10. Burgués; cláusulas; Shokrollahi (1997). Teoría de la complejidad algebraica . Springer-Verlag. ISBN 3-540-60582-7.
  11. Frigo, M.; Leiserson, CE ; Prokop, H .; Ramachandran, S. (1999). Algoritmos ajenos a la caché (PDF) . Actas del Simposio IEEE sobre Fundamentos de la Informática (FOCS). págs. 285–297 . 
  12. Higham, Nicholas J. (1990). "Exploiting fast matrix multiplication within the level 3 BLAS" (PDF) . ACM Transactions on Mathematical Software . 16 (4): 352– 368. doi : 10.1145/98267.98290 . hdl : 1813/6900 . S2CID 5715053 .