Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I know the calc package can perform infix-notation arithmetic in LaTeX... but I want more!

I'd like to perform (not necessarily infix-notation) linear-algebra operations such as scalar multiplication, matrix addition and product in LaTeX, and then print the result in an array or in some matrix environment from amsmath.

Why would I want to do that in LaTeX directly? Why do I not simply use some linear-algebra software, such as Matlab, Mathematica, etc.?

Well, suppose I want to walk my readers through a detailed linear-algebra calculation with many numerical examples. Of course, I could perform all the steps manually first and then hardcode the result of each step in my input file. However, this approach

  • is prone to errors (LaTeX' arrays are not very user-friendly to typeset),
  • lacks maintainability (I may decide to change the data in my example, which means I have to modify everything that follows).

Hence my question: Is there a way of easily performing linear-algebra operations in LaTeX?


Ideally, I would like to

  1. mimick Matlab's syntax for defining matrices (using commas as column separators and semicolons as end-of-row characters), performing operations on them, extracting sub matrices, etc.. The syntax could be something like the following:

    \let\A{1,2,3;4,5,6}
    \let\b{1;0;0}
    \let\c\matrixprod{\A,\b}
    \let\d\submatrix{\c}{(2,1)}
    
  2. have a command that typesets a "matrix object" in an array or matrix environment, e.g.

    \typesetmatrix[bmatrix]{A}
    
  3. be able to perform operations on matrices of arbitrary--albeit relatively small--dimensions (edit: not just 2x2 and 3x3 as in the calculator package).

Is that currently possible with some package? If not, I'm considering rolling up my sleeves and implementing something, but this would probably prove quite difficult, and I would like to avoid reinventing the wheel :)


Edit about other operations that would be useful:

  • diag (extraction of the diagonal of a square matrix)
  • trace
  • determinant
  • norm(s)
  • condition number(s)
  • inverse

Even more advanced matrix operations that would be awesome, but probably tough to implement:

  • eigenvalues & eigenvectors,
  • QR, least squares etc.
  • SVD,
  • other common matrix factorisations.
share|improve this question
    
Give me a week or so. What operations do you need to be able to do? I'm planning to start with additions/subtractions and multiplications only, at first, but presumably it shouldn't be too hard to write code for exponentiation and exp (using the power series). What other operations are interesting? –  Bruno Le Floch May 5 '13 at 17:50
    
@BrunoLeFloch Wow, you're picking up the gauntlet! Have you been planning on coding such a package for a long time? I'd be interested in having a chat about the approach you're thinking about... Ok, other operations that would be useful, aside from exponentiation and exp: trace and determinant. –  Jubobs May 5 '13 at 18:40
    
For now I will not implement a "proper" package, because it may be better to add a 'matrix' data type to LaTeX3's l3fp. Currently, this package (l3fp) evaluates floating point expressions (expandably). If I can find a good way of defining data types for l3fp, and if I can convince the other members of the LaTeX3 team, it will be possible to write a package that adds matrix operations, letting us make use of the already existing parser: for instance, \fpa_set:Nn \A { matrix(1,2;4,5) } \fpa_set:Nn \B { 1 + \A + \A * \A / 2 - exp(\A) }. –  Bruno Le Floch May 5 '13 at 18:52
7  
Why not use LuaLaTeX, or a preprocessor in some other language (e.g. Python/NumPy)? Of course it is possible to do anything in plain LaTeX, but... –  leftaroundabout May 5 '13 at 19:24
1  
If you give LuaLaTeX a try maybe this could be helpful for you: tex.stackexchange.com/q/73543/10570. Scott H. did a lot of work to create a LaTeX-Lua(TeX) interface for matrix calculations. –  Holle May 7 '13 at 14:44

5 Answers 5

up vote 38 down vote accepted
+500

Here is some code to manipulate matrices of any size. Currently, it can perform additions, subtractions, and multiplication (as well as fetching individual entries, and transposing a matrix, for instance). Entries are floating points that l3fp supports (16 digits of precision).

% Programming-level functions: \fpm_new:N, \fpm_set:Nn, \fpm_gset:Nn,
% \fpm_add:NNN, \fpm_sub:NNN, \fp_neg:NN, \fp_transpose:NN, \fp_mul:NNN.
%
% Expandable programming-level functions: \fpm_lines:N, \fpm_columns:N,
% \fpm_get:Nnn.
%
% Document-level functions: \matnew, \matset, \matgset, \matadd,
% \matsub, \matmul, \mattypeset.
%
\RequirePackage{expl3}
{
  \ExplSyntaxOn
  %
  % Programming-level code, for adding, multiplying, matrices.  A matrix
  % of size |MxN| is stored as a token list of the form
  %
  % \s__fpm { M } { N } { {a11} ... {a1N} } ... { {aM1} ... {aMN} } ;
  %
  % where |\s__fpm| is a marker used to recognize matrices, |M| and |N|
  % are non-negative integers, and |aij| are floating point numbers.
  %
  % (1) Variables.
  %
  \cs_new_eq:NN \s__fpm \scan_stop: % A marker.
  \tl_const:Nn \c_empty_fpm { \s__fpm { 0 } { 0 } ; }
  \cs_new_eq:NN \l__fpm_tmpa_fpm \c_empty_fpm
  \seq_new:N \l__fpm_lines_seq
  \int_new:N \l__fpm_lines_A_int
  \int_new:N \l__fpm_lines_B_int
  \int_new:N \l__fpm_columns_A_int
  \int_new:N \l__fpm_columns_B_int
  \tl_new:N \l__fpm_matrix_A_tl
  \tl_new:N \l__fpm_matrix_B_tl
  \tl_new:N \l__fpm_matrix_C_tl
  \seq_new:N \l__fpm_matrix_A_seq
  \seq_new:N \l__fpm_matrix_B_seq
  \seq_new:N \l__fpm_one_line_A_seq
  \seq_new:N \l__fpm_one_line_B_seq
  \tl_new:N \l__fpm_one_line_A_tl
  \int_new:N \l__fpm_tmpa_int
  %
  % (3) Storing matrices.
  %
  \cs_new_protected:Npn \fpm_new:N #1
    { \cs_new_eq:NN #1 \c_empty_fpm }
  \cs_new_protected_nopar:Npn \fpm_set:Nn
    { \__fpm_set:NNn \tl_set:Nx }
  \cs_new_protected_nopar:Npn \fpm_gset:Nn
    { \__fpm_set:NNn \tl_gset:Nx }
  \cs_new_protected:Npn \__fpm_set:NNn #1#2#3
    {
      \seq_set_split:Nnn \l__fpm_lines_seq { ; } {#3}
      \seq_set_filter:NNn \l__fpm_lines_seq \l__fpm_lines_seq
        { ! \tl_if_empty_p:n {##1} }
      %
      % Now all lines are non-empty.
      %
      \tl_clear:N \l__fpm_matrix_A_tl
      \int_zero:N \l__fpm_lines_A_int
      \int_zero:N \l__fpm_columns_A_int
      \seq_map_inline:Nn \l__fpm_lines_seq
        {
          \int_incr:N \l__fpm_lines_A_int
          \seq_set_from_clist:Nn \l__fpm_one_line_A_seq {##1}
          \int_set:Nn \l__fpm_tmpa_int { \seq_count:N \l__fpm_one_line_A_seq }
          \int_compare:nNnT \l__fpm_columns_A_int = \c_zero
            { \int_set_eq:NN \l__fpm_columns_A_int \l__fpm_tmpa_int }
          \int_compare:nNnF \l__fpm_tmpa_int = \l__fpm_columns_A_int
            { \seq_map_break:n { \msg_error:nn { fpm } { invalid-size } } }
          \tl_put_right:Nx \l__fpm_matrix_A_tl
            { { \seq_map_function:NN \l__fpm_one_line_A_seq \__fpm_set_aux:n } }
        }
      #1 #2
        {
          \s__fpm
          { \int_use:N \l__fpm_lines_A_int }
          { \int_use:N \l__fpm_columns_A_int }
          \l__fpm_matrix_A_tl
          ;
        }
    }
  \cs_new:Npn \__fpm_set_aux:n #1 { { \fp_to_tl:n {#1} } }
  %
  % (4) Extracting the size of a matrix, and its contents.
  % |#1| is the matrix, |#2|, |#3| integer variables receiving the
  % number of lines and of columns, and |#4| a token list receiving the
  % contents of the matrix.
  %
  \cs_new_protected:Npn \__fpm_get_parts:NNNN #1#2#3#4
    { \exp_after:wN \__fpm_get_parts:NnnwNNN #1 #2 #3 #4 }
  \cs_new_protected:Npn \__fpm_get_parts:NnnwNNN \s__fpm #1#2#3 ; #4#5#6
    {
      \int_set:Nn #4 {#1}
      \int_set:Nn #5 {#2}
      \tl_set:Nn #6 {#3}
    }
  %
  % (5) Some expandable functions: getting one entry, getting the size.
  %
  \cs_new:Npn \fpm_lines:N #1
    { \exp_after:wN \__fpm_lines:NnnwN #1 \use_i:nn }
  \cs_new:Npn \fpm_columns:N #1
    { \exp_after:wN \__fpm_lines:NnnwN #1 \use_ii:nn }
  \cs_new:Npn \__fpm_lines:NnnwN \s__fpm #1#2#3 ; #4 { #4 {#1} {#2} }
  \cs_new:Npn \fpm_get:Nnn #1#2#3
    { \exp_after:wN \__fpm_get:Nnnwnn #1 #2 #3 }
  \cs_new:Npn \__fpm_get:Nnnwnn \s__fpm #1#2#3 ; #4#5
    { \exp_args:Nf \tl_item:nn { \tl_item:nn {#3} {#4} } {#5} }
  %
  % (6) Summing matrices
  %
  \cs_new_protected_nopar:Npn \fpm_add:NNN { \__fpm_add:NNNN + }
  \cs_new_protected_nopar:Npn \fpm_sub:NNN { \__fpm_add:NNNN - }
  \cs_new_protected:Npn \__fpm_add:NNNN #1#2#3#4
    {
      \tl_set:Nn \l__fpm_sign_tl {#1}
      \__fpm_get_parts:NNNN #3
        \l__fpm_lines_A_int \l__fpm_columns_A_int \l__fpm_matrix_A_tl
      \__fpm_get_parts:NNNN #4
        \l__fpm_lines_B_int \l__fpm_columns_B_int \l__fpm_matrix_B_tl
      \int_compare:nNnTF \l__fpm_lines_A_int = \l__fpm_lines_B_int
        {
          \int_compare:nNnTF \l__fpm_columns_A_int = \l__fpm_columns_B_int
            { \__fpm_add:N #2 }
            { \msg_error:nn { fpm } { invalid-size } }
        }
        { \msg_error:nn { fpm } { invalid-size } }
    }
  \cs_new_protected:Npn \__fpm_add:N #1
    {
      \seq_set_split:NnV \l__fpm_matrix_A_seq { } \l__fpm_matrix_A_tl
      \seq_set_split:NnV \l__fpm_matrix_B_seq { } \l__fpm_matrix_B_tl
      \tl_clear:N \l__fpm_matrix_C_tl
      \seq_mapthread_function:NNN
        \l__fpm_matrix_A_seq
        \l__fpm_matrix_B_seq
        \__fpm_add_lines:nn
      \tl_set:Nx #1
        {
          \s__fpm
          { \int_use:N \l__fpm_lines_A_int }
          { \int_use:N \l__fpm_columns_A_int }
          \l__fpm_matrix_C_tl
          ;
        }
    }
  \cs_new_protected:Npn \__fpm_add_lines:nn #1#2
    {
      \seq_set_split:Nnn \l__fpm_one_line_A_seq { } {#1}
      \seq_set_split:Nnn \l__fpm_one_line_B_seq { } {#2}
      \tl_put_right:Nx \l__fpm_matrix_C_tl
        {
          {
            \seq_mapthread_function:NNN
              \l__fpm_one_line_A_seq
              \l__fpm_one_line_B_seq
              \__fpm_add_entries:nn
          }
        }
    }
  \cs_new:Npn \__fpm_add_entries:nn #1#2
    { { \fp_to_tl:n { #1 \l__fpm_sign_tl #2 } } }
  %
  % (7) Negating all entries.
  %
  \cs_new_protected:Npn \fpm_neg:NN #1#2
    { \tl_set:Nx #1 { \exp_after:wN \__fpm_neg:Nnnw #2 } }
  \cs_new:Npn \__fpm_neg:Nnnw \s__fpm #1#2#3 ;
    { \s__fpm {#1} {#2} \tl_map_function:nN {#3} \__fpm_neg_aux:n ; }
  \cs_new:Npn \__fpm_neg_aux:n #1
    { { \tl_map_function:nN {#1} \__fpm_neg_auxii:n } }
  \cs_new:Npn \__fpm_neg_auxii:n #1
    { { \fp_to_tl:n { - #1 } } }
  %
  % (8) Transposing a matrix.
  %
  \cs_new_protected:Npn \fpm_transpose:NN #1#2
    {
      \__fpm_get_parts:NNNN #2
        \l__fpm_lines_A_int \l__fpm_columns_A_int \l__fpm_matrix_A_tl
      \seq_set_split:NnV \l__fpm_matrix_A_seq { } \l__fpm_matrix_A_tl
      \tl_clear:N \l__fpm_matrix_B_tl
      \prg_replicate:nn { \l__fpm_columns_A_int }
        {
          \tl_put_right:Nx \l__fpm_matrix_B_tl
            { { \seq_map_function:NN \l__fpm_matrix_A_seq \__fpm_wrap_head:n } }
          \seq_set_map:NNn \l__fpm_matrix_A_seq \l__fpm_matrix_A_seq
            { \tl_tail:n {##1} }
        }
      \tl_set:Nx #1
        {
          \s__fpm
          { \int_use:N \l__fpm_columns_A_int }
          { \int_use:N \l__fpm_lines_A_int }
          \l__fpm_matrix_B_tl
          ;
        }
    }
  \cs_new:Npn \__fpm_wrap_head:n #1 { { \tl_head:n {#1} } }
  %
  % (9) Multiplying matrices.
  %
  \cs_new_protected:Npn \fpm_mul:NNN #1#2#3
    {
      \int_compare:nNnTF { \fpm_columns:N #2 } = { \fpm_lines:N #3 }
        {
          \fpm_transpose:NN \l__fpm_tmpa_fpm #3
          \__fpm_get_parts:NNNN #2
            \l__fpm_lines_A_int \l__fpm_columns_A_int \l__fpm_matrix_A_tl
          \__fpm_get_parts:NNNN #3
            \l__fpm_lines_B_int \l__fpm_columns_B_int \l__fpm_matrix_B_tl
          \tl_set:Nx #1
            {
              \s__fpm
              { \int_use:N \l__fpm_lines_A_int }
              { \int_use:N \l__fpm_columns_B_int }
              \tl_map_function:NN \l__fpm_matrix_A_tl \__fpm_mul_line:n
              ;
            }
        }
        { \msg_error:nn { fpm } { invalid-size } }
    }
  \cs_new:Npn \__fpm_mul_line:n #1
    { { \exp_after:wN \__fpm_mul_line:Nnnwn \l__fpm_tmpa_fpm {#1} } }
  \cs_new:Npn \__fpm_mul_line:Nnnwn \s__fpm #1#2#3 ; #4
    { \__fpm_mul_line:nn {#4} #3 \q_recursion_tail \q_recursion_stop }
  \cs_new:Npn \__fpm_mul_line:nn #1#2
    {
      \quark_if_recursion_tail_stop:n {#2}
      {
        \fp_to_tl:n
          {
            \__fpm_mul_one:nwn #1 \use_none_delimit_by_q_stop:w
              \q_mark #2 \q_nil \q_stop
            0
          }
      }
      \__fpm_mul_line:nn {#1}
    }
  \cs_new:Npn \__fpm_mul_one:nwn #1#2 \q_mark #3
    { #1 * #3 + \__fpm_mul_one:nwn #2 \q_mark }
  %
  %
  % Messages.
  %
  \msg_new:nnn { fpm } { invalid-size }
    { Sizes~of~matrices~or~lines~don't~match. }
}
\RequirePackage{amsmath, siunitx}
{
  \ExplSyntaxOn
  %
  % Turning matrices into arrays for display.
  %
  \cs_new_protected:Npn \fpm_to_array:N #1
    {
      \begin{pmatrix}
        \exp_after:wN \__fpm_to_array:Nnnw #1
      \end{pmatrix}
    }
  \cs_new_eq:NN \__fpm_newline: ? % Dummy def.
  \cs_new_protected:Npn \__fpm_to_array:Nnnw \s__fpm #1#2#3 ;
    {
      \cs_gset_nopar:Npn \__fpm_newline:
        { \cs_gset_nopar:Npn \__fpm_newline: { \\ } }
      \tl_map_inline:nn {#3}
        {
          \__fpm_newline:
          \seq_set_split:Nnn \l__fpm_one_line_A_seq { } {##1}
          \seq_set_map:NNn \l__fpm_one_line_A_seq \l__fpm_one_line_A_seq
            { \__fpm_to_array_entry:n {####1} }
          \seq_use:Nnnn \l__fpm_one_line_A_seq { & } { & } { & }
        }
    }
  \cs_new_protected:Npn \__fpm_to_array_entry:n #1
    {
      \str_case:nnn {#1}
        {
          { nan } { \text{nan} }
          { inf } { \infty }
          { -inf } { -\infty }
        }
        { \num{#1} }
    }
}

\RequirePackage{xparse}
\ExplSyntaxOn
%
% Document-level functions.
%
\NewDocumentCommand { \matnew } { m } { \fpm_new:N #1 }
\NewDocumentCommand { \matset } { mm } { \fpm_set:Nn #1 {#2} }
\NewDocumentCommand { \matgset } { mm } { \fpm_gset:Nn #1 {#2} }
\NewDocumentCommand { \matadd } { mmm } { \fpm_add:NNN #1 #2 #3 }
\NewDocumentCommand { \matsub } { mmm } { \fpm_sub:NNN #1 #2 #3 }
\NewDocumentCommand { \matneg } { mm } { \fpm_neg:NN #1 #2 }
\NewDocumentCommand { \mattranspose } { mm } { \fpm_transpose:NN #1 #2 }
\NewDocumentCommand { \matmul } { mmm } { \fpm_mul:NNN #1 #2 #3 }
\NewDocumentCommand { \mattypeset } { m }
  { \fpm_to_array:N #1 }
\DeclareExpandableDocumentCommand { \matget } { mmm }
  { \fp_to_tl:n { \fpm_get:Nnn #1 {#2} {#3} } }
\ExplSyntaxOff

\documentclass{article}
\begin{document}
  \matnew \X
  \matnew \Y
  \matnew \Z
  \matset \X { 1 , 2 + 3 ; 4 , 3.4e22 }
  \matset \Y { 3 , 4 ; -5 , 6 }
  \begin{align}
    \matadd \Z \X \Y
    \mattypeset \Z & = \mattypeset \X + \mattypeset \Y \\
    \matsub \Z \X \Y
    \mattypeset \Z & = \mattypeset \X - \mattypeset \Y \\
    \matmul \Z \X \Y
    \mattypeset \Z & = \mattypeset \X \times \mattypeset \Y \\
    \matmul \Z \Y \X
    \mattypeset \Z & = \mattypeset \Y \times \mattypeset \X
  \end{align}
  \(X[1,2] = \matget\X{1}{2}\).
\end{document}

Edit: added \matget, which extracts an individual entry in the matrix.

enter image description here

share|improve this answer
    
This is really nice! Are you going to (1) develop this further and (2) make it into a package and put it on CTAN? :) –  Svend Tveskæg May 6 '13 at 1:08
3  
@SvendTveskæg: Probably not before next year. As I mentioned in comments to the question, this is not quite the correct approach for writing a linear algebra package. I would like to reuse the (expandable) expression parser that I wrote for l3fp: one could then write expressions involving matrices, e.g., \matset\B{(1 + \A/5)^5}, and have them be evaluated on the fly. Adding data types to l3fp is not easy to do without slowing down the core stuff (floating points usd internally for rotations etc.). Also, I have to decide how extensible to make the data-type system. –  Bruno Le Floch May 6 '13 at 13:10
    
I completey missed your comments to the other answer when I wrote the other comment; sorry. –  Svend Tveskæg May 7 '13 at 3:10
    
Bruno, I must be missing something (I've never used any LaTeX3 before), but I seem unable to compile the code you posted. –  Jubobs May 7 '13 at 8:40
    
@Jubobs What error message do you get? My guess is that your version of expl3 (hence of l3fp) is too old. –  Bruno Le Floch May 7 '13 at 10:52

calculator package might help.

enter image description here

enter image description here

share|improve this answer
    
Cool. Does it do arbitrary matrices or 2x2 only? –  Matthew Leingang May 5 '13 at 16:18
    
@MatthewLeingang: See the last update at the bottom. –  Please don't touch May 5 '13 at 16:39
    
Thanks. That's a start. –  Jubobs May 5 '13 at 16:56

enter image description here

This is one more option you can check out. Asymptote supports matrix operations, and here is a brief example to demonstrate what is possible. It includes matrix expressions, transpose and inverse. Usage:

  • define matrices inside the asy environment along with operations on them;
  • define TeX names with matrixdata function, e.g.: matrixdata("D^T",transpose(a*(b-a)));, here a TeX name for typesetting a matrix is D^T, and the matrix is a result of the matrix expression transpose(a*(b-a)), where a,b were previously defined.

  • access matrix data inside a standard matrix environment with \mxData{} , e.g. \mxData{D^T}

Example matr.tex:

\documentclass[10pt,a4paper]{article}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage[inline]{asymptote}
\usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry}

\begin{asydef}
typedef real[][] matrix;

string smatrixdata(string texName, matrix a){
  string s="\expandafter\gdef\csname texMatrix["+texName+"]\endcsname{";
  for(int i=0;i<a.length;++i){
    for(int j=0;j<a[0].length;++j){
      s+=((j==0)?"":"&")+format("%#.3f",a[i][j]);
    }
    if(i<a.length-1){
      s+="\\"+'\n';
    }
  }
  s+="}";
  return s;
}

void matrixdata(string texName, matrix a){
  tex(smatrixdata(texName, a));
};

\end{asydef}

\gdef\mxData#1{\ifcsname texMatrix[#1]\endcsname\relax\csname texMatrix[#1]\endcsname\relax\fi}


\begin{document}

\begin{asy}
  matrix a={
  {-8,-1,6,0},
  {10,-4,-5,-5},
  {-2,-5,-8,2},
  {-4,7,9,-3},
  };

  matrix b={
  {3,-3,9,-9},
  {-5,9,6,7},
  {-9,-8,-6,1},
  {7,-4,-9,9},
  }; 

  matrix a_squared;
  a_squared=a*a;

  matrixdata("A",a);
  matrixdata("B",b);
  matrixdata("C",a*b);
  matrixdata("A^2",a_squared);
  matrixdata("D",a*(b-a));

  matrixdata("D^T",transpose(a*(b-a)));

  matrix va={ 
    {10},
    {20}
  };

  matrix vb={ 
    {1,2,3,4,5,6}
  };

  matrixdata("va",va);
  matrixdata("vb",vb);
  matrixdata("va*vb",va*vb);
  matrixdata("A^-1",inverse(a));
  matrixdata("A*A^-1",a*inverse(a));

\end{asy}


\begin{align}
A&=\left[
\begin{matrix}
\mxData{A}
\end{matrix}
\right]
\\
B&=\left[
\begin{matrix}
\mxData{B}
\end{matrix}
\right]
\\
C=A\times B&=\left[
\begin{matrix}
\mxData{C}
\end{matrix}
\right]
\\
A^2&=\left[
\begin{matrix}
\mxData{A^2}
\end{matrix}
\right]
\\
D=A\times (B-A)&=\left[
\begin{matrix}
\mxData{D}
\end{matrix}
\right]
\\
D^T&=\left[
\begin{matrix}
\mxData{D^T}
\end{matrix}
\right]
\\
a&=\left[
\begin{matrix}
\mxData{va}
\end{matrix}
\right]
\\
b&=\left[
\begin{matrix}
\mxData{vb}
\end{matrix}
\right]
\\
a\times b&=\left[
\begin{matrix}
\mxData{va*vb}
\end{matrix}
\right]
\\
A^{-1}&=\left[
\begin{matrix}
\mxData{A^-1}
\end{matrix}
\right]
%
\\
A\times A^{-1}&=\left[
\begin{matrix}
\mxData{A*A^-1}
\end{matrix}
\right]
%
\end{align}

\end{document}

To process it with latexmk, create file latexmkrc:

sub asy {return system("asy '$_[0]'");}
add_cus_dep("asy","eps",0,"asy");
add_cus_dep("asy","pdf",0,"asy");
add_cus_dep("asy","tex",0,"asy");

and run latexmk -pdf matr.tex.

As for the other operations you mentioned, feel free to add them as a functions inside the asydef block. I suppose, the C-implementations of the algorithms could be found somewhere, and since the Asymptote syntax is very similar, a translation should not be difficult.

share|improve this answer
    
Very nice! I'll look into that. Thanks. –  Jubobs May 8 '13 at 10:35

You could also use the sagetex package, working with the free software Sage.

Pros:

  • Maintainability
  • Full power of Sage: matrices, but also polynomials, plots, etc... and any kind of operations (such as the ones required in the edit!)
  • Don't reinvent the wheel, build a bike!
  • Easy export to — or integration into — LaTeX
  • Easy inclusion of the source code if this is needed
  • Free software!

Cons:

  • Needs Sage on your computer, or a server to perform computations
  • Needs some compilation outside LaTeX
share|improve this answer
    
Thanks for your answer. I'm not familiar with Sage. I'd like to keep everything in LaTeX if possible, though. –  Jubobs May 7 '13 at 23:19
1  
As long as you deal with simple computations, it makes sense to stay within LaTeX. The package sagetex can be a good alternative if you need more complicated stuffs. I think that the things you want (such as the determinant or the condition number) may already need some nontrivial implementations, for which I would clearly prefer using a real computer algebra software. Of course, it's up to you! ;-) –  Bruno May 8 '13 at 9:24
    
Yes, some of the useful operations I list in my question are nontrivial, but one may need them for exposition purposes. I'll look into sagetex though; thanks again. –  Jubobs May 8 '13 at 10:22

Matrix operations (muliplications, inverses, determinants) either exactly or with floats (arbitrary precision)

[Nov 28: code used an xintfrac semi-private macro whose name will change in next release, so I add a compatibility layer]

UPDATE: I have added inverses and determinants (both for exact computations and for computations using floats) as well as display macros and some additional utilities, such as \MATdef which allows to define matrix by giving a formula for its entries.

The code for inverses and determinants is done with an expandable loop, \xintloop (a slightly different one has since been incorporated to xint), and count registers, rather than the \xintFor of the earlier code.

inverses

Due to limitation on answer length I must suppress earlier text and most code comments.


I have implemented matrix operations; I was hesitating for some time because I initially was going to stick to the completely expandable way tenaciously followed in xint; but ultimately I decided to go the easier route.

\documentclass{article}
\usepackage[paperheight=100cm,vscale=0.9]{geometry}
\usepackage{xintfrac}
\RequirePackage{array} 
% november 8-11, 2013
\catcode`_ 11
\makeatletter

% Nov 28: a (slightly different) \xintloop already in xinttools >=1.09g
\ifdefined\xintloop\expandafter\@gobble\else\expandafter\@firstofone\fi
{\def\xintloop #1\repeat{#1\else\expandafter\xintbreakloop\fi \xintdoagain {#1}}%
\def\xintdoagain #1{#1\else\expandafter\xintbreakloop\fi \xintdoagain {#1}}%
\def\xintbreakloop {\xintbreakloopanddo {}}%
\def\xintbreakloopanddo #1#2\xintdoagain #3{#1}%
}

% Nov 28 compatibility with xintfrac <1.09h (\xintFloatSum renamed)
\ifdefined\XINTinFloatSum\let\MAT_xintfloatsum\XINTinFloatSum
                    \else\let\MAT_xintfloatsum\xintFloatSum
\fi

\newcount\MAT_cnta
\newcount\MAT_cntb
\newcount\MAT_cntc
\newcount\MAT_cntd
\newcount\MAT_cnte

\def\MATset      {\def\MAT_xintin {\xintRaw}\MATset_ }%
\def\MATfloatset {\def\MAT_xintin {\XINTinFloat [\XINTdigits]}\MATset_ }%

\def\MATset_ #1#2{%
    \def\MATset_name{#1}%
    \edef\MAT_tmpa {#2}%
    \MAT_cnta \xint_c_ % sets \MAT_cnta to zero
    \expandafter\MATset_a 
    \romannumeral0\expandafter\xintzapspaces\expandafter{\MAT_tmpa};!;%
}%
\def\MATset_a {\futurelet\XINT_token\MATset_b }%
\def\MATset_b #1;{\def\MAT_tmpa{#1}%
                  \ifx\XINT_token;\expandafter\MATset_w
                  \else
                  \ifx\XINT_token!%
                          \expandafter\expandafter\expandafter\MATset_x
                     \else
                          \expandafter\expandafter\expandafter\MATset_c
                  \fi\fi }%
\def\MATset_w !;{\MATset_x }%
\def\MATset_x {\expandafter\def
  \csname MAT@\expandafter\string\MATset_name {I}\expandafter\endcsname
  \expandafter {\the\MAT_cnta }%
               \expandafter\def
  \csname MAT@\expandafter\string\MATset_name {J}\expandafter\endcsname 
  \expandafter {\the\MAT_cntb }%
  \expandafter\edef \MATset_name [##1]%
     {\noexpand\csname MAT@\expandafter\string\MATset_name 
               \noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%
%
\def\MAT_in #1,#2,{\xint_bye #2\xint_gobble_iv\xint_bye
                   {\the\numexpr #1}{\the\numexpr #2}\xint_gobble_iii 
                   {\xintZapSpaces{#1}}}%
%
\def\MATset_c {\advance\MAT_cnta \xint_c_i % row count ++
               \MAT_cntb \xint_c_ % column count intially zero
               \expandafter\MATset_d\romannumeral0\expandafter
               \xintzapspaces\expandafter {\MAT_tmpa},!,}%
\def\MATset_d {\futurelet\XINT_token\MATset_e }%
\def\MATset_e #1,{\ifx\XINT_token!\expandafter\MATset_a
  \else
      \advance\MAT_cntb \xint_c_i
      \expandafter\def
  \csname MAT@\expandafter\string\MATset_name 
            {\the\MAT_cnta}{\the\MAT_cntb}\expandafter\endcsname
  \expandafter{\romannumeral-`0\MAT_xintin{\xintZapSpacesB{#1}}}%
  \expandafter\MATset_d\fi
}%

% \MATdef

\def\MATdef      {\def\MAT_xintin {\xintRaw}%
                  \MATdef_ }%
\def\MATfloatdef {\def\MAT_xintin {\XINTinFloat [\XINTdigits]}%
                  \MATdef_ }%

% #3 should be a replacement text with #1 and #2 for horizontal and vertical
% indices, which can be expanded to its final result inside an \edef, and this
% result must be parsable by the xint macros. 

% WARNING! version of NOV 10 defined only square matrices, this one of NOV 11
% defines *rectangular matrices and has one more argument*

\def\MATdef_ #1#2#3#4{%
    \MAT_cnta #2\relax
    \MAT_cntb #3\relax
    \def\MAT_tmpa ##1##2{#4}%
    \MAT_cntc \xint_c_i % =1
    \xintloop
      {\expandafter\def\expandafter\MAT_tmpc\expandafter 
                           {\expandafter{\the\MAT_cntc}}%
       \MAT_cntd \xint_c_i %=1
       \xintloop
         \expandafter\def\expandafter\MAT_tmpd\expandafter 
                            {\expandafter{\the\MAT_cntd}}%
         \edef\MAT_tmpb {\expandafter\expandafter\expandafter\MAT_tmpa 
                         \expandafter\MAT_tmpc\MAT_tmpd}%
         \expandafter\def
         \csname MAT@\string#1\MAT_tmpc\MAT_tmpd\expandafter\endcsname 
         \expandafter {\romannumeral-`0\MAT_xintin 
                 {\expandafter\xintZapSpacesB\expandafter{\MAT_tmpb}}}%
       \ifnum\MAT_cntd<\MAT_cntb
         \advance\MAT_cntd \xint_c_i
       \repeat
     \ifnum\MAT_cntc<\MAT_cnta
        \advance\MAT_cntc \xint_c_i
    }\repeat
    \expandafter\def
    \csname MAT@\string#1{I}\expandafter\endcsname\expandafter {\the\MAT_cnta}%
    \expandafter\def
    \csname MAT@\string#1{J}\expandafter\endcsname\expandafter {\the\MAT_cntb}%
    \edef #1[##1]%
       {\noexpand\csname 
         MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% \MATsetentry
\def\MATsetentry      {\def\MAT_xintin {\xintRaw}%
                       \MATsetentry_ }%
\def\MATfloatsetentry {\def\MAT_xintin 
                             {\XINTinFloat [\XINTdigits]}%
                       \MATsetentry_ }%

\def\MATsetentry_ #1#2#3{%
    \edef\MAT_tmpa {#3}%
    \expandafter\def
    \csname MAT@\string#1\MAT_in #2,\xint_bye,\expandafter\endcsname\expandafter
    {\romannumeral-`0\MAT_xintin 
     {\expandafter\xintZapSpaces\expandafter{\MAT_tmpa}}}%
}%


% NOTA BENE
% use of \xintFor is for ease of coding. In an official package, I would use
% special loops for optimal efficiency (the \xintFor is a general tool which has
% safeguards against situations which do not arise here, like groups suddenly
% closing)

% 10 november:
% Current version has already replaced use of \xintFor by \xintloop in a number
% of places, notably for the computation of inverses and determinants.

% but I leave \xintFor in a number of macros.
% Improvements from using less \edef's in various places

\def\MATrelax #1{%
    \toks2 \expandafter {\romannumeral-`0\xintSeq {1}{#1[I]}}%
    \toks4 \expandafter {\romannumeral-`0\xintSeq {1}{#1[J]}}%
    \xintFor* ##1 in {\the\toks2 }
    \do{\xintFor* ##2 in {\the\toks4 }
        \do{\expandafter\let\csname MAT@\string#1{##1}{##2}\endcsname\relax }}%
  \expandafter\let\csname MAT@\string#1{I}\endcsname \relax
  \expandafter\let\csname MAT@\string#1{J}\endcsname \relax
  \let #1\relax
}%

\def\MATlet #1#2{%
    \toks2 \expandafter {\romannumeral-`0\xintSeq {1}{#2[I]}}%
    \toks4 \expandafter {\romannumeral-`0\xintSeq {1}{#2[J]}}%
    \xintFor* ##1 in {\the\toks2 }
    \do{\xintFor* ##2 in {\the\toks4 }
        \do{\expandafter\let
     \csname MAT@\string#1{##1}{##2}\expandafter\endcsname
     \csname MAT@\string#2{##1}{##2}\endcsname
     }}%
  \expandafter\edef\csname MAT@\string#1{I}\endcsname {#2[I]}%
  \expandafter\edef\csname MAT@\string#1{J}\endcsname {#2[J]}%
  \edef #1[##1]%
     {\noexpand\csname 
      MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% \MATapply
% argument #1 is \macro or \macro {arg1}..{argn} where \macro is a macro with
% n+1 arguments.

\def\MATapply #1#2{%
    \toks2 \expandafter {\romannumeral-`0\xintSeq {1}{#2[I]}}%
    \toks4 \expandafter {\romannumeral-`0\xintSeq {1}{#2[J]}}%
    \xintFor* ##1 in {\the\toks2 }
    \do{\xintFor* ##2 in {\the\toks4 }
        \do
        {\toks@ {#1}%
         \expandafter\edef
        \csname MAT@\string#2{##1}{##2}\expandafter\expandafter\expandafter
        \endcsname\expandafter\expandafter\expandafter
        {\expandafter\the\expandafter\toks@\expandafter
             {\romannumeral-`0\csname MAT@\string#2{##1}{##2}\endcsname }}%
        }%
    }%
}%

% TRANSPOSE
% Code rewritten to illustrate how one can proceed with \xintloop and counts
% rather than \xintFor. 
\def\MATtranspose #1#2{%
    \MAT_cnta #2[I]\relax
    \MAT_cntb #2[J]\relax
    \MAT_cntd \xint_c_i
    \xintloop {%
      \toks0 \expandafter{\the\MAT_cntd}%
      \MAT_cnte \xint_c_i
      \xintloop
         \toks2 \expandafter{\the\MAT_cnte}%
         \expandafter\let
         \csname MAT@_tmp{\the\toks2}{\the\toks0}\expandafter\endcsname
         \csname MAT@\string#2{\the\toks0}{\the\toks2}\endcsname 
         \ifnum \MAT_cnte < \MAT_cntb \advance\MAT_cnte \xint_c_i
      \repeat
      \ifnum \MAT_cntd < \MAT_cnta \advance\MAT_cntd \xint_c_i
    }\repeat
    \MAT_cntd \xint_c_i
    \xintloop {%
      \toks0 \expandafter{\the\MAT_cntd}%
      \MAT_cnte \xint_c_i
      \xintloop
         \toks2 \expandafter{\the\MAT_cnte}%
         \expandafter\let
         \csname MAT@\string#1{\the\toks0}{\the\toks2}\expandafter\endcsname
         \csname MAT@_tmp{\the\toks0}{\the\toks2}\endcsname 
         \ifnum \MAT_cnte < \MAT_cnta \advance\MAT_cnte \xint_c_i
      \repeat
      \ifnum \MAT_cntd < \MAT_cntb \advance\MAT_cntd \xint_c_i
    }\repeat
  \expandafter\def\csname MAT@\string#1{I}\expandafter\endcsname
         \expandafter {\the\MAT_cntb }%
  \expandafter\def\csname MAT@\string#1{J}\expandafter\endcsname
         \expandafter {\the\MAT_cnta }%
  \edef #1[##1]%
     {\noexpand\csname 
      MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% SCALAR MULTIPLICATION

\def\MATsmul {\def\MAT_xintin {\xintRaw}%
              \def\MAT_MUL {\xintMul}%
              \MATsmul_ }%
\def\MATfloatsmul {\def\MAT_xintin {\XINTinFloat [\XINTdigits]}%
                   \def\MAT_MUL {\XINTinFloatMul}%
                   \MATsmul_ }%
\def\MATsmul_ #1#2#3{%
    \edef\MAT_tmpa {#2}%
    \expandafter\def\expandafter\MAT_tmpa\expandafter
    {\romannumeral-`0\MAT_xintin 
      {\expandafter\xintZapSpaces\expandafter{\MAT_tmpa}}}%
    \toks0 \expandafter {\romannumeral-`0\xintSeq {1}{#3[I]}}%
    \toks2 \expandafter {\romannumeral-`0\xintSeq {1}{#3[J]}}%
    \xintFor* ##1 in {\the\toks0 }
    \do{\xintFor* ##2 in {\the\toks2 }
         \do{\expandafter
             \def\csname MAT@\string#1{##1}{##2}\expandafter\endcsname 
             \expandafter{\romannumeral-`0\MAT_MUL\MAT_tmpa {#3[##1,##2]}}%
        }%
      }%
  \expandafter\edef\csname MAT@\string#1{I}\endcsname {#3[I]}%
  \expandafter\edef\csname MAT@\string#1{J}\endcsname {#3[J]}%
  \edef #1[##1]%
     {\noexpand\csname 
      MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% ADDITION

\def\MATadd {\def\MAT_ADD ##1##2 {\xintIrr {\xintAdd {##1}{##2}}[0]}%
             \MATadd_ }%

\def\MATfloatadd {\def\MAT_ADD {\XINTinFloatAdd }\MATadd_ }%

\def\MATadd_ #1#2#3{%
    \edef\MAT_tmpa {\xintSeq {1}{#2[I]}}%
    \edef\MAT_tmpb {\xintSeq {1}{#2[J]}}%
    \xintFor* ##1 in \MAT_tmpa
    \do{\xintFor* ##2 in \MAT_tmpb
         \do{\expandafter\def\csname MAT@_tmp{##1}{##2}\expandafter\endcsname 
             \expandafter{\romannumeral-`0\MAT_ADD {#2[##1,##2]}{#3[##1,##2]}}%
         }%
      }%
   \xintFor* ##1 in \MAT_tmpa
   \do{\xintFor* ##2 in \MAT_tmpb
        \do{\expandafter\let
            \csname MAT@\string#1{##1}{##2}\expandafter\endcsname 
            \csname MAT@_tmp{##1}{##2}\endcsname 
           }%
       }%   
  \expandafter\edef\csname MAT@\string#1{I}\endcsname {#2[I]}%
  \expandafter\edef\csname MAT@\string#1{J}\endcsname {#2[J]}%
  \edef #1[##1]%
     {\noexpand\csname 
       MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% MULTIPLICATION

\def\MATmul {\def\MAT_MUL    {\xintMul }%
             \def\MAT_SUM ##1{\xintIrr {\xintSum {##1}}[0]}%
             \MATmul_ }%

\def\MATfloatmul  {\def\MAT_MUL {\XINTinFloatMul}%
                   \def\MAT_SUM {\MAT_xintfloatsum}%
                   \MATmul_ }%

\def\MATmul_ #1#2#3{%
    \edef\MAT_tmpa {\xintSeq {1}{#2[I]}}%
    \edef\MAT_tmpb {\xintSeq {1}{#3[J]}}%
    \edef\MAT_tmpc {\xintSeq {1}{#2[J]}}%
    \xintFor* ##1 in \MAT_tmpa
    \do{\xintFor* ##2 in \MAT_tmpb
         \do{%
            \def\MAT_tmpd ####1{\MAT_MUL {#2[##1,####1]}{#3[####1,##2]}}%
            \expandafter
                \def\csname MAT@_tmp{##1}{##2}\expandafter\endcsname 
            \expandafter
            {\romannumeral-`0\MAT_SUM{\xintApply\MAT_tmpd\MAT_tmpc}}%
         }%
      }%
   \xintFor* ##1 in \MAT_tmpa
   \do{\xintFor* ##2 in \MAT_tmpb
        \do{\expandafter\let
            \csname MAT@\string#1{##1}{##2}\expandafter\endcsname 
            \csname MAT@_tmp{##1}{##2}\endcsname 
           }%
       }%   
  \expandafter\edef\csname MAT@\string#1{I}\endcsname {#2[I]}%
  \expandafter\edef\csname MAT@\string#1{J}\endcsname {#3[J]}%
  \edef #1[##1]%
     {\noexpand\csname 
      MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% IDENTITY MATRIX
\def\MATid      {\def\MAT_tmpf{/1}\MAT_id }%
\def\MATfloatid {\def\MAT_tmpf{}\MAT_id }%
\def\MAT_id #1#2{%
    \MAT_cntc #2\relax
    \MAT_cnta \xint_c_i % 1
    \xintloop
      {\expandafter\def\expandafter\MAT_tmpa \expandafter{\the\MAT_cnta}%
       \MAT_cntb \xint_c_i % 1
       \xintloop
         \expandafter\edef
         \csname MAT@\string#1{\MAT_tmpa}{\the\MAT_cntb}\endcsname 
           {\ifnum\MAT_cntb=\MAT_cnta 1\else 0\fi \MAT_tmpf[0]}%
       \ifnum\MAT_cntb<\MAT_cntc
         \advance\MAT_cntb \xint_c_i
       \repeat
     \ifnum\MAT_cnta<\MAT_cntc
        \advance\MAT_cnta \xint_c_i
    }\repeat
    \expandafter\def\csname MAT@\string#1{I}\expandafter\endcsname
            \expandafter {\the\MAT_cntc}%
    \expandafter\def\csname MAT@\string#1{J}\expandafter\endcsname 
            \expandafter {\the\MAT_cntc}%
    \edef #1[##1]%
       {\noexpand\csname 
         MAT@\string#1\noexpand\MAT_in ##1,\noexpand\xint_bye,\endcsname }%
}%

% INVERSES AND DETERMINANTS

\def\MATinv {\def\MATinvordet_Ia{\MATinv_Ia}%
             \def\MATinvordet_II{\MATinv_II}%
             \MATinvordet }

\def\MATdet {\def\MAT_det {1/1[0]}% initial value
             \def\MATinvordet_Ia{\MATdet_Ia}%
             \def\MATinvordet_II{\edef\MAT_det{\xintIrr{\MAT_det}[0]}%
                                 \MATdet_end}%
             \MATinvordet }

\def\MATfloatinv {\def\MATinvordet_Ia{\MATinv_Ia}%
             \def\MATinvordet_II{\MATinv_II}%
             \MATfloatinvordet }

\def\MATfloatdet {\def\MAT_det {1[0]}% initial value
            \def\MATinvordet_Ia{\MATdet_Ia}%
            \def\MATinvordet_II{\edef\MAT_det{\xintFloat{\MAT_det}}\MATdet_end}%
            \MATfloatinvordet }

\def\MATandinverse #1{\def\MAT_name {#1}\MATinv_II }%

\def\MATinvordet #1#2{%
    \def\MAT_ZERO {0/1[0]}%
    \def\MAT_DIV ##1##2{\xintIrr{\xintDiv {##1}{##2}}}%
    \def\MAT_SUB ##1##2{\xintIrr{\xintSub {##1}{##2}}}%
    \def\MAT_MUL {\xintMul }%
    \MATid \MAT_invN {#2[I]}%
    \MATlet\MAT_invM  #2%
    \def\MAT_name {#1}%
    % \MAT_cntc is the size of the matrix. Will NOT be changed in subroutines.
    \MAT_cntc  #2[I]\relax
    \MAT_cnta \xint_c_i
    \MATinvordet_I 
}%
\def\MATfloatinvordet #1#2{%
    \def\MAT_ZERO {0.e0}%
    \def\MAT_DIV {\XINTinFloatDiv }%
    \def\MAT_SUB {\XINTinFloatSub }%
    \def\MAT_MUL {\XINTinFloatMul }%
    \MATfloatid \MAT_invN {#2[I]}%
    \MATlet\MAT_invM  #2%
    \def\MAT_name {#1}%
    % \MAT_cntc is the size of the matrix. Will NOT be changed in subroutines.
    \MAT_cntc  #2[I]\relax
    \MAT_cnta \xint_c_i
    \MATinvordet_I 
}%
\def\MATinvordet_I {\ifnum\MAT_cnta>\MAT_cntc 
                    \expandafter\MATinvordet_II
               \else\expandafter\MATinvordet_Ia
               \fi }%

\def\MATinv_II {\ifnum\MAT_cnta=\xint_c_i 
                     \expandafter\MATinv_end
                \else\expandafter\MATinv_IIa
                \fi }%
\def\MATinv_end {\expandafter\MATlet\MAT_name\MAT_invN }%
\def\MATdet_end {\expandafter\let\MAT_name\MAT_det }

\catcode`! 11
\def\MATinv_Ia {%    
    \MAT_cntb \MAT_cnta\relax
    \xintloop
       \xintifZero {\MAT_invM [\MAT_cntb,\MAT_cnta]}
       {\advance\MAT_cntb \xint_c_i 
        \ifnum\MAT_cntb>\MAT_cntc \MATinv_!\MATinvordet_I\fi
        \iftrue}
       {\iffalse}%
    \repeat
    \MATinv_Ipivot
    \ifnum\MAT_cntb>\MAT_cnta \MATinv_exc\fi
    \advance\MAT_cnta \xint_c_i
    \MATinvordet_I
}%
\def\MATdet_Ia {%    
    \MAT_cntb \MAT_cnta\relax
    \xintloop
       \xintifZero {\MAT_invM [\MAT_cntb,\MAT_cnta]}
       {\advance\MAT_cntb \xint_c_i 
        \ifnum\MAT_cntb>\MAT_cntc \MATdet_!\MATinvordet_I\fi
        \iftrue}
       {\iffalse}%
    \repeat
    \MATinv_Ipivot
    \ifodd\numexpr\MAT_cntb-\MAT_cnta\relax
          \edef\MAT_det{\xintOpp {\MAT_det}}%
    \fi
    \edef\MAT_det {\MAT_MUL {\MAT_pivot}{\MAT_det}}% 
    \ifnum\MAT_cntb>\MAT_cnta \MATinv_exc\fi
    \advance\MAT_cnta \xint_c_i
    \MATinvordet_I
}%
\def\MATinv_! #1\fi{\fi 
                   \xintbreakloopanddo
                   {NOT INVERTIBLE \on@line\typeout{NOT INVERTIBLE \on@line}%
                   \MATinv_end \def\MAT_tmpa ##1#1{}\MAT_tmpa }%
                   }%
\def\MATdet_! #1\fi{\fi 
                   \xintbreakloopanddo
                   {\edef\MAT_det{\MAT_ZERO}%
                    \MATdet_end \def\MAT_tmpa ##1#1{}\MAT_tmpa }%
                   }%
\catcode`! 12

\def\MATinv_IIa {%
    \advance\MAT_cnta -\xint_c_i 
    \MATinv_IIpivot
    \MATinv_II
}%

\def\MATinv_exc {%
% we optimize as we only need to do in M the indices > \MAT_cnta
% and in N the indices at most \MAT_cntb
    \toks0 \expandafter{\the\MAT_cnta}%
    \toks2 \expandafter{\the\MAT_cntb}%
% first we do in matrix M, column indices > "a"
    \MAT_cntd \MAT_cnta
    \xintloop
    \ifnum \MAT_cntd<\MAT_cntc
      \advance \MAT_cntd \xint_c_i
      \toks4 \expandafter{\the\MAT_cntd}%
      \expandafter\def\expandafter\MAT_tmpd\expandafter
        {\csname MAT@\string\MAT_invM{\the\toks0}{\the\toks4}\endcsname }%
      \expandafter\def\expandafter\MAT_tmpe\expandafter
        {\csname MAT@\string\MAT_invM{\the\toks2}{\the\toks4}\endcsname }%
      \expandafter\let\expandafter\MAT_tmpc\MAT_tmpd
      \expandafter\expandafter\expandafter\let\expandafter\MAT_tmpd\MAT_tmpe
      \expandafter\let\MAT_tmpe\MAT_tmpc
    \repeat
% Then we do in matrix N, column indices <= "b"
    \MAT_cntd \xint_c_i % 1
    \xintloop
      \toks4 \expandafter{\the\MAT_cntd}%
      \expandafter\def\expandafter\MAT_tmpd\expandafter
        {\csname MAT@\string\MAT_invN{\the\toks0}{\the\toks4}\endcsname }%
      \expandafter\def\expandafter\MAT_tmpe\expandafter
        {\csname MAT@\string\MAT_invN{\the\toks2}{\the\toks4}\endcsname }%
      \expandafter\let\expandafter\MAT_tmpc\MAT_tmpd
      \expandafter\expandafter\expandafter\let\expandafter\MAT_tmpd\MAT_tmpe
      \expandafter\let\MAT_tmpe\MAT_tmpc
    \ifnum \MAT_cntd<\MAT_cntb
        \advance\MAT_cntd \xint_c_i
    \repeat
}%

\def\MATinv_Ipivot {%
% does pivot simplification on both matrices M and N
% pivot is from matrice M at location (cntb,cnta)
    \expandafter\def\expandafter\MAT_tmpa\expandafter {\the\MAT_cnta}%
    \expandafter\def\expandafter\MAT_tmpb\expandafter {\the\MAT_cntb}%
    \expandafter\let\expandafter\MAT_pivot
    \csname MAT@\string\MAT_invM{\the\MAT_cntb}{\the\MAT_cnta}\endcsname 
    \MAT_cntd \MAT_cnta 
    \xintloop
      \ifnum\MAT_cntd<\MAT_cntc
      \advance\MAT_cntd\xint_c_i
    % divide in M all entries to the right of the pivot by pivot 
     \expandafter\def\expandafter\MAT_tmpd\expandafter {\the\MAT_cntd}%
     \expandafter
          \edef\csname MAT@\string\MAT_invM{\MAT_tmpb}{\MAT_tmpd}\endcsname
     {\MAT_DIV{\csname MAT@\string\MAT_invM{\MAT_tmpb}{\MAT_tmpd}\endcsname }
              {\MAT_pivot}}%
    \repeat
    \MAT_cntd \xint_c_i
    \xintloop
    % divide in N all elements on the "b" row with column indices at most
    % equal to "b" by the pivot value
    \edef\MAT_tmpd {\the\MAT_cntd}
     \expandafter
          \edef\csname MAT@\string\MAT_invN{\MAT_tmpb}{\MAT_tmpd}\endcsname
     {\MAT_DIV{\csname MAT@\string\MAT_invN{\MAT_tmpb}{\MAT_tmpd}\endcsname }%
              {\MAT_pivot}%
      }%
    \ifnum\MAT_cntd<\MAT_cntb
    \advance\MAT_cntd \xint_c_i
    \repeat
  % we now will simplify the next rows, in both matrices M and N
  % Again we don't have to do all entries: >a in M and <= b in N
    \MAT_cntd \MAT_cntb % will be increased by 1, row index
    \xintloop
    {% will not create a group!
    \ifnum\MAT_cntd<\MAT_cntc 
    \advance\MAT_cntd \xint_c_i % we start with the "b+1" row
    % We are working with row \cntd
    \edef\MAT_tmpd {\the\MAT_cntd}%
    % we need the (\cntd, \cnta) entry
    \edef\MAT_tmpf 
        {\csname MAT@\string\MAT_invM{\MAT_tmpd}{\MAT_tmpa}\endcsname }%
    % We now multiply by tmpf the cntb row and subtract it from the cntd row
    % this sets to zero the (cntd,cnta) entry:
    % in matrix M, only need to look at columns to the right
    \MAT_cnte\MAT_cnta % necessarily cnta< size of M, as cnta<= cntb<cntd
    \advance\MAT_cnte \xint_c_i 
    \xintloop
        \edef\MAT_tmpe {\the\MAT_cnte}%
        \expandafter
        \edef\csname MAT@\string\MAT_invM{\MAT_tmpd}{\MAT_tmpe}\endcsname 
        {\MAT_SUB{\csname MAT@\string\MAT_invM{\MAT_tmpd}{\MAT_tmpe}\endcsname }
           {\MAT_MUL \MAT_tmpf
            {\csname MAT@\string\MAT_invM{\MAT_tmpb}{\MAT_tmpe}\endcsname }}%
         }%
    \ifnum\MAT_cnte<\MAT_cntc
        \advance\MAT_cnte \xint_c_i
    \repeat% end of subloop for matrix M, row "d", columns "e>=a"
    % we now do the row "d" in matrix N, columns "e<=b"
    \MAT_cnte \xint_c_i
    \xintloop
        \edef\MAT_tmpe {\the\MAT_cnte}%
        \expandafter
        \edef\csname MAT@\string\MAT_invN{\MAT_tmpd}{\MAT_tmpe}\endcsname 
        {\MAT_SUB{\csname MAT@\string\MAT_invN{\MAT_tmpd}{\MAT_tmpe}\endcsname }
           {\MAT_MUL \MAT_tmpf
            {\csname MAT@\string\MAT_invN{\MAT_tmpb}{\MAT_tmpe}\endcsname }}%
         }%
    \ifnum\MAT_cnte<\MAT_cntb
      \advance\MAT_cnte \xint_c_i
    \repeat% end of subloop for matrix N, row "d"
   }\repeat 
}% 

\def\MATinv_IIpivot {%
% does pivot simplification on matrices M and N
% M is now upper triangular with 1's on the diagonal
% pivot = 1 is in the \MAT_cnta row. We simplify rows above.
% There is no need to keep track of the computations for M itself
% Only need to read M data and modify rows of N accordingly
    \expandafter\def\expandafter\MAT_tmpa\expandafter {\the\MAT_cnta}%
    \MAT_cntb \MAT_cnta
    \xintloop
    {% will not create a group!
    \ifnum\MAT_cntb>\xint_c_i 
       \advance\MAT_cntb -\xint_c_i 
    \expandafter\def\expandafter\MAT_tmpb\expandafter {\the\MAT_cntb}%
    \expandafter\let\expandafter\MAT_tmpf 
        \csname MAT@\string\MAT_invM{\MAT_tmpb}{\MAT_tmpa}\endcsname
    \MAT_cntd\xint_c_i 
    \xintloop
        \expandafter\def\expandafter\MAT_tmpd\expandafter {\the\MAT_cntd}%
        \expandafter
        \edef\csname MAT@\string\MAT_invN{\MAT_tmpb}{\MAT_tmpd}\endcsname 
        {\MAT_SUB
           {\csname MAT@\string\MAT_invN{\MAT_tmpb}{\MAT_tmpd}\endcsname }
           {\MAT_MUL \MAT_tmpf
            {\csname MAT@\string\MAT_invN{\MAT_tmpa}{\MAT_tmpd}\endcsname }}%
         }%
    \ifnum\MAT_cntd<\MAT_cntc
       \advance\MAT_cntd \xint_c_i
    \repeat
   }\repeat
}% 


% DISPLAYING MACROS
\makeatother

\def\MATraw {\MATrawwith {\MATrawone}}%

\def\MATrawone {\xintPRaw}%

\def\MATrawwith #1#2{%
     \xintListWithSep {; }%
     {\xintApply { \MAT_raw_row {#1}#2}{\xintSeq {1}{#2[I]}}}%
}%
\def\MAT_raw_row #1#2#3{%
    \xintListWithSep {, }%
    {\xintApply { \MAT_raw_one {#1}#2{#3}}{\xintSeq {1}{#2[J]}}}%
}%
\def\MAT_raw_one #1#2#3#4{#1{#2[#3,#4]}}%

%% MATH MODE DISPLAYING

\newcommand\MATdisplay [1][1.25]{\MATdisplaywith [#1]{\MATdisplayone}}

\def\MATdisplayone {\xintSignedFrac}

\newcolumntype\MATdisplaycoltype {c}
\newcolumntype\MATdisplaypreamble [1]{@{}*{#1[J]}\MATdisplaycoltype@{}}

\newcommand\MATdisplaywith [3][1.25]
   {\left(\def\arraystretch{#1}%
    \begin{array}{\MATdisplaypreamble {#3}}
         \xintListWithSep {\\}
       {\xintApply { \MAT_display_row {#2}#3}{\xintSeq {1}{#3[I]}}}
    \end{array}\right)%
}%

\def\MAT_display_row #1#2#3{%
    \xintListWithSep {&}
     {\xintApply{ \MAT_display_one {#1}#2{#3}}{\xintSeq {1}{#2[J]}}}%
}%

\def\MAT_display_one #1#2#3#4{#1{#2[#3,#4]}}%

\def\MATminus     {\expandafter\MAT_minus_a\romannumeral-`0}%
\def\MAT_minus_a  {\futurelet\XINT_token\MAT_minus_b }%
\def\MAT_minus_b  {\ifx\XINT_token-\else\phantom{-}\fi }%

\usepackage {siunitx}
\usepackage {numprint}

\newcommand{\MATfloatdisplay}[1][\XINTdigits]
           {\MATfloatdisplaywith [#1]{\MATfloatone}}%

\def\MATfloatone #1{\expandafter\MAT_flone\romannumeral-`0#1!}%

\def\MAT_flone #1.#2e#3!{%
    \xintSgnFork{\xintiiSgn{\XINT_Abs #3}}%
    {}{#1.#2}{#1.#2\times 10^{#3}}}%

\newcommand{\MATfloatdisplaywith}[3][\XINTdigits]
   {\left(\edef\MAT_tmpa{#1}%
    \begin{array}{\MATdisplaypreamble{#3}}
     \xintListWithSep {\\}
       {\xintApply { \MAT_fldisplay_row {#2}#3}{\xintSeq {1}{#3[I]}}}%
    \end{array}\right)}%

\def\MAT_fldisplay_row #1#2#3{%
    \xintListWithSep {&}
     {\xintApply{ \MAT_fldisplay_one {#1}#2{#3}}{\xintSeq {1}{#2[J]}}}}%

\def\MAT_fldisplay_one #1#2#3#4{#1{\xintFloat [\MAT_tmpa]{#2[#3,#4]}}}%

\catcode`_ 8   

\begin{document}
I am sorry but there is no space anymore for anything here, see the images.
\end{document}

matricesi matricesii matricesiii matricesiv matricesv

share|improve this answer
    
beware that \MATapply has been redefined in update, it has only two arguments, not three like in the second displayed image (from the earlier code). There is a typo in the first displayed image with a 5 where there should be a 6 for the last Cauchy matrix example. –  jfbu Nov 10 '13 at 23:50
    
beware too that \MATdef is used in the image as \MATdef \A {5}{1/\numexpr #1+#2-1\relax} but this was when it defined only square matrices, so with the current code it must be \MATdef \A {5}{5}{formula in #1 and #2}. –  jfbu Nov 11 '13 at 16:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.