representation, transposition âŚ
The previous post from this series hinted that a powerful implementation of infinite matrices can easily be obtained in the Wolfram Language. Here we will begin building this code starting with the definition of the infinite matrix type:
iM = {
Positive | \[Infinity]) (*1*) ,
(_Integer?Positive | \[Infinity]) (*2*) ,
(_Integer?(*3*) ,
_Function (*4*)
_Function };
We will call the type iM
and represent infinite matrices
by using a list with four elements.
The first two elements (marked 1 and 2 above) will determine the size
of the matrix. The number of rows (marked 1) and columns (marked 2) can
take one of two types of values: a positive integer
(_Integer?Positive
) means that the number of rows or
columns is finite white infinity (\[Infinity]
, if entered
in Mathematica using the key combination ESC
+
inf
+ ESC
this will be displayed as the \(\infty\) symbol) ⌠means an infinite number
of rows or columns.
The final two elements in iM
(marked 3 and 4) contain
anonymous functions (_Function
). The first function (marked
3) when called with an integer argument r
returns a list of
non zero elements for the r
th row of the matrix.
Analogously, the second function (marked 4) when called with an integer
argument c
returns a list of non zero elements of the
c
th row of the matrix. Actually I should probably replace
âreturnsâ with âshould returnâ in this paragraph since the current
definition of the iM
pattern does not put any constraints
on the argument or return types of the anonymous functions. The code we
are producing here will be short therefore it is not obvious that
benefits of a stricter definition would result in a significantly better
implementation. Plus, Iâm lazy.
The next step is to define a type to represent the matrix elements. Here it is:
= {
eL (*1*) ,
_Integer (*2*),
_Integer (*3*)
_ };
The type consists from three elements. The first two (marked 1 and 2 above) are integers that determine the row and column numbers. Note that we do not specify that they are positive although indexing will start with 1 - the reason for this will be given soon. The third element (marked 3) is just a blank pattern that can fit any single Wolfram Language expression. This final element should be a type of number, that is, it should be possible to add and multiply elements of itâs type. Again, it would most likely be possible to define a stricter pattern definition but the benefits would probably not outweigh the costs.
Now the constructor for iM
. Here it is:
[
newiMPositive | \[Infinity]) (*1*),
nofRows : (_Integer?Positive | \[Infinity]) (*2*),
nofColumns : (_Integer?rowFunction_Function (*3*),
colFunction_Function (*4*)
] := {
,
nofRows ,
nofColumns Function[row,
[nofRows , nofColumns][rowFunction[row]]] (*5*),
trimElementsFunction[col,
[nofRows , nofColumns][colFunction[col]]] (*6*)
trimElements};
Parts 1-4 of this definition are in direct correspondence to elements
1-4 of the iM
definition. Furthermore, newiM
also returns a list. Whatâs the purpose of this function then? Well,
take a look at parts 5 and 6. We modify the anonymous functions that
return non zero elements in rows and columns by surrounding them with
trimElements
. This is strictly for convenience - it is
sometimes more convenient to define the two functions to return
âillegalâ elements like {-1 , -1 , 10}
and have
trimElements
remove them automatically. These types of
elements are also the reason behind skipping ?Positive
in
the definition of eL
.
The definition of trimElements
has four parts that
correspond to the four cases of matrix size specification:
[nofRows_ , nofCols_][elements : {eL ...}] :=
trimElementsDeleteCases[
, {a_ , b_ , _} /;
elements a <= 0 || b <= 0 || a > nofRows || b > nofCols];
[\[Infinity] , nofCols_][elements : {eL ...}] :=
trimElementsDeleteCases[
, {a_ , b_ , _} /; a <= 0 || b <= 0 || b > nofCols];
elements
[nofRows_ , \[Infinity]][elements : {eL ...}] :=
trimElementsDeleteCases[
, {a_ , b_ , _} /; a <= 0 || b <= 0 || a > nofRows];
elements
[\[Infinity] , \[Infinity]][elements : {eL ...}] :=
trimElementsDeleteCases[elements , {a_ , b_ , _} /; a <= 0 || b <= 0];
These are simple definitions that make use of the built in
DeleteCases
function to remove âillegalâ matrix elements.
In all four of them we assume that the indexing starts with 1. Depending
on whether the number of rows is infinite (\[Infinity]
) or
not elements whose indexes exceed the maximum value allowed by
nofRows
and nofCols
or are below 0 are
deleted.
Below is the definition of the \(P\) matrix from the previous post (part 1) that uses the constructor:
= newiM[
tMat \[Infinity] (*1*),
\[Infinity] (*2*),
Function[row ,
{{row , row , 1 - p} , {row , row + 1 , p}}] (*3*),
Function[column ,
{{column , column , 1 - p} , {column - 1 , column , p}}] (*4*)
]
In 1 and 2 we specify that our matrix will have an infinite number of
rows and columns. In parts 3 and 4 we specify two functions. The
function in 1 returns the non zero elements in row row
and
the function in 2 returns the non zero elements in a column
column
. Notice that the functions in 3 and 4 have no regard
for the âillegalityâ of the element indexes. We can afford this
carelessness because the constructor will wrap these functions with
trimElements
. Here is the result:
{
\[Infinity],
\[Infinity],
Function[row$,
[\[Infinity],\[Infinity]][
trimElementsFunction[row,{{row,row,1-pp},{row,row+1,pp}}][row$]]],
Function[col$,
[\[Infinity],\[Infinity]][
trimElementsFunction[column,{{column,column,1-pp},{column-1,column,pp}}][col$]]]
}
Now for something a little more exciting - letâs draw our matrix. Here is the function:
[
matrixFormiM{r1_Integer , r2_Integer} /; r1 > 0 && r2 > 0 && r2 > r1 (*1*) ,
{c1_Integer , c2_Integer} /; c1 > 0 && c2 > 0 && c2 > c1 (*2*)
][im : iM (*3*)] :=
Module[
{nofRows , nofCols , rowFunction , colFunction ,
, elements},
fragment {nofRows , nofCols , rowFunction , colFunction} = im; (*4*)
= Table[0 , {r2 - r1 + 1} , {c2 - c1 + 1}] (*5*);
fragment Do[ (*6*)
= rowFunction[r];
elements If[#[[2]] <= c2 && #[[2]] >= c1 ,
[[r - r1 + 1 , #[[2]] - c1 + 1]] = #[[3]]] & /@ elements;
fragment, {r , r1 , r2}];
MatrixForm[ (*7*)
ArrayPad[
, {{Min[3 , r1 - 1] ,
fragment Min[3 , nofRows - r2]}, {Min[3 , c1 - 1] ,
Min[3 , nofCols - c2]}} , "\[CenterDot]"]]
]
Arguments marked 1 and 2 specify the range of rows and columns to be
drawn. Argument 3 is the infinite matrix im
with type
iM
. In 4 the elements of im
are unpacked into
local variables. The array fragment
in 5 will contain the
non zero elements in the row, column range specified by r1
,
r2
, c1
, c2
. The loop 5 inserts
non zero elements into fragment
and next in 7 this array is
padded with the appropriate amount of dots and returned in matrix form.
Running this function on tMat
[{1 , 5} , {1 , 5}][tMat] matrixFormiM
will result in five rows and five columns being displayed, with traditional dotted padding: \[ \left( \begin{array}{cccccccc} 1-p & p & 0 & 0 & 0 & \cdot & \cdot & \cdot \\ 0 & 1-p & p & 0 & 0 & \cdot & \cdot & \cdot \\ 0 & 0 & 1-p & p & 0 & \cdot & \cdot & \cdot \\ 0 & 0 & 0 & 1-p & p & \cdot & \cdot & \cdot \\ 0 & 0 & 0 & 0 & 1-p & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \end{array} \right) \]
Letâs do one more thing and define matrix transposition. Here it is:
[im : iM] :=
transposeiMModule[{nofRows , nofCols , rowFunction , colFunction},
{nofRows , nofCols , rowFunction , colFunction} = im;
{
(*1*),
nofCols (*2*),
nofRows Function[row, {#[[2]] , #[[1]] , #[[3]]} & /@ colFunction[row]] (*3*),
Function[col, {#[[2]] , #[[1]] , #[[3]]} & /@ rowFunction[col]]} (*4*)
]
The implementation is simple and just carefully swaps rows and columns. This operation is obvious in 1 or 2 and more subtle in 4 and 5 where we have to additionally swap the indexes inside each matrix element.
To verify that this works letâs try:
[{1 , 5} , {1 , 5}][transposeiM[tMat]] matrixFormiM
The result is what we would probably expect: \[ \left( \begin{array}{cccccccc} 1-p & 0 & 0 & 0 & 0 & \cdot & \cdot & \cdot \\ p & 1-p & 0 & 0 & 0 & \cdot & \cdot & \cdot \\ 0 & p & 1-p & 0 & 0 & \cdot & \cdot & \cdot \\ 0 & 0 & p & 1-p & 0 & \cdot & \cdot & \cdot \\ 0 & 0 & 0 & p & 1-p & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot & \cdot \\ \end{array} \right) \]
Next time we will tackle matrix addition, multiplication and mapping functions over a matrix. Additionally the code will be made available.
2020-01-18 EDIT: fixed some typos, added link to previous post