Matrix manipulation and matrix predicate functions for STACK
The matrix functions formerly provided by matrix.mac are now available in core STACK.
Their generated reference documents the operations,
predicates, and
equation-display functions.
Existing questions can still call stack_include_contrib("matrix.mac"). That compatibility file
retains its historical call to stack_linear_algebra_declare(true), enabling the short c and r
vector notation. To use that notation in new questions, make the declaration explicitly.
stack_include_contrib("matrix.mac") downloads the package from STACK's master branch, including
on older installations, so it also retains the function definitions. The package is generated from
the maintained core sources. To use the shipped copy instead, question authors can call
stack_include("contribl://matrix.mac").
This page gives examples and guidance for using these functions.
Working with matrices
You can find more information on how Maxima handles matrices in the Matrix section of the documentation. There is more discussion in the Vectors and Linear Algebra pages with more of a focus on writing questions or dealing with student input.
STACK adds the following functions to help manipulate matrices.
Row and column operations
The Maxima library linearalgebra (loaded by default in STACK) provides the rowop, rowswap, columnop and columnswap functions, and STACK adds rowadd and rowmul. These are described in the matrix page. STACK adds rowscale and columnscale. When simp:true, rowscale is identical to rowmul, but rowscale also works when simp:false, which could be useful for making model answers.
(simp:false, rowscale(matrix([1,2,3],[4,5,6],[7,8,9]),2,100))will returnmatrix([1,2,3],[100*4,100*5,100*6],[7,8,9])
Changing the entries of a matrix
Maxima provides the setelmx function to change an element of a matrix and return the adjusted matrix. This is described in the matrix page. STACK provides a set of complementary functions to replace multiple entries at once.
setrowmx(r,i,M)will replace rowiof matrixMwithrand return the resulting matrix.rcan be a matrix, a list, or a single variable/number. In the latter case,rwill populate every entry of the designated row.M[i]: rwill do the same thing, but returns the rowrinstead of the matrix. Also,rmust be a literal list in this case.- Unlike
setelmx, this does not change the original matrix. Users may wish to use code such asM: setrowmx(r,i,M)to achieve this. setcolmx(c,i,M)does the equivalent thing for columns- There is no way to set the values of a column directly like for rows. Users may prefer to take the transpose of the matrix, adjust the row, and then transpose the result.
setdiagmx(L,M,k)will replace the diagonal of matrixMwith the list or numberL. IfLis a list, the function will do its best to insertLappropriately. i.e. ifLis too long, it will ignore overhanging entries, and ifLis too short it will leave remaining entries untouched.kindicates which diagonal to replace, where the main diagonal isk=0, the immediate superdiagonal isk=1and immediate subdiagonal isk=-1. Ifkis omitted, the main diagonal is assumed. Some examples:setdiagmx(1,zeromatrix(2,3))will returnmatrix([1,0,0],[0,1,0])setdiagmx(1,zeromatrix(2,3),1)will returnmatrix([0,1,0],[0,0,1])setdiagmx([1,2,3,4,5],ident(3),-1)will returnmatrix([1,0,0],[1,1,0],[0,2,1])
STACK also provides some functions to shape a given matrix.
triu(M)will create a matrix that is the same asMwith all entries below the main diagonal set to zero. This does not edit the original matrix.tril(M)will create a matrix that is the same asMwith all entries above the main diagonal set to zero. This does not edit the original matrix.diagonal(M)will create a matrix that is the same asMwith all off-diagonal entries set to zero. This does not edit the original matrix.
These functions could be useful for setting decomposition questions.
diag_entries(M)will extract the elements on the diagonal ofMand return them as a list.diagmatrix_like(L,m,n)will create an diagonal matrix with listLas the diagonal entries. The listLbehaves much like the listLinsetdiagmxabove, except it must be a list.
Predicate functions for matrices
triup(M)tests whether every below-diagonal entry is recognised as zero. Rectangular matrices are accepted.trilp(M)tests whether every above-diagonal entry is recognised as zero. Rectangular matrices are accepted.diagp(M)checks whetherMis diagonal.
All predicates on this page are available in core STACK.
Note that all of these predicates are checking whether the relevant entries are exactly equal to zero. That produces the following behaviour:
(simp: false, trilp(matrix([1,1-1],[2,3])))returnsfalse(simp: true, trilp(matrix([1,1-1],[2,3])))returnstrue
This is intentional. After all, it is slightly ambiguous whether the matrix is lower triangular, and this ambiguity gets worse with more complicated expressions than simply 1-1. This allows teachers to check whether students have simplified their answers.
REFp(M) tests whether a given matrix is in row echelon form. There is some disagreement about exactly what constitutes row echelon form. For clarity, REFp tests the following properties:
- Do all non-zero rows appear above any zero rows (that is, have all the redundant rows been moved to the bottom?)
- Does the pivot in each row appear strictly to the right of the pivot in the above row?
REFpdoes not check whether the pivots are equal to 1 by default, butREFp(M, true)will additionally require this property.
There is no RREFp function because this is unique for each matrix. Users can use is(M = rref(M)) to test this.
Note: question authors may like to pair REFp with row_equivp from the vectorspaces.mac contributed library when writing questions on row reduction. Alternatively, REFp(ans1) and is(rref(ans1) = rref(ta)) would be sufficient.
symp(M)tests whetherMis a symmetric matrix. That is, isMa square matrix such thattranspose(M)is equal toM?symmetricp(M,n)can check whether the submatrix ofMis symmetric.invertiblep(M)tests whetherMis an invertible matrix.diagonalizablep(M)tests whetherMis a diagonalisable matrix. That is, isMan matrix with linearly independent eigenvectors?orthogonal_columnsp(M)tests whether the columns ofMare orthogonal to each other. That is, istranspose(M) . Ma diagonal matrix?orthonormal_columnsp(M)tests whether the columns ofMform an orthonormal set. That is, are the columns all orthogonal to each other, and do the columns all have a Euclidean length of 1? Or: istranspose(M) . Mthe identity matrix?orth_matrixp(M)tests whetherMis an orthogonal matrix. That is, aretranspose(M) . MandM . transpose(M)both equal to the identity matrix?
The two column predicates, orthogonal_columnsp and orthonormal_columnsp, accept an optional argument, sp, that defines a scalar product. This can either be a bilinear function (takes two vectors and returns a scalar), or a symmetric positive definite matrix. If given, the checks for orthogonality and length will utilise this scalar product instead of the standard inner product (dot product).
Functions for displaying matrices and systems of equations
Augmented matrices
STACK provides aug, aug_matrix and de_aug for displaying augmented matrices.
Perhaps you have a question in which students are asked to solve the matrix equation using Gaussian elimination and you wanted to display this problem as an augmented matrix. Then, with matrix A and right hand side vector (really a matrix) b already defined, you could use aug(A,b) to display
Really what is happening here is that aug is converting a matrix with concatenated columns A and b to an aug_matrix, which then displays as a matrix with its final column separated by a vertical bar. aug_matrix is an inert function used for display purposes. You can save this to a variable, perhaps Ab, but Maxima doesn't know that this is a matrix at all and so matrix operations won't work on it. To turn it back into a matrix, you can use de_aug(Ab). de_aug concatenates the input matrices instead of returning a list, so if you need more control you may prefer to programme your row operations separately and display aug(A1,b1), aug(A2,b2) etc. manually.
This can generalise to as many inputs as needed. For instance, aug(matrix([1,2,3],[4,5,6],[7,8,9]), matrix([1],[1],[1]), ident(3)) will display as
Systems of equations
Sometimes it is nice to have a system of equations with all the variables and coefficients vertically aligned appropriately whilst still allowing for randomisation. The equation-display functions provide this layout.
disp_eqns(eqns, vars) will display the system of linear equations eqns with variables vars (in order!) with everything vertically aligned. It will omit variables with a coefficient of 0, omit unitary coefficients, use negative signs appropriately, and can handle parameters. An example is probably the easiest way to show this.
`disp_eqns([2*x+y-z+(-3)*w = 7,-x-2*y+(-7)*w = -1,3*z = 0,x+w = 0,0 = 0],[x,y,z,w])`
The function mat_disp_eqns(A,b,vars) will produce the same output using coefficient matrix A, right-hand side vector b, and list of variables vars. The below produces the same output as the above.
`mat_disp_eqns(matrix([2,1,-1,-3],[-1,-2,0,-7],[0,0,3,0],[1,0,0,1],[0,0,0,0]),matrix([7],[-1],[0],[0],[0]),[x,y,z,w])`
Both functions also work with variables. For example:
`mat_disp_eqns(matrix([-2, k-1, -1],[0, 2*k+2, 0], [-1, k, -2]),matrix([k-1],[1],[-1]),[x,y,z])`