Home » excel » Excel VBA: Transpose Vector and Matrix Multiply in a function

Excel VBA: Transpose Vector and Matrix Multiply in a function

Posted by: admin May 14, 2020 Leave a comment

Questions:

I have an 3×3 matrix “mat” and a vector “vec” (3×1) of real numbers I want to matrix multiply (in the linear algebra sense) in a VBA function like so: t(vec)matvec to produce a 1×1 real number I can use in an equation.

I do not want to interact with a worksheet in the function. The values in the matrix and vector are eiter hard-coded or calculated from within the function. There should be a simple way to transpose then do a couple matrix multiplications like in MATLAB or R. Here is where I am so far:

Public Function QuickMaths()
Dim vec As Variant
Dim mat As Variant
mat = Array(Array(1,1+1,3), _ 
          Array(2^2,5,6), _
          Array(7,8,9))
vec = Array(2*5,11,12)
QuickMaths = Application.WorksheetFunction.MMult(Application.WorksheetFunction.MMult(Application.WorksheetFunction.Transpose(vec), mat), vec)

End Function

I get #VALUE from this in a worksheet when I run it. I would expect the output to be a 1×1 matrix, but I don’t know if Excel VBA would consider that a scalar that can be output into a sheet as a single value (e.g. Double).

Please send help.

How to&Answers:

It would have been good if you had provided expected output (the specific scalar you’re expecting at the end).

Based on what I gather from your code and question, I’m going to assume you’re trying to perform two steps. The first being:

First step

The second being:

Second step

(It’s been a while since I’ve done any matrix multiplication, so if you think I’ve misunderstood, let me know.)

Your first array (mat) is an array of arrays (not a two dimensional array), which I don’t think MMULT handles (https://support.office.com/en-us/article/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb). So you might need to replace:

mat = Array(Array(1, 1 + 1, 3), _
          Array(2 ^ 2, 5, 6), _
          Array(7, 8, 9))

with:

ReDim mat(0 To 2, 0 To 2)
mat(0, 0) = 1
mat(0, 1) = 2
mat(0, 2) = 3
mat(1, 0) = 4
mat(1, 1) = 5
mat(1, 2) = 6
mat(2, 0) = 7
mat(2, 1) = 8
mat(2, 2) = 9

That said, manually assigning each array element can be impractical, so maybe make a small function to do it for you (see FlattenAnArrayOfArrays function in code below).

From what I’ve read online in the last 30 minutes, matrix multiplication is not commutative and also requires that the number of columns in your first matrix match the number of rows in the second matrix. (You may already know all of this, but just mentioning it anyway.)

Based on the above, your code might look something like:

Option Explicit

Public Function QuickMaths() As Variant

    ' This function returns a value of type Variant.
    ' Could return a Long/Double/numeric type; scalar should be at QuickMaths(1,1)
    ' But MMULT can return non-numeric values, so you risk
    ' getting a type mismatch error if the matrix multiplication
    ' is not successful (for whatever reason).
    ' Maybe this shouldn't be this function's concern -- or maybe it should.

    Dim mat As Variant
    mat = Array(Array(1, 1 + 1, 3), _
              Array(2 ^ 2, 5, 6), _
              Array(7, 8, 9))
    mat = FlattenAnArrayOfArrays(mat)

    Dim vec As Variant
    vec = Array(2 * 5, 11, 12)

    Dim resultantMatrix As Variant
    resultantMatrix = Application.MMult(vec, mat) ' Number of columns in "vec" must match number of rows in "mat"
    resultantMatrix = Application.MMult(vec, Application.Transpose(resultantMatrix))

    QuickMaths = resultantMatrix

End Function

Private Function FlattenAnArrayOfArrays(ByRef arrayOfArrays As Variant) As Variant()
    ' Given an array of arrays, returns a two-dimensional array.
    ' This function is very basic and has no error handling implemented.

    Dim firstArray() As Variant
    firstArray = arrayOfArrays(LBound(arrayOfArrays)) ' Columns inferred from first array in "arrayOfArrays"

    Dim outputArray() As Variant
    ReDim outputArray(LBound(arrayOfArrays) To UBound(arrayOfArrays), LBound(firstArray) To UBound(firstArray))

    Dim rowIndex As Long
    For rowIndex = LBound(outputArray, 1) To UBound(outputArray, 1)
        Dim columnIndex As Long
        For columnIndex = LBound(outputArray, 2) To UBound(outputArray, 2)
            outputArray(rowIndex, columnIndex) = arrayOfArrays(rowIndex)(columnIndex)
        Next columnIndex
    Next rowIndex

    FlattenAnArrayOfArrays = outputArray

End Function

Closing points:

  • The return value of the QuickMaths function is a 1×1 matrix, but you can assign it to a cell’s value.
  • Similarly, if you call the QuickMaths function from a worksheet cell, the cell will display the return value (without any issues or need for an array formula).