[Tex/LaTex] Matrix multiplication (and other operations) macro using lua module

lualuatexmacros

I am hoping to be able to create a macro to multiply matrices using a lua module. The module is here:

https://raw.github.com/davidm/lua-matrix/master/lua/matrix.lua

As suggested to me in a previous question I put my lua code in a separate lua file and dofiled it as below:

\documentclass{article}
\usepackage{xparse}
\usepackage{luacode}
\usepackage{fontspec}

\directlua{dofile("matrix.lua")}
\directlua{dofile("lua2.lua")}
\ExplSyntaxOn

\NewDocumentCommand{\matrixop}{ m m }
{
    \luaexec{
        matrop(#1,#2)
    }
}
\ExplSyntaxOff

\begin{document}
\matrixop{{1,2,3},{4,5,6}}{{7,8,9},{10,11,12}}
\end{document}

Where the file lua2.lua contained the following:

function matrop (mat1, mat2)
local matrix = require 'matrix'
m1 = matrix {mat1}
m2 = matrix {mat2}
m3 = matrix.add(m1,m2)
a  = matrix.latex(m3,c)
tex.print(a)
end

The issue that arose with this is that in matrix.lua a matrix is defined as e.g.

m = matrix{{1,2,3},{4,5,6}}

Thus, I have to pass as mat1 something that looks like {1,2,3},{4,5,6}. However, as it stands, in the code above matrop(#1,#2) is equivalent to

matrop({1,2,3},{4,5,6},{7,8,9},{10,11,12})

Where the comma separating the rows in mat1 is visible to the macro and so matrop sees 4 comma separated arguments rather than 2 arguments that are comma separated. Lua understands me to have passed a pair of single row matrices after dropping the "extra" two arguments (this took forever to figure out). Adding extra braces around each argument to hide the commas doesn't work because it screws up the table that matrix is expecting. My idea was to replace the row separating comma by a *, pass that to matrop, revert the * to a comma in lua using strings, convert the strings back into tables and then pass that to the matrix command (this took much reading and fiddling, I don't know lua at all). The result is below:

\documentclass{article}
\usepackage{xparse}
\usepackage{luacode}
\usepackage{fontspec}

\directlua{dofile("matrix.lua")}
\directlua{dofile("lua2.lua")}
\ExplSyntaxOn

\NewDocumentCommand{\matrixop}{ m m }
{
    % quotes are so that matrop sees args as strings
    \luaexec{
        matrop("{#1}","{#2}")
    }
}
\ExplSyntaxOff

\begin{document}
% row separator now a *
\matrixop{{1,2,3}*{4,5,6}}{{7,8,9}*{10,11,12}}
\end{document}

Where lua2.lua is as follows:

function matrop (mat1, mat2)
    -- replace *'s with ,'s
    s1 = string.gsub(mat1,"*",",")
    s2 = string.gsub(mat2,"*",",")

    -- functions that load resulting strings
    f1 = assert(loadstring("return"..s1))
    f2 = assert(loadstring("return"..s2))

    -- t1 and t2 are now tables (as I understand it)
    t1 = f1()
    t2 = f2()

    local matrix = require 'matrix'
    -- pass table entries (rows) to matrix definers
    m1 = matrix {t1[1],t1[2]}
    m2 = matrix {t2[1],t2[2]}
    m3 = matrix.add(m1,m2)
    a  = matrix.latex(m3,c)
    tex.print(a)
end

I am sure that I have done many bad/improper/horrible things above. My question is: how in the world should I actually be doing this? Is there an easy way to pass the information needed to define the matrices to lua? Any and all suggestions, comments, tips, solutions etc. are welcome.

Best Answer

If you use (IMHO) a more user-friendly input similar to (a, b, c)(d, e, f) than it is easy to create the matrices with basic lua commands without the need to know the sizes of the matrices.

\documentclass{article}
\usepackage{xparse}
\usepackage{fontspec}
\usepackage{filecontents}

\begin{filecontents*}{luaFunctions.lua}
function matrop (mat1, mat2)
    local matrix = require 'matrix'

    m1 = matrix{unpack(CreateMatrix(mat1))}
    m2 = matrix{unpack(CreateMatrix(mat2))}
    m3 = matrix.add(m1,m2)

    --tex.print("\\\\")
    tex.sprint( matrix.latex(m1,c).."+")
    tex.sprint( matrix.latex(m2,c).."=")
    tex.sprint( matrix.latex(m3,c))
end

function CreateMatrix(str)
    rows={}

    -- get the elements between the braces 
    -- and execute the following function
    string.gsub(str, "%((.-)%)",
        function(sub)
            -- for debugging
            --tex.print("Row: "..sub.."\\\\")
            -- split the string at ','
            elements = string.explode(sub,",")
            row={}
            for i,e in ipairs(elements) do
                -- remove spaces (not really necessary)
                e = string.gsub(e," ","")
                -- insert the element to the row-table
                table.insert(row,e)
                -- for debugging
                --tex.print("Element :"..e.."\\\\")            
            end
            -- insert the row-table to the rows-table
            table.insert(rows,row)
        end 
    )

    return rows
end
\end{filecontents*}

\directlua{dofile("luaFunctions.lua")}
\ExplSyntaxOn

\NewDocumentCommand{\matrixop}{mm}{
    \directlua{matrop("#1","#2")}}

\ExplSyntaxOff

\begin{document}\noindent
\matrixop{(-1)} {( 10)}\\[2ex]
%
\matrixop{(-1, -2, -3)} {( 10, 11, 12)}\\[2ex]
%
\matrixop{(-1, -2, -3)(4, 5, -6)}{(7, -98, 9) (10, 11, 12)}\\[2ex]
%
\matrixop{(-1, -2, -3, 4, 5, -6)(8, 4, 1, 6, 8, 3)} {(7, -98, 9, 10, 11, 12)(8, 4, 1, 6, 8, 3)}\\[2ex]
%
\matrixop{(-1, -2, -3)(4, 5, -6)(8, 4, 1)(6, 8, 3)} {(7, -98, 9) (10, 11, 12)(8, 4, 1)(6, 8, 3)}\\[2ex]
%
\matrixop{(-1, -2)( -3, 4)( 5, -6)(8, 4)( 1, 6)( 8, 3)} {(7, -98)( 9, 10)( 11, 12)(8, 4)( 1, 6)( 8, 3)}\\[2ex]
%
\matrixop{(-1)( -3)( 5)} {(7)( 9)( 11)}\\[2ex]
\end{document}

enter image description here

Edit Matlab style: If you want to use a matrix-input similar to Matlab you can use the following functions. Here it is not necessary to use the xparse package and it is not needed to know how many matrices (and there dimensions) the user give to the function.

\documentclass{article}
\usepackage{fontspec}
\usepackage{filecontents}

\begin{filecontents*}{luaFunctions.lua}
function matropMatlab (mat)
    local matrix = require 'matrix'

    matrices = CreateMatrixMatlab(mat)
    m1 = matrix{unpack(matrices[1])}
    m2 = matrix{unpack(matrices[2])}
    m3 = matrix.add(m1,m2)

    tex.sprint( matrix.latex(m1,c).."+")
    tex.sprint( matrix.latex(m2,c).."=")
    tex.sprint( matrix.latex(m3,c))
end

function CreateMatrixMatlab(str)    
    matrices={}
    string.gsub(str, "%[(.-)%]",
        function(sub)
            splitedRows = string.explode(sub,";")
            rows={}
            for i,sr in ipairs(splitedRows) do
               elements = string.explode(sr)
               row={}
               for k,e in ipairs(elements) do
                  table.insert(row,e)
               end       
               table.insert(rows,row)
            end
            table.insert(matrices,rows)
        end 
    )    
    return matrices
end
\end{filecontents*}

\directlua{dofile("luaFunctions.lua")}

\def\matrixopMatlab#1{%
    \directlua{matropMatlab("#1")}}

\begin{document}
\matrixopMatlab{
[16 3 2 13; 5 -10 11 8; 19 65 7 12; 41 15 -14 1] 
[4 3 21 17; 5 14 11 23; 18 1 71 12; 24 15 44 14]}\\[2ex]
\matrixopMatlab{
[16 3 2 13 1; 5 -10 11 8 2; 19 65 7 12 3] 
[4 3 21 17 1; 5 14 11 23 2; 18 1 71 12 3]}
\end{document}
Related Question