]> gitweb.factorcode.org Git - factor.git/commitdiff
math.matrices: rewrite, modernize and overhaul
authorCat Stevens <catb0t@protonmail.ch>
Sun, 4 Feb 2018 05:00:21 +0000 (00:00 -0500)
committerJohn Benediktsson <mrjbq7@gmail.com>
Sun, 8 Dec 2019 16:08:54 +0000 (08:08 -0800)
math.matrices.elimination: move to extra
math.matrices.extras: expand with esoteric, less-used and unfinished code from basis

- math.matrices and .extras receive more words, tests, and docs
- matrix has become a predicate class
- 94% of matrices words have complete docs
- 77% of matrices.extras words have complete docs
- much more consistent naming for constructors etc
- added missing words / features such as main-diagonal and anti-transpose
- optimizations
- lots of documentation

32 files changed:
basis/compression/run-length/run-length.factor
basis/math/matrices/authors.txt
basis/math/matrices/elimination/authors.txt [deleted file]
basis/math/matrices/elimination/elimination-docs.factor [deleted file]
basis/math/matrices/elimination/elimination-tests.factor [deleted file]
basis/math/matrices/elimination/elimination.factor [deleted file]
basis/math/matrices/elimination/summary.txt [deleted file]
basis/math/matrices/matrices-docs.factor
basis/math/matrices/matrices-tests.factor
basis/math/matrices/matrices.factor
basis/math/vectors/vectors-docs.factor
basis/math/vectors/vectors-tests.factor
basis/math/vectors/vectors.factor
extra/benchmark/3d-matrix-scalar/3d-matrix-scalar.factor
extra/benchmark/3d-matrix-vector/3d-matrix-vector.factor
extra/benchmark/matrix-exponential-scalar/matrix-exponential-scalar.factor
extra/game/debug/tests/tests.factor
extra/gml/geometry/geometry.factor
extra/gpu/demos/raytrace/raytrace.factor
extra/gpu/util/wasd/wasd.factor
extra/math/matrices/elimination/authors.txt [new file with mode: 0644]
extra/math/matrices/elimination/elimination-docs.factor [new file with mode: 0644]
extra/math/matrices/elimination/elimination-tests.factor [new file with mode: 0644]
extra/math/matrices/elimination/elimination.factor [new file with mode: 0644]
extra/math/matrices/elimination/summary.txt [new file with mode: 0644]
extra/math/matrices/extras/authors.txt [new file with mode: 0644]
extra/math/matrices/extras/extras-docs.factor [new file with mode: 0644]
extra/math/matrices/extras/extras-tests.factor [new file with mode: 0644]
extra/math/matrices/extras/extras.factor [new file with mode: 0644]
extra/math/matrices/extras/summary.txt [new file with mode: 0644]
extra/rosetta-code/bitmap/bitmap.factor
extra/rosetta-code/conjugate-transpose/conjugate-transpose.factor

index 8faaaffc1e7f63aca934c22290071427eacb43bd..401024ffcee73648ec1f9da0023b046f5c4a8e84 100644 (file)
@@ -13,7 +13,7 @@ IN: compression.run-length
 
 :: run-length-uncompress-bitmap4 ( byte-array m n -- byte-array' )
     byte-array <sequence-parser> :> sp
-    m  1 + n zero-matrix :> matrix
+    m  1 + n <zero-matrix> :> matrix
     n 4 mod n + :> stride
     0 :> i!
     0 :> j!
@@ -45,7 +45,7 @@ IN: compression.run-length
 
 :: run-length-uncompress-bitmap8 ( byte-array m n -- byte-array' )
     byte-array <sequence-parser> :> sp
-    m  1 + n zero-matrix :> matrix
+    m  1 + n <zero-matrix> :> matrix
     n 4 mod n + :> stride
     0 :> i!
     0 :> j!
index 1901f27a24507e2512d93a1f956aaaa0d2f05714..26ec2689aa2e3e8811ef65a4c3678264fb1403d6 100644 (file)
@@ -1 +1,4 @@
 Slava Pestov
+Joe Groff
+Doug Coleman
+Cat Stevens
diff --git a/basis/math/matrices/elimination/authors.txt b/basis/math/matrices/elimination/authors.txt
deleted file mode 100644 (file)
index 1901f27..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Slava Pestov
diff --git a/basis/math/matrices/elimination/elimination-docs.factor b/basis/math/matrices/elimination/elimination-docs.factor
deleted file mode 100644 (file)
index 9ea3f60..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-USING: help.markup help.syntax math sequences ;
-
-IN: math.matrices.elimination
-
-HELP: inverse
-{ $values { "matrix" sequence } }
-{ $description "Computes the multiplicative inverse of a matrix. Assuming the matrix is invertible." }
-{ $examples
-  "A matrix multiplied by its inverse is the identity matrix."
-  { $example
-    "USING: kernel math.matrices math.matrices.elimination prettyprint ;"
-    "{ { 3 4 } { 7 9 } } dup inverse m. 2 identity-matrix = ."
-    "t"
-  }
-} ;
-
-HELP: echelon
-{ $values { "matrix" sequence } { "matrix'" sequence } }
-{ $description "Computes the reduced row-echelon form of the matrix." } ;
-
-HELP: nonzero-rows
-{ $values { "matrix" sequence } { "matrix'" sequence } }
-{ $description "Removes all all-zero rows from the matrix" }
-{ $examples
-  { $example
-    "USING: math.matrices.elimination prettyprint ;"
-    "{ { 0 0 } { 5 6 } { 0 0 } { 4 0 } } nonzero-rows ."
-    "{ { 5 6 } { 4 0 } }"
-  }
-} ;
-
-HELP: leading
-{ $values
-  { "seq" sequence }
-  { "n" "the index of the first match, or " { $snippet f } "." }
-  { "elt" "the first non-zero element, or " { $snippet f } "." }
-}
-{ $description "Find the first non-zero element of a sequence." } ;
diff --git a/basis/math/matrices/elimination/elimination-tests.factor b/basis/math/matrices/elimination/elimination-tests.factor
deleted file mode 100644 (file)
index cd2ed06..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-IN: math.matrices.elimination.tests
-USING: kernel math.matrices math.matrices.elimination
-tools.test sequences ;
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-        { 0 0 0 1 }
-    }
-} [
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-        { 0 0 0 1 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-        { 0 0 0 1 }
-    }
-} [
-    {
-        { 1 0 0 0 }
-        { 1 1 0 0 }
-        { 1 0 1 0 }
-        { 1 0 0 1 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-        { 0 0 0 1 }
-    }
-} [
-    {
-        { 1 0 0 0 }
-        { 1 1 0 0 }
-        { 1 0 1 0 }
-        { 1 1 0 1 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-        { 0 0 0 1 }
-    }
-} [
-    {
-        { 1 0 0 0 }
-        { 1 1 0 0 }
-        { 1 1 0 1 }
-        { 1 0 1 0 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 0 0 }
-        { 0 0 0 0 }
-    }
-} [
-    {
-        { 0 1 0 0 }
-        { 1 0 0 0 }
-        { 1 0 0 0 }
-        { 1 0 0 0 }
-    } [
-        [ 1 ] [ 0 0 pivot-row ] unit-test
-        1 0 do-row
-    ] with-matrix
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 0 0 }
-        { 0 0 0 0 }
-    }
-} [
-    {
-        { 0 1 0 0 }
-        { 1 0 0 0 }
-        { 1 0 0 0 }
-        { 1 0 0 0 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 0 1 }
-        { 0 0 0 0 }
-    }
-} [
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 1 0 0 1 }
-        { 1 0 0 1 }
-    } echelon
-] unit-test
-
-{
-    {
-        { 1 0 0 1 }
-        { 0 1 0 1 }
-        { 0 0 0 -1 }
-        { 0 0 0 0 }
-    }
-} [
-    {
-        { 0 1 0 1 }
-        { 1 0 0 1 }
-        { 1 0 0 0 }
-        { 1 1 0 1 }
-    } echelon
-] unit-test
-
-{
-    2
-} [
-    {
-        { 0 0 }
-        { 0 0 }
-    } nullspace length
-] unit-test
-
-{
-    1 3
-} [
-    {
-        { 0 1 0 1 }
-        { 1 0 0 1 }
-        { 1 0 0 0 }
-        { 1 1 0 1 }
-    } null/rank
-] unit-test
-
-{
-    1 3
-} [
-    {
-        { 0 0 0 0 0 1 0 1 }
-        { 0 0 0 0 1 0 0 1 }
-        { 0 0 0 0 1 0 0 0 }
-        { 0 0 0 0 1 1 0 1 }
-    } null/rank
-] unit-test
-
-{ { { 1 0 -1 } { 0 1 2 } } }
-[ { { 1 2 3 } { 4 5 6 } } solution ] unit-test
diff --git a/basis/math/matrices/elimination/elimination.factor b/basis/math/matrices/elimination/elimination.factor
deleted file mode 100644 (file)
index 244cf2f..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-! Copyright (C) 2006, 2010 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel locals math math.vectors math.matrices
-namespaces sequences fry sorting ;
-IN: math.matrices.elimination
-
-SYMBOL: matrix
-
-: with-matrix ( matrix quot -- )
-    matrix swap [ matrix get ] compose with-variable ; inline
-
-: nth-row ( row# -- seq ) matrix get nth ;
-
-: change-row ( ..a row# quot: ( ..a seq -- ..b seq ) -- ..b )
-    matrix get swap change-nth ; inline
-
-: exchange-rows ( row# row# -- ) matrix get exchange ;
-
-: rows ( -- n ) matrix get length ;
-
-: cols ( -- n ) 0 nth-row length ;
-
-: skip ( i seq quot -- n )
-    over [ find-from drop ] dip swap [ ] [ length ] ?if ; inline
-
-: first-col ( row# -- n )
-    ! First non-zero column
-    0 swap nth-row [ zero? not ] skip ;
-
-: clear-scale ( col# pivot-row i-row -- n )
-    overd nth dup zero? [
-        3drop 0
-    ] [
-        [ nth dup zero? ] dip swap [
-            2drop 0
-        ] [
-            swap / neg
-        ] if
-    ] if ;
-
-: (clear-col) ( col# pivot-row i -- )
-    [ [ clear-scale ] 2keep [ n*v ] dip v+ ] change-row ;
-
-: rows-from ( row# -- slice )
-    rows dup <iota> <slice> ;
-
-: clear-col ( col# row# rows -- )
-    [ nth-row ] dip [ [ 2dup ] dip (clear-col) ] each 2drop ;
-
-: do-row ( exchange-with row# -- )
-    [ exchange-rows ] keep
-    [ first-col ] keep
-    dup 1 + rows-from clear-col ;
-
-: find-row ( row# quot -- i elt )
-    [ rows-from ] dip find ; inline
-
-: pivot-row ( col# row# -- n )
-    [ dupd nth-row nth zero? not ] find-row 2nip ;
-
-: (echelon) ( col# row# -- )
-    over cols < over rows < and [
-        2dup pivot-row [ over do-row 1 + ] when*
-        [ 1 + ] dip (echelon)
-    ] [
-        2drop
-    ] if ;
-
-: echelon ( matrix -- matrix' )
-    [ 0 0 (echelon) ] with-matrix ;
-
-: nonzero-rows ( matrix -- matrix' )
-    [ [ zero? ] all? ] reject ;
-
-: null/rank ( matrix -- null rank )
-    echelon dup length swap nonzero-rows length [ - ] keep ;
-
-: leading ( seq -- n elt ) [ zero? not ] find ;
-
-: reduced ( matrix' -- matrix'' )
-    [
-        rows <iota> <reversed> [
-            dup nth-row leading drop
-            [ swap dup <iota> clear-col ] [ drop ] if*
-        ] each
-    ] with-matrix ;
-
-:: basis-vector ( row col# -- )
-    row clone :> row'
-    col# row' nth neg recip :> a
-    0 col# row' set-nth
-    a row n*v col# matrix get set-nth ;
-
-: nullspace ( matrix -- seq )
-    echelon reduced dup empty? [
-        dup first length identity-matrix [
-            [
-                dup leading drop
-                [ basis-vector ] [ drop ] if*
-            ] each
-        ] with-matrix flip nonzero-rows
-    ] unless ;
-
-: 1-pivots ( matrix -- matrix )
-    [ dup leading nip [ recip v*n ] when* ] map ;
-
-: solution ( matrix -- matrix )
-    echelon nonzero-rows reduced 1-pivots ;
-
-: inverse ( matrix -- matrix ) ! Assumes an invertible matrix
-    dup length
-    [ identity-matrix [ append ] 2map solution ] keep
-    [ tail ] curry map ;
diff --git a/basis/math/matrices/elimination/summary.txt b/basis/math/matrices/elimination/summary.txt
deleted file mode 100644 (file)
index 83972ab..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Solving systems of linear equations
index 2242715c93c8918a0c2fa711548b63e48d4aae4c..e7832d73a17f1ba1957c3fe9a97aa7cf17f481d0 100644 (file)
-USING: help.markup help.syntax math sequences ;
+! Copyright (C) 2005, 2010, 2018 Slava Pestov, Joe Groff, and Cat Stevens.
+USING: accessors arrays assocs generic.single formatting locals help.markup help.markup.private help.syntax io
+kernel math math.functions math.order math.ratios math.vectors opengl.gl prettyprint
+sequences sequences.generalizations urls ;
 IN: math.matrices
 
+<PRIVATE
+! like $subsections but skip the extra blank line
+: $subs-nobl ( children -- )
+    [ $subsection* ] each ;
+
+! swapped and n-matrix variants
+: $equiv-word-note ( children -- )
+    [ "This word is the " ] dip
+    first2
+    " variant of " swap
+    [ { $link } ] dip suffix
+    "."
+    5 narray print-element ;
+
+! words like <scale-matrix3> which have an array of inputs
+: $finite-input-note ( children -- )
+    [ "Only the first " ] dip
+    first2
+    " values in " swap
+    [ { $snippet } ] dip suffix
+    " are used."
+    5 narray print-element ;
+
+! a note for when a word assumes a 2d matrix
+: $2d-only-note ( children -- )
+    drop { "This word is intended for use with \"flat\" (2-dimensional) matrices. "
+      ! "Using it with matrices of 3 or more dimensions may lead to unexpected results."
+    }
+    print-element ;
+
+! a note for numeric-specific operations
+: $matrix-scalar-note ( children -- )
+    \ $subs-nobl prefix
+    "This word assumes that elements of the input matrix are compatible with the following words:"
+    swap 2array
+    print-element ;
+
+: $keep-shape-note ( children -- )
+    drop { "The shape of the input matrix is preserved in the output." } print-element ;
+
+: $link2 ( children -- )
+    first2 swap [ write-link ] topic-span ;
+
+! so that we don't end up with multiple $notes calls leading to multiple Notes sections
+: $notelist ( children -- )
+    \ $list prefix $notes ;
+PRIVATE>
+
+ABOUT: "math.matrices"
+
+ARTICLE: "math.matrices" "Matrix operations"
+
+"The " { $vocab-link "math.matrices" } " vocabulary implements many ways of working with " { $emphasis "matrices" } " â€” sequences which have a minimum of 2 dimensions. Operations on 1-dimensional numeric vectors are implemented in " { $vocab-link "math.vectors" } ", upon which this vocabulary relies."
+$nl
+"In this vocabulary's documentation, " { $snippet "m" } " and " { $snippet "matrix" } " are the conventional names used for a given matrix object. " { $snippet "m" } " may also refer to a number."
+$nl
+"The " { $vocab-link "math.matrices.extras" } "vocabulary implements extensions to this one."
+$nl
+"Matrices are classified their mathematical properties, and by predicate words:"
+$nl
+! split up intentionally
+{ $subsections
+    matrix
+    irregular-matrix
+    square-matrix
+    zero-matrix
+    zero-square-matrix
+    null-matrix
+
+} { $subsections
+    matrix?
+    irregular-matrix?
+    square-matrix?
+    zero-matrix?
+    zero-square-matrix?
+    null-matrix?
+
+}
+
+"There are many ways to create 2-dimensional matrices:"
+{ $subsections
+    <matrix>
+    <matrix-by>
+    <matrix-by-indices>
+
+} { $subsections
+    <zero-matrix>
+    <zero-square-matrix>
+    <diagonal-matrix>
+    <anti-diagonal-matrix>
+    <identity-matrix>
+    <simple-eye>
+    <eye>
+
+} { $subsections
+    <coordinate-matrix>
+    <square-rows>
+    <square-cols>
+    <upper-matrix>
+    <lower-matrix>
+    <cartesian-square-indices>
+}
+
+"By-element mathematical operations on a matrix:"
+{ $subsections mneg m+n m-n m*n m/n n+m n-m n*m n/m }
+
+"By-element mathematical operations of two matricess:"
+{ $subsections m+ m- m* m/ m~ }
+
+"Dot product (multiplication) of vectors and matrices:"
+{ $subsections v.m m.v m. }
+
+"Transformations and elements of matrices:"
+{ $subsections
+    dimension
+    transpose anti-transpose
+    matrix-nth matrix-nths
+    matrix-set-nth matrix-set-nths
+
+} { $subsections
+    row rows rows-except
+    col cols cols-except
+
+} { $subsections
+    matrix-except matrix-except-all
+
+} { $subsections
+    matrix-map column-map stitch
+
+} { $subsections
+    main-diagonal
+    anti-diagonal
+
+} ;
+
+! PREDICATE CLASSES
+
+HELP: matrix
+{ $class-description "The class of regular, rectangular matrices. In mathematics and linear algebra, a matrix is a rectangular collection of scalar elements for the purpose of the uniform application of algorithms." }
+{ $notes "In Factor, any sequence with two or more dimensions (one or more layers of subsequences) can be a " { $link matrix } ", and the elements may be any " { $link object } "."
+$nl "A regular matrix is a sequence with two or more dimensions, whose subsequences are all of equal length. See " { $link regular-matrix? } "." }
+$nl "Irregular matrices are classified by " { $link irregular-matrix } "." ;
+
+HELP: irregular-matrix
+{ $class-description "The most common matrix, and most easily manipulated by this vocabulary, is rectangular. This predicate classifies irregular (non-rectangular) matrices." } ;
+
+HELP: square-matrix
+{ $class-description "The class of square matrices. A square matrix is a " { $link matrix } " which has the same number of rows and columns. In other words, its outermost two dimensions are of equal size." } ;
+
 HELP: zero-matrix
-{ $values { "m" integer } { "n" integer } { "matrix" sequence } }
-{ $description "Creates a matrix of size " { $snippet "m x n" } ", filled with zeroes." } ;
+{ $class-description "The class of zero matrices. A zero matrix is a matrix whose only elements are the scalar " { $snippet "0" } "." }
+{ $notes "In mathematics, a zero-filled matrix is called a null matrix. In Factor, a "{ $link null-matrix } " is an empty matrix." } ;
 
-HELP: diagonal-matrix
-{ $values { "diagonal-seq" sequence } { "matrix" sequence } }
-{ $description "Creates a matrix with the specified diagonal values." } ;
+HELP: zero-square-matrix
+{ $class-description "The class of square zero matrices. This predicate is a composition of " { $link zero-matrix } " and " { $link square-matrix } "." } ;
 
-HELP: identity-matrix
-{ $values { "n" integer } { "matrix" sequence } }
-{ $description "Creates an identity matrix of size " { $snippet "n x n" } ", where the diagonal values are all ones." } ;
+HELP: null-matrix
+{ $class-description "The class of null matrices. A null matrix is an empty sequence, or a sequence which consists only of empty sequences." }
+{ $notes "In mathematics, a null matrix is a matrix full of zeroes. In Factor, such a matrix is called a " { $link zero-matrix } "." } ;
 
-HELP: m.v
-{ $values { "m" sequence } { "v" sequence } }
-{ $description "Computes the dot product between a matrix and a vector." }
+{ matrix irregular-matrix square-matrix zero-matrix null-matrix zero-square-matrix null-matrix } related-words
+
+! NON-PREDICATE TESTS
+
+HELP: regular-matrix?
+{ $values { "object" object } { "?" boolean } }
+{ $description "Tests if the object is a regular (well-formed, rectangular, etc) " { $link matrix } ". A regular matrix is a sequence with an equal number of elements in every row, and an equal number of elements in every column, such that there are no empty slots." }
+{ $notes "The " { $link null-matrix } " is considered regular, because of semantic requirements of the matrix implementation." }
 { $examples
-  { $example
-    "USING: math.matrices prettyprint ;"
-    "{ { 1 -1 2 } { 0 -3 1 } } { 2 1 0 } m.v ."
-    "{ 1 -3 }"
-  }
+    "The example is an irregular matrix, because the rows have an unequal number of elements."
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 } { } } regular-matrix? ."
+        "f"
+    }
+    "The example is a regular matrix, because the rows have an equal number of elements."
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 } { 2 } } regular-matrix? ."
+        "t"
+    }
 } ;
 
-HELP: m.
-{ $values { "m" sequence } }
-{ $description "Computes the dot product between two matrices, i.e multiplies them." }
+! BUILDERS
+HELP: <matrix>
+{ $values { "m" integer } { "n" integer } { "element" object } { "matrix" matrix } }
+{ $description "Creates a matrix of size " { $snippet "m x n" } ", filled with " { $snippet "element" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "3 2 10 <matrix> ."
+        "{ { 10 10 } { 10 10 } { 10 10 } }"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "4 1 \"¢\" <matrix> ."
+        "{ { \"¢\" } { \"¢\" } { \"¢\" } { \"¢\" } }"
+    }
+} ;
+
+HELP: <matrix-by>
+{ $values { "m" integer } { "n" integer } { "quot" { $quotation ( ... -- elt ) } } { "matrix" matrix } }
+{ $description "Creates a matrix of size " { $snippet "m x n" } " using elements given by " { $snippet "quot" } ", a quotation called to create each element."  }
+{ $notes "The following are equivalent:"
+  { $code "m n [ 2drop foo ] <matrix-by-indices>" }
+  { $code "m n [ foo ] <matrix-by>" }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "4 5 [ 5 ] <matrix-by> ."
+        "{ { 5 5 5 5 5 } { 5 5 5 5 5 } { 5 5 5 5 5 } { 5 5 5 5 5 } }"
+    }
+} ;
+
+HELP: <matrix-by-indices>
+{ $values { "m" integer } { "n" integer } { "quot" { $quotation ( ... m' n' -- ... elt ) } } { "matrix" matrix } }
+{ $description "Creates an " { $snippet "m x n" } " " { $link matrix } " using elements given by " { $snippet "quot" } " . This word differs from " { $link <matrix-by> } " in that the indices are placed on the stack (in the same order) before " { $snippet "quot" } " runs. The output of the quotation will be the element at the given position in the matrix." }
+{ $notes "The following are equivalent:"
+  { $code "m n [ 2drop foo ] <matrix-by-indices>" }
+  { $code "m n [ foo ] <matrix-by>" }
+}
+{ $examples
+    { $example
+        "USING: math math.matrices prettyprint ;"
+        "3 4 [ * ] <matrix-by-indices> ."
+        "{ { 0 0 0 0 } { 0 1 2 3 } { 0 2 4 6 } }"
+    }
+} ;
+
+HELP: <zero-matrix>
+{ $values { "m" integer } { "n" integer } { "matrix" matrix } }
+{ $description "Creates a matrix of size " { $snippet "m x n" } ", filled with zeroes." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "2 3 <zero-matrix> ."
+        "{ { 0 0 0 } { 0 0 0 } }"
+    }
+} ;
+
+HELP: <zero-square-matrix>
+{ $values { "n" integer } { "matrix" matrix } }
+{ $description "Creates a matrix of size " { $snippet "n x n" } ", filled with zeroes. Shorthand for " { $code "n n <zero-matrix>" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "2 <zero-square-matrix> ."
+        "{ { 0 0 } { 0 0 } }"
+    }
+} ;
+
+HELP: <diagonal-matrix>
+{ $values { "diagonal-seq" sequence } { "matrix" matrix } }
+{ $description "Creates a matrix with the specified main diagonal. This word has the opposite effect of " { $link anti-diagonal } "." }
+{ $notes "To use a diagonal starting in the lower right, reverse the input sequence before calling this word." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 1 2 3 } <diagonal-matrix> ."
+        "{ { 1 0 0 } { 0 2 0 } { 0 0 3 } }"
+    }
+} ;
+
+HELP: <anti-diagonal-matrix>
+{ $values { "diagonal-seq" sequence } { "matrix" matrix } }
+{ $description "Creates a matrix with the specified anti-diagonal. This word has the opposite effect of " { $link main-diagonal } "." }
+{ $notes "To use a diagonal starting in the lower left, reverse the input sequence before calling this word." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 1 2 3 } <anti-diagonal-matrix> ."
+        "{ { 0 0 1 } { 0 2 0 } { 3 0 0 } }"
+    }
+} ;
+
+HELP: <identity-matrix>
+{ $values { "n" integer } { "matrix" matrix } }
+{ $description "Creates an " { $url URL" http://enwp.org/Identity_matrix" "identity matrix" } " of size " { $snippet "n x n" } ", where the diagonal values are all ones." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "4 <identity-matrix> ."
+        "{ { 1 0 0 0 } { 0 1 0 0 } { 0 0 1 0 } { 0 0 0 1 } }"
+    }
+} ;
+
+HELP: <eye>
+{ $values { "m" integer } { "n" integer } { "k" integer } { "z" object } { "matrix" matrix } }
+{ $description "Creates an " { $snippet "m x n" } " matrix with a diagonal of " { $snippet "z" } " offset by " { $snippet "k" } " from the main diagonal. A positive value of " { $snippet "k" } " gives a diagonal above the main diagonal, whereas a negative value of " { $snippet "k" } " gives a diagonal below the main diagonal." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "5 6 0 4 <eye> ."
+        "{
+    { 4 0 0 0 0 0 }
+    { 0 4 0 0 0 0 }
+    { 0 0 4 0 0 0 }
+    { 0 0 0 4 0 0 }
+    { 0 0 0 0 4 0 }
+}"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "5 5 2 2 <eye> ."
+        "{
+    { 0 0 2 0 0 }
+    { 0 0 0 2 0 }
+    { 0 0 0 0 2 }
+    { 0 0 0 0 0 }
+    { 0 0 0 0 0 }
+}"
+    }
+} ;
+
+HELP: <simple-eye>
+{ $values { "m" integer } { "n" integer } { "k" integer } { "matrix" matrix } }
+{ $description
+    "Creates an " { $snippet "m x n" } " matrix with a diagonal of ones offset by " { $snippet "k" } " from the main diagonal."
+    "The following are equivalent for any " { $snippet "m n k" } ":" { $code "m n k 1 <eye>" } { $code "m n k <simple-eye>" }
+    $nl
+    "Specify a different diagonal value with " { $link <eye> } "."
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "4 5 2 <simple-eye> ."
+        "{ { 0 0 1 0 0 } { 0 0 0 1 0 } { 0 0 0 0 1 } { 0 0 0 0 0 } }"
+    }
+} ;
+
+HELP: <coordinate-matrix>
+{ $values { "dim" pair } { "coordinates" matrix } }
+{ $description "Create a matrix in which each element is its own coordinate pair, also called a " { $link cartesian-product } "." }
+{ $notelist
+    { $equiv-word-note "non-square" <cartesian-square-indices> }
+    { $finite-input-note "two" "dim" }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 2 4 } <coordinate-matrix> ."
+"{
+    { { 0 0 } { 0 1 } { 0 2 } { 0 3 } }
+    { { 1 0 } { 1 1 } { 1 2 } { 1 3 } }
+}"
+    }
+} ;
+
+HELP: <cartesian-indices>
+{ $values { "dim" pair } { "coordinates" matrix } }
+{ $description "An alias for " { $link <coordinate-matrix> } " which serves as the logical non-square companion to " { $link <cartesian-square-indices> } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 2 4 } <cartesian-indices> ."
+"{
+    { { 0 0 } { 0 1 } { 0 2 } { 0 3 } }
+    { { 1 0 } { 1 1 } { 1 2 } { 1 3 } }
+}"
+    }
+} ;
+
+HELP: <cartesian-square-indices>
+{ $values { "n" integer } { "matrix" square-matrix } }
+{ $description "Create a " { $link square-matrix } " full of " { $link cartesian-product } "s. See " { $url URL" https://en.wikipedia.org/wiki/Cartesian_product" "cartesian product" } "." }
+{ $notes
+    { $equiv-word-note "square" <cartesian-indices> }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "1 <cartesian-square-indices> ."
+        "{ { { 0 0 } } }"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "3 <cartesian-square-indices> ."
+"{
+    { { 0 0 } { 0 1 } { 0 2 } }
+    { { 1 0 } { 1 1 } { 1 2 } }
+    { { 2 0 } { 2 1 } { 2 2 } }
+}"
+    }
+} ;
+
+HELP: <square-rows>
+{ $values { "desc" { $or sequence integer matrix } } { "matrix" matrix } }
+{ $contract "Generate a " { $link square-matrix } " from a descriptor." }
+{ $description "If the descriptor is an " { $link integer } ", it is used to generate square rows within that range." $nl "If it is a 1-dimensional sequence, it is " { $link replicate } "d to create each row." $nl "If it is a " { $link matrix } ", it is cropped into a " { $link square-matrix } "." $nl "If it is a " { $link square-matrix } ", it is returned unchanged." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "3 <square-rows> ."
+        "{ { 0 1 2 } { 0 1 2 } { 0 1 2 } }"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 2 3 5 } <square-rows> ."
+        "{ { 2 3 5 } { 2 3 5 } { 2 3 5 } }"
+    }
+} ;
+
+HELP: <square-cols>
+{ $values { "desc" { $or sequence integer matrix } } { "matrix" matrix } }
+{ $contract "Generate a " { $link square-matrix } " from a descriptor." }
+{ $description "If the descriptor is an " { $link integer } ", it is used to generate square columns within that range." $nl "If it is a 1-dimensional sequence, it is " { $link replicate } "d to create each column." $nl "If it is a " { $link matrix } ", it is cropped into a " { $link square-matrix } "." $nl "If it is a " { $link square-matrix } ", it is returned unchanged." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "3 <square-cols> ."
+        "{ { 0 0 0 } { 1 1 1 } { 2 2 2 } }"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 2 3 5 } <square-cols> ."
+        "{ { 2 2 2 } { 3 3 3 } { 5 5 5 } }"
+    }
+} ;
+
+HELP: <lower-matrix>
+{ $values { "object" object } { "m" integer } { "n" integer } { "matrix" matrix } }
+{ $description "Make a lower triangular matrix, where all the values above the main diagonal are " { $snippet "0" } ". " { $snippet "object" } " will be used as the value for the nonzero part of the matrix, while " { $snippet "m" } " and " { $snippet "n" } " are used as the dimensions. The inverse of this word is " { $link <upper-matrix> } ". See " { $url URL" https://en.wikipedia.org/wiki/Triangular_matrix" "triangular matrix" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "1 5 5 <lower-matrix> ."
+"{
+    { 1 0 0 0 0 }
+    { 1 1 0 0 0 }
+    { 1 1 1 0 0 }
+    { 1 1 1 1 0 }
+    { 1 1 1 1 1 }
+}"
+    }
+} ;
+
+HELP: <upper-matrix>
+{ $values { "object" object } { "m" integer } { "n" integer } { "matrix" matrix } }
+{ $description "Make an upper triangular matrix, where all the values below the main diagonal are " { $snippet "0" } ". " { $snippet "object" } " will be used as the value for the nonzero part of the matrix, while " { $snippet "m" } " and " { $snippet "n" } " are used as the dimensions. The inverse of this word is " { $link <lower-matrix> } ". See " { $url URL" https://en.wikipedia.org/wiki/Triangular_matrix" "triangular matrix" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "1 5 5 <upper-matrix> ."
+"{
+    { 1 1 1 1 1 }
+    { 0 1 1 1 1 }
+    { 0 0 1 1 1 }
+    { 0 0 0 1 1 }
+    { 0 0 0 0 1 }
+}"
+    }
+} ;
+
+HELP: stitch
+{ $values { "m" matrix } { "m'" matrix } }
+{ $description "Folds an " { $snippet "n>2" } "-dimensional matrix onto itself." }
+{ $examples
+    { $unchecked-example
+        "USING: math.matrices prettyprint ;"
+"{
+    { { 0 5 } { 6 7 } { 0 15 } { 18 21 } }
+    { { 0 10 } { 12 14 } { 0 20 } { 24 28 } }
+} stitch ."
+"{
+    { 0 5 0 10 }
+    { 6 7 12 14 }
+    { 0 15 0 20 }
+    { 18 21 24 28 }
+}"
+    }
+} ;
+
+HELP: row
+{ $values { "n" integer } { "matrix" matrix } { "row" sequence } }
+{ $description "Get the nth row of the matrix." }
+{ $notes "Like most Factor sequences, indexing is 0-based. The first row is given by " { $snippet "m 0 row" } "." }
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "{ { 1 2 } { 3 4 } } 1 swap row ."
+        "{ 3 4 }"
+    }
+} ;
+
+HELP: rows
+{ $values { "seq" sequence } { "matrix" matrix } { "rows" sequence } }
+{ $description "Get the rows from " { $snippet "matrix" } " listed by " { $snippet "seq" } "." }
+{ $notelist { $equiv-word-note "multiplexing" row } }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 0 1 } { { 1 2 } { 3 4 } } rows ."
+        "{ { 1 2 } { 3 4 } }"
+    }
+} ;
+
+HELP: col
+{ $values { "n" integer } { "matrix" matrix } { "col" sequence } }
+{ $description "Get the " { $snippet "n" } "th column of the matrix." }
+{ $notes "Like most Factor sequences, indexing is 0-based. The first column is given by " { $snippet "m 0 col" } "." }
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "{ { 1 2 } { 3 4 } } 1 swap col ."
+        "{ 2 4 }"
+    }
+} ;
+
+HELP: cols
+{ $values { "seq" sequence } { "matrix" matrix } { "cols" sequence } }
+{ $description "Get the columns from " { $snippet "matrix" } " listed by " { $snippet "seq" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 0 1 } { { 1 2 } { 3 4 } } cols ."
+        "{ { 1 3 } { 2 4 } }"
+    }
+} ;
+
+HELP: matrix-map
+{ $values { "matrix" matrix } { "quot" { $quotation ( ... elt -- ... elt' ) } } { "matrix'" matrix } }
+{ $description "Apply the quotation to every element of the matrix." }
+{ $notelist $2d-only-note }
+{ $examples
+    { $example
+        "USING: math.matrices kernel math prettyprint ;"
+        "3 <identity-matrix> [ zero? 15 -8 ? ] matrix-map ."
+        "{ { -8 15 15 } { 15 -8 15 } { 15 15 -8 } }"
+    }
+} ;
+
+HELP: column-map
+{ $values { "matrix" matrix } { "quot" { $quotation ( ... col -- ... col' ) } } { "matrix'" { $maybe sequence matrix } } }
+{ $description "Apply the quotation to every column of the matrix. The output of the quotation must be a sequence." }
+{ $notelist $2d-only-note { $equiv-word-note "transpose" map } }
 { $examples
-  { $example
-    "USING: math.matrices prettyprint ;"
-    "{ { 1 -1 2 } { 0 -3 1 } } { { 3 7 } { 9 12 } } m. ."
-    "{ { -6 -5 } { -27 -36 } }"
-  }
+    { $example
+        "USING: sequences math.matrices prettyprint ;"
+        "3 <identity-matrix> [ reverse ] column-map ."
+        "{ { 0 0 1 } { 0 1 0 } { 1 0 0 } }"
+    }
+} ;
+
+HELP: matrix-nth
+{ $values { "pair" pair } { "matrix" matrix } { "elt" object } }
+{ $description "Retrieve the element in the matrix at the zero-indexed " { $snippet "row, column" } " pair." }
+{ $notelist { $equiv-word-note "two-dimensional" nth } $2d-only-note }
+{ $errors { $list
+    { { $link bounds-error } " if the first element in " { $snippet "pair" } " is greater than the maximum row index in " { $snippet "matrix" } }
+    { { $link bounds-error } " if the second element in " { $snippet "pair" } " is greater than the maximum column index in " { $snippet "matrix" } }
+} }
+{ $examples
+    "Get the entry at row 1, column 0."
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 1 0 } { { 0 1 } { 2 3 } } matrix-nth ."
+        "2"
+    }
+} ;
+
+HELP: matrix-nths
+{ $values { "pairs" assoc } { "matrix" matrix } { "elts" sequence } }
+{ $description "Retrieve all the elements in the matrix at each of the zero-indexed " { $snippet "row, column" } " pairs in " { $snippet "pairs" } "." }
+{ $notelist { $equiv-word-note "two-dimensional" nths } $2d-only-note }
+{ $errors { $list
+    { { $link bounds-error } " if the first element of a pair in " { $snippet "pairs" } " is greater than the maximum row index in " { $snippet "matrix" } }
+    { { $link bounds-error } " if the second element of a pair in " { $snippet "pairs" } " is greater than the maximum column index in " { $snippet "matrix" } }
+} }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 0 } { 1 1 } } { { 0 1 } { 2 3 } } matrix-nths ."
+        "{ 2 3 }"
+    }
+} ;
+
+HELP: matrix-set-nth
+{ $values { "obj" object } { "pair" pair } { "matrix" matrix } }
+{ $description "Set the element in the matrix at the 2D index given by " { $snippet "pair" } " to " { $snippet "obj" } ". This operation is destructive." }
+{ $side-effects "matrix" }
+{ $notelist { $equiv-word-note "two-dimensional" set-nth } $2d-only-note  }
+{ $errors { $list
+    { { $link bounds-error } " if the first element of a pair in " { $snippet "pairs" } " is greater than the maximum row index in " { $snippet "matrix" } }
+    { { $link bounds-error } " if the second element of a pair in " { $snippet "pairs" } " is greater than the maximum column index in " { $snippet "matrix" } }
+    "Throws an error if the sequence cannot hold elements of the given type."
+} }
+{ $examples
+    "Change the entry at row 1, column 0."
+    { $example
+        "USING: math.matrices kernel prettyprint ;"
+        "{ { 0 1 } { 2 3 } } \"a\" { 1 0 } pick matrix-set-nth ."
+        "{ { 0 1 } { \"a\" 3 } }"
+    }
+} ;
+
+HELP: matrix-set-nths
+{ $values { "obj" object } { "pairs" assoc } { "matrix" matrix } }
+{ $description "Applies " { $link matrix-set-nth } " to " { $snippet "matrix" } " for each " { $snippet "row, column" } " pair in " { $snippet "pairs" } ", setting the elements to " { $snippet "obj" } "." }
+{ $side-effects "matrix" }
+{ $notelist { $equiv-word-note "multiplexing" matrix-set-nth } $2d-only-note }
+{ $errors { $list
+    { { $link bounds-error } " if the first element of a pair in " { $snippet "pairs" } " is greater than the maximum row index in " { $snippet "matrix" } }
+    { { $link bounds-error } " if the second element of a pair in " { $snippet "pairs" } " is greater than the maximum column index in " { $snippet "matrix" } }
+    "Throws an error if the sequence cannot hold elements of the given type."
+} }
+{ $examples
+    "Change both entries on row 0."
+    { $example
+        "USING: math.matrices kernel prettyprint ;"
+        "{ { 0 1 } { 2 3 } } \"a\" { { 1 0 } { 1 1 } } pick matrix-set-nths ."
+        "{ { 0 1 } { \"a\" \"a\" } }"
+    }
+} ;
+
+
+HELP: mneg
+{ $values { "m" matrix } { "m'" matrix } }
+{ $description "Negate (invert the sign) of every element in the matrix. The resulting matrix is called the " { $emphasis "additive inverse" } " of the input matrix." }
+{ $notelist
+    { $equiv-word-note "companion" mabs }
+    $2d-only-note
+    { $matrix-scalar-note neg }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 -17 } } mneg ."
+        "{ { -5 -9 } { -15 17 } }"
+    }
+} ;
+
+HELP: mabs
+{ $values { "m" matrix } { "m'" matrix } }
+{ $description "Compute the absolute value (" { $link abs } ") of each element in the matrix." }
+{ $notelist
+    { $equiv-word-note "companion" mneg }
+    $2d-only-note
+    { $matrix-scalar-note abs }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { -5 -9 } { -15 17 } } mabs ."
+        "{ { 5 9 } { 15 17 } }"
+    }
+} ;
+
+HELP: n+m
+{ $values { "n" object } { "m" matrix }  }
+{ $description { $snippet "n" } " is treated as a scalar and added to each element of the matrix " { $snippet "m" } "." }
+{ $notelist
+    { $equiv-word-note "swapped" m+n }
+    $2d-only-note
+    { $matrix-scalar-note + }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "1 3 <identity-matrix> n+m ."
+        "{ { 2 1 1 } { 1 2 1 } { 1 1 2 } }"
+    }
+} ;
+
+HELP: m+n
+{ $values { "m" matrix } { "n" object } }
+{ $description { $snippet "n" } " is treated as a scalar and added to each element of the matrix " { $snippet "m" } "." }
+{ $notelist
+    { $equiv-word-note "swapped" n+m }
+    $2d-only-note
+    { $matrix-scalar-note + }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "3 <identity-matrix> 1 m+n ."
+        "{ { 2 1 1 } { 1 2 1 } { 1 1 2 } }"
+    }
+} ;
+
+HELP: n-m
+{ $values { "n" object } { "m" matrix }  }
+{ $description { $snippet "n" } " is treated as a scalar and subtracted from each element of the matrix " { $snippet "m" } "." }
+{ $notelist
+    { $equiv-word-note "swapped" m-n }
+    $2d-only-note
+    { $matrix-scalar-note - }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "1 3 <identity-matrix> n-m ."
+        "{ { 0 1 1 } { 1 0 1 } { 1 1 0 } }"
+    }
+} ;
+
+HELP: m-n
+{ $values { "m" matrix } { "n" object } }
+{ $description { $snippet "n" } " is treated as a scalar and subtracted from each element of the matrix " { $snippet "m" } "." }
+{ $notelist
+    { $equiv-word-note "swapped" n-m }
+    $2d-only-note
+    { $matrix-scalar-note - }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "3 <identity-matrix> 1 m-n ."
+        "{ { 0 -1 -1 } { -1 0 -1 } { -1 -1 0 } }"
+    }
+} ;
+
+HELP: n*m
+{ $values { "n" object } { "m" matrix }  }
+{ $description "Every element in the input matrix " { $snippet "m" } " is multiplied by the scalar "{ $snippet "n" } "." }
+{ $notelist
+    $keep-shape-note
+    { $equiv-word-note "swapped" m*n }
+    $2d-only-note
+    { $matrix-scalar-note * }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "3 3 <identity-matrix> n*m ."
+        "{ { 3 0 0 } { 0 3 0 } { 0 0 3 } }"
+    }
+} ;
+
+HELP: m*n
+{ $values { "m" matrix } { "n" object } }
+{ $description "Every element in the input matrix " { $snippet "m" } " is multiplied by the scalar "{ $snippet "n" } "." }
+{ $notelist
+    $keep-shape-note
+    { $equiv-word-note "swapped" n*m }
+    $2d-only-note
+    { $matrix-scalar-note * }
+}
+
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "3 <identity-matrix> 3 m*n ."
+        "{ { 3 0 0 } { 0 3 0 } { 0 0 3 } }"
+    }
+} ;
+
+HELP: n/m
+{ $values { "n" object } { "m" matrix }  }
+{ $description "Every element in the input matrix " { $snippet "m" } " is divided by the scalar "{ $snippet "n" } "." }
+{ $notelist
+    $keep-shape-note
+    { $equiv-word-note "swapped" m/n }
+    $2d-only-note
+    { $matrix-scalar-note / }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "2 { { 4 5 } { 2 1 } } n/m ."
+        "{ { 1/2 2/5 } { 1 2 } }"
+    }
+} ;
+
+HELP: m/n
+{ $values { "m" matrix } { "n" object } }
+{ $description "Every element in the input matrix " { $snippet "m" } " is divided by the scalar "{ $snippet "n" } "." }
+{ $notelist
+    $keep-shape-note
+    { $equiv-word-note "swapped" n/m }
+    $2d-only-note
+    { $matrix-scalar-note / }
+}
+{ $examples
+    { $example
+        "USING: kernel math.matrices prettyprint ;"
+        "{ { 4 5 } { 2 1 } } 2 m/n ."
+        "{ { 2 2+1/2 } { 1 1/2 } }"
+    }
 } ;
 
 HELP: m+
-{ $values { "m" sequence } }
-{ $description "Adds the matrices component-wise." }
+{ $values { "m1" matrix } { "m2" matrix } { "m" matrix } }
+{ $description "Adds two matrices element-wise." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note + }
+}
 { $examples
-  { $example
-    "USING: math.matrices prettyprint ;"
-    "{ { 1 2 } { 3 4 } } { { 5 6 } { 7 8 } } m+ ."
-    "{ { 6 8 } { 10 12 } }"
-  }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 2 3 } { 3 2 1 } } { { 4 5 6 } { 6 5 4 } } m+ ."
+        "{ { 5 7 9 } { 9 7 5 } }"
+    }
 } ;
 
 HELP: m-
-{ $values { "m" sequence } }
-{ $description "Subtracts the matrices component-wise." }
+{ $values { "m1" matrix } { "m2" matrix } { "m" matrix } }
+{ $description "Subtracts two matrices element-wise." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note - }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 4 5 6 } { 6 5 4 } } { { 1 2 3 } { 3 2 1 } } m- ."
+        "{ { 3 3 3 } { 3 3 3 } }"
+    }
+} ;
+
+HELP: m*
+{ $values { "m1" matrix } { "m2" matrix } { "m" matrix } }
+{ $description "Multiplies two matrices element-wise." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note * }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } { { 3 2 } { 4 9 } } m* ."
+        "{ { 15 18 } { 60 153 } }"
+    }
+} ;
+
+HELP: m/
+{ $values { "m1" matrix } { "m2" matrix } { "m" matrix } }
+{ $description "Divides two matrices element-wise." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note / }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } { { 3 2 } { 4 9 } } m/ ."
+        "{ { 1+2/3 4+1/2 } { 3+3/4 1+8/9 } }"
+    }
+} ;
+
+HELP: m.v
+{ $values { "m" matrix } { "v" sequence } { "p" matrix } }
+{ $description "Computes the dot product of a matrix and a vector." }
+{ $notelist
+    { $equiv-word-note "swapped" v.m }
+    $2d-only-note
+    { $matrix-scalar-note * + }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 -1 2 } { 0 -3 1 } } { 2 1 0 } m.v ."
+        "{ 1 -3 }"
+    }
+} ;
+
+HELP: v.m
+{ $values { "v" sequence } { "m" matrix } { "p" matrix } }
+{ $description "Computes the dot product of a vector and a matrix." }
+{ $notelist
+    { $equiv-word-note "swapped" m.v }
+    $2d-only-note
+    { $matrix-scalar-note * + }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ 2 1 0 } { { 1 -1 2 } { 0 -3 1 } } v.m ."
+        "{ 2 -5 5 }"
+    }
+} ;
+
+HELP: m.
+{ $values { "m" matrix } }
+{ $description "Computes the dot product of two matrices, i.e multiplies them." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note * + }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 1 -1 2 } { 0 -3 1 } } { { 3 7 } { 9 12 } } m. ."
+        "{ { -6 -5 } { -27 -36 } }"
+    }
+} ;
+
+HELP: m~
+{ $values { "m1" matrix } { "m2" matrix } { "epsilon" number } { "?" boolean } }
+{ $description "Compares the matrices like " { $link ~ } ", using the " { $snippet "epsilon" } "." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note ~ }
+}
 { $examples
-  { $example
-    "USING: math.matrices prettyprint ;"
-    "{ { 5 9 } { 15 17 } } { { 3 2 } { 4 9 } } m- ."
-    "{ { 2 7 } { 11 8 } }"
-  }
+    { "In the example, only " { $snippet ".01" } " was added to each element, so the new matrix is within the epsilon " { $snippet ".1" } "of the original." }
+    { $example
+        "USING: kernel math math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } dup [ .01 + ] matrix-map .1 m~ ."
+        "t"
+    }
 } ;
 
-HELP: kron
-{ $values { "m1" sequence } { "m2" sequence } { "m" sequence } }
-{ $description "Calculates the Kronecker product of two matrices." }
+HELP: mmin
+{ $values { "m" matrix } { "n" object } }
+{ $description "Determine the minimum value of the matrix." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note min }
+}
 { $examples
-    { $example "USING: math.matrices prettyprint ;"
-        "{ { 1 2 } { 3 4 } } { { 0 5 } { 6 7 } } kron ."
-        "{ { 0 5 0 10 } { 6 7 12 14 } { 0 15 0 20 } { 18 21 24 28 } }" }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } mmin ."
+        "5"
+    }
 } ;
 
-HELP: outer
-{ $values { "u" sequence } { "v" sequence } { "m" sequence } }
-{ $description "Computers the outer product of " { $snippet "u" } " and " { $snippet "v" } "." }
+HELP: mmax
+{ $values { "m" matrix } { "n" object } }
+{ $description "Determine the maximum value of the matrix." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note max }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } mmax ."
+        "17"
+    }
+} ;
+
+HELP: mnorm
+{ $values { "m" matrix } { "m'" matrix } }
+{ $description "Normalize a matrix. Each element from the input matrix is computed as a fraction of the maximum element. The maximum element becomes " { $snippet "1/1" } "." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note max abs / }
+}
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 5 9 } { 15 17 } } mnorm ."
+        "{ { 5/17 9/17 } { 15/17 1 } }"
+    }
+} ;
+
+HELP: m-infinity-norm
+{ $values { "m" matrix } { "n" number } } ;
+
+HELP: m-1norm
+{ $values { "m" matrix } { "n" number } } ;
+
+HELP: frobenius-norm
+{ $values { "m" matrix } { "n" number } }
+{ $notes "Also known as the Hilbert-Schmidt norm." } ;
+
+HELP: >square-matrix
+{ $values { "m" matrix } { "subset" square-matrix } }
+{ $description "Find only the " { $link2 square-matrix "square" } " subset of the input matrix." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 0 2 4 6 } { 1 3 5 7 } } >square-matrix ."
+        "{ { 0 2 } { 1 3 } }"
+    }
+} ;
+
+HELP: main-diagonal
+{ $values { "matrix" matrix } { "seq" sequence } }
+{ $description "Find the main diagonal of a matrix." $nl "This diagonal begins in the upper left of the matrix at index " { $snippet "{ 0 0 }" } ", continuing downward and rightward for all indices " { $snippet "{ n n }" } " in the " { $link square-matrix } " subset of the input (see " { $link <square-rows> } ")." }
+{ $notelist
+    { "If the number of rows in the square subset of the input is even, then this diagonal will not contain elements found in the " { $link anti-diagonal } ". However, if the size of the square subset is odd, then this diagonal will share at most one element with " { $link anti-diagonal } "." }
+    { "This diagonal is sometimes called the " { $emphasis "first diagonal" } "." }
+    { $equiv-word-note "opposite" anti-diagonal }
+}
+{ $examples
+    { "The operation is simple on a " { $link square-matrix } ":" }
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 7 2 11 }
+    { 9 7 7 }
+    { 1 8 0 }
+} main-diagonal ."
+        "{ 7 7 0 }"
+    }
+    "The square subset of the following input matrix consists of all rows but the last. The main diagonal does not include the last row because it has no fourth element."
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 6 5 0 }
+    { 7 2 6 }
+    { 4 3 9 }
+    { 3 3 3 }
+} main-diagonal ."
+        "{ 6 2 9 }"
+    }
+} ;
+
+HELP: anti-diagonal
+{ $values { "matrix" matrix } { "seq" sequence } }
+{ $description "Find the anti-diagonal of a matrix." $nl "This diagonal begins in the upper right of the matrix, continuing downward and leftward for all indices in the " { $link square-matrix } " subset of the input (see " { $link <square-rows> } ")." }
+{ $notelist
+    { "If the number of rows in the square subset of the input is even, then this diagonal will not contain elements found in the " { $link main-diagonal } ". However, if the size of the square subset is odd, then this diagonal will share at most one element with " { $link main-diagonal } "." }
+    { "This diagonal is sometimes called the " { $emphasis "second diagonal" } "." }
+    { $equiv-word-note "opposite" main-diagonal }
+}
+{ $examples
+    { "The operation is simple on a " { $link square-matrix } ":" }
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 7 2 11 }
+    { 9 7 7 }
+    { 1 8 0 }
+} anti-diagonal ."
+        "{ 11 7 1 }"
+    }
+    "The square subset of the following input matrix consists of all rows but the last. The anti-diagonal does not include the last row because it has no fourth element."
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 6 5 0 }
+    { 7 2 6 }
+    { 4 3 9 }
+    { 3 3 3 }
+} anti-diagonal ."
+        "{ 0 2 4 }"
+    }
+} ;
+
+
+HELP: transpose
+{ $values { "matrix" matrix } { "newmatrix" matrix } }
+{ $description "Transpose the input matrix over its " { $link main-diagonal } ". The main diagonal itself is preserved, whereas the anti-diagonal is reversed." }
+{ $notelist
+    { "This word is an alias for " { $link flip } ", so that it may be recognised as the common mathematical operation." }
+    { $equiv-word-note "opposite" anti-transpose }
+}
+{ $examples
+    { $example
+        "USING: math.matrices sequences prettyprint ;"
+        "5 <iota> <anti-diagonal-matrix> transpose ."
+"{
+    { 0 0 0 0 4 }
+    { 0 0 0 3 0 }
+    { 0 0 2 0 0 }
+    { 0 1 0 0 0 }
+    { 0 0 0 0 0 }
+}"
+    }
+} ;
+
+HELP: anti-transpose
+{ $values { "matrix" matrix } { "newmatrix" matrix } }
+{ $description "Like " { $link transpose } " except that the matrix is transposed over the " { $link anti-diagonal } ", so that the anti-diagonal itself is preserved and the " { $link main-diagonal } " is reversed." }
+{ $notes { $equiv-word-note "opposite" transpose } }
+{ $examples
+    { $example
+        "USING: math.matrices sequences prettyprint ;"
+        "5 <iota> <diagonal-matrix> anti-transpose ."
+"{
+    { 4 0 0 0 0 }
+    { 0 3 0 0 0 }
+    { 0 0 2 0 0 }
+    { 0 0 0 1 0 }
+    { 0 0 0 0 0 }
+}"
+    }
+} ;
+
+HELP: rows-except
+{ $values { "matrix" matrix } { "desc" { $or integer sequence } } { "others" matrix } }
+{ $contract "Get all the rows from " { $snippet "matrix" } " " { $emphasis "not" } " described by " { $snippet "desc" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 2 7 12 2 }
+    { 8 9 10 0 }
+    { 1 3 3 5 }
+    { 8 13 7 12 }
+} { 1 3 } rows-except ."
+        "{ { 2 7 12 2 } { 1 3 3 5 } }"
+    }
+} ;
+
+HELP: cols-except
+{ $values { "matrix" matrix } { "desc" { $or integer sequence } } { "others" matrix } }
+{ $contract "Get all the columns from " { $snippet "matrix" } " " { $emphasis "not" } " described by " { $snippet "desc" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+"{
+    { 2 7 12 2 }
+    { 8 9 10 0 }
+    { 1 3 3 5 }
+    { 8 13 7 12 }
+} { 1 3 } cols-except . "
+        "{ { 2 12 } { 8 10 } { 1 3 } { 8 7 } }"
+    }
+} ;
+HELP: matrix-except
+{ $values { "matrix" matrix } { "exclude-pair" pair } { "submatrix" matrix } }
+{ $description "Get all the rows and columns from " { $snippet "matrix" } " except the row and column given in " { $snippet "exclude-pair" } ". The result is the " { $snippet "submatrix" } " containing no values from the given row and column." }
+{ $examples
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 0 1 } { 2 3 } } { 0 1 } matrix-except ."
+        "{ { 2 } }"
+    }
+} ;
+
+HELP: submatrix-excluding
+{ $values { "matrix" matrix } { "exclude-pair" pair } { "submatrix" matrix } }
+{ $description "A possibly more obvious word for " { $link matrix-except } "." } ;
+
+HELP: matrix-except-all
+{ $values { "matrix" matrix } { "submatrices" { $sequence matrix } } }
+{ $description "Find every possible submatrix of " { $snippet "matrix" } " by using " { $link matrix-except } " for every value's row-column pair." }
+{ $examples
+    "There are 9 possible 2x2 submatrices of a 3x3 matrix with 9 indices, because there are 9 indices to exclude creating a new submatrix."
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ { 0 1 2 } { 3 4 5 } { 6 7 8 } } matrix-except-all ."
+        "{
+    {
+        { { 4 5 } { 7 8 } }
+        { { 3 5 } { 6 8 } }
+        { { 3 4 } { 6 7 } }
+    }
+    {
+        { { 1 2 } { 7 8 } }
+        { { 0 2 } { 6 8 } }
+        { { 0 1 } { 6 7 } }
+    }
+    {
+        { { 1 2 } { 4 5 } }
+        { { 0 2 } { 3 5 } }
+        { { 0 1 } { 3 4 } }
+    }
+}"
+    }
+} ;
+
+HELP: all-submatrices
+{ $values { "matrix" matrix } { "submatrices" { $sequence matrix } } }
+{ $description "A possibly more obvious name for " { $link matrix-except-all } "." } ;
+
+HELP: dimension
+{ $values { "matrix" matrix } { "dimension" pair } }
+{ $description "Find the dimension of the input matrix, in the order of " { $snippet "{ rows cols }"} "." }
+{ $notelist $2d-only-note "Not to be confused with dimensionality, or the number of dimension scalars needed to describe a matrix." }
 { $examples
-    { $example "USING: math.matrices prettyprint ;"
-        "{ 5 6 7 } { 1 2 3 } outer ."
-        "{ { 5 10 15 } { 6 12 18 } { 7 14 21 } }" }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "4 30 1 <matrix> dimension ."
+        "{ 4 30 }"
+    }
+    { $example
+        "USING: math.matrices prettyprint ;"
+        "{ } dimension ."
+        "{ 0 0 }"
+    }
 } ;
index f82fef7c857f2ffd10d7339f69e81e25d632a157..02139fb856b1e2091ba717a94ef65848f6277bea 100644 (file)
-USING: math.matrices math.vectors tools.test math kernel ;
-IN: math.matrices.tests
+! Copyright (C) 2005, 2010, 2018 Slava Pestov, Joe Groff, and Cat Stevens.
+USING: arrays combinators.short-circuit grouping kernel math math.matrices math.matrices.private
+math.statistics math.vectors sequences sequences.deep sets tools.test ;
+IN: math.matrices
+
+<PRIVATE
+: call-eq? ( obj quots -- ? )
+    [ call( x -- x ) ] with map all-eq? ; !  inline
+PRIVATE>
+! ------------------------
+! predicates
+
+{ t } [ { }                 regular-matrix? ] unit-test
+{ t } [ { { } }             regular-matrix? ] unit-test
+{ t } [ { { 1 2 } }         regular-matrix? ] unit-test
+{ t } [ { { 1 2 } { 3 4 } } regular-matrix? ] unit-test
+{ t } [ { { 1 } { 3 } }     regular-matrix? ] unit-test
+{ f } [ { { 1 2 } { 3 } }   regular-matrix? ] unit-test
+{ f } [ { { 1 } { 3 2 } }   regular-matrix? ] unit-test
 
-{
-    { { 0 } { 0 } { 0 } }
-} [
-    3 1 zero-matrix
-] unit-test
 
-{
-    { { 1 0 0 }
-       { 0 1 0 }
-       { 0 0 1 } }
-} [
-    3 identity-matrix
-] unit-test
+{ t } [ { } square-matrix? ] unit-test
+{ t } [ { { 1 } } square-matrix? ] unit-test
+{ t } [ { { 1 2 } { 3 4 } } square-matrix? ] unit-test
+{ f } [ { { 1 } { 2 3 } } square-matrix? ] unit-test
+{ f } [ { { 1 2 } } square-matrix? ] unit-test
 
-{
-    { { 1 0 0 }
-       { 0 2 0 }
-       { 0 0 3 } }
-} [
-    { 1 2 3 } diagonal-matrix
+! any deep-empty matrix is null
+! it doesn't make any sense for { } to be null while { { } } to be considered nonnull
+{ t } [ {
+    { }
+    { { } }
+    { { { } } }
+    { { } { } { } }
+    { { { } } { { { } } } }
+} [ null-matrix? ] map [ ] all?
 ] unit-test
 
-{
-    { { 1 1 1 }
-      { 4 2 1 }
-      { 9 3 1 }
-      { 25 5 1 } }
-} [
-    { 1 2 3 5 } 3 vandermonde-matrix
+{ f } [ {
+    { 1 2 }
+    { { 1 2 } }
+    { { 1 } { 2 } }
+    { { { 1 } } { 2 } { } }
+} [ null-matrix? ] map [ ] any?
 ] unit-test
 
-{
-    {
-        { 1 0 0 }
-        { 0 1 0 }
-        { 0 0 1 }
-    }
-} [
-    3 3 0 eye
+{ t } [ 10 dup <zero-matrix> zero-matrix? ] unit-test
+{ t } [ 10 10 15 <simple-eye> zero-matrix? ] unit-test
+{ t } [ 0 dup <zero-matrix> null-matrix? ] unit-test
+{ f } [ 0 dup <zero-matrix> zero-matrix? ] unit-test
+{ f } [ 4 <identity-matrix> zero-matrix? ] unit-test
+! make sure we're not using the sum-to-zero strategy
+{ f } [ { { 0 -2 } { 1 -1 } } zero-matrix? ] unit-test
+{ f } [ { { 0 0 } { 1 -1 } } zero-matrix? ] unit-test
+{ f } [ { { 0 1 } { 0 -1 } } zero-matrix? ] unit-test
+
+! nth etc
+
+{ 3 } [ { 1 2 3 } 0 swap nth-end ] unit-test
+{ 2 } [ { 1 2 3 } 1 swap nth-end ] unit-test
+{ 1 } [ { 1 2 3 } 2 swap nth-end ] unit-test
+
+[ { 1 2 3 } -1 swap nth-end ] [ bounds-error? ] must-fail-with
+[ { 1 2 3 } 3 swap nth-end ] [ bounds-error? ] must-fail-with
+[ { 1 2 3 } 4 swap nth-end ] [ bounds-error? ] must-fail-with
+
+{ { 0 0 1 } } [ { 0 0 0 } dup 1 0 rot set-nth-end ] unit-test
+{ { 0 2 0 } } [ { 0 0 0 } dup 2 1 rot set-nth-end ] unit-test
+{ { 3 0 0 } } [ { 0 0 0 } dup 3 2 rot set-nth-end ] unit-test
+
+[ { 0 0 0 } dup 1 -1 rot set-nth-end ] [ bounds-error? ] must-fail-with
+[ { 0 0 0 } dup 2 3 rot set-nth-end ] [ bounds-error? ] must-fail-with
+[ { 0 0 0 } dup 3 4 rot set-nth-end ] [ bounds-error? ] must-fail-with
+
+! constructors
+
+{ {
+    { 5 5 }
+    { 5 5 }
+} } [ 2 2 5 <matrix> ] unit-test
+! a matrix-matrix
+{ { {
+    { { -1 -1 } { -1 -1 } }
+    { { -1 -1 } { -1 -1 } }
+    { { -1 -1 } { -1 -1 } }
+} {
+    { { -1 -1 } { -1 -1 } }
+    { { -1 -1 } { -1 -1 } }
+    { { -1 -1 } { -1 -1 } }
+} } } [ 2 3 2 2 -1 <matrix> <matrix> ] unit-test
+
+{ {
+    { 5 5 }
+    { 5 5 }
+} } [ 2 2 [ 5 ] <matrix-by> ] unit-test
+{ {
+    { 6 6 }
+    { 6 6 }
+} } [ 2 2 [ 3 2 * ] <matrix-by> ] unit-test
+
+{ {
+    { 0 1 2 }
+    { 1 2 3 }
+} } [ 2 3 [ + ] <matrix-by-indices> ] unit-test
+{ {
+    { 0 0 0 }
+    { 0 1 2 }
+    { 0 2 4 }
+} } [ 3 3 [ * ] <matrix-by-indices> ] unit-test
+
+{ t } [ 3 3 <zero-matrix> zero-square-matrix? ] unit-test
+{ t } [ 3 <zero-square-matrix> zero-square-matrix? ] unit-test
+{ t f } [ 3 1 <zero-matrix> [ zero-matrix? ] [ square-matrix? ] bi ] unit-test
+
+{ {
+    { 1 0 0 }
+    { 0 2 0 }
+    { 0 0 3 }
+} } [
+    { 1 2 3 } <diagonal-matrix>
 ] unit-test
 
-{
-    {
-        { 0 1 0 }
-        { 0 0 1 }
-        { 0 0 0 }
-    }
-} [
-    3 3 1 eye
+{ {
+    { -11 0 0 0 }
+    { 0 -12 0 0 }
+    { 0 0 -33 0 }
+    { 0 0 0 -14 }
+} } [ { -11 -12 -33 -14 } <diagonal-matrix> ] unit-test
+
+{ {
+    { 0 0 1 }
+    { 0 2 0 }
+    { 3 0 0 }
+} } [ { 1 2 3 } <anti-diagonal-matrix> ] unit-test
+
+{ {
+    { 0 0 0 -11 }
+    { 0 0 -12 0 }
+    { 0 -33 0 0 }
+    { -14 0 0 0 }
+} } [ { -11 -12 -33 -14 } <anti-diagonal-matrix> ] unit-test
+
+{ {
+    { 1 0 0 }
+    { 0 1 0 }
+    { 0 0 1 }
+} } [
+    3 <identity-matrix>
 ] unit-test
 
-{
-    {
-        { 0 0 0 }
-        { 1 0 0 }
-        { 0 1 0 }
-    }
-} [
-    3 3 -1 eye
+{ {
+    { 2 0 0 }
+    { 0 2 0 }
+    { 0 0 2 }
+} } [
+    3 3 0 2 <eye>
 ] unit-test
 
-{
-    {
-        { 1 0 0 0 }
-        { 0 1 0 0 }
-        { 0 0 1 0 }
-    }
-} [
-    3 4 0 eye
+{ {
+    { 0 2 0 }
+    { 0 0 2 }
+    { 0 0 0 }
+} } [
+    3 3 1 2 <eye>
 ] unit-test
 
-{
-    {
-        { 0 1 0 }
-        { 0 0 1 }
-        { 0 0 0 }
-        { 0 0 0 }
-    }
-} [
-    4 3 1 eye
-] unit-test
-
-{
-    {
-        { 0 0 0 }
-        { 1 0 0 }
-        { 0 1 0 }
-        { 0 0 1 }
-    }
-} [
-    4 3 -1 eye
+{ {
+    { 0 0 0 0 }
+    { 2 0 0 0 }
+    { 0 2 0 0 }
+} } [
+    3 4 -1 2 <eye>
 ] unit-test
 
-{
-    { { 1   1/2 1/3 1/4 }
-      { 1/2 1/3 1/4 1/5 }
-      { 1/3 1/4 1/5 1/6 }
-    }
-} [ 3 4 hilbert-matrix ] unit-test
 
-{
-    { { 1 2 3 4 }
-      { 2 1 2 3 }
-      { 3 2 1 2 }
-      { 4 3 2 1 } }
-} [ 4 toeplitz-matrix ] unit-test
-
-{
-    { { 1 2 3 4 }
-      { 2 3 4 0 }
-      { 3 4 0 0 }
-      { 4 0 0 0 } }
-} [ 4 hankel-matrix ] unit-test
-
-{
-    { { 1 0 4 }
-      { 0 7 0 }
-      { 6 0 3 } }
-} [
-    { { 1 0 0 }
-      { 0 2 0 }
-      { 0 0 3 } }
-
-    { { 0 0 4 }
-      { 0 5 0 }
-      { 6 0 0 } }
+{ {
+    { 1 0 0 }
+    { 0 1 0 }
+    { 0 0 1 }
+} } [
+    3 3 0 <simple-eye>
+] unit-test
 
-    m+
+{ {
+    { 0 1 0 }
+    { 0 0 1 }
+    { 0 0 0 }
+} } [
+    3 3 1 <simple-eye>
 ] unit-test
 
-{
-    { { 1 0 4 }
-       { 0 7 0 }
-       { 6 0 3 } }
-} [
-    { { 1 0 0 }
-       { 0 2 0 }
-       { 0 0 3 } }
+{ {
+    { 0 0 0 }
+    { 1 0 0 }
+    { 0 1 0 }
+} } [
+    3 3 -1 <simple-eye>
+] unit-test
 
-    { { 0 0 -4 }
-       { 0 -5 0 }
-       { -6 0 0 } }
+{ {
+    { 1 0 0 0 }
+    { 0 1 0 0 }
+    { 0 0 1 0 }
+} } [
+    3 4 0 <simple-eye>
+] unit-test
 
-    m-
+{ {
+    { 0 1 0 }
+    { 0 0 1 }
+    { 0 0 0 }
+    { 0 0 0 }
+} } [
+    4 3 1 <simple-eye>
 ] unit-test
 
-{
-    { 10 20 30 }
-} [
-    10 { 1 2 3 } n*v
+{ {
+    { 0 0 0 }
+    { 1 0 0 }
+    { 0 1 0 }
+    { 0 0 1 }
+} } [
+    4 3 -1 <simple-eye>
 ] unit-test
 
-{
+{ {
+    { { 0 0 } { 0 1 } { 0 2 } }
+    { { 1 0 } { 1 1 } { 1 2 } }
+    { { 2 0 } { 2 1 } { 2 2 } }
+    { { 3 0 } { 3 1 } { 3 2 } }
+} } [ { 4 3 } <coordinate-matrix> ] unit-test
+
+{ {
+    { 0 1 }
+    { 0 1 }
+} } [ 2 <square-rows> ] unit-test
+
+{ {
+    { 0 0 }
+    { 1 1 }
+} } [ 2 <square-cols> ] unit-test
+
+{ {
+    { 5 6 }
+    { 5 6 }
+} } [ { 5 6 } <square-rows> ] unit-test
+
+{ {
+    { 5 5 }
+    { 6 6 }
+} } [ { 5 6 } <square-cols> ] unit-test
+
+{  {
+    { 1 }
+} } [ {
+    { 1 2 }
+} <square-rows> ] unit-test
+
+{  {
+    { 1 2 }
     { 3 4 }
-} [
-    { { 1 0 }
-       { 0 1 } }
+} } [ {
+    { 1 2 5 }
+    { 3 4 6 }
+} <square-rows> ] unit-test
 
+{  {
+    { 1 2 }
     { 3 4 }
-
-    m.v
-] unit-test
-
-{
-    { 4 3 }
-} [
-    { { 0 1 }
-       { 1 0 } }
-
+} } [ {
+    { 1 2 }
     { 3 4 }
-
-    m.v
-] unit-test
-
-{
-    { { 6 } }
-} [
-    { { 3 } } { { 2 } } m.
+    { 5 6 }
+} <square-rows> ] unit-test
+
+{ {
+    { 1 0 4 }
+    { 0 7 0 }
+    { 6 0 3 } }
+} [ {
+    { 1 0 0 }
+    { 0 2 0 }
+    { 0 0 3 }
+} {
+    { 0 0 4 }
+    { 0 5 0 }
+    { 6 0 0 }
+}
+    m+
 ] unit-test
 
-{
-    { { 11 } }
-} [
-    { { 1 3 } } { { 5 } { 2 } } m.
+{ {
+    { 1 0 4 }
+    { 0 7 0 }
+    { 6 0 3 }
+} } [ {
+    { 1 0 0 }
+    { 0 2 0 }
+    { 0 0 3 }
+} {
+    { 0 0 -4 }
+    { 0 -5 0 }
+    { -6 0 0 }
+}
+    m-
 ] unit-test
 
-{
-    { { 28 } }
-} [
-    { { 2 4 6 } }
+{ { 3 4 } } [ { { 1 0 } { 0 1 } } { 3 4 } m.v ] unit-test
+{ { 4 3 } } [ { { 0 1 } { 1 0 } } { 3 4 } m.v ] unit-test
 
-    { { 1 }
-       { 2 }
-       { 3 } }
+{ { { 6 } } } [ { { 3 } } { { 2 } } m. ] unit-test
+{ { { 11 } } } [ { { 1 3 } } { { 5 } { 2 } } m. ] unit-test
 
+{ { { 28 } } } [
+    { { 2 4 6 } }
+    { { 1 } { 2 } { 3 } }
     m.
 ] unit-test
 
-{ { 0 0 1 } } [ { 1 0 0 } { 0 1 0 } cross ] unit-test
-{ { 1 0 0 } } [ { 0 1 0 } { 0 0 1 } cross ] unit-test
-{ { 0 1 0 } } [ { 0 0 1 } { 1 0 0 } cross ] unit-test
-{ { 0.0 -0.707 0.707 } } [ { 1.0 0.0 0.0 } { 0.0 0.707 0.707 } cross ] unit-test
-{ { 0 -2 2 } } [ { -1 -1 -1 } { 1 -1 -1 } cross ] unit-test
-{ { 1 0 0 } } [ { 1 1 0 } { 1 0 0 } proj ] unit-test
-
-{ { { 4181 6765 } { 6765 10946 } } }
-[ { { 0 1 } { 1 1 } } 20 m^n ] unit-test
-[ { { 0 1 } { 1 1 } } -20 m^n ] [ negative-power-matrix? ] must-fail-with
 
-{
-    { { 0 5 0 10 } { 6 7 12 14 } { 0 15 0 20 } { 18 21 24 28 } }
-}
-[ { { 1 2 } { 3 4 } } { { 0 5 } { 6 7 } } kron ] unit-test
+! TODO: note: merge conflict from HEAD contained the following
+! ------------------------
+! predicates
 
-{
-    {
-        { 1 1 1 1 }
-        { 1 -1 1 -1 }
-        { 1 1 -1 -1 }
-        { 1 -1 -1 1 }
-    }
-} [ { { 1 1 } { 1 -1 } } dup kron ] unit-test
-
-{
-    {
-        { 1 1 1 1 1 1 1 1 }
-        { 1 -1 1 -1 1 -1 1 -1 }
-        { 1 1 -1 -1 1 1 -1 -1 }
-        { 1 -1 -1 1 1 -1 -1 1 }
-        { 1 1 1 1 -1 -1 -1 -1 }
-        { 1 -1 1 -1 -1 1 -1 1 }
-        { 1 1 -1 -1 -1 -1 1 1 }
-        { 1 -1 -1 1 -1 1 1 -1 }
-    }
-} [ { { 1 1 } { 1 -1 } } dup dup kron kron ] unit-test
+{ t } [ { } square-matrix? ] unit-test
+{ t } [ { { 1 } } square-matrix? ] unit-test
+{ t } [ { { 1 2 } { 3 4 } } square-matrix? ] unit-test
+{ f } [ { { 1 } { 2 3 } } square-matrix? ] unit-test
+{ f } [ { { 1 2 } } square-matrix? ] unit-test
 
-{
-    {
-        { 1 1 1 1 1 1 1 1 }
-        { 1 -1 1 -1 1 -1 1 -1 }
-        { 1 1 -1 -1 1 1 -1 -1 }
-        { 1 -1 -1 1 1 -1 -1 1 }
-        { 1 1 1 1 -1 -1 -1 -1 }
-        { 1 -1 1 -1 -1 1 -1 1 }
-        { 1 1 -1 -1 -1 -1 1 1 }
-        { 1 -1 -1 1 -1 1 1 -1 }
-    }
-} [ { { 1 1 } { 1 -1 } } dup dup kron swap kron ] unit-test
+{ 9 }
+[ { { 2 -2 1 } { 1 3 -1 } { 2 -4 2 } } m-1norm ] unit-test
 
+{ 8 }
+[ { { 2 -2 1 } { 1 3 -1 } { 2 -4 2 } } m-infinity-norm ] unit-test
 
-! kron is not generally commutative, make sure we have the right order
-{
-    {
-        { 1 2 3 4 5 1 2 3 4 5 }
-        { 6 7 8 9 10 6 7 8 9 10 }
-        { 1 2 3 4 5 -1 -2 -3 -4 -5 }
-        { 6 7 8 9 10 -6 -7 -8 -9 -10 }
-    }
-}
-[
-    { { 1 1 } { 1 -1 } }
-    { { 1 2 3 4 5 } { 6 7 8 9 10 } } kron
+{ 2.0 }
+[ { { 1 1 } { 1 1 } } frobenius-norm ] unit-test
+! from "intermediate commit"
+! any deep-empty matrix is null
+! it doesn't make any sense for { } to be null while { { } } to be considered nonnull
+{ t } [ {
+    { }
+    { { } }
+    { { { } } }
+    { { } { } { } }
+    { { { } } { { { } } } }
+} [ null-matrix? ] map [ ] all?
 ] unit-test
 
-{
-    {
-        { 1 1 2 2 3 3 4 4 5 5 }
-        { 1 -1 2 -2 3 -3 4 -4 5 -5 }
-        { 6 6 7 7 8 8 9 9 10 10 }
-        { 6 -6 7 -7 8 -8 9 -9 10 -10 }
-    }
-}
-[
-    { { 1 1 } { 1 -1 } }
-    { { 1 2 3 4 5 } { 6 7 8 9 10 } } swap kron
+{ f } [ {
+    { 1 2 }
+    { { 1 2 } }
+    { { 1 } { 2 } }
+    { { { 1 } } { 2 } { } }
+} [ null-matrix? ] map [ ] any?
 ] unit-test
 
-{
-    { { 5 10 15 }
-      { 6 12 18 }
-      { 7 14 21 } }
-} [ { 5 6 7 } { 1 2 3 } outer ] unit-test
-
-
-CONSTANT: test-points {
-    { 80  27  89 } { 80  27  88 } { 75  25  90 }
-    { 62  24  87 } { 62  22  87 } { 62  23  87 }
-    { 62  24  93 } { 62  24  93 } { 58  23  87 }
-    { 58  18  80 } { 58  18  89 } { 58  17  88 }
-    { 58  18  82 } { 58  19  93 } { 50  18  89 }
-    { 50  18  86 } { 50  19  72 } { 50  19  79 }
-    { 50  20  80 } { 56  20  82 } { 70  20  91 }
-}
-
-{
-    {
-        { 84+2/35 22+23/35 24+4/7 }
-        { 22+23/35 9+104/105 6+87/140 }
-        { 24+4/7 6+87/140 28+5/7 }
-    }
-} [
-    test-points sample-cov-matrix
+{ t } [ 10 dup <zero-matrix> zero-matrix? ] unit-test
+{ t } [ 10 10 15 <simple-eye> zero-matrix? ] unit-test
+{ t } [ 0 dup <zero-matrix> null-matrix? ] unit-test
+{ f } [ 0 dup <zero-matrix> zero-matrix? ] unit-test
+{ f } [ 4 <identity-matrix> zero-matrix? ] unit-test
+
+{ t } [ { }                 regular-matrix? ] unit-test
+{ t } [ { { } }             regular-matrix? ] unit-test
+{ t } [ { { 1 2 } }         regular-matrix? ] unit-test
+{ t } [ { { 1 2 } { 3 4 } } regular-matrix? ] unit-test
+{ t } [ { { 1 } { 3 } }     regular-matrix? ] unit-test
+{ f } [ { { 1 2 } { 3 } }   regular-matrix? ] unit-test
+{ f } [ { { 1 } { 3 2 } }   regular-matrix? ] unit-test
+! TODO: note: lines since last HEAD comment were deleted in "fix more code and add more rigorous tests"
+
+! diagonals
+
+! diagonal getters
+{ { 1 1 1 1 } } [ 4 <identity-matrix> main-diagonal ] unit-test
+{ { 0 0 0 0 } } [ 4 <identity-matrix> anti-diagonal ] unit-test
+{ { 4 8 } } [ { { 4 6 } { 3 8 } } main-diagonal ] unit-test
+{ { 6 3 } } [ { { 4 6 } { 3 8 } } anti-diagonal ] unit-test
+{ { 1 2 3 } } [ { { 0 0 1 } { 0 2 0 } { 3 0 0 } } anti-diagonal ] unit-test
+{ { 1 2 3 4 } } [ { 1 2 3 4 } <diagonal-matrix> main-diagonal ] unit-test
+
+! transposition
+{ { 1 2 3 4 } } [ { 1 2 3 4 } <diagonal-matrix> transpose main-diagonal ] unit-test
+{ t } [ 50 <identity-matrix> dup transpose = ] unit-test
+{ { 4 3 2 1 } } [ { 1 2 3 4 } <anti-diagonal-matrix> transpose anti-diagonal ] unit-test
+
+{ {
+ { 1 4 7 }
+ { 2 5 8 }
+ { 3 6 9 }
+} } [ {
+ { 1 2 3 }
+ { 4 5 6 }
+ { 7 8 9 }
+} transpose ] unit-test
+
+! anti transposition
+{ { 1 2 3 4 } } [ { 1 2 3 4 } <anti-diagonal-matrix> anti-transpose anti-diagonal ] unit-test
+{ t } [ 50 <iota> <anti-diagonal-matrix> dup anti-transpose = ] unit-test
+{ { 4 3 2 1 } } [ { 1 2 3 4 } <diagonal-matrix> anti-transpose main-diagonal ] unit-test
+
+{ {
+ { 9 6 3 }
+ { 8 5 2 }
+ { 7 4 1 }
+} } [ {
+ { 1 2 3 }
+ { 4 5 6 }
+ { 7 8 9 }
+} anti-transpose ] unit-test
+
+<PRIVATE
+SYMBOLS: A B C D E F G H I J K L M N O P ;
+PRIVATE>
+{ { {
+    { E F G H }
+    { I J K L }
+    { M N O P }
+} {
+    { A B C D }
+    { I J K L }
+    { M N O P }
+} {
+    { A B C D }
+    { E F G H }
+    { M N O P }
+} {
+    { A B C D }
+    { E F G H }
+    { I J K L }
+} } } [
+    4 {
+        { A B C D }
+        { E F G H }
+        { I J K L }
+        { M N O P }
+    } <repetition>
+    [ rows-except ] map-index
 ] unit-test
 
-{
-    {
-        { 80+8/147 21+85/147 23+59/147 }
-        { 21+85/147 9+227/441 6+15/49 }
-        { 23+59/147 6+15/49 27+17/49 }
-    }
-} [
-    test-points population-cov-matrix
+{ { { 2 } } } [ { { 1 } { 2 } } 0 rows-except ] unit-test
+{ { { 1 } } } [ { { 1 } { 2 } } 1 rows-except ] unit-test
+{ { } } [ { { 1 } }       0 rows-except ] unit-test
+{ { { 1 } } } [ { { 1 } } 1 rows-except ] unit-test
+{ {
+    { 2 7 12 2 } ! 0
+    { 1 3 3 5 }  ! 2
+} } [ {
+    { 2 7 12 2 }
+    { 8 9 10 0 }
+    { 1 3 3 5 }
+    { 8 13 7 12 }
+} { 1 3 } rows-except ] unit-test
+
+{ { {
+    { B C D }
+    { F G H }
+    { J K L }
+    { N O P }
+} {
+    { A C D }
+    { E G H }
+    { I K L }
+    { M O P }
+} {
+    { A B D }
+    { E F H }
+    { I J L }
+    { M N P }
+} {
+    { A B C }
+    { E F G }
+    { I J K }
+    { M N O }
+} } } [
+    4 {
+        { A B C D }
+        { E F G H }
+        { I J K L }
+        { M N O P }
+    } <repetition>
+    [ cols-except ] map-index
 ] unit-test
 
-{
-    {
-        { 5 5 }
-        { 5 5 }
-    }
-} [
-    2 2 5 <matrix>
+{ { } } [ { { 1 } { 2 } } 0 cols-except ] unit-test
+{ { { 1 } { 2 } } } [ { { 1 } { 2 } } 1 cols-except ] unit-test
+{ { } } [ { { 1 } }       0 cols-except ] unit-test
+{ { { 1 } } } [ { { 1 } } 1 cols-except ] unit-test
+{ { { 2 } { 4 } } } [ { { 1 2 } { 3 4 } } 0 cols-except ] unit-test
+{ { { 1 } { 3 } } } [ { { 1 2 } { 3 4 } } 1 cols-except ] unit-test
+{ {
+    { 2 12 }
+    { 8 10 }
+    { 1 3 }
+    { 8 7 }
+} } [ {
+    { 2 7 12 2 }
+    { 8 9 10 0 }
+    { 1 3 3 5 }
+    { 8 13 7 12 }
+} { 1 3 } cols-except ] unit-test
+
+{ { {
+    { F G H }
+    { J K L }
+    { N O P }
+} {
+    { A C D }
+    { I K L }
+    { M O P }
+} {
+    { A B D }
+    { E F H }
+    { M N P }
+} {
+    { A B C }
+    { E F G }
+    { I J K }
+} } } [
+    4 {
+        { A B C D }
+        { E F G H }
+        { I J K L }
+        { M N O P }
+    } <repetition>
+    [ dup 2array matrix-except ] map-index
 ] unit-test
 
-{
-    {
-        { 5 5 }
-        { 5 5 }
-    }
-} [
-    2 2 [ 5 ] make-matrix
-] unit-test
+! prepare for bracket hell
+! going to test the Matrix of Minors permutation strategy
 
-{
-    {
-        { 0 1 2 }
-        { 1 2 3 }
-    }
-} [
-    2 3 [ + ] make-matrix-with-indices
-] unit-test
+! going to test 1x2 inputs
+! the input had 2 elements, the output has 2 0-matrices across 2 arrays ;)
+{ { { { } { } } } } [ { { 1 2 } } matrix-except-all ] unit-test
 
-{
-    {
-        { 0 1 }
-        { 0 1 }
-    }
-} [
-    2 square-rows
-] unit-test
+! any matrix with a 1 in its dimensions will give a void matrix output
+{ t } [ { { 1 2 } }     matrix-except-all null-matrix? ] unit-test
+{ t } [ { { 1 } { 2 } } matrix-except-all null-matrix? ] unit-test
 
-{
-    {
-        { 0 0 }
-        { 1 1 }
-    }
-} [
-    2 square-cols
-] unit-test
+! going to test 2x2 inputs
+! these 1x1 output matrices have omitted a row and column from the 2x2 input
 
+! the input had 4 elements, the output has 4 1-matrices across 2 arrays
+! the permutations of indices 0 1 are: 0 0, 0 1, 1 0, 1 1
 {
-    {
-        { 5 6 }
-        { 5 6 }
+    { ! output array
+        { ! item #1: excluding row 0...
+            { { 3 } } ! and col 0 = 0 0
+            { { 2 } } ! and col 1 = 0 1
+        }
+        { ! item #2: excluding row 1...
+            { { 1 } } ! and col 0 = 1 0
+            { { 0 } } ! and col 1 = 1 1
+        }
     }
 } [
-    { 5 6 } square-rows
+    ! the input to the function is a simple 2x2
+    { { 0 1 } { 2 3 } } matrix-except-all
 ] unit-test
 
+! we are going to ensure that "duplicate" matrices are not omitted in the output
 {
     {
-        { 5 5 }
-        { 6 6 }
+        { ! item 1
+            { { 0 } }
+            { { 0 } }
+        }
+        { ! item 2
+            { { 0 } }
+            { { 0 } }
+        }
     }
-} [
-    { 5 6 } square-cols
+} [ { { 0 0 } { 0 0 } } matrix-except-all ] unit-test
+! the output only has elements from the input
+{ t } [ 44 <zero-square-matrix> matrix-except-all zero-matrix? ] unit-test
+
+! going to test 2x3 and 3x2 inputs
+{
+    { ! output array
+        { ! excluding row 0
+            { { 2 } { 3 } } ! and col 0
+            { { 1 } { 2 } } ! and col 1
+        }
+        { ! excluding row 1
+            { { 1 } { 3 } } ! and col 0
+            { { 0 } { 2 } } ! and col 1
+        }
+        { ! excluding row 2
+            { { 1 } { 2 } } ! col 0
+            { { 0 } { 1 } } ! col 1
+        }
+    }
+} [ {
+    { 0 1 }
+    { 1 2 }
+    { 2 3 }
+} matrix-except-all ] unit-test
+
+{
+    { ! output array
+        { ! excluding row 0
+            { { 2 3 } } ! col 0
+            { { 1 3 } } ! col 1
+            { { 1 2 } } ! col 2
+        }
+        { ! row 1
+            { { 1 2 } } ! col 0
+            { { 0 2 } } ! col 1
+            { { 0 1 } } ! col 2
+        }
+    }
+} [ {
+    { 0 1 2 }
+    { 1 2 3 }
+} matrix-except-all ] unit-test
+
+! going to test 3x3 inputs
+
+! the input had 9 elements, the output has 9 2-matrices across 3 arrays
+! every element from the input is represented 4 times in the output
+! the number of copies of each element found in the output is the side length of the next smaller square matrix
+! 3x3 input gives 4 copies of each element; (N-1) ^ 2 = 4 where N=3
+! the permutations of indices 0 1 2 are: 0 0, 0 1, 0 2; 1 0, 1 1, 1 2; 2 0, 2 1, 2 2
+{
+    { ! output array
+        { ! item #1: excluding row 0...
+            { ! and col 0 = 0 0
+                { 4 5 }
+                { 7 8 }
+            }
+            { ! and col 1 = 0 1
+                { 3 5 }
+                { 6 8 }
+            }
+            { ! and col 2 = 0 2
+                { 3 4 }
+                { 6 7 }
+            }
+        }
+
+        { ! item #2: excluding row 1...
+            { ! and col 0 = 1 0
+                { 1 2 }
+                { 7 8 }
+            }
+            { ! and col 1 = 1 1
+                { 0 2 }
+                { 6 8 }
+            }
+            { ! and col 2 = 1 2
+                { 0 1 }
+                { 6 7 }
+            }
+        }
+
+        { ! item #2: excluding row 2...
+            { ! and col 0 = 2 0
+                { 1 2 }
+                { 4 5 }
+            }
+            { ! and col 1 = 2 1
+                { 0 2 }
+                { 3 5 }
+            }
+            { ! and col 2 = 2 2
+                { 0 1 }
+                { 3 4 }
+            }
+        }
+    }
+    t ! note this
+} [ {
+    { 0 1 2 }
+    { 3 4 5 }
+    { 6 7 8 }
+} matrix-except-all dup flatten sorted-histogram [ second ] map
+    { [ length 9 = ] [ [ 4 = ] all? ] }
+    1&&
 ] unit-test
 
-{ t } [ { } square-matrix? ] unit-test
-{ t } [ { { 1 } } square-matrix? ] unit-test
-{ t } [ { { 1 2 } { 3 4 } } square-matrix? ] unit-test
-{ f } [ { { 1 } { 2 3 } } square-matrix? ] unit-test
-{ f } [ { { 1 2 } } square-matrix? ] unit-test
-
-{ 9 }
-[ { { 2 -2 1 } { 1 3 -1 } { 2 -4 2 } } m-1norm ] unit-test
-
-{ 8 }
-[ { { 2 -2 1 } { 1 3 -1 } { 2 -4 2 } } m-infinity-norm ] unit-test
-
-{ 2.0 }
-[ { { 1 1 } { 1 1 } } frobenius-norm ] unit-test
+! going to test 4x4 inputs
+
+! don't feel like handwriting this right now, so a sanity check test instead
+! the input contains 4 rows and 4 columns for 16 elements
+! 4x4 input gives 9 copies of each element; (N-1) ^ 2 = 9 where N = 4
+{ t } [ {
+    { 0 1 2 3 }
+    { 4 5 6 7 }
+    { 8 9 10 11 }
+    { 12 13 14 15 }
+} matrix-except-all flatten sorted-histogram [ second ] map
+    { [ length 16 = ] [ [ 9 = ] all? ] }
+    1&&
+] unit-test
index ef5a06a22f1fbd617043b57129b0d799bf010b83..1090c2af5cfd209f2d2ca12805d6881462f2916a 100644 (file)
-! Copyright (C) 2005, 2010 Slava Pestov, Joe Groff.
+! Copyright (C) 2005, 2010, 2018 Slava Pestov, Joe Groff, and Cat Stevens.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors arrays columns kernel locals math math.bits
-math.functions math.order math.vectors sequences
-sequences.private fry math.statistics grouping
-combinators.short-circuit math.ranges combinators.smart ;
+USING: accessors arrays classes.singleton columns combinators
+combinators.short-circuit combinators.smart formatting fry
+grouping kernel locals math math.bits math.functions math.order
+math.private math.ranges math.statistics math.vectors
+math.vectors.private sequences sequences.deep sequences.private
+slots.private summary ;
 IN: math.matrices
 
-! Matrices
-: make-matrix ( m n quot -- matrix )
-    '[ _ _ replicate ] replicate ; inline
+! defined here because of issue #1943
+DEFER: regular-matrix?
+: regular-matrix? ( object -- ? )
+    [ t ] [
+        dup first-unsafe length
+        '[ length _ = ] all?
+    ] if-empty ;
+
+! the MRO (class linearization) is performed in the order the predicates appear here
+! except that null-matrix is last (but it is relied upon by zero-matrix)
+! in other words:
+! sequence > matrix > zero-matrix > square-matrix > zero-square-matrix > null-matrix
+
+! Factor bug that's hard to repro: using `bi and` in these predicates
+! instead of 1&& will cause spirious no-method and bounds-error errors in <square-cols>
+! and the tests/docs for no apparent reason
+PREDICATE: matrix < sequence
+    { [ [ sequence? ] all? ] [ regular-matrix? ] } 1&& ;
+
+PREDICATE: irregular-matrix < sequence
+    { [ [ sequence? ] all? ] [ regular-matrix? not ] } 1&& ;
+
+DEFER: dimension
+! can't define dim using this predicate for this reason,
+! unless we are going to write two versions of dim, one of which is generic
+PREDICATE: square-matrix < matrix
+    dimension first2-unsafe = ;
+
+PREDICATE: null-matrix < matrix
+    flatten empty? ;
 
+PREDICATE: zero-matrix < matrix
+    dup null-matrix? [ drop f ] [ flatten [ zero? ] all? ] if ;
+
+PREDICATE: zero-square-matrix < square-matrix
+    { [ zero-matrix? ] [ square-matrix? ] } 1&& ;
+
+! Benign matrix constructors
 : <matrix> ( m n element -- matrix )
     '[ _ _ <array> ] replicate ; inline
 
-: zero-matrix ( m n -- matrix )
+: <matrix-by> ( m n quot: ( ... -- elt ) -- matrix )
+    '[ _ _ replicate ] replicate ; inline
+
+: <matrix-by-indices> ( ... m n quot: ( ... m' n' -- ... elt ) -- ... matrix )
+    [ [ <iota> ] bi@ ] dip cartesian-map ; inline
+
+: <zero-matrix> ( m n -- matrix )
     0 <matrix> ; inline
 
-: diagonal-matrix ( diagonal-seq -- matrix )
-    dup length dup zero-matrix
-    [ '[ dup _ nth set-nth ] each-index ] keep ; inline
-
-: identity-matrix ( n -- matrix )
-    1 <repetition> diagonal-matrix ; inline
-
-: eye ( m n k -- matrix )
-    [ [ <iota> ] bi@ ] dip neg '[ _ + = 1 0 ? ] cartesian-map ;
-
-: hilbert-matrix ( m n -- matrix )
-    [ <iota> ] bi@ [ + 1 + recip ] cartesian-map ;
-
-: toeplitz-matrix ( n -- matrix )
-    <iota> dup [ - abs 1 + ] cartesian-map ;
-
-: hankel-matrix ( n -- matrix )
-    [ <iota> dup ] keep '[ + abs 1 + dup _ > [ drop 0 ] when ] cartesian-map ;
-
-: box-matrix ( r -- matrix )
-    2 * 1 + dup '[ _ 1 <array> ] replicate ;
-
-: vandermonde-matrix ( u n -- matrix )
-    <iota> [ v^n ] with map reverse flip ;
-
-:: rotation-matrix3 ( axis theta -- matrix )
-    theta cos :> c
-    theta sin :> s
-    axis first3 :> ( x y z )
-    x sq 1.0 x sq - c * +     x y * 1.0 c - * z s * -   x z * 1.0 c - * y s * + 3array
-    x y * 1.0 c - * z s * +   y sq 1.0 y sq - c * +     y z * 1.0 c - * x s * - 3array
-    x z * 1.0 c - * y s * -   y z * 1.0 c - * x s * +   z sq 1.0 z sq - c * +   3array
-    3array ;
-
-:: rotation-matrix4 ( axis theta -- matrix )
-    theta cos :> c
-    theta sin :> s
-    axis first3 :> ( x y z )
-    x sq 1.0 x sq - c * +     x y * 1.0 c - * z s * -   x z * 1.0 c - * y s * +   0 4array
-    x y * 1.0 c - * z s * +   y sq 1.0 y sq - c * +     y z * 1.0 c - * x s * -   0 4array
-    x z * 1.0 c - * y s * -   y z * 1.0 c - * x s * +   z sq 1.0 z sq - c * +     0 4array
-    { 0.0 0.0 0.0 1.0 } 4array ;
-
-:: translation-matrix4 ( offset -- matrix )
-    offset first3 :> ( x y z )
-    {
-        { 1.0 0.0 0.0 x   }
-        { 0.0 1.0 0.0 y   }
-        { 0.0 0.0 1.0 z   }
-        { 0.0 0.0 0.0 1.0 }
-    } ;
-
-: >scale-factors ( number/sequence -- x y z )
-    dup number? [ dup dup ] [ first3 ] if ;
-
-:: scale-matrix3 ( factors -- matrix )
-    factors >scale-factors :> ( x y z )
-    {
-        { x   0.0 0.0 }
-        { 0.0 y   0.0 }
-        { 0.0 0.0 z   }
-    } ;
-
-:: scale-matrix4 ( factors -- matrix )
-    factors >scale-factors :> ( x y z )
-    {
-        { x   0.0 0.0 0.0 }
-        { 0.0 y   0.0 0.0 }
-        { 0.0 0.0 z   0.0 }
-        { 0.0 0.0 0.0 1.0 }
-    } ;
-
-: ortho-matrix4 ( dim -- matrix )
-    [ recip ] map scale-matrix4 ;
-
-:: frustum-matrix4 ( xy-dim near far -- matrix )
-    xy-dim first2 :> ( x y )
-    near x /f :> xf
-    near y /f :> yf
-    near far + near far - /f :> zf
-    2 near far * * near far - /f :> wf
-
-    {
-        { xf  0.0  0.0 0.0 }
-        { 0.0 yf   0.0 0.0 }
-        { 0.0 0.0  zf  wf  }
-        { 0.0 0.0 -1.0 0.0 }
-    } ;
-
-:: skew-matrix4 ( theta -- matrix )
-    theta tan :> zf
-
-    {
-        { 1.0 0.0 0.0 0.0 }
-        { 0.0 1.0 0.0 0.0 }
-        { 0.0 zf  1.0 0.0 }
-        { 0.0 0.0 0.0 1.0 }
-    } ;
-
-! Matrix operations
-: mneg ( m -- m ) [ vneg ] map ;
-
-: n+m  ( n m -- m ) [ n+v ] with map ;
-: m+n  ( m n -- m ) [ v+n ] curry map ;
-: n-m  ( n m -- m ) [ n-v ] with map ;
-: m-n  ( m n -- m ) [ v-n ] curry map ;
-: n*m ( n m -- m ) [ n*v ] with map ;
-: m*n ( m n -- m ) [ v*n ] curry map ;
-: n/m ( n m -- m ) [ n/v ] with map ;
-: m/n ( m n -- m ) [ v/n ] curry map ;
+: <zero-square-matrix> ( n -- matrix )
+    dup <zero-matrix> ; inline
 
-: m+   ( m m -- m ) [ v+ ] 2map ;
-: m-   ( m m -- m ) [ v- ] 2map ;
-: m*   ( m m -- m ) [ v* ] 2map ;
-: m/   ( m m -- m ) [ v/ ] 2map ;
+<PRIVATE
+: (nth-from-end) ( n seq -- n )
+    length 1 - swap - ; inline flushable
 
-: v.m ( v m -- v ) flip [ v. ] with map ;
-: m.v ( m v -- v ) [ v. ] curry map ;
-: m.  ( m m -- m ) flip [ swap m.v ] curry map ;
+: nth-end ( n seq -- elt )
+    [ (nth-from-end) ] keep nth ; inline flushable
 
-: m~  ( m m epsilon -- ? ) [ v~ ] curry 2all? ;
+: nth-end-unsafe ( n seq -- elt )
+    [ (nth-from-end) ] keep nth-unsafe ; inline flushable
 
-: mmin ( m -- n ) [ 1/0. ] dip [ [ min ] each ] each ;
-: mmax ( m -- n ) [ -1/0. ] dip [ [ max ] each ] each ;
-: mnorm ( m -- n ) dup mmax abs m/n ;
-: m-infinity-norm ( m -- n ) [ [ abs ] map-sum ] map supremum ;
-: m-1norm ( m -- n ) flip m-infinity-norm ;
-: frobenius-norm ( m -- n ) [ [ sq ] map-sum ] map-sum sqrt ;
+: array-nth-end-unsafe ( n seq -- elt )
+    [ (nth-from-end) ] keep swap 2 fixnum+fast slot ; inline flushable
 
-: cross ( vec1 vec2 -- vec3 )
-    [ [ { 1 2 0 } vshuffle ] [ { 2 0 1 } vshuffle ] bi* v* ]
-    [ [ { 2 0 1 } vshuffle ] [ { 1 2 0 } vshuffle ] bi* v* ] 2bi v- ; inline
+: set-nth-end ( elt n seq -- )
+    [ (nth-from-end) ] keep set-nth ; inline
 
-:: normal ( vec1 vec2 vec3 -- vec4 )
-    vec2 vec1 v- vec3 vec1 v- cross normalize ; inline
+: set-nth-end-unsafe ( elt n seq -- )
+    [ (nth-from-end) ] keep set-nth-unsafe ; inline
+PRIVATE>
 
-: proj ( v u -- w )
-    [ [ v. ] [ norm-sq ] bi / ] keep n*v ;
+! main-diagonal matrix
+: <diagonal-matrix> ( diagonal-seq -- matrix )
+    [ length <zero-square-matrix> ] keep over
+    '[ dup _ nth set-nth-unsafe ] each-index ; inline
 
-: perp ( v u -- w )
-    dupd proj v- ;
+! could also be written slower as: <diagonal-matrix> [ reverse ] map
+: <anti-diagonal-matrix> ( diagonal-seq -- matrix )
+    [ length <zero-square-matrix> ] keep over
+    '[ dup _ nth set-nth-end-unsafe ] each-index ; inline
 
-: angle-between ( v u -- a )
-    [ normalize ] bi@ h. acos ;
+: <identity-matrix> ( n -- matrix )
+    1 <repetition> <diagonal-matrix> ; inline
 
-: (gram-schmidt) ( v seq -- newseq )
-    [ dupd proj v- ] each ;
+: <eye> ( m n k z -- matrix )
+    [ [ <iota> ] bi@ ] 2dip
+    '[ _ neg + = _ 0 ? ]
+    cartesian-map ; inline
 
-: gram-schmidt ( seq -- orthogonal )
-    V{ } clone [ over (gram-schmidt) suffix! ] reduce ;
+! if m = n and k = 0 then <identity-matrix> is (possibly) more efficient
+:: <simple-eye> ( m n k -- matrix )
+    m n = k 0 = and
+    [ n <identity-matrix> ]
+    [ m n k 1 <eye> ] if ; inline
 
-: norm-gram-schmidt ( seq -- orthonormal )
-    gram-schmidt [ normalize ] map ;
+: <coordinate-matrix> ( dim -- coordinates )
+  first2 [ <iota> ] bi@ cartesian-product ; inline
 
-ERROR: negative-power-matrix m n ;
+ALIAS: <cartesian-indices> <coordinate-matrix>
 
-: (m^n) ( m n -- n )
-    make-bits over first length identity-matrix
-    [ [ dupd m. ] when [ dup m. ] dip ] reduce nip ;
+: <cartesian-square-indices> ( n -- matrix )
+    dup 2array <cartesian-indices> ; inline
 
-: m^n ( m n -- n )
-    dup 0 >= [ (m^n) ] [ negative-power-matrix ] if ;
+ALIAS: transpose flip
 
-: stitch ( m -- m' )
-    [ ] [ [ append ] 2map ] map-reduce ;
+<PRIVATE
+: array-matrix? ( matrix -- ? )
+    [ array? ]
+    [ [ array? ] all? ] bi and ; inline foldable flushable
+
+: matrix-cols-iota ( matrix -- cols-iota )
+  first-unsafe length <iota> ; inline
 
-: kron ( m1 m2 -- m )
-    '[ [ _ n*m  ] map ] map stitch stitch ;
+: unshaped-cols-iota ( matrix -- cols-iota )
+  [ first-unsafe length 1 ] keep
+  [ length min ] (each) (each-integer) <iota> ; inline
 
-: outer ( u v -- m )
-    [ n*v ] curry map ;
+: generic-anti-transpose-unsafe ( cols-iota matrix -- newmatrix )
+    [ <reversed> [ nth-end-unsafe ] with { } map-as ] curry { } map-as ; inline
 
-: row ( n matrix -- col )
+: array-anti-transpose-unsafe ( cols-iota matrix -- newmatrix )
+    [ <reversed> [ array-nth-end-unsafe ] with map ] curry map ; inline
+PRIVATE>
+
+! much faster than [ reverse ] map flip [ reverse ] map
+: anti-transpose ( matrix -- newmatrix )
+    dup empty? [ ] [
+        [ dup regular-matrix?
+            [ matrix-cols-iota ] [ unshaped-cols-iota ] if
+        ] keep
+
+        dup array-matrix? [
+            array-anti-transpose-unsafe
+        ] [
+            generic-anti-transpose-unsafe
+        ] if
+    ] if ;
+
+ALIAS: anti-flip anti-transpose
+
+: row ( n matrix -- row )
     nth ; inline
 
-: rows ( seq matrix -- cols )
+: rows ( seq matrix -- rows )
     '[ _ row ] map ; inline
 
 : col ( n matrix -- col )
@@ -200,77 +162,151 @@ ERROR: negative-power-matrix m n ;
 : cols ( seq matrix -- cols )
     '[ _ col ] map ; inline
 
-: set-index ( object pair matrix -- )
-    [ first2 swap ] dip nth set-nth ; inline
+:: >square-matrix ( m -- subset )
+    m dimension first2 :> ( x y ) {
+        { [ x y = ] [ m ] }
+        { [ x y < ] [ x <iota> m cols transpose ] }
+        { [ x y > ] [ y <iota> m rows ] }
+    } cond ;
+
+GENERIC: <square-rows> ( desc -- matrix )
+M: integer <square-rows>
+    <iota> <square-rows> ;
+M: sequence <square-rows>
+    [ length ] keep >array '[ _ clone ] { } replicate-as ;
+
+M: square-matrix <square-rows> ;
+M: matrix <square-rows> >square-matrix ; ! coercing to square is more useful than no-method
+
+GENERIC: <square-cols> ( desc -- matrix )
+M: integer <square-cols>
+    <iota> <square-cols> ;
+M: sequence <square-cols>
+    <square-rows> flip ;
+
+M: square-matrix <square-cols> ;
+M: matrix <square-cols>
+    >square-matrix ;
 
-: set-indices ( object sequence matrix -- )
-    '[ _ set-index ] with each ; inline
+<PRIVATE ! implementation details of <lower-matrix> and <upper-matrix>
+: dimension-range ( matrix -- dim range )
+    dimension [ <coordinate-matrix> ] [ first [1,b] ] bi ;
+
+: upper-matrix-indices ( matrix -- matrix' )
+    dimension-range <reversed> [ tail-slice* >array ] 2map concat ;
+
+: lower-matrix-indices ( matrix -- matrix' )
+    dimension-range [ head-slice >array ] 2map concat ;
+PRIVATE>
+
+! triangulars
+DEFER: matrix-set-nths
+: <lower-matrix> ( object m n -- matrix )
+    <zero-matrix> [ lower-matrix-indices ] [ matrix-set-nths ] [ ] tri ;
+
+: <upper-matrix> ( object m n -- matrix )
+    <zero-matrix> [ upper-matrix-indices ] [ matrix-set-nths ] [ ] tri ;
 
-: matrix-map ( matrix quot -- )
+! element- and sequence-wise operations, getters and setters
+: stitch ( m -- m' )
+    [ ] [ [ append ] 2map ] map-reduce ;
+
+: matrix-map ( matrix quot: ( ... elt -- ... elt' ) -- matrix' )
     '[ _ map ] map ; inline
 
-: column-map ( matrix quot -- seq )
-    [ [ first length <iota> ] keep ] dip '[ _ col @ ] map ; inline
+: column-map ( matrix quot: ( ... col -- ... col' ) -- matrix' )
+    [ transpose ] dip map transpose ; inline
 
-: cartesian-square-indices ( n -- matrix )
-    <iota> dup cartesian-product ; inline
+: matrix-nth ( pair matrix -- elt )
+    [ first2 swap ] dip nth nth ; inline
 
-: cartesian-matrix-map ( matrix quot -- matrix' )
-    [ [ first length cartesian-square-indices ] keep ] dip
-    '[ _ @ ] matrix-map ; inline
+: matrix-nths ( pairs matrix -- elts )
+    '[ _ matrix-nth ] map ; inline
 
-: cartesian-matrix-column-map ( matrix quot -- matrix' )
-    [ cols first2 ] prepose cartesian-matrix-map ; inline
+: matrix-set-nth ( obj pair matrix -- )
+    [ first2 swap ] dip nth set-nth ; inline
 
-: cov-matrix-ddof ( matrix ddof -- cov )
-    '[ _ cov-ddof ] cartesian-matrix-column-map ; inline
+: matrix-set-nths ( obj pairs matrix -- )
+    '[ _ matrix-set-nth ] with each ; inline
 
-: population-cov-matrix ( matrix -- cov ) 0 cov-matrix-ddof ; inline
+! -------------------------------------------
+! simple math of matrices follows
+: mneg ( m -- m' ) [ vneg ] map ;
+: mabs ( m -- m' ) [ vabs ] map ;
 
-: sample-cov-matrix ( matrix -- cov ) 1 cov-matrix-ddof ; inline
+: n+m ( n m -- m ) [ n+v ] with map ;
+: m+n ( m n -- m ) [ v+n ] curry map ;
+: n-m ( n m -- m ) [ n-v ] with map ;
+: m-n ( m n -- m ) [ v-n ] curry map ;
+: n*m ( n m -- m ) [ n*v ] with map ;
+: m*n ( m n -- m ) [ v*n ] curry map ;
+: n/m ( n m -- m ) [ n/v ] with map ;
+: m/n ( m n -- m ) [ v/n ] curry map ;
 
-GENERIC: square-rows ( object -- matrix )
-M: integer square-rows <iota> square-rows ;
-M: sequence square-rows
-    [ length ] keep >array '[ _ clone ] { } replicate-as ;
+: m+  ( m1 m2 -- m ) [ v+ ] 2map ;
+: m-  ( m1 m2 -- m ) [ v- ] 2map ;
+: m*  ( m1 m2 -- m ) [ v* ] 2map ;
+: m/  ( m1 m2 -- m ) [ v/ ] 2map ;
 
-GENERIC: square-cols ( object -- matrix )
-M: integer square-cols <iota> square-cols ;
-M: sequence square-cols
-    [ length ] keep [ <array> ] with { } map-as ;
+: v.m ( v m -- p ) flip [ v. ] with map ;
+: m.v ( m v -- p ) [ v. ] curry map ;
+: m. ( m m -- m ) flip [ swap m.v ] curry map ;
 
-: make-matrix-with-indices ( m n quot -- matrix )
-    [ [ <iota> ] bi@ ] dip cartesian-map ; inline
+: m~  ( m1 m2 epsilon -- ? ) [ v~ ] curry 2all? ;
 
-: null-matrix? ( matrix -- ? ) empty? ; inline
+: mmin ( m -- n ) [ 1/0. ] dip [ [ min ] each ] each ;
+: mmax ( m -- n ) [ -1/0. ] dip [ [ max ] each ] each ;
+: mnorm ( m -- m' ) dup mmax abs m/n ;
+: m-infinity-norm ( m -- n ) [ [ abs ] map-sum ] map supremum ;
+: m-1norm ( m -- n ) flip m-infinity-norm ;
+: frobenius-norm ( m -- n ) [ [ sq ] map-sum ] map-sum sqrt ;
 
-: well-formed-matrix? ( matrix -- ? )
-    [ t ] [
-        [ ] [ first length ] bi
-        '[ length _ = ] all?
-    ] if-empty ;
+! well-defined for square matrices; but works on nonsquare too
+: main-diagonal ( matrix -- seq )
+    >square-matrix [ swap nth-unsafe ] map-index ; inline
 
-: dim ( matrix -- pair/f )
-    [ 2 0 <array> ]
-    [ [ length ] [ first length ] bi 2array ] if-empty ;
+! top right to bottom left; reverse the result if you expected it to start in the lower left
+: anti-diagonal ( matrix -- seq )
+    >square-matrix [ swap nth-end-unsafe ] map-index ; inline
 
-: square-matrix? ( matrix -- ? )
-    { [ well-formed-matrix? ] [ dim all-eq? ] } 1&& ;
+<PRIVATE
+: (rows-iota) ( matrix -- rows-iota )
+    dimension first-unsafe <iota> ;
+: (cols-iota) ( matrix -- cols-iota )
+    dimension second-unsafe <iota> ;
 
-: matrix-coordinates ( dim -- coordinates )
-    first2 [ <iota> ] bi@ cartesian-product ; inline
+: simple-rows-except ( matrix desc quot -- others )
+    curry [ dup (rows-iota) ] dip
+    pick reject-as swap rows ; inline
 
-: dimension-range ( matrix -- dim range )
-    dim [ matrix-coordinates ] [ first [1,b] ] bi ;
+: simple-cols-except ( matrix desc quot -- others )
+    curry [ dup (cols-iota) ] dip
+    pick reject-as swap cols transpose ; inline ! need to un-transpose the result of cols
 
-: upper-matrix-indices ( matrix -- matrix' )
-    dimension-range <reversed> [ tail-slice* >array ] 2map concat ;
+CONSTANT: scalar-except-quot [ = ]
+CONSTANT: sequence-except-quot [ member? ]
+PRIVATE>
 
-: lower-matrix-indices ( matrix -- matrix' )
-    dimension-range [ head-slice >array ] 2map concat ;
+GENERIC: rows-except ( matrix desc -- others )
+M: integer rows-except  scalar-except-quot   simple-rows-except ;
+M: sequence rows-except sequence-except-quot simple-rows-except ;
+
+GENERIC: cols-except ( matrix desc -- others )
+M: integer cols-except  scalar-except-quot   simple-cols-except ;
+M: sequence cols-except sequence-except-quot simple-cols-except ;
+
+! well-defined for any regular matrix
+: matrix-except ( matrix exclude-pair -- submatrix )
+    first2 [ rows-except ] dip cols-except ;
+
+ALIAS: submatrix-excluding matrix-except
+
+:: matrix-except-all ( matrix -- submatrices )
+    matrix dimension [ <iota> ] map first2-unsafe cartesian-product
+    [ [ matrix swap matrix-except ] map ] map ;
 
-: make-lower-matrix ( object m n -- matrix )
-    zero-matrix [ lower-matrix-indices ] [ set-indices ] [ ] tri ;
+ALIAS: all-submatrices matrix-except-all
 
-: make-upper-matrix ( object m n -- matrix )
-    zero-matrix [ upper-matrix-indices ] [ set-indices ] [ ] tri ;
+: dimension ( matrix -- dimension )
+    [ { 0 0 } ]
+    [ [ length ] [ first-unsafe length ] bi 2array ] if-empty ;
index ddc0c45020f9ca624be817231fc3633ebde47bb5..8912672bdc21a908e5adcf437b25f77c28be014f 100644 (file)
@@ -315,11 +315,11 @@ HELP: vclamp
 } ;
 
 HELP: v.
-{ $values { "u" { $sequence real } } { "v" { $sequence real } } { "x" "a real number" } }
+{ $values { "u" { $sequence real } } { "v" { $sequence real } } { "x" real } }
 { $description "Computes the dot product of two vectors." } ;
 
 HELP: h.
-{ $values { "u" { $sequence real } } { "v" { $sequence real } } { "x" "a real number" } }
+{ $values { "u" { $sequence real } } { "v" { $sequence real } } { "x" real } }
 { $description "Computes the Hermitian inner product of two vectors." } ;
 
 HELP: vs+
index bf4e99ee5ae1c8d8ab013d3c19edf994e31622e1..140fae23aa42cbb2b1bcd40188604e2b3317876d 100644 (file)
@@ -2,6 +2,7 @@ USING: math.vectors tools.test kernel specialized-arrays compiler
 kernel.private alien.c-types math.functions ;
 SPECIALIZED-ARRAY: int
 
+{ { 10 20 30 } } [ 10 { 1 2 3 } n*v ] unit-test
 { { 1 2 3 } } [ 1/2 { 2 4 6 } n*v ] unit-test
 { { 1 2 3 } } [ { 2 4 6 } 1/2 v*n ] unit-test
 { { 1 2 3 } } [ { 2 4 6 } 2 v/n ] unit-test
@@ -49,3 +50,10 @@ SPECIALIZED-ARRAY: int
 { { 0 30 100 } } [
     { -10 30 120 } { 0 0 0 } { 100 100 100 } vclamp
 ] unit-test
+
+{ { 0 0 1 } } [ { 1 0 0 } { 0 1 0 } cross ] unit-test
+{ { 1 0 0 } } [ { 0 1 0 } { 0 0 1 } cross ] unit-test
+{ { 0 1 0 } } [ { 0 0 1 } { 1 0 0 } cross ] unit-test
+{ { 0.0 -0.707 0.707 } } [ { 1.0 0.0 0.0 } { 0.0 0.707 0.707 } cross ] unit-test
+{ { 0 -2 2 } } [ { -1 -1 -1 } { 1 -1 -1 } cross ] unit-test
+{ { 1 0 0 } } [ { 1 1 0 } { 1 0 0 } proj ] unit-test
index 8ae3cc4fbf8faeb305d2640b9e22cd69cc1e9ea2..a35b9696a9b28320c7d5314175c8fb51966296c1 100644 (file)
@@ -279,3 +279,19 @@ PRIVATE>
 
 : vclamp ( v min max -- w )
     rot vmin vmax ; inline
+
+: cross ( vec1 vec2 -- vec3 )
+    [ [ { 1 2 0 } vshuffle ] [ { 2 0 1 } vshuffle ] bi* v* ]
+    [ [ { 2 0 1 } vshuffle ] [ { 1 2 0 } vshuffle ] bi* v* ] 2bi v- ; inline
+
+:: normal ( vec1 vec2 vec3 -- vec4 )
+    vec2 vec1 v- vec3 vec1 v- cross normalize ; inline
+
+: proj ( v u -- w )
+    [ [ v. ] [ norm-sq ] bi / ] keep n*v ;
+
+: perp ( v u -- w )
+    dupd proj v- ;
+
+: angle-between ( v u -- a )
+    [ normalize ] bi@ h. acos ;
index 9515a330efc4702aded0a6fc1460bba5cf91f754..1db2e013c58dfc754f3d88f6194017468bf102dd 100644 (file)
@@ -1,15 +1,15 @@
-USING: kernel locals math math.matrices math.order math.vectors
-prettyprint sequences ;
+USING: kernel locals math math.matrices math.matrices.extras
+math.order math.vectors prettyprint sequences ;
 IN: benchmark.3d-matrix-scalar
 
 :: p-matrix ( dim fov near far -- matrix )
     dim dup first2 min v/n fov v*n near v*n
-    near far frustum-matrix4 ;
+    near far <frustum-matrix4> ;
 
 :: mv-matrix ( pitch yaw location -- matrix )
-    { 1.0 0.0 0.0 } pitch rotation-matrix4
-    { 0.0 1.0 0.0 } yaw   rotation-matrix4
-    location vneg translation-matrix4 m. m. ;
+    { 1.0 0.0 0.0 } pitch <rotation-matrix4>
+    { 0.0 1.0 0.0 } yaw   <rotation-matrix4>
+    location vneg <translation-matrix4> m. m. ;
 
 :: 3d-matrix-scalar-benchmark ( -- )
     f :> result!
index c489079bd49a1d26e7fc319dcbece4db1e0df1b2..218091cce4be53ffbdf7a29565405d1bf5f68499 100644 (file)
@@ -1,5 +1,6 @@
-USING: kernel locals math math.matrices.simd math.order math.vectors
-math.vectors.simd prettyprint sequences typed ;
+USING: kernel locals math math.matrices math.matrices.simd
+math.order math.vectors math.vectors.simd prettyprint sequences
+typed ;
 QUALIFIED-WITH: alien.c-types c
 IN: benchmark.3d-matrix-vector
 
index 8324f8266e2e15052fdcd674f07d28fdec85bec2..caee9bb773db042d7b1d5c0192d4424c92e893ce 100644 (file)
@@ -1,4 +1,4 @@
-USING: locals math math.combinatorics math.matrices
+USING: locals math math.combinatorics math.matrices math.matrices.extras
 prettyprint sequences typed ;
 IN: benchmark.matrix-exponential-scalar
 
@@ -15,7 +15,7 @@ IN: benchmark.matrix-exponential-scalar
 
 :: matrix-exponential-scalar-benchmark ( -- )
     f :> result!
-    4 identity-matrix :> i4
+    4 <identity-matrix> :> i4
     10000 [
         i4 20 e^m result!
     ] times
index 818b4045347cfbe9e0df94c16111f3c9cd89b849..15638156d0fdbf6bb85c13830b9b2eb13ad33fa8 100644 (file)
@@ -2,8 +2,9 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors colors.constants game.debug game.loop
 game.worlds gpu gpu.framebuffers gpu.util.wasd kernel literals
-locals make math math.matrices math.parser math.trig sequences
-specialized-arrays ui.gadgets.worlds ui.pixel-formats ;
+locals make math math.matrices math.matrices.extras math.parser
+math.trig sequences specialized-arrays ui.gadgets.worlds
+ui.pixel-formats ;
 FROM: alien.c-types => float ;
 SPECIALIZED-ARRAY: float
 IN: game.debug.tests
@@ -21,7 +22,7 @@ IN: game.debug.tests
         { 0 0 0 } { 1 0 0 } COLOR: red   debug-line
         { 0 0 0 } { 0 1 0 } COLOR: green debug-line
         { 0 0 0 } { 0 0 1 } COLOR: blue  debug-line
-        { -1.2 0 0 } { 0 1 0 } 0 deg>rad rotation-matrix3 debug-axes
+        { -1.2 0 0 } { 0 1 0 } 0 deg>rad <rotation-matrix3> debug-axes
         { 3 5 -2 } { 3 2 1 } COLOR: white debug-box
         { 0 9 0 } 8 2 COLOR: blue debug-cylinder
     ] float-array{ } make
index 0a1acff7453af6eb040f14b9677b4ba322c918a4..1197e992161ba5c6378b61ebc6c83037a1369589 100644 (file)
@@ -1,8 +1,8 @@
 ! Copyright (C) 2010 Slava Pestov.
-USING: arrays kernel math.matrices math.vectors.simd.cords
-math.trig gml.runtime ;
+USING: arrays gml.runtime kernel math.matrices
+math.matrices.extras math.trig math.vectors.simd.cords   ;
 IN: gml.geometry
 
 GML: rot_vec ( v n alpha -- v )
     ! Inefficient!
-    deg>rad rotation-matrix4 swap >array m.v >double-4 ;
+    deg>rad <rotation-matrix4> swap >array m.v >double-4 ;
index fdffa86c9c62abc48f04ff40e4bd225e5c3bc883..ced1af381672e71f5152b7c22fd811e961dae417 100644 (file)
@@ -2,9 +2,9 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors arrays combinators.tuple game.loop game.worlds
 generalizations gpu gpu.render gpu.shaders gpu.util gpu.util.wasd
-kernel literals math math.libm math.matrices math.order math.vectors
-method-chains sequences ui ui.gadgets ui.gadgets.worlds
-ui.pixel-formats audio.engine audio.loader locals ;
+kernel literals math math.libm math.matrices math.matrices.extras
+math.order math.vectors method-chains sequences ui ui.gadgets
+ui.gadgets.worlds ui.pixel-formats audio.engine audio.loader locals ;
 IN: gpu.demos.raytrace
 
 GLSL-SHADER-FILE: raytrace-vertex-shader vertex-shader "raytrace.v.glsl"
@@ -48,7 +48,7 @@ TUPLE: raytrace-world < wasd-world
     dup dtheta>> [ + ] curry change-theta drop ;
 
 : sphere-center ( sphere -- center )
-    [ [ axis>> ] [ theta>> ] bi rotation-matrix4 ]
+    [ [ axis>> ] [ theta>> ] bi <rotation-matrix4> ]
     [ home>> ] bi m.v ;
 
 M: sphere audio-position sphere-center ; inline
index 46f265e9e5ab9503ef4c80a1a9cf3a85dce82fbb..40c5f653f27d2914f6c4b6291f378bd6dcb6ef07 100644 (file)
@@ -4,8 +4,8 @@ USING: accessors arrays combinators.smart game.input
 game.input.scancodes game.loop game.worlds
 gpu.render gpu.state kernel literals
 locals math math.constants math.functions math.matrices
-math.order math.vectors opengl.gl sequences
-ui ui.gadgets.worlds specialized-arrays audio.engine ;
+math.matrices.extras math.order math.vectors opengl.gl
+sequences ui ui.gadgets.worlds specialized-arrays audio.engine ;
 FROM: alien.c-types => float ;
 SPECIALIZED-ARRAY: float
 IN: gpu.util.wasd
@@ -38,14 +38,14 @@ GENERIC: wasd-fly-vertically? ( world -- ? )
 M: wasd-world wasd-fly-vertically? drop t ;
 
 : wasd-mv-matrix ( world -- matrix )
-    [ { 1.0 0.0 0.0 } swap pitch>> rotation-matrix4 ]
-    [ { 0.0 1.0 0.0 } swap yaw>>   rotation-matrix4 ]
-    [ location>> vneg translation-matrix4 ] tri m. m. ;
+    [ { 1.0 0.0 0.0 } swap pitch>> <rotation-matrix4> ]
+    [ { 0.0 1.0 0.0 } swap yaw>>   <rotation-matrix4> ]
+    [ location>> vneg <translation-matrix4> ] tri m. m. ;
 
 : wasd-mv-inv-matrix ( world -- matrix )
-    [ location>> translation-matrix4 ]
-    [ {  0.0 -1.0 0.0 } swap yaw>>   rotation-matrix4 ]
-    [ { -1.0  0.0 0.0 } swap pitch>> rotation-matrix4 ] tri m. m. ;
+    [ location>> <translation-matrix4> ]
+    [ {  0.0 -1.0 0.0 } swap yaw>>   <rotation-matrix4> ]
+    [ { -1.0  0.0 0.0 } swap pitch>> <rotation-matrix4> ] tri m. m. ;
 
 : wasd-p-matrix ( world -- matrix )
     p-matrix>> ;
@@ -63,7 +63,7 @@ CONSTANT: fov 0.7
     world wasd-far-plane :> far-plane
 
     world wasd-fov-vector near-plane v*n
-    near-plane far-plane frustum-matrix4 ;
+    near-plane far-plane <frustum-matrix4> ;
 
 :: wasd-pixel-ray ( world loc -- direction )
     loc world dim>> [ /f 0.5 - 2.0 * ] 2map
diff --git a/extra/math/matrices/elimination/authors.txt b/extra/math/matrices/elimination/authors.txt
new file mode 100644 (file)
index 0000000..1901f27
--- /dev/null
@@ -0,0 +1 @@
+Slava Pestov
diff --git a/extra/math/matrices/elimination/elimination-docs.factor b/extra/math/matrices/elimination/elimination-docs.factor
new file mode 100644 (file)
index 0000000..d0de9d1
--- /dev/null
@@ -0,0 +1,39 @@
+USING: help.markup help.syntax math sequences ;
+
+IN: math.matrices.elimination
+
+HELP: inverse
+{ $values { "matrix" sequence } }
+{ $description "Computes the multiplicative inverse of a matrix. Assuming the matrix is invertible." }
+{ $examples
+  "A matrix multiplied by its inverse is the identity matrix."
+  { $example
+    "USING: kernel math.matrices prettyprint ;"
+    "FROM: math.matrices.elimination => inverse ;"
+    "{ { 3 4 } { 7 9 } } dup inverse m. 2 <identity-matrix> = ."
+    "t"
+  }
+} ;
+
+HELP: echelon
+{ $values { "matrix" sequence } { "matrix'" sequence } }
+{ $description "Computes the reduced row-echelon form of the matrix." } ;
+
+HELP: nonzero-rows
+{ $values { "matrix" sequence } { "matrix'" sequence } }
+{ $description "Removes all all-zero rows from the matrix" }
+{ $examples
+  { $example
+    "USING: math.matrices.elimination prettyprint ;"
+    "{ { 0 0 } { 5 6 } { 0 0 } { 4 0 } } nonzero-rows ."
+    "{ { 5 6 } { 4 0 } }"
+  }
+} ;
+
+HELP: leading
+{ $values
+  { "seq" sequence }
+  { "n" "the index of the first match, or " { $snippet f } "." }
+  { "elt" "the first non-zero element, or " { $snippet f } "." }
+}
+{ $description "Find the first non-zero element of a sequence." } ;
diff --git a/extra/math/matrices/elimination/elimination-tests.factor b/extra/math/matrices/elimination/elimination-tests.factor
new file mode 100644 (file)
index 0000000..cd2ed06
--- /dev/null
@@ -0,0 +1,168 @@
+IN: math.matrices.elimination.tests
+USING: kernel math.matrices math.matrices.elimination
+tools.test sequences ;
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 1 0 }
+        { 0 0 0 1 }
+    }
+} [
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 1 0 }
+        { 0 0 0 1 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 1 0 }
+        { 0 0 0 1 }
+    }
+} [
+    {
+        { 1 0 0 0 }
+        { 1 1 0 0 }
+        { 1 0 1 0 }
+        { 1 0 0 1 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 1 0 }
+        { 0 0 0 1 }
+    }
+} [
+    {
+        { 1 0 0 0 }
+        { 1 1 0 0 }
+        { 1 0 1 0 }
+        { 1 1 0 1 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 1 0 }
+        { 0 0 0 1 }
+    }
+} [
+    {
+        { 1 0 0 0 }
+        { 1 1 0 0 }
+        { 1 1 0 1 }
+        { 1 0 1 0 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 0 0 }
+        { 0 0 0 0 }
+    }
+} [
+    {
+        { 0 1 0 0 }
+        { 1 0 0 0 }
+        { 1 0 0 0 }
+        { 1 0 0 0 }
+    } [
+        [ 1 ] [ 0 0 pivot-row ] unit-test
+        1 0 do-row
+    ] with-matrix
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 0 0 }
+        { 0 0 0 0 }
+    }
+} [
+    {
+        { 0 1 0 0 }
+        { 1 0 0 0 }
+        { 1 0 0 0 }
+        { 1 0 0 0 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 0 0 0 1 }
+        { 0 0 0 0 }
+    }
+} [
+    {
+        { 1 0 0 0 }
+        { 0 1 0 0 }
+        { 1 0 0 1 }
+        { 1 0 0 1 }
+    } echelon
+] unit-test
+
+{
+    {
+        { 1 0 0 1 }
+        { 0 1 0 1 }
+        { 0 0 0 -1 }
+        { 0 0 0 0 }
+    }
+} [
+    {
+        { 0 1 0 1 }
+        { 1 0 0 1 }
+        { 1 0 0 0 }
+        { 1 1 0 1 }
+    } echelon
+] unit-test
+
+{
+    2
+} [
+    {
+        { 0 0 }
+        { 0 0 }
+    } nullspace length
+] unit-test
+
+{
+    1 3
+} [
+    {
+        { 0 1 0 1 }
+        { 1 0 0 1 }
+        { 1 0 0 0 }
+        { 1 1 0 1 }
+    } null/rank
+] unit-test
+
+{
+    1 3
+} [
+    {
+        { 0 0 0 0 0 1 0 1 }
+        { 0 0 0 0 1 0 0 1 }
+        { 0 0 0 0 1 0 0 0 }
+        { 0 0 0 0 1 1 0 1 }
+    } null/rank
+] unit-test
+
+{ { { 1 0 -1 } { 0 1 2 } } }
+[ { { 1 2 3 } { 4 5 6 } } solution ] unit-test
diff --git a/extra/math/matrices/elimination/elimination.factor b/extra/math/matrices/elimination/elimination.factor
new file mode 100644 (file)
index 0000000..19af0a4
--- /dev/null
@@ -0,0 +1,113 @@
+! Copyright (C) 2006, 2010 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel locals math math.vectors math.matrices
+namespaces sequences fry sorting ;
+IN: math.matrices.elimination
+
+SYMBOL: matrix
+
+: with-matrix ( matrix quot -- )
+    matrix swap [ matrix get ] compose with-variable ; inline
+
+: nth-row ( row# -- seq ) matrix get nth ;
+
+: change-row ( ..a row# quot: ( ..a seq -- ..b seq ) -- ..b )
+    matrix get swap change-nth ; inline
+
+: exchange-rows ( row# row# -- ) matrix get exchange ;
+
+: rows ( -- n ) matrix get length ;
+
+: cols ( -- n ) 0 nth-row length ;
+
+: skip ( i seq quot -- n )
+    over [ find-from drop ] dip swap [ ] [ length ] ?if ; inline
+
+: first-col ( row# -- n )
+    ! First non-zero column
+    0 swap nth-row [ zero? not ] skip ;
+
+: clear-scale ( col# pivot-row i-row -- n )
+    overd nth dup zero? [
+        3drop 0
+    ] [
+        [ nth dup zero? ] dip swap [
+            2drop 0
+        ] [
+            swap / neg
+        ] if
+    ] if ;
+
+: (clear-col) ( col# pivot-row i -- )
+    [ [ clear-scale ] 2keep [ n*v ] dip v+ ] change-row ;
+
+: rows-from ( row# -- slice )
+    rows dup <iota> <slice> ;
+
+: clear-col ( col# row# rows -- )
+    [ nth-row ] dip [ [ 2dup ] dip (clear-col) ] each 2drop ;
+
+: do-row ( exchange-with row# -- )
+    [ exchange-rows ] keep
+    [ first-col ] keep
+    dup 1 + rows-from clear-col ;
+
+: find-row ( row# quot -- i elt )
+    [ rows-from ] dip find ; inline
+
+: pivot-row ( col# row# -- n )
+    [ dupd nth-row nth zero? not ] find-row 2nip ;
+
+: (echelon) ( col# row# -- )
+    over cols < over rows < and [
+        2dup pivot-row [ over do-row 1 + ] when*
+        [ 1 + ] dip (echelon)
+    ] [
+        2drop
+    ] if ;
+
+: echelon ( matrix -- matrix' )
+    [ 0 0 (echelon) ] with-matrix ;
+
+: nonzero-rows ( matrix -- matrix' )
+    [ [ zero? ] all? ] reject ;
+
+: null/rank ( matrix -- null rank )
+    echelon dup length swap nonzero-rows length [ - ] keep ;
+
+: leading ( seq -- n elt ) [ zero? not ] find ;
+
+: reduced ( matrix' -- matrix'' )
+    [
+        rows <iota> <reversed> [
+            dup nth-row leading drop
+            [ swap dup <iota> clear-col ] [ drop ] if*
+        ] each
+    ] with-matrix ;
+
+:: basis-vector ( row col# -- )
+    row clone :> row'
+    col# row' nth neg recip :> a
+    0 col# row' set-nth
+    a row n*v col# matrix get set-nth ;
+
+: nullspace ( matrix -- seq )
+    echelon reduced dup empty? [
+        dup first length <identity-matrix> [
+            [
+                dup leading drop
+                [ basis-vector ] [ drop ] if*
+            ] each
+        ] with-matrix flip nonzero-rows
+    ] unless ;
+
+: 1-pivots ( matrix -- matrix )
+    [ dup leading nip [ recip v*n ] when* ] map ;
+
+: solution ( matrix -- matrix )
+    echelon nonzero-rows reduced 1-pivots ;
+
+: inverse ( matrix -- matrix ) ! Assumes an invertible matrix
+    dup length
+    [ <identity-matrix> [ append ] 2map solution ] keep
+    [ tail ] curry map ;
diff --git a/extra/math/matrices/elimination/summary.txt b/extra/math/matrices/elimination/summary.txt
new file mode 100644 (file)
index 0000000..83972ab
--- /dev/null
@@ -0,0 +1 @@
+Solving systems of linear equations
diff --git a/extra/math/matrices/extras/authors.txt b/extra/math/matrices/extras/authors.txt
new file mode 100644 (file)
index 0000000..26ec268
--- /dev/null
@@ -0,0 +1,4 @@
+Slava Pestov
+Joe Groff
+Doug Coleman
+Cat Stevens
diff --git a/extra/math/matrices/extras/extras-docs.factor b/extra/math/matrices/extras/extras-docs.factor
new file mode 100644 (file)
index 0000000..b16f015
--- /dev/null
@@ -0,0 +1,641 @@
+USING: arrays generic.single help.markup help.syntax kernel math
+math.matrices math.matrices.private math.matrices.extras
+math.order math.ratios math.vectors opengl.gl random sequences
+urls ;
+IN: math.matrices.extras
+
+ABOUT: "math.matrices.extras"
+
+ARTICLE: "math.matrices.extras" "Extra matrix operations"
+
+"These constructions have special mathematical properties:"
+{ $subsections
+    <box-matrix>
+    <hankel-matrix>
+    <hilbert-matrix>
+    <toeplitz-matrix>
+    <vandermonde-matrix>
+}
+
+"Common transformation matrices:"
+{ $subsections
+    <frustum-matrix4>
+    <ortho-matrix4>
+    <rotation-matrix3>
+    <rotation-matrix4>
+    <scale-matrix3>
+    <scale-matrix4>
+    <skew-matrix4>
+    <translation-matrix4>
+
+    <random-integer-matrix>
+    <random-unit-matrix>
+
+}
+
+
+{ $subsections
+    invertible-matrix?
+    linearly-independent-matrix?
+}
+
+"Common algorithms on matrices:"
+{ $subsections
+    gram-schmidt
+    gram-schmidt-normalize
+    kronecker-product
+    outer-product
+}
+
+"Matrix algebra:"
+{ $subsections
+    mmin
+    mmax
+    mnorm
+    rank
+    nullity
+
+} { $subsections
+    determinant 1/det m*1/det
+    >minors >cofactors
+    multiplicative-inverse
+}
+
+"Covariance in matrices:"
+{ $subsections
+    covariance-matrix
+    covariance-matrix-ddof
+    sample-covariance-matrix
+}
+
+"Errors thrown by this vocabulary:"
+{ $subsections negative-power-matrix non-square-determinant undefined-inverse } ;
+
+HELP: invertible-matrix?
+{ $values { "matrix" matrix } { "?" boolean } }
+{ $description "Tests whether the input matrix has a " { $link multiplicative-inverse } ". In order for a matrix to be invertible, it must be a " { $link square-matrix } ", " { $emphasis "or" } ", if it is non-square, it must not be of " { $link +deficient-rank+ } "." }
+{ $examples { $example "USING: math.matrices.extras prettyprint ;" "" } } ;
+
+HELP: linearly-independent-matrix?
+{ $values { "matrix" matrix } { "?" boolean } }
+{ $description "Tests whether the input matrix is linearly independent." }
+{ $examples { $example "USING: math.matrices.extras prettyprint ;" "" } } ;
+
+! SINGLETON RANK TYPES
+HELP: rank-kind
+{ $class-description "The class of matrix rank quantifiers." } ;
+
+HELP: +full-rank+
+{ $class-description "A " { $link rank-kind } " describing a matrix of full rank." } ;
+HELP: +half-rank+
+{ $class-description "A " { $link rank-kind } " describing a matrix of half rank." } ;
+HELP: +zero-rank+
+{ $class-description "A " { $link rank-kind } " describing a matrix of zero rank." } ;
+HELP: +deficient-rank+
+{ $class-description "A " { $link rank-kind } " describing a matrix of deficient rank." } ;
+HELP: +uncalculated-rank+
+{ $class-description "A " { $link rank-kind } " describing a matrix whose rank is not (yet) known." } ;
+
+! ERRORS
+
+HELP: negative-power-matrix
+{ $values { "m" matrix } { "n" integer } }
+{ $description "Throws a " { $link negative-power-matrix } " error." }
+{ $error-description "Given the semantics of " { $link m^n } ", negative exponents are not within the domain of the power matrix function." } ;
+
+HELP: non-square-determinant
+{ $values { "m" integer } { "n" integer } }
+{ $description "Throws a " { $link non-square-determinant } " error." }
+{ $error-description { $link determinant } " was used with a non-square matrix whose dimensions are " { $snippet "m x n" } ". It is not generally possible to find the determinant of a non-square matrix." } ;
+
+HELP: undefined-inverse
+{ $values { "m" integer } { "n" integer } { "r" rank-kind } }
+{ $description "Throws an " { $link undefined-inverse } " error." }
+{ $error-description { $link multiplicative-inverse } " was used with a non-square matrix of rank " { $snippet "rank" } " whose dimensions are " { $snippet "m x n" } ". It is not generally possible to find the inverse of a " { $link +deficient-rank+ } " non-square " { $link matrix } "." } ;
+
+HELP: <random-integer-matrix>
+{ $values { "m" integer } { "n" integer } { "max" integer } { "matrix" matrix } }
+{ $description "Creates a " { $snippet "m x n" } " " { $link matrix } " full of random, possibly signed " { $link integer } "s whose absolute values are less than or equal to " { $snippet "max" } ", as given by " { $link random-integers } "." }
+{ $notelist
+    { "The signedness of the numbers in the resulting matrix will be randomized. Use " { $link mabs } " with this word to generate a matrix of random positive integers." }
+    { $equiv-word-note "integral" <random-unit-matrix> }
+}
+{ $errors { $link no-method } " if " { $snippet "max"} " is not an " { $link integer } "." }
+{ $examples
+    { $unchecked-example
+        "USING: math.matrices.extras prettyprint ;"
+        "2 4 15 <random-integer-matrix> ."
+        "{ { -9 -9 1 3 } { -14 -8 14 10 } }"
+    }
+} ;
+
+HELP: <random-unit-matrix>
+{ $values { "m" integer } { "n" integer } { "max" number } { "matrix" matrix } }
+{ $description "Creates a " { $snippet "m x n" } " " { $link matrix } " full of random, possibly signed " { $link float } "s  as a fraction of " { $snippet "max" } "." }
+{ $notelist
+    { "The signedness of the numbers in the resulting matrix will be randomized. Use " { $link mabs } " with this word to generate a matrix of random positive numbers." }
+    { $equiv-word-note "real" <random-integer-matrix> }
+    { "This word is implemented by generating sub-integral floats through " { $link random-units } " and multiplying by random integers less than or equal to " { $snippet "max" } "." }
+}
+{ $examples
+    { $unchecked-example
+        "USING: math.matrices.extras prettyprint ;"
+        "4 2 15 <random-unit-matrix> ."
+"{
+    { -3.713295909201797 3.815787135075961 }
+    { -2.460506890603817 1.535222788710546 }
+    { 3.692213981267878 -1.462963244399762 }
+    { 13.8967592095433 -6.688509969360172 }
+}"
+    }
+} ;
+
+
+
+HELP: <hankel-matrix>
+{ $values { "n" integer } { "matrix" matrix } }
+{ $description
+    "A Hankel matrix is a symmetric, " { $link square-matrix } " in which each ascending skew-diagonal from left to right is constant. See " { $url URL" https://en.wikipedia.org/wiki/Hankel_matrix" "hankel matrix" } "."
+    $nl
+    "The following is true of any Hankel matrix" { $snippet "A" } ": " { $snippet "A[i][j] = A[j][i] = a[i+j-2]" } "."
+    $nl
+    "The " { $link <toeplitz-matrix> } " is an upside-down Hankel matrix."
+    $nl
+    "The " { $link <hilbert-matrix> } " is a special case of the Hankel matrix."
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "4 <hankel-matrix> ."
+        "{ { 1 2 3 4 } { 2 3 4 0 } { 3 4 0 0 } { 4 0 0 0 } }"
+    }
+} ;
+
+HELP: <hilbert-matrix>
+{ $values { "m" integer } { "n" integer } { "matrix" matrix } }
+{ $description
+    "A Hilbert matrix is a " { $link square-matrix } " " { $snippet "A" } " in which entries are the unit fractions "
+    { $snippet "A[i][j] = 1/(i+j-1)" }
+    ". See " { $url URL" https://en.wikipedia.org/wiki/Hilbert_matrix" "hilbert matrix" } "."
+    $nl
+    "A Hilbert matrix is a special case of the " { $link <hankel-matrix> } "."
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "1 2 <hilbert-matrix> ."
+        "{ { 1 1/2 } }"
+    }
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "3 6 <hilbert-matrix> ."
+"{
+    { 1 1/2 1/3 1/4 1/5 1/6 }
+    { 1/2 1/3 1/4 1/5 1/6 1/7 }
+    { 1/3 1/4 1/5 1/6 1/7 1/8 }
+}"
+    }
+} ;
+
+HELP: <toeplitz-matrix>
+{ $values { "n" integer } { "matrix" matrix } }
+{ $description "A Toeplitz matrix is an upside-down " { $link <hankel-matrix> } ". Unlike the Hankel matrix, a Toeplitz matrix can be non-square. See " { $url URL" https://en.wikipedia.org/wiki/Hankel_matrix" "hankel matrix" } "."
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "4 <toeplitz-matrix> ."
+        "{ { 1 2 3 4 } { 2 1 2 3 } { 3 2 1 2 } { 4 3 2 1 } }"
+    }
+} ;
+
+HELP: <box-matrix>
+{ $values { "r" integer } { "matrix" matrix } }
+{ $description "Create a box matrix (a " { $link square-matrix } ") with the dimensions of " { $snippet "r x r" } ", filled with ones. The number of elements in the output scales linearly (" { $snippet "(r*2)+1" } ") with " { $snippet "r" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "2 <box-matrix> ."
+"{
+    { 1 1 1 1 1 }
+    { 1 1 1 1 1 }
+    { 1 1 1 1 1 }
+    { 1 1 1 1 1 }
+    { 1 1 1 1 1 }
+}"
+    }
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "3 <box-matrix> ."
+"{
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+    { 1 1 1 1 1 1 1 }
+}"
+    }
+
+} ;
+
+HELP: <scale-matrix3>
+{ $values { "factors" sequence } { "matrix" matrix } }
+{ $description "Make a " { $snippet "3 x 3" } " scaling matrix, used to scale an object in 3 dimensions. See " { $url URL" https://en.wikipedia.org/wiki/Scaling_(geometry)#Matrix_representation" "scaling matrix on Wikipedia" } "." }
+{ $notelist
+    { $finite-input-note "three" "factors" }
+    { $equiv-word-note "3-matrix" <scale-matrix4> }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ 22 33 -44 } <scale-matrix4> ."
+"{
+    { 22 0.0 0.0 0.0 }
+    { 0.0 33 0.0 0.0 }
+    { 0.0 0.0 -44 0.0 }
+    { 0.0 0.0 0.0 1.0 }
+}"
+    }
+} ;
+
+HELP: <scale-matrix4>
+{ $values { "factors" sequence } { "matrix" matrix } }
+{ $description "Make a " { $snippet "4 x 4" } " scaling matrix, used to scale an object in 3 or more dimensions. See " { $url URL" https://en.wikipedia.org/wiki/Scaling_(geometry)#Matrix_representation" "scaling matrix on Wikipedia" } "." }
+{ $notelist
+    { $finite-input-note "three" "factors" }
+    { $equiv-word-note "4-matrix" <scale-matrix3> }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ 22 33 -44 } <scale-matrix4> ."
+"{
+    { 22 0.0 0.0 0.0 }
+    { 0.0 33 0.0 0.0 }
+    { 0.0 0.0 -44 0.0 }
+    { 0.0 0.0 0.0 1.0 }
+}"
+    }
+} ;
+
+HELP: <ortho-matrix4>
+{ $values { "factors" sequence } { "matrix" matrix } }
+{ $description "Create a " { $link <scale-matrix4> } ", with the scale factors inverted." }
+{ $notelist
+    { $finite-input-note "three" "factors" }
+    { $equiv-word-note "inverse" <scale-matrix4> }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ -9.3 100 1/2 } <ortho-matrix4> ."
+"{
+    { -0.1075268817204301 0.0 0.0 0.0 }
+    { 0.0 1/100 0.0 0.0 }
+    { 0.0 0.0 2 0.0 }
+    { 0.0 0.0 0.0 1.0 }
+}"
+    }
+} ;
+
+HELP: <frustum-matrix4>
+{ $values { "xy-dim" pair } { "near" number } { "far" number } { "matrix" matrix } }
+{ $description "Make a " { $snippet "4 x 4" } " matrix suitable for representing an occlusion frustum. A viewing or occlusion frustum is the three-dimensional region of a three-dimensional object which is visible on the screen. See " { $url URL" https://en.wikipedia.org/wiki/Frustum" "frustum on Wikipedia" } "." }
+{ $notes { $finite-input-note "two" "xy-dim" } }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ 5 4 } 5 6 <frustum-matrix4> ."
+"{
+    { 1.0 0.0 0.0 0.0 }
+    { 0.0 1.25 0.0 0.0 }
+    { 0.0 0.0 -11.0 -60.0 }
+    { 0.0 0.0 -1.0 0.0 }
+}"
+    }
+} ;
+{ <frustum-matrix4> glFrustum } related-words
+
+HELP: cartesian-matrix-map
+{ $values { "matrix" matrix } { "quot" { $quotation ( ... pair matrix -- ... matrix' ) } } { "matrix-seq" { $sequence matrix } } }
+{ $description "Calls the quotation with the matrix and the coordinate pair of the current element on the stack, with the matrix on the top of the stack." }
+{ $examples
+  { $example
+    "USING: arrays math.matrices.extras prettyprint ;"
+    "{ { 21 22 } { 23 24 } } [ 2array ] cartesian-matrix-map ."
+"{
+    {
+        { { 0 0 } { { 21 22 } { 23 24 } } }
+        { { 0 1 } { { 21 22 } { 23 24 } } }
+    }
+    {
+        { { 1 0 } { { 21 22 } { 23 24 } } }
+        { { 1 1 } { { 21 22 } { 23 24 } } }
+    }
+}"
+  }
+}
+{ $notelist
+  { $equiv-word-note "orthogonal" cartesian-column-map }
+  { $equiv-word-note "two-dimensional" map-index }
+  $2d-only-note
+} ;
+
+HELP: cartesian-column-map
+{ $values { "matrix" matrix } { "quot" { $quotation ( ... pair matrix -- ... matrix' ) } } { "matrix-seq" { $sequence matrix } } }
+{ $notelist
+  { $equiv-word-note "orthogonal" cartesian-matrix-map }
+  $2d-only-note
+} ;
+
+HELP: gram-schmidt
+{ $values { "matrix" matrix } { "orthogonal" matrix } }
+{ $description "Apply a Gram-Schmidt transform on the matrix." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ { 1 2 } { 3 4 } { 5 6 } } gram-schmidt ."
+        "{ { 1 2 } { 4/5 -2/5 } { 0 0 } }"
+    }
+} ;
+
+HELP: gram-schmidt-normalize
+{ $values { "matrix" matrix } { "orthonormal" matrix } }
+{ $description "Apply a Gram-Schmidt transform on the matrix, and " { $link normalize } " each row of the result, resulting in an orthogonal and normalized matrix (orthonormal)." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ { 1 2 } { 3 4 } { 5 6 } } gram-schmidt-normalize ."
+"{
+    { 0.4472135954999579 0.8944271909999159 }
+    { 0.894427190999916 -0.447213595499958 }
+    { NAN: 8000000000000 NAN: 8000000000000 }
+}"
+    }
+} ;
+
+HELP: m^n
+{ $values { "m" matrix } { "n" object } }
+{ $description "Compute the " { $snippet "nth" } " power of the input matrix. If " { $snippet "n" } " is " { $snippet "-1" } ", the inverse of the matrix is calculated (but see " { $link multiplicative-inverse } " for pitfalls)." }
+{ $errors
+    { $link negative-power-matrix } " if " { $snippet "n" } " is a negative number other than " { $snippet "-1" } "."
+    $nl
+    { $link undefined-inverse } " if " { $snippet "n" } " is " { $snippet "-1" } " and the " { $link multiplicative-inverse } " of " { $snippet "m" } " is undefined."
+}
+{ $notelist
+    { $equiv-word-note "swapped" n^m }
+    $2d-only-note
+    { $matrix-scalar-note max abs / }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ { 1 2 } { 3 4 } } 2 m^n ."
+        "{ { 7 10 } { 15 22 } }"
+    }
+} ;
+
+HELP: n^m
+{ $values { "n" object } { "m" matrix } }
+{ $description "Because it is nonsensical to raise a number to the power of a matrix, this word exists to save typing " { $snippet "swap m^n" } ". See " { $link m^n } " for more information." }
+{ $errors
+    { $link negative-power-matrix } " if " { $snippet "n" } " is a negative number other than " { $snippet "-1" } "."
+    $nl
+    { $link undefined-inverse } " if " { $snippet "n" } " is " { $snippet "-1" } " and the " { $link multiplicative-inverse } " of " { $snippet "m" } " is undefined."
+}
+{ $notelist
+    { $equiv-word-note "swapped" m^n }
+    $2d-only-note
+    { $matrix-scalar-note max abs / }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "2 { { 1 2 } { 3 4 } } n^m ."
+        "{ { 7 10 } { 15 22 } }"
+    }
+} ;
+
+HELP: kronecker-product
+{ $values { "m1" matrix } { "m2" matrix } { "m" matrix } }
+{ $description "Calculates the " { $url URL" http://enwp.org/Kronecker_product" "Kronecker product" } " of two matrices. This product can be described as a generalization of the vector-based " { $link outer-product } " to matrices. The Kronecker product gives the matrix of the tensor product with respect to a standard choice of basis." }
+{ $notelist
+    { $equiv-word-note "matrix" outer-product }
+    $2d-only-note
+    { $matrix-scalar-note * }
+}
+{ $examples
+    { $unchecked-example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { 1 2 }
+    { 3 4 }
+} {
+    { 0 5 }
+    { 6 7 }
+} kronecker-product ."
+"{
+    { 0 5 0 10 }
+    { 6 7 12 14 }
+    { 0 15 0 20 }
+    { 18 21 24 28 }
+}" }
+} ;
+
+HELP: outer-product
+{ $values { "u" sequence } { "v" sequence } { "matrix" matrix } }
+{ $description "Computes the " { $url URL" http://  enwp.org/Outer_product" "outer-product product" } " of " { $snippet "u" } " and " { $snippet "v" } "." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+        "{ 5 6 7 } { 1 2 3 } outer-product ."
+        "{ { 5 10 15 } { 6 12 18 } { 7 14 21 } }" }
+} ;
+
+HELP: rank
+{ $values { "matrix" matrix } { "rank" rank-kind } }
+{ $contract "The " { $emphasis "rank" } " of a " { $link matrix } " is how its number of linearly independent columns compare to the maximal number of linearly independent columns for a matrix with the same dimension." }
+{ $notes "See " { $url "https://en.wikipedia.org/wiki/Rank_(linear_algebra)" } " for more information." } ;
+
+HELP: nullity
+{ $values { "matrix" matrix } { "nullity" rank-kind } }
+;
+
+HELP: determinant
+{ $values { "matrix" square-matrix } { "determinant" number } }
+{ $contract "Compute the determinant of the input matrix. Generally, the determinant of a matrix is a scaling factor of the transformation described by the matrix." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note max - * }
+}
+{ $errors { $link non-square-determinant } " if the input matrix is not a " { $link square-matrix } "." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    {  3  0 -1 }
+    { -3  1  3 }
+    {  2 -5  4 }
+} determinant ."
+        "44"
+    }
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { -8 -8 13 11 10 -5 -14 }
+    { 3 -11 -8 3 -7 -3 4 }
+    { 10 4 -5 3 0 -6 -12 }
+    { -14 0 -3 -8 10 0 10 }
+    { 3 -6 1 -10 -9 10 0 }
+    { 5 -12 -14 6 5 -1 -7 }
+    { -9 -14 -8 5 2 2 -2 }
+} determinant ."
+        "-103488155"
+    }
+} ;
+
+HELP: 1/det
+{ $values { "matrix" square-matrix } { "1/det" number } }
+{ $description "Find the inverse (" { $link recip } ") of the " { $link determinant } " of the input matrix." }
+{ $notelist
+    $2d-only-note
+    { $matrix-scalar-note determinant recip }
+}
+{ $errors
+    { $link non-square-determinant } " if the input matrix is not a " { $link square-matrix } "."
+    $nl
+    { $link division-by-zero } " if the " { $link determinant } " of the input matrix is " { $snippet "0" } "."
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { 0 10 -12 4 }
+    { -9 6 -11 9 }
+    { -5 -10 0 2 }
+    { -7 -11 10 11 }
+} 1/det ."
+        "-1/9086"
+    }
+} ;
+
+HELP: m*1/det
+{ $values { "matrix" square-matrix } { "matrix'" square-matrix } }
+{ $description "Multiply the input matrix by the inverse (" { $link recip } ") of its " { $link determinant } "." }
+{ $notelist
+    { "This word is used to implement " { $link recip } " for " { $link square-matrix } "." }
+    $2d-only-note
+    { $matrix-scalar-note determinant recip }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { -14 0 -13 7 }
+    { -4 11 7 -12 }
+    { -3 2 9 -14 }
+    { 3 -5 10 -2 }
+} m*1/det ."
+"{
+    { 7/6855 0 13/13710 -7/13710 }
+    { 2/6855 -11/13710 -7/13710 2/2285 }
+    { 1/4570 -1/6855 -3/4570 7/6855 }
+    { -1/4570 1/2742 -1/1371 1/6855 }
+}"
+    }
+}
+;
+
+HELP: >minors
+{ $values { "matrix" square-matrix } { "matrix'" square-matrix } }
+{ $description "Calculate the " { $emphasis "matrix of minors" } " of the input matrix. See " { $url URL" https://en.wikipedia.org/wiki/Minor_(linear_algebra)" "minor on Wikipedia" } "." }
+{ $notelist
+    $keep-shape-note
+    $2d-only-note
+    { $matrix-scalar-note determinant }
+}
+{ $errors { $link non-square-determinant } " if the input matrix is not a " { $link square-matrix } "." }
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { -8 0 7 -11 }
+    { 15 0 -3 -11 }
+    { 1 -10 -4 6 }
+    { 11 -15 3 -15 }
+} >minors ."
+"{
+    { 1710 -130 2555 -1635 }
+    { -690 -286 -2965 1385 }
+    { 1650 -754 3795 -1215 }
+    { 1100 416 2530 -810 }
+}"
+    }
+} ;
+
+HELP: >cofactors
+{ $values { "matrix" matrix } { "matrix'" matrix } }
+{ $description "Calculate the " { $emphasis "matrix of cofactors" } " of the input matrix. See " { $url URL" https://en.wikipedia.org/wiki/Minor_(linear_algebra)#Inverse_of_a_matrix" "matrix of cofactors on Wikipedia" } ". Alternating elements of the input matrix have their signs inverted." $nl "On odd rows, the even elements have their signs inverted. On even rows, odd elements have their signs inverted." }
+{ $notelist
+    $keep-shape-note
+    $2d-only-note
+    { $matrix-scalar-note neg }
+}
+{ $examples
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { 8 0 7 11 }
+    { 15 0 3 11 }
+    { 1 10 4 6 }
+    { 11 15 3 15 }
+} >cofactors ."
+"{
+    { 8 0 7 -11 }
+    { -15 0 -3 11 }
+    { 1 -10 4 -6 }
+    { -11 15 -3 15 }
+}"
+    }
+    { $example
+        "USING: math.matrices.extras prettyprint ;"
+"{
+    { -8 0 7 -11 }
+    { 15 0 -3 -11 }
+    { 1 -10 -4 6 }
+    { 11 -15 3 -15 }
+} >cofactors ."
+"{
+    { -8 0 7 11 }
+    { -15 0 3 -11 }
+    { 1 10 -4 -6 }
+    { -11 -15 -3 -15 }
+}"
+    }
+} ;
+
+HELP: multiplicative-inverse
+{ $values { "x" matrix } { "y" matrix } }
+{ $description "Calculate the multiplicative inverse of the input." $nl "If the input is a " { $link square-matrix } ", this is done by multiplying the " { $link transpose } " of the " { $link2 >cofactors "cofactors" } " of the " { $link2 >minors "minors" } " of the input matrix by the " { $link2 1/det "inverse of the determinant" } " of the input matrix."  }
+{ $notelist
+    $keep-shape-note
+    $2d-only-note
+    { $matrix-scalar-note determinant >cofactors 1/det }
+}
+{ $errors { $link non-square-determinant } " if the input matrix is not a " { $link square-matrix } "." } ;
+
+
+HELP: covariance-matrix-ddof
+{ $values { "matrix" matrix } { "ddof" object } { "cov" matrix } }
+;
+HELP: covariance-matrix
+{ $values { "matrix" matrix } { "cov" matrix } }
+;
+HELP: sample-covariance-matrix
+{ $values { "matrix" matrix } { "cov" matrix } }
+;
+HELP: population-covariance-matrix
+{ $values { "matrix" matrix } { "cov" matrix } }
+;
diff --git a/extra/math/matrices/extras/extras-tests.factor b/extra/math/matrices/extras/extras-tests.factor
new file mode 100644 (file)
index 0000000..86c65a1
--- /dev/null
@@ -0,0 +1,362 @@
+! Copyright (C) 2005, 2010, 2018 Slava Pestov, Joe Groff, and Cat Stevens.
+USING: arrays combinators.short-circuit grouping kernel math
+math.matrices math.matrices.extras math.matrices.extras.private
+math.statistics math.vectors sequences sequences.deep sets
+tools.test ;
+IN: math.matrices.extras
+
+<PRIVATE
+: call-eq? ( obj quots -- ? )
+    [ call( x -- x ) ] with map all-eq? ; !  inline
+PRIVATE>
+
+{ {
+    { 4181 6765 }
+    { 6765 10946 }
+} } [
+  { { 0 1 } { 1 1 } } 20 m^n
+] unit-test
+
+[ { { 0 1 } { 1 1 } } -20 m^n ] [ negative-power-matrix? ] must-fail-with
+[ { { 0 1 } { 1 1 } } -8 m^n ] [ negative-power-matrix? ] must-fail-with
+
+{ { 1 -2 3 -4 } } [ { 1 2 3 4 } t alternating-sign ] unit-test
+{ { -1 2 -3 4 } } [ { 1 2 3 4 } f alternating-sign ] unit-test
+
+
+{ t } [ 50 <box-matrix> dup transpose = ] unit-test
+{ t } [ 50 <box-matrix> dup                  anti-transpose = ] unit-test
+{ f } [ 4 <box-matrix> zero-matrix? ] unit-test
+
+{ t } [ 2 4 15 <random-integer-matrix> mabs {
+    [ flatten [ 15 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 2 = ]
+    [ first length 4 = ]
+} 1&& ] unit-test
+
+{ t } [ 4 4 -45 <random-integer-matrix> mabs {
+    [ flatten [ 45 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 4 = ]
+    [ first length 4 = ]
+} 1&& ] unit-test
+
+{ t } [ 2 2 1 <random-integer-matrix> mabs {
+    [ flatten [ 1 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 2 = ]
+    [ first length 2 = ]
+} 1&& ] unit-test
+
+{ t } [ 2 4 .89 <random-unit-matrix> mabs {
+    [ flatten [ .89 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 2 = ]
+    [ first length 4 = ]
+} 1&& ] unit-test
+
+{ t } [ 2 4 -45.89 <random-unit-matrix> mabs {
+    [ flatten [ 45.89 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 2 = ]
+    [ first length 4 = ]
+} 1&& ] unit-test
+
+{ t } [ 4 4 .89 <random-unit-matrix> mabs {
+    [ flatten [ .89 <= ] all? ]
+    [ regular-matrix? ]
+    [ length 4 = ]
+    [ first length 4 = ]
+} 1&& ] unit-test
+
+{ {
+    { 1   1/2 1/3 1/4 }
+    { 1/2 1/3 1/4 1/5 }
+    { 1/3 1/4 1/5 1/6 }
+} } [ 3 4 <hilbert-matrix> ] unit-test
+
+{ {
+    { 1 2 3 4 }
+    { 2 1 2 3 }
+    { 3 2 1 2 }
+    { 4 3 2 1 }
+} } [ 4 <toeplitz-matrix> ] unit-test
+
+{ {
+    { 1 2 3 4 }
+    { 2 3 4 0 }
+    { 3 4 0 0 }
+    { 4 0 0 0 } }
+} [ 4 <hankel-matrix> ] unit-test
+
+{ {
+    { 1 1 1 }
+    { 4 2 1 }
+    { 9 3 1 }
+    { 25 5 1 } }
+} [
+    { 1 2 3 5 } 3 <vandermonde-matrix>
+] unit-test
+
+{ {
+    { 0 5 0 10 }
+    { 6 7 12 14 }
+    { 0 15 0 20 }
+    { 18 21 24 28 }
+} } [ {
+    { 1 2 }
+    { 3 4 }
+} {
+    { 0 5 }
+    { 6 7 }
+} kronecker-product ] unit-test
+
+{ {
+    { 1  1  1  1 }
+    { 1 -1  1 -1 }
+    { 1  1 -1 -1 }
+    { 1 -1 -1  1 }
+} } [ {
+    { 1  1 }
+    { 1 -1 }
+} dup kronecker-product ] unit-test
+
+{ {
+    { 1 1 1 1 1 1 1 1 }
+    { 1 -1 1 -1 1 -1 1 -1 }
+    { 1 1 -1 -1 1 1 -1 -1 }
+    { 1 -1 -1 1 1 -1 -1 1 }
+    { 1 1 1 1 -1 -1 -1 -1 }
+    { 1 -1 1 -1 -1 1 -1 1 }
+    { 1 1 -1 -1 -1 -1 1 1 }
+    { 1 -1 -1 1 -1 1 1 -1 }
+} } [ {
+    { 1 1 }
+    { 1 -1 }
+} dup dup kronecker-product kronecker-product ] unit-test
+
+{ {
+    { 1 1 1 1 1 1 1 1 }
+    { 1 -1 1 -1 1 -1 1 -1 }
+    { 1 1 -1 -1 1 1 -1 -1 }
+    { 1 -1 -1 1 1 -1 -1 1 }
+    { 1 1 1 1 -1 -1 -1 -1 }
+    { 1 -1 1 -1 -1 1 -1 1 }
+    { 1 1 -1 -1 -1 -1 1 1 }
+    { 1 -1 -1 1 -1 1 1 -1 }
+} } [ {
+    { 1 1 }
+    { 1 -1 }
+} dup dup kronecker-product swap kronecker-product ] unit-test
+
+
+! kronecker-product is not generally commutative, make sure we have the right order
+{ {
+    { 1 2 3 4 5 1 2 3 4 5 }
+    { 6 7 8 9 10 6 7 8 9 10 }
+    { 1 2 3 4 5 -1 -2 -3 -4 -5 }
+    { 6 7 8 9 10 -6 -7 -8 -9 -10 }
+} } [ {
+    { 1 1 }
+    { 1 -1 }
+} {
+    { 1 2 3 4 5 }
+    { 6 7 8 9 10 }
+} kronecker-product ] unit-test
+
+{ {
+    { 1 1 2 2 3 3 4 4 5 5 }
+    { 1 -1 2 -2 3 -3 4 -4 5 -5 }
+    { 6 6 7 7 8 8 9 9 10 10 }
+    { 6 -6 7 -7 8 -8 9 -9 10 -10 }
+} } [ {
+    { 1 1 }
+    { 1 -1 }
+} {
+    { 1 2 3 4 5 }
+    { 6 7 8 9 10 }
+} swap kronecker-product ] unit-test
+
+{ {
+    { 5 10 15 }
+    { 6 12 18 }
+    { 7 14 21 }
+} } [
+    { 5 6 7 }
+    { 1 2 3 }
+    outer-product
+] unit-test
+
+
+CONSTANT: test-points {
+    { 80  27  89 } { 80  27  88 } { 75  25  90 }
+    { 62  24  87 } { 62  22  87 } { 62  23  87 }
+    { 62  24  93 } { 62  24  93 } { 58  23  87 }
+    { 58  18  80 } { 58  18  89 } { 58  17  88 }
+    { 58  18  82 } { 58  19  93 } { 50  18  89 }
+    { 50  18  86 } { 50  19  72 } { 50  19  79 }
+    { 50  20  80 } { 56  20  82 } { 70  20  91 }
+}
+
+{ {
+    { 84+2/35 22+23/35 24+4/7 }
+    { 22+23/35 9+104/105 6+87/140 }
+    { 24+4/7 6+87/140 28+5/7 }
+} } [ test-points sample-covariance-matrix ] unit-test
+
+{ {
+    { 80+8/147 21+85/147 23+59/147 }
+    { 21+85/147 9+227/441 6+15/49 }
+    { 23+59/147 6+15/49 27+17/49 }
+} } [ test-points covariance-matrix ] unit-test
+
+{
+    {
+        { 80+8/147 21+85/147 23+59/147 }
+        { 21+85/147 9+227/441 6+15/49 }
+        { 23+59/147 6+15/49 27+17/49 }
+    }
+} [
+    test-points population-covariance-matrix
+] unit-test
+
+{ t } [ { { 1 } }
+    { [ drop 1 ] [ (1determinant) ] [ 1 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ 0 } [ { { 0 } } determinant ] unit-test
+
+{ t } [ {
+    { 4 6 } ! order is significant
+    { 3 8 }
+} { [ drop 14 ] [ (2determinant) ] [ 2 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 3 8 }
+    { 4 6 }
+} { [ drop -14 ] [ (2determinant) ] [ 2 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 2 5 }
+    { 1 -3 }
+} { [ drop -11 ] [ (2determinant) ] [ 2 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 1 -3 }
+    { 2 5 }
+} { [ drop 11 ] [ (2determinant) ] [ 2 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 3 0 -1 }
+    { 2 -5 4 }
+    { -3 1 3 }
+} { [ drop -44 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 3 0 -1 }
+    { -3 1 3 }
+    { 2 -5 4 }
+} { [ drop 44 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 2 -3 1 }
+    { 4 2 -1 }
+    { -5 3 -2 }
+} { [ drop -19 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 2 -3 1 }
+    { -5 3 -2 }
+    { 4 2 -1 }
+} { [ drop 19 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 4 2 -1 }
+    { 2 -3 1 }
+    { -5 3 -2 }
+} { [ drop 19 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 5 1 -2 }
+    { -1 0 4 }
+    { 2 -3 3 }
+} { [ drop 65 ] [ (3determinant) ] [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 6 1 1 }
+    { 4 -2 5 }
+    { 2 8 7 }
+} { [ drop -306 ] [ (3determinant) ]  [ 3 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { -5  4 -3  2 }
+    { -2  1  0 -1 }
+    { -2 -3 -4 -5  }
+    {  0  2  0  4 }
+} { [ drop -24 ] [ 4 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ t } [ {
+    { 2 4 2 2 }
+    { 5 1 -6 10 }
+    { 4 3 -1 7 }
+    { 9 8 7 3 }
+} { [ drop 272 ] [ 4 swap (ndeterminant) ] [ determinant ] }
+    call-eq?
+] unit-test
+
+{ {
+    { 2 2 2 }
+    { -2 3 3 }
+    { 0 -10 0 }
+} } [ {
+    { 3 0 2 }
+    { 2 0 -2 }
+    { 0 1 1 }
+} >minors ] unit-test
+
+! i think this unit test is wrong
+! { {
+!     { 1 -6 -13 }
+!     { 0 0 0 }
+!     { 1 -6 -13 }
+! } } [ {
+!     { 1 2 1 }
+!     { 6 -1 0 }
+!     { 1 -2 -1 }
+! } >minors ] unit-test
+
+{ {
+    { 1 6 -13 }
+    { 0 0 0 }
+    { 1 6 -13 }
+} } [ {
+    { 1 -6 -13 }
+    { 0 0 0 }
+    { 1 -6 -13 }
+} >cofactors ] unit-test
diff --git a/extra/math/matrices/extras/extras.factor b/extra/math/matrices/extras/extras.factor
new file mode 100644 (file)
index 0000000..cdbef24
--- /dev/null
@@ -0,0 +1,338 @@
+! Copyright (C) 2005, 2010, 2018 Slava Pestov, Joe Groff, and Cat Stevens.
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors arrays combinators formatting fry kernel locals
+math math.bits math.functions math.matrices
+math.matrices.private math.order math.statistics math.text.english
+math.vectors random sequences sequences.deep summary ;
+IN: math.matrices.extras
+
+! this is a questionable implementation
+SINGLETONS:      +full-rank+ +half-rank+ +zero-rank+ +deficient-rank+ +uncalculated-rank+ ;
+UNION: rank-kind +full-rank+ +half-rank+ +zero-rank+ +deficient-rank+ +uncalculated-rank+ ;
+
+ERROR: negative-power-matrix
+    { m matrix } { n integer } ;
+ERROR: non-square-determinant
+    { m integer }  { n integer } ;
+ERROR: undefined-inverse
+    { m integer }  { n integer } { r rank-kind initial: +uncalculated-rank+ } ;
+
+M: negative-power-matrix summary
+    n>> dup ordinal-suffix "%s%s power of a matrix is undefined" sprintf ;
+M: non-square-determinant summary
+    [ m>> ] [ n>> ] bi "non-square %s x %s matrix has no determinant" sprintf ;
+M: undefined-inverse summary
+    [ m>> ] [ n>> ] [ r>> name>> ] tri "%s x %s matrix of rank %s has no inverse" sprintf ;
+
+<PRIVATE
+DEFER: alternating-sign
+: finish-randomizing-matrix ( matrix -- matrix' )
+    [ f alternating-sign randomize ] map randomize ; inline
+PRIVATE>
+
+: <random-integer-matrix> ( m n max -- matrix )
+    '[ _ _ 1 + random-integers ] replicate
+    finish-randomizing-matrix ; inline
+
+: <random-unit-matrix> ( m n max -- matrix )
+    '[ _ random-units [ _ * ] map ] replicate
+    finish-randomizing-matrix ; inline
+
+<PRIVATE
+: (gram-schmidt) ( v seq -- newseq )
+    [ dupd proj v- ] each ;
+PRIVATE>
+
+: gram-schmidt ( matrix -- orthogonal )
+    [ V{ } clone [ over (gram-schmidt) suffix! ] reduce ] keep like ;
+
+: gram-schmidt-normalize ( matrix -- orthonormal )
+    gram-schmidt [ normalize ] map ; inline
+
+: kronecker-product ( m1 m2 -- m )
+    '[ [ _ n*m  ] map ] map stitch stitch ;
+
+: outer-product ( u v -- matrix )
+    '[ _ n*v ] map ;
+
+! Special matrix constructors follow
+: <hankel-matrix> ( n -- matrix )
+  [ <iota> dup ] keep '[ + abs 1 + dup _ > [ drop 0 ] when ] cartesian-map ;
+
+: <hilbert-matrix> ( m n -- matrix )
+    [ <iota> ] bi@ [ + 1 + recip ] cartesian-map ;
+
+: <toeplitz-matrix> ( n -- matrix )
+    <iota> dup [ - abs 1 + ] cartesian-map ;
+
+: <box-matrix> ( r -- matrix )
+    2 * 1 + dup '[ _ 1 <array> ] replicate ;
+
+: <vandermonde-matrix> ( u n -- matrix )
+    <iota> [ v^n ] with map reverse flip ;
+
+! Transformation matrices
+:: <rotation-matrix3> ( axis theta -- matrix )
+    theta cos :> c
+    theta sin :> s
+    axis first3 :> ( x y z )
+    x sq 1.0 x sq - c * +    x y * 1.0 c - * z s * -  x z * 1.0 c - * y s * + 3array
+    x y * 1.0 c - * z s * +  y sq 1.0 y sq - c * +    y z * 1.0 c - * x s * - 3array
+    x z * 1.0 c - * y s * -  y z * 1.0 c - * x s * +  z sq 1.0 z sq - c * +   3array
+    3array ;
+
+:: <rotation-matrix4> ( axis theta -- matrix )
+    theta cos :> c
+    theta sin :> s
+    axis first3 :> ( x y z )
+    x sq 1.0 x sq - c * +    x y * 1.0 c - * z s * -  x z * 1.0 c - * y s * +  0 4array
+    x y * 1.0 c - * z s * +  y sq 1.0 y sq - c * +    y z * 1.0 c - * x s * -  0 4array
+    x z * 1.0 c - * y s * -  y z * 1.0 c - * x s * +  z sq 1.0 z sq - c * +    0 4array
+    { 0.0 0.0 0.0 1.0 } 4array ;
+
+:: <translation-matrix4> ( offset -- matrix )
+    offset first3 :> ( x y z )
+    {
+        { 1.0 0.0 0.0 x   }
+        { 0.0 1.0 0.0 y   }
+        { 0.0 0.0 1.0 z   }
+        { 0.0 0.0 0.0 1.0 }
+    } ;
+
+<PRIVATE
+GENERIC: >scale-factors ( object -- x y z )
+M: number >scale-factors
+    dup dup ;
+M: sequence >scale-factors
+    first3 ;
+PRIVATE>
+
+:: <scale-matrix3> ( factors -- matrix )
+    factors >scale-factors :> ( x y z )
+    {
+        { x   0.0 0.0 }
+        { 0.0 y   0.0 }
+        { 0.0 0.0 z   }
+    } ;
+
+:: <scale-matrix4> ( factors -- matrix )
+    factors >scale-factors :> ( x y z )
+    {
+        { x   0.0 0.0 0.0 }
+        { 0.0 y   0.0 0.0 }
+        { 0.0 0.0 z   0.0 }
+        { 0.0 0.0 0.0 1.0 }
+    } ;
+
+: <ortho-matrix4> ( factors -- matrix )
+    [ recip ] map <scale-matrix4> ;
+
+:: <frustum-matrix4> ( xy-dim near far -- matrix )
+    xy-dim first2 :> ( x y )
+    near x /f :> xf
+    near y /f :> yf
+    near far + near far - /f :> zf
+    2 near far * * near far - /f :> wf
+
+    {
+        { xf  0.0  0.0 0.0 }
+        { 0.0 yf   0.0 0.0 }
+        { 0.0 0.0  zf  wf  }
+        { 0.0 0.0 -1.0 0.0 }
+    } ;
+
+:: <skew-matrix4> ( theta -- matrix )
+    theta tan :> zf
+    {
+        { 1.0 0.0 0.0 0.0 }
+        { 0.0 1.0 0.0 0.0 }
+        { 0.0 zf  1.0 0.0 }
+        { 0.0 0.0 0.0 1.0 }
+    } ;
+
+! a simpler verison of this, like matrix-map -except, but map-index, should be possible
+: cartesian-matrix-map ( matrix quot: ( ... pair matrix -- ... matrix' ) -- matrix-seq )
+    [ [ first length <cartesian-square-indices> ] keep ] dip
+    '[ _ @ ] matrix-map ; inline
+
+: cartesian-column-map ( matrix quot: ( ... pair matrix -- ... matrix' ) -- matrix-seq )
+    [ cols first2 ] prepose cartesian-matrix-map ; inline
+
+! -------------------------------------------------
+! numerical analysis of matrices follows
+<PRIVATE
+
+: square-rank ( square-matrix -- rank ) ;
+: nonsquare-rank ( matrix -- rank ) ;
+PRIVATE>
+
+GENERIC: rank ( matrix -- rank )
+M: zero-matrix rank
+    drop +zero-rank+ ;
+
+M: square-matrix rank
+    square-rank ;
+
+M: matrix rank
+    nonsquare-rank ;
+
+GENERIC: nullity ( matrix -- nullity )
+
+
+! implementation details of determinant and inverse
+<PRIVATE
+: alternating-sign ( seq odd-elts? -- seq' )
+    '[ even? _ = [ neg ] unless ] map-index ;
+
+! the determinant of a 1x1 matrix is the value itself
+! this works for any-dimensional matrices too
+: (1determinant) ( matrix -- 1det ) flatten first ; inline
+
+! optimized to find the determinant of a 2x2 matrix
+: (2determinant) ( matrix -- 2det )
+    ! multiply the diagonals and subtract
+    [ main-diagonal ] [ anti-diagonal ] bi [ first2 * ] bi@ - ; inline
+
+! optimized for 3x3
+! https://www.mathsisfun.com/algebra/matrix-determinant.html
+:: (3determinant) ( matrix-seq -- 3det )
+    ! first 3 elements of row 1
+    matrix-seq first first3 :> ( a b c )
+    ! last 2 rows, transposed to make the next step easier
+    matrix-seq rest transpose
+    ! get the lower sub-matrices in reverse order of a b c columns
+    [ rest ] [ [ first ] [ third ] bi 2array ] [ 1 head* ] tri 3array
+    ! find determinants
+    [ (2determinant) ] map
+    ! negate odd elements of a b c and multiply by the new determinants
+    { a b c } t alternating-sign v*
+    ! sum the resulting sequence
+    sum ;
+
+DEFER: (ndeterminant)
+: make-determinants ( n matrix -- seq )
+    <repetition> [
+        cols-except [ length ] keep (ndeterminant) ! recurses here
+    ] map-index ;
+
+DEFER: (determinant)
+! generalized to 4 and higher
+: (ndeterminant) ( n matrix -- ndet )
+    ! TODO? recurse for n < 3
+    over 4 < [ (determinant) ] [
+        [ nip first t alternating-sign ] [ rest make-determinants ] 2bi
+        v* sum
+    ] if ;
+
+! switches on dimensions only
+: (determinant) ( n matrix -- determinant )
+    over {
+        { 1 [ nip (1determinant) ] }
+        { 2 [ nip (2determinant) ] }
+        { 3 [ nip (3determinant) ] }
+        [ drop (ndeterminant) ]
+    } case ;
+PRIVATE>
+
+GENERIC: determinant ( matrix -- determinant )
+M: zero-square-matrix determinant
+    drop 0 ;
+
+M: square-matrix determinant
+    [ length ] keep (determinant) ;
+
+! determinant is undefined for m =/= n, unlike inverse
+M: matrix determinant
+    dimension first2 non-square-determinant ;
+
+: 1/det ( matrix -- 1/det )
+    determinant recip ; inline
+
+! -----------------------------------------------------
+! inverse operations and implementations follow
+ALIAS: multiplicative-inverse recip
+
+! per element, find the determinant of all other elements except the element's row / col
+! https://www.mathsisfun.com/algebra/matrix-inverse-minors-cofactors-adjugate.html
+: >minors ( matrix -- matrix' )
+    matrix-except-all [ [ determinant ] map ] map ;
+
+! alternately invert values of the matrix (see alternating-sign)
+: >cofactors ( matrix -- matrix' )
+    [ even? alternating-sign ] map-index ;
+
+! multiply a matrix by the inverse of its determinant
+: m*1/det ( matrix -- matrix' )
+    [ 1/det ] keep n*m ; inline
+
+! inverse implementation
+<PRIVATE
+! https://www.mathsisfun.com/algebra/matrix-inverse-minors-cofactors-adjugate.html
+: (square-inverse) ( square-matrix -- inverted )
+    ! inverse of the determinant of the input matrix
+    [ 1/det ]
+    ! adjugate of the cofactors of the matrix of minors
+    [ >minors >cofactors transpose ]
+    ! adjugate * 1/det
+    bi n*m ;
+
+! TODO
+: (left-inverse) ( matrix -- left-invert )   ;
+: (right-inverse) ( matrix -- right-invert ) ;
+
+! TODO update this when rank works properly
+! only defined for rank(A) = rows(A) OR rank(A) = cols(M)
+! https://en.wikipedia.org/wiki/Invertible_matrix
+: (specialized-inverse) ( rect-matrix -- inverted )
+    dup [ rank ] [ dimension ] bi [ = ] with map {
+        { { t f } [ (left-inverse) ] }
+        { { f t } [ (right-inverse) ] }
+        [ no-case ]
+    } case ;
+PRIVATE>
+
+M: zero-square-matrix recip
+    ; inline
+
+M: square-matrix recip
+    (square-inverse) ; inline
+
+M: zero-matrix recip
+    transpose ; inline ! TODO: error based on rankiness
+
+M: matrix recip
+    (specialized-inverse) ; inline
+
+! TODO: use the faster algorithm: [ determinant zero? ]
+: invertible-matrix? ( matrix -- ? )
+    [ dimension first2 max <identity-matrix> ] keep
+    dup recip m. = ;
+
+: linearly-independent-matrix? ( matrix -- ? ) ;
+
+<PRIVATE
+! this is the original definition of m^n as committed in 2012; it has not been lost
+: (m^n) ( m n -- n )
+    make-bits over first length <identity-matrix>
+    [ [ dupd m. ] when [ dup m. ] dip ] reduce nip ;
+PRIVATE>
+
+! A^-1 is the inverse but other negative powers are nonsense
+: m^n ( m n -- n ) {
+        { [ dup -1 = ] [ drop recip ] }
+        { [ dup 0 >= ] [ (m^n) ] }
+        [ negative-power-matrix ]
+    } cond ;
+
+: n^m ( n m -- n ) swap m^n ; inline
+
+: covariance-matrix-ddof ( matrix ddof -- cov )
+    '[ _ cov-ddof ] cartesian-column-map ; inline
+
+: covariance-matrix ( matrix -- cov )
+    0 covariance-matrix-ddof ; inline
+
+: sample-covariance-matrix ( matrix -- cov )
+    1 covariance-matrix-ddof ; inline
+
+: population-covariance-matrix ( matrix -- cov ) 0 covariance-matrix-ddof ; inline
diff --git a/extra/math/matrices/extras/summary.txt b/extra/math/matrices/extras/summary.txt
new file mode 100644 (file)
index 0000000..87a52d7
--- /dev/null
@@ -0,0 +1 @@
+Matrix arithmetic - extra and miscellaneous words
\ No newline at end of file
index e45fbb9a7430db7030ee6455aee2a92cb97c8590..54b1456b8958b27838f8bd38153b8b40af60d524 100644 (file)
@@ -36,7 +36,7 @@ IN: rosetta-code.bitmap
 
 ! The storage functions
 : <raster-image> ( width height -- image )
-    zero-matrix [ drop { 0 0 0 } ] mmap ;
+    <zero-matrix> [ drop { 0 0 0 } ] mmap ;
 : fill-image ( {R,G,B} image -- image )
     swap '[ drop _ ] mmap! ;
 : set-pixel ( {R,G,B} {i,j} image -- ) set-Mi,j ; inline
index b44b7fdae120ff639c31a1228740d4271051909d..b84a88e2f856bdcab7405b119875057eaa19300c 100644 (file)
@@ -38,4 +38,4 @@ IN: rosetta-code.conjugate-transpose
     dup conj-t [ m. ] [ swap m. ] 2bi = ;
 
 : unitary-matrix? ( matrix -- ? )
-    [ dup conj-t m. ] [ length identity-matrix ] bi = ;
+    [ dup conj-t m. ] [ length <identity-matrix> ] bi = ;