stringtranslate.com

programación de matrices

En informática , la programación de matrices se refiere a soluciones que permiten aplicar operaciones a un conjunto completo de valores a la vez. Estas soluciones se utilizan habitualmente en entornos científicos y de ingeniería.

Los lenguajes de programación modernos que admiten la programación de matrices (también conocidos como lenguajes vectoriales o multidimensionales ) se han diseñado específicamente para generalizar operaciones en escalares para aplicarlas de forma transparente a vectores , matrices y matrices de dimensiones superiores. Estos incluyen APL , J , Fortran , MATLAB , Analytica , Octave , R , Cilk Plus , Julia , Perl Data Language (PDL) . En estos lenguajes, una operación que opera sobre arreglos completos puede denominarse operación vectorizada , [1] independientemente de si se ejecuta en un procesador vectorial , que implementa instrucciones vectoriales. Las primitivas de programación de matrices expresan de manera concisa ideas amplias sobre la manipulación de datos. El nivel de concisión puede ser dramático en ciertos casos: no es raro [ se necesita ejemplo ] encontrar frases ingeniosas en lenguaje de programación de matrices que requieren varias páginas de código orientado a objetos.

Conceptos de matriz

La idea fundamental detrás de la programación de matrices es que las operaciones se aplican a la vez a un conjunto completo de valores. Esto lo convierte en un modelo de programación de alto nivel , ya que permite al programador pensar y operar con agregados completos de datos, sin tener que recurrir a bucles explícitos de operaciones escalares individuales.

Kenneth E. Iverson describió el fundamento detrás de la programación de matrices (en realidad refiriéndose a APL) de la siguiente manera: [2]

la mayoría de los lenguajes de programación son decididamente inferiores a la notación matemática y se utilizan poco como herramientas de pensamiento en formas que serían consideradas significativas, por ejemplo, por un matemático aplicado.

La tesis es que las ventajas de ejecutabilidad y universalidad que se encuentran en los lenguajes de programación se pueden combinar efectivamente, en un único lenguaje coherente, con las ventajas que ofrece la notación matemática. Es importante distinguir la dificultad de describir y aprender una notación de la dificultad de dominar sus implicaciones. Por ejemplo, aprender las reglas para calcular un producto matricial es fácil, pero dominar sus implicaciones (como su asociatividad, su distributividad sobre la suma y su capacidad para representar funciones lineales y operaciones geométricas) es una cuestión diferente y mucho más difícil. .

De hecho, el carácter mismo sugestivo de una notación puede hacer que parezca más difícil de aprender debido a las muchas propiedades que sugiere para las exploraciones.

[...]

Los usuarios de computadoras y lenguajes de programación a menudo se preocupan principalmente por la eficiencia de la ejecución de los algoritmos y, por lo tanto, podrían descartar sumariamente muchos de los algoritmos presentados aquí. Tal desestimación sería miope, ya que una declaración clara de un algoritmo generalmente puede usarse como base a partir de la cual uno puede derivar fácilmente un algoritmo más eficiente.

La base detrás del pensamiento y la programación de matrices es encontrar y explotar las propiedades de los datos donde los elementos individuales son similares o adyacentes. A diferencia de la orientación a objetos, que implícitamente descompone los datos en sus partes constituyentes (o cantidades escalares ), la orientación de matrices busca agrupar datos y aplicar un manejo uniforme.

El rango de función es un concepto importante para los lenguajes de programación de matrices en general, por analogía con el rango de tensor en matemáticas: las funciones que operan con datos pueden clasificarse según el número de dimensiones en las que actúan. La multiplicación ordinaria, por ejemplo, es una función escalar porque opera con datos de dimensión cero (números individuales). La operación de producto cruzado es un ejemplo de función de rango vectorial porque opera con vectores, no con escalares. La multiplicación de matrices es un ejemplo de una función de 2 rangos, porque opera en objetos bidimensionales (matrices). Los operadores de colapso reducen la dimensionalidad de una matriz de datos de entrada en una o más dimensiones. Por ejemplo, la suma de elementos colapsa la matriz de entrada en 1 dimensión.

Usos

La programación de matrices se adapta muy bien a la paralelización implícita ; un tema de mucha investigación hoy en día. Además, Intel y las CPU compatibles desarrolladas y producidas después de 1997 contenían varias extensiones de conjuntos de instrucciones, comenzando con MMX y continuando hasta SSSE3 y 3DNow. , que incluyen capacidades rudimentarias de matriz SIMD . Esto ha continuado hasta la década de 2020 con conjuntos de instrucciones como AVX-512 , lo que convierte a las CPU modernas en procesadores vectoriales sofisticados. El procesamiento de matrices se diferencia del procesamiento paralelo en que un procesador físico realiza operaciones en un grupo de elementos simultáneamente, mientras que el procesamiento paralelo tiene como objetivo dividir un problema más grande en otros más pequeños ( MIMD ) para que numerosos procesadores los resuelvan poco a poco. Los procesadores con múltiples núcleos y GPU con miles de núcleos informáticos generales son comunes a partir de 2023.

Idiomas

Los ejemplos canónicos de lenguajes de programación de matrices son Fortran , APL y J. Otros incluyen: A+ , Analytica , Chapel , IDL , Julia , K , Klong, Q , MATLAB , GNU Octave , Scilab , FreeMat , PDL , R , S-Lang , SAC , Nial , ZPL , Futhark y TI-BASIC .

Lenguajes escalares

En lenguajes escalares como C y Pascal , las operaciones se aplican sólo a valores únicos, por lo que a + b expresa la suma de dos números. En tales lenguajes, agregar una matriz a otra requiere indexación y bucles, cuya codificación es tediosa.

para ( i = 0 ; i < n ; i ++ ) para ( j = 0 ; j < n ; j ++ ) a [ i ][ j ] += b [ i ][ j ];                  

En lenguajes basados ​​en matrices, por ejemplo en Fortran, el bucle for anidado anterior se puede escribir en formato de matriz en una línea,

a = a + b    

o alternativamente, para enfatizar la naturaleza de matriz de los objetos,

a (:,:) = a (:,:) + b (:,:)    

Si bien los lenguajes escalares como C no tienen elementos de programación de matrices nativos como parte del lenguaje propiamente dicho, esto no significa que los programas escritos en estos lenguajes nunca aprovechen las técnicas subyacentes de vectorización (es decir, utilizar las instrucciones basadas en vectores de una CPU si tiene ellos o usando múltiples núcleos de CPU). Algunos compiladores de C como GCC en algunos niveles de optimización detectan y vectorizan secciones de código que su heurística determina que se beneficiarían de ello. Otro enfoque lo ofrece la API OpenMP , que permite paralelizar secciones de código aplicables aprovechando múltiples núcleos de CPU.

Idiomas de matriz

En los lenguajes de matrices, las operaciones se generalizan para aplicarse tanto a escalares como a matrices. Así, a + b expresa la suma de dos escalares si a y b son escalares, o la suma de dos matrices si son matrices.

Un lenguaje de matrices simplifica la programación, pero posiblemente a un costo conocido como penalización de abstracción . [3] [4] [5] Debido a que las adiciones se realizan de forma aislada del resto de la codificación, es posible que no produzcan el código óptimamente más eficiente . (Por ejemplo, es posible que posteriormente se encuentren adiciones de otros elementos de la misma matriz durante la misma ejecución, lo que provocaría búsquedas repetidas innecesarias). Incluso el compilador de optimización más sofisticado tendría dificultades extremas para amalgamar dos o más funciones aparentemente dispares que podrían aparecer en diferentes secciones o subrutinas del programa, aunque un programador podría hacer esto fácilmente, agregando sumas en la misma pasada sobre la matriz para minimizar la sobrecarga ).

ada

El código C anterior pasaría a ser el siguiente en el lenguaje Ada , [6] que admite la sintaxis de programación de matrices.

A  :=  A  +  B ;

APL

APL utiliza símbolos Unicode de un solo carácter sin azúcar sintáctico.

A A + B    

Esta operación funciona en matrices de cualquier rango (incluido el rango 0) y en un escalar y una matriz. Dyalog APL amplía el idioma original con asignaciones aumentadas :

A + B  

analítica

Analytica proporciona la misma economía de expresión que Ada.

A := A + B;

BÁSICO

Dartmouth BASIC tenía declaraciones MAT para manipulación de matrices y matrices en su tercera edición (1966).

DIM A ( 4 ), B ( 4 ), C ( 4 ) MAT A = 1 MAT B = 2 * A MAT C = A + B MAT IMPRIMIR A , B , C                

matá

El lenguaje de programación matricial de Stata , Mata, admite la programación de matrices. A continuación, ilustramos la suma, la multiplicación, la suma de una matriz y un escalar, la multiplicación elemento por elemento, el subíndice y una de las muchas funciones matriciales inversas de Mata.

. mata :: A = ( 1 , 2 , 3 ) \( 4 , 5 , 6 ): A 1  2  3  +-------------+  1 | 1  2  3 | 2 | 4  5  6 | +------------+: B = ( 2 .. 4 ) \( 1 .. 3 ): B 1  2  3  +-------------+  1 | 2  3  4 | 2 | 1  2  3 | +------------+: C = J ( 3 , 2 , 1 ) // Una matriz de unos de 3 por 2: C 1  2  +---------+  1 | 1  1 | 2 | 1  1 | 3 | 1  1 | +---------+: D = A + B: D 1  2  3  +-------------+  1 | 3  5  7 | 2 | 5  7  9 | +------------+: mi = A * C: mi 1  2  +-----------+  1 | 6  6 | 2 | 15  15 | +-----------+: F = A: * B:F 1  2  3  +----------------+  1 | 2  6  12 | 2 | 4  10  18 | +----------------+: GRAMO = MI : +  3: GRAMO 1  2  +-----------+  1 | 9  9 | 2 | 18  18 | +-----------+: H = F[( 2 \ 1 ), ( 1 , 2 )] // Subíndice para obtener una submatriz de F y: // cambia las filas 1 y 2:H 1  2  +-----------+  1 | 4  10 | 2 | 2  6 | +-----------+: I = invsym (F' * F) // Inversa generalizada (F*F^(-1)F=F) de a: // matriz semidefinida positiva simétrica: I[simétrico] 1  2  3  +-------------------------------------------+  1 | 0 | 2 | 0  3,25 | 3 | 0-1,75 . _ _ 9444444444 | +-------------------------------------+: fin

MATLAB

La implementación en MATLAB permite la misma economía que permite el uso del lenguaje Fortran.

A = A + B ;    

Una variante del lenguaje MATLAB es el lenguaje GNU Octave , que extiende el lenguaje original con asignaciones aumentadas:

A  +=  B ;

Tanto MATLAB como GNU Octave admiten de forma nativa operaciones de álgebra lineal como la multiplicación de matrices, la inversión de matrices y la solución numérica de sistemas de ecuaciones lineales , incluso utilizando el pseudoinverso de Moore-Penrose . [7] [8]

El ejemplo de Nial del producto interno de dos matrices se puede implementar utilizando el operador de multiplicación de matrices nativo. Si aes un vector fila de tamaño [1 n] y bes un vector columna correspondiente de tamaño [n 1].

a*b;

Por el contrario, el producto de entrada se implementa como:

a .*b;

El producto interno entre dos matrices que tienen el mismo número de elementos se puede implementar con el operador auxiliar (:), que transforma una matriz dada en un vector columna, y el operador de transposición' :

A(:)' * B(:);

rasql

El lenguaje de consulta rasdaman es un lenguaje de programación de matrices orientado a bases de datos. Por ejemplo, se podrían agregar dos matrices con la siguiente consulta:

SELECCIONE A + B DE A , B     

R

El lenguaje R admite el paradigma de matriz de forma predeterminada. El siguiente ejemplo ilustra un proceso de multiplicación de dos matrices seguido de la suma de un escalar (que es, de hecho, un vector de un elemento) y un vector:

> A <- matriz ( 1 : 6 , nrow = 2 ) # !! esto tiene nrow=2 ... y A tiene 2 filas > A  [,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 > B <- t ( matriz ( 6 : 1 , nrow = 2 ) ) # t() es un operador de transposición !! Esto tiene nrow=2 ... y B tiene 3 filas - - una clara contradicción con la definición de A > B  [,1] [,2] [1,] 6 5 [2,] 4 3 [3,] 2 1 > C <- A %*% B > C  [, 1] [,2] [1,] 28 19 [2,] 40 28 > D <- C + 1 > D  [,1] [,2] [1,] 29 20 [2,] 41 29 > D + c ( 1 , 1 ) # c() crea un vector  [,1] [,2] [1,] 30 21 [2,] 42 30                      

Razonamiento matemático y notación del lenguaje.

El operador de división por la izquierda de la matriz expresa de manera concisa algunas propiedades semánticas de las matrices. Como en el equivalente escalar, si el ( determinante del) coeficiente (matriz) Ano es nulo, entonces es posible resolver la ecuación (vectorial) A * x = bmultiplicando por la izquierda ambos lados por el inverso de A: (tanto en lenguajes MATLAB como GNU Octave : ). Las siguientes afirmaciones matemáticas son válidas cuando se trata de una matriz cuadrada de rango completo :A−1A^-1A

A^-1 *(A * x)==A^-1 * (b)
(A^-1 * A)* x ==A^-1 * b ( asociatividad       matriz-multiplicación )
x = A^-1 * b

¿ Dónde ==está el operador relacional de equivalencia ? Las declaraciones anteriores también son expresiones válidas de MATLAB si la tercera se ejecuta antes que las demás (las comparaciones numéricas pueden ser falsas debido a errores de redondeo).

Si el sistema está sobredeterminado (de modo que Atiene más filas que columnas), el pseudoinverso (en lenguajes MATLAB y GNU Octave:) puede reemplazar al inverso , de la siguiente manera:A+pinv(A)A−1

pinv(A) *(A * x)==pinv(A) * (b)
(pinv(A) * A)* x ==pinv(A) * b       (asociatividad de multiplicación de matrices)
x = pinv(A) * b

Sin embargo, estas soluciones no son ni las más concisas (por ejemplo, todavía sigue siendo necesario diferenciar notacionalmente los sistemas sobredeterminados) ni las más eficientes desde el punto de vista computacional. Este último punto es fácil de entender si se considera nuevamente el equivalente escalar a * x = b, cuya solución x = a^-1 * brequeriría dos operaciones en lugar de la más eficiente x = b / a. El problema es que generalmente las multiplicaciones de matrices no son conmutativas ya que la extensión de la solución escalar al caso matricial requeriría:

(a * x)/ a ==b / a
(x * a)/ a ==b / a       (¡La conmutatividad no es válida para matrices!)
x * (a / a)==b / a       (la asociatividad también es válida para matrices)
x = b / a

El lenguaje MATLAB introduce el operador de división por la izquierda \para mantener la parte esencial de la analogía con el caso escalar, simplificando así el razonamiento matemático y preservando la concisión:

A \ (A * x)==A \ b
(A \ A)* x ==A \ b       (la asociatividad también es válida para matrices, la conmutatividad ya no es necesaria)
x = A \ b

Este no es solo un ejemplo de programación de matrices concisas desde el punto de vista de la codificación, sino también desde la perspectiva de la eficiencia computacional, que en varios lenguajes de programación de matrices se beneficia de bibliotecas de álgebra lineal bastante eficientes como ATLAS o LAPACK . [9]

Volviendo a la cita anterior de Iverson, la razón detrás de ella ahora debería ser evidente:

Es importante distinguir la dificultad de describir y aprender una notación de la dificultad de dominar sus implicaciones. Por ejemplo, aprender las reglas para calcular un producto matricial es fácil, pero dominar sus implicaciones (como su asociatividad, su distributividad sobre la suma y su capacidad para representar funciones lineales y operaciones geométricas) es una cuestión diferente y mucho más difícil. . De hecho, el carácter mismo sugestivo de una notación puede hacer que parezca más difícil de aprender debido a las muchas propiedades que sugiere para las exploraciones.

Bibliotecas de terceros

El uso de bibliotecas especializadas y eficientes para proporcionar abstracciones más concisas también es común en otros lenguajes de programación. En C++, varias bibliotecas de álgebra lineal explotan la capacidad del lenguaje para sobrecargar operadores . En algunos casos, una abstracción muy concisa en esos lenguajes está explícitamente influenciada por el paradigma de programación de matrices, como lo hace la biblioteca de extensión NumPy para las bibliotecas Python , Armadillo y Blitz++ . [10] [11]

Ver también

Referencias

  1. ^ Stefan van der Walt; S. Chris Colbert y Gaël Varoquaux (2011). "La matriz NumPy: una estructura para el cálculo numérico eficiente". Computación en Ciencias e Ingeniería . IEEE. 13 (2): 22–30. arXiv : 1102.1523 . Código Bib : 2011CSE....13b..22V. doi : 10.1109/mcse.2011.37. S2CID  16907816.
  2. ^ Iverson, KE (1980). "La notación como herramienta de pensamiento". Comunicaciones de la ACM . 23 (8): 444–465. doi : 10.1145/358896.358899 .
  3. ^ Surana P (2006). Metacompilación de abstracciones del lenguaje (Tesis).
  4. ^ Kuketáyev. "El punto de referencia de penalización por abstracción de datos (DAP) para objetos pequeños en Java". Archivado desde el original el 11 de enero de 2009 . Consultado el 17 de marzo de 2008 .
  5. ^ Chatzigeorgiou; Estefanides (2002). "Evaluación del rendimiento y el poder de los lenguajes de programación orientados a objetos frente a los procedimentales". En Blieberger; Strohmeier (eds.). Actas - Séptima Conferencia Internacional sobre Tecnologías de Software Confiables - Ada-Europe'2002. Saltador. pag. 367.ISBN _ 978-3-540-43784-0.
  6. ^ Manual de referencia de Ada: G.3.1 Matrices y vectores reales
  7. ^ "Manual de octavas GNU. Operadores aritméticos" . Consultado el 19 de marzo de 2011 .
  8. ^ "Documentación MATLAB. Operadores aritméticos". Archivado desde el original el 7 de septiembre de 2010 . Consultado el 19 de marzo de 2011 .
  9. ^ "Manual de GNU Octave. Apéndice G Instalación de Octave" . Consultado el 19 de marzo de 2011 .
  10. ^ "Referencia para Armadillo 1.1.8. Ejemplos de sintaxis de Matlab/Octave y sintaxis de Armadillo conceptualmente correspondiente" . Consultado el 19 de marzo de 2011 .
  11. ^ "Guía del usuario de Blitz ++. 3. Expresiones de matriz". Archivado desde el original el 23 de marzo de 2011 . Consultado el 19 de marzo de 2011 .

enlaces externos