programas - Función Fortran para sobrecargar la multiplicación entre tipos derivados con componentes asignados
guia fortran (1)
Prefacio
Para almacenar matrices en bandas cuyas contrapartes completas puedan tener filas y columnas indexadas de índices distintos a 1
, definí un tipo de datos derivado como
TYPE CDS
REAL, DIMENSION(:,:), ALLOCATABLE :: matrix
INTEGER, DIMENSION(2) :: lb, ub
INTEGER :: ld, ud
END TYPE CDS
donde CDS significa almacenamiento diagonal comprimido. Dada la declaración TYPE(CDS) :: A
,
- Se supone que la
matrix
componentes de rango 2 contiene, como columnas, las diagonales de la matriz completa real (como aquí , excepto que almaceno las diagonales como columnas y no como filas). - Se supone que los componentes
ld
yud
deben contener el número de diagonales inferior y superior, respectivamente, que es-lbound(A%matrix,2)
y+ubound(A%matrix,2)
. - Se supone que los componentes de 2 elementos
lb
yub
contienen los límites inferiores y los límites superiores de la matriz completa real a lo largo de las dos dimensiones. En particular,lb(1)
yub(1)
deberían ser iguales albound(A%matrix,1)
ylbound(A%matrix,2)
.
Como puede ver en los puntos 2 y 3., el tipo derivado contiene información redundante, pero no me importa, ya que son solo 3 parejas de números enteros. Además, en el código que estoy escribiendo, la información sobre los límites y la banda de la matriz completa real se conoce antes de que se pueda completar la matriz. Así que primero asigno los valores a los componentes ld
, ud
, lb
y ub
, y luego usé estos componentes para ALLOCATE
el componente de la matrix
(luego puedo llenarlo correctamente).
El problema
Tengo que realizar una multiplicación de matrices entre matrices tan dispersas, así que escribí una FUNCTION
para ejecutar dicho producto y lo utilicé para sobrecargar el *
operador.
Por el momento, la función es la siguiente,
FUNCTION CDS_mat_x_CDS_mat(A, B)
IMPLICIT NONE
TYPE(CDS), INTENT(IN) :: A, B
TYPE(CDS) :: cds_mat_x_cds_mat
! determine the lower and upper bounds and band of the result based on those of the operands
CDS_mat_x_CDS_mat%lb(1) = A%lb(1)
CDS_mat_x_CDS_mat%ub(1) = A%ub(1)
CDS_mat_x_CDS_mat%lb(2) = B%lb(2)
CDS_mat_x_CDS_mat%ub(2) = B%ub(2)
CDS_mat_x_CDS_mat%ld = A%ld + B%ld
CDS_mat_x_CDS_mat%ud = A%ud + B%ud
! allocate the matrix component
ALLOCATE(CDS_mat_x_CDS_mat%matrix(CDS_mat_x_CDS_mat%lb(1):CDS_mat_x_CDS_mat%ub(1),&
& -CDS_mat_x_CDS_mat%ld:+CDS_mat_x_CDS_mat%ud))
! perform the product
:
:
END FUNCTION
Esto significa que, si tengo que hacer el producto varias veces, la asignación se realiza varias veces dentro de la función. Creo que esto no es bueno desde el punto de vista del rendimiento.
Pido sugerencias sobre cómo llevar a cabo la tarea de matrices dispersas en bandas por matriz dispersa con bandas. Me gustaría usar el tipo que definí porque necesito que sea tan general, en términos de límites, como lo es en este momento. Pero podría cambiar el procedimiento para realizar el producto (de FUNCTION
a SUBROUTINE
, si es necesario).
Ideas
Podría volver a escribir el procedimiento como una SUBROUTINE
para declarar CDS_mat_x_CDS_mat
con CDS_mat_x_CDS_mat
INTENT(INOUT)
hacer la asignación de componentes distintos de la matrix
, así como la asignación, fuera de la SUBROUTINE
. El inconveniente sería que no podría sobrecargar el *
operador.
Noté que la función intrínseca MATMUL
puede operar en cualquier operando de rango 2, cualquiera que sea el límite superior e inferior a lo largo de las dos dimensiones. Esto significa que la asignación se realiza dentro de la función. Supongo que es eficiente (ya que es intrínseco). La diferencia con respecto a mi función es que acepta matrices de rango 2 de cualquier forma, mientras que las mías aceptan objetos de tipos de datos derivados con un componente de arreglo de rango 2 de cualquier forma.
La función intrínseca MATMUL tiene el equivalente de un resultado automático (F2008 5.2.2) - la forma del resultado se expresa de tal manera que se convierte en una característica de la función (F2008 12.3.3) - la forma del resultado de la función se determina en la parte de especificación de la función y (en términos de implementación), el compilador, por lo tanto, sabe cómo calcular la forma del resultado de la función antes de ejecutar realmente la función propiamente dicha.
Como consecuencia, no hay variables ASLOCATABLE en el lenguaje Fortran asociadas con el equivalente del resultado intrínseco de la función MATMUL. Esto no es lo mismo que "no hay asignación de memoria", es posible que el compilador necesite asignar memoria detrás de la escena para sus propios fines, para cosas como expresiones temporales y similares.
(Digo "equivalente a" arriba, porque los procedimientos intrínsecos son inherentemente especiales, pero pretendo por un momento que MATMUL era solo una función de usuario).
El equivalente de ese tipo de resultado automático para su caso se puede lograr mediante el uso de parámetros de tipo de longitud. Esta es la función Fortran 2003, el mismo estándar de lenguaje base que introdujo componentes asignables, pero no es una que haya sido implementada por todos los compiladores mantenidos activamente.
MODULE xyz
IMPLICIT NONE
TYPE CDS(lb1, ub1, ld, ud)
INTEGER, LEN :: lb1, ub1, ld, ud
REAL :: matrix(lb1:ub1, ld:ud)
INTEGER :: lb2, ub2
END TYPE CDS
INTERFACE OPERATOR(*)
MODULE PROCEDURE CDS_mat_x_CDS_mat
END INTERFACE OPERATOR(*)
CONTAINS
FUNCTION CDS_mat_x_CDS_mat(A, B) RESULT(C)
TYPE(CDS(*,*,*,*)), INTENT(IN) :: A, B
TYPE(CDS(A%lb1, A%ub1, A%ld+B%ld, A%ud+B%ud)) :: C
C%lb2 = B%lb2
C%ub2 = B%ub2
! perform the product.
! :
END FUNCTION CDS_mat_x_CDS_mat
END MODULE xyz
Teóricamente, esto le da al compilador más oportunidades para la optimización, porque tiene más información antes de la llamada a la función para el almacenamiento requerido para el resultado de la función. Si esto realmente da como resultado un mejor rendimiento en el mundo real depende de la implementación del compilador y la naturaleza de las referencias a funciones.