![]() |
|
||||||||||||||||||||||||
|
|
Beat Visual Basic sluggish math, create a Safe Array multiplication DLL using Fortran (Example 4.4) In this example, we create a Fortran subroutine (MatrixProduct) that receives two matrices or vectors and multiplies them, returning the result in a third matrix. Fortran includes an intrinsic function called matmul that is highly optimized to perform matrix multiplications. The ability to use such functionality from a Visual Basic program would be very advantageous, in particular because Visual Basic has never been known as a speed demon when it comes to numerical analysis. This is the Fortran code of subroutine MatrixProduct. This subroutine does not include the additional compiler-dependent code necessary to indicate calling convention, modify name-mangling or to create a DLL. These topics are explained in detail in User Manual:
subroutine MatrixProduct(SAHndlA, SAHndlB, SAHndlR, iError)
!This subroutine receives two arrays (SAHndlA and SAHndlB)
!multiplies them and returns the result in SAHndlR
!If the operation is successful, iError contains 0,
!otherwise it contains a negative value
use f90VBDefs
use f90VBSafeArrays
implicit none
!Subroutine arguments
integer(SAFEARRAY_KIND),intent(in)::SAHndlA, SAHndlB
integer(SAFEARRAY_KIND),intent(inout)::SAHndlR
integer(SHORT_KIND),intent(inout)::iError
!Accessory variables
!Fortran arrays to map Safe Arrays
real(SINGLE_KIND),pointer::MtrxA(:,:),MtrxB(:,:),MtrxR(:,:)
integer(VARTYPE_KIND)::vt_type
integer(LONG_KIND)::nDimsA,nDimsB,nElementsA,nElementsB
integer(HRESULT_KIND):: iRet
!Check that input arrays conform in type
call SafeArrayGetVarType(SAHndlA,vt_type,iRet)
if (iRet.ne.S_OK .or. vt_type.ne. VT_R4) then
iError=-1
return
endif
call SafeArrayGetVarType(SAHndlB,vt_type,iRet)
if (iRet.ne.S_OK .or. vt_type.ne.VT_R4) then
iError=-1
return
endif
!Get dimensions of input matrices
nDimsA = SafeArrayGetDim(SAHndlA)
nDimsB = SafeArrayGetDim(SAHndlB)
!Check that array dimensions conform to multiplication
if (nDimsA.ne.2 .or. nDimsB.ne.2) then
iError = -1
return
endif
!check that size of arrays conform to multiplication
call SafeArrayGetNElements(SAHndlA,nDimsA,nElementsA,iRet)
call SafeArrayGetNElements(SAHndlB,1,nElementsB,iRet)
if (nElementsA.ne.nElementsB) then
iError = -1
return
endif
!Create the Safe Array for the result matrix
call SafeArrayGetNElements(SAHndlA,1,nElementsA,iRet)
call SafeArrayGetNElements(SAHndlB,nDimsB,nElementsB,iRet)
call SafeArrayCreate(SAHndlR, VT_I4, 1,nElementsA,1,nElementsB)
!Check that output safe array was created
if (SAHndlR.eq.f90VB_NULL_PTR) then
iError=-1
return
endif
!Map safe arrays into Fortran arrays for fast access
call SafeArrayAccessData(SAHndlA,MtrxA,iRet)
call SafeArrayAccessData(SAHndlB,MtrxB,iRet)
call SafeArrayAccessData(SAHndlR,MtrxR,iRet)
!Perform the multiplication
MtrxR = MATMUL(MtrxA,MtrxB)
iError=0
!Un-map Fortran arrays (and unlock safe arrays)
call SafeArrayUnAccessData(SAHndlR)
call SafeArrayUnAccessData(SAHndlA)
call SafeArrayUnAccessData(SAHndlB)
end subroutine
Subroutine MatrixProduct has four arguments. The first two (SAHndlA and SAHndlB) are input arguments that contain handles to the Safe Arrays with the two matrices to multiply. The third argument (SAHndlR) is an output argument; the subroutine creates a Safe Array with the product of the two input matrices and returns its handle in SAHndlR. The fourth argument (iError) is also an output argument and is used to return error flags to the calling program. Note that most of the code in subroutine MatrixProduct is dedicated to check the characteristics of the input Safe Arrays, and to ensure that they conform to the requirements of matrix multiplication Note that for simplicity, subroutine MatrixProduct is designed to work only on bi-dimensional matrices. It cannot multiply, for example, a matrix times a vector. . The subroutine, however, only performs the most basic error checking to keep the example as simple as possible. A Safe Array can have any number of dimensions and any type, so you must verify that the content and dimensions of passed Safe Arrays are adequate to the task performed by the subroutine. In addition to the verifications included in the example above, the subroutine should also check that handle SAHndlR is null, or that the array represented by this handle in the calling program can be reallocated. The matrix multiplication is performed through a call to Fortran intrinsic function matmul using Fortran matrices that have been mapped (using SafeArrayAccessData) to the data-block of the Safe Arrays. Using your Fortran compiler, you can export the subroutine into Example44.dll (you may have to add some compiler-dependent directives to the subroutine to do this). To call MatrixProduct from Visual Basic or Visual Basic for Applications, you need to declare the subroutine as an external procedure, and provide the location of the DLL file that contains the procedure. Start by creating a new Visual Basic project, and add a module file with the following declaration: Declare Sub MatrixProduct Lib _ "Example44.dll" (a() As Single, _ b() As Single, c() As Single, _ iError As Integer) As in the previous example, we assume that Example44.dll and the visual basic executable reside in the same directory. You can now add a simple form to drive the subroutine:
The code associated to Form1 is listed below: 'Matrices dimensions
Dim m As Long
Dim n As Long
Dim q As Long
'Matrices to multiply
Dim a() As Single 'Matrix a(mxn)
Dim b() As Single 'matrix b(nxq)
'Utility variables
Dim i As Long, j As Long
Private Sub Form_Load()
Randomize
End Sub
Private Sub cmdGenerate_Click()
Dim TmpText As String
'Generate random sizes for matrices a and b
m = Int((50 - 2 + 1) * Rnd + 2)
n = Int((50 - 2 + 1) * Rnd + 2)
q = Int((50 - 2 + 1) * Rnd + 2)
'Dimension matrices a and b
ReDim a(1 To m, 1 To n)
ReDim b(1 To n, 1 To q)
'Fill matrix a with random values
For i = 1 To n
For j = 1 To m
a(j, i) = Rnd
Next j
For j = 1 To q
b(i, j) = Rnd
Next j
Next i
'Show matrices in text boxes
Label1.Caption = "Matrix A (" & m & "," & n & ")"
TmpText = ""
For i = 1 To m
For j = 1 To n
TmpText = TmpText & Format(a(i, j), "0.000")
If j < n Then
TmpText = TmpText & vbTab
End If
Next j
TmpText = TmpText & vbCrLf
Next i
txtArrayA.Text = TmpText
Label2.Caption = "Matrix B (" & n & "," & q & ")"
TmpText = ""
For i = 1 To n
For j = 1 To q
TmpText = TmpText & Format(b(i, j), "0.000")
If j < q Then
TmpText = TmpText & vbTab
End If
Next j
TmpText = TmpText & vbCrLf
Next i
txtArrayB.Text = TmpText
Label3.Caption = "Result (mxq)"
End Sub
Private Sub cmdMultiply_Click()
Dim MatrixR() As Single
Dim m As Long
Dim q As Long
Dim iError As Integer
Dim TmpText As String
'call Fortran subroutine
Call MatrixProduct(a, b, MatrixR, iError)
If iError < 0 Then
MsgBox "Error in MatrixProduct. Try generating another set " & _
"of input matrices", vbOKOnly
Else
'get dimensions from resulting matrix
m = UBound(MatrixR, 1)
q = UBound(MatrixR, 2)
'Show resulting matrix in text box
Label3.Caption = "Result (" & m & "," & q & ")"
TmpText = ""
For i = 1 To m
For j = 1 To q
TmpText = TmpText & Format(MatrixR(i, j), "0.000")
If j < q Then
TmpText = TmpText & vbTab
End If
Next j
TmpText = TmpText & vbCrLf
Next i
txtArrayR.Text = TmpText
End If
End Sub
When you run this program and click button cmdGenerate, the program executes subroutine cmdGenerate_Click, which creates two random matrices and shows them in the corresponding text controls (i.e. txtArrayA and txtArrayB). Note that the cmdGenerate_Click chooses the dimensions of the matrices randomly, but makes sure they conform to the multiplication operation (i.e. that the second dimensions of the first matrix is equal to the first dimension of the second matrix). Clicking button cmdMultiply, executes subroutine cmdMultiply_Click, which calls Fortran subroutine MatrixProduct. You should note that MatrixR, which contains the result after calling MatrixProduct, has been declared as a dimensionless array. MatrixProduct takes care of creating variable MatrixR and returning it to the Visual Basic program.
|
||||||||||||||||||||||||
|
Copyright
© 1998-2000 Canaima Software
For questions regarding this site, send an e-mail to webmaster@canaimasoft.com |
|||||||||||||||||||||||||