#lang scribble/manual
@(require (for-label racket "beta.rkt" "yaml.rkt"))
@title{SlowThought's Math Library}
This library contains functions related to various mathematical topics (currently probability and matrices),
which might be of general interest.
@section{Beta distributions}
@defmodule[beta]{
Beta functions are sort of binomial distributions in reverse - given a set of data, how likely
is it that a given binomial distribution produced it? Questions like this are pondered in
Bayesian statistics and its many applications. They are handy in many areas where a family of
functions with a domain [0,1] is needed.
Note that @racket[(make-beta 0 0)] is equivalent in many implementations to @racket[beta[1,1]].
This is because many implementations build the beta function from the gamma function, and
then in a typical application have expressions like @racket[beta[x-1,y-1]]. This implementation
was built around the Chebyshev integral, and so starting at zero came naturally.
Also note that, other than @racket[make-binomial], these functions' arguments are limited to
non-negative integers. This allows @racket[chebyshev-integral] to return an analytic solution,
as opposed to the typical numerical one.}
@defproc[(make-binomial [x real?]) procedure?]{
returns a thunk that returns either #t or #f, with x providing the expectation of #t. It is
provided to generate data sets for testing the other functions described in this section.}
@defproc[(make-beta [t natural-number/c]
[f natural-number/c])
procedure?]{
returns a probability density function of the form @racket[(f x)], which describes the likelihood
that a function like those generated by @racket[make-binomial] produced so many #t and #f.}
@defproc[(make-cumulative-beta [t natural-number/c]
[f natural-number/c])
procedure?]{
returns a function that calculates the integral of @racket[(make-beta t f)].}
@defproc[(chebyshev-integral [p natural-number/c]
[q natural-number/c])
procedure?]{
returns a function which evaluates the expression
@(centered (image/plain "chebyshev.png"))
from 0 to x. This is the horsepower behind the other functions. It is provided for those who need to
"roll their own" solutions for performance reasons.}
@section{YAML -- Yet Another Matrix Library}
@defmodule[yaml]{
Two dimensional matrices are implemented as vectors of vectors, and attempt to emulate vectors' syntax and behaviour.
The unique part of this implementation is the ability to easily substitute different arithmetic operators in some
functions so that you can do operations on, for example, matrices of quaternions or polynomials (coming soon to a planet
near you).
Also coming soon, a more complete implementation, including, for instance, determinants that can
handle quaternions or polynomials. For now, there are Racket-like constructors,
accessors, and operators, and some arithmetic operators.}
@subsection{Constructors}
@defproc*[([(make-matrix [nrows (and/c positive? integer?)]
[ncolumns (and/c positive? integer?)])
matrix?]
[(make-matrix [nrows (and/c positive? integer?)]
[ncolumns (and/c positive? integer?)]
[initialize any])
matrix?])]{
returns a matrix. Each value in the matrix is set to @racket[0], or to @racket[initialize], if provided.}
@defproc[(build-matrix [nrows (and/c positive? integer?)]
[ncolumns (and/c positive? integer?)]
[initialize procedure?])
matrix?]{
@scheme[initialize] is a function of the form @scheme[(f irow icolumn)] which is used to fill the returned matrix.}
Immutable matrices are just as you'd expect:
@schemeblock[(define my-immutable-matrix #(#(1 2 3)
#(4 5 6)))]
As are mutable matrices:
@schemeblock[(define my-mutable-matrix (vector (vector 6 5 4)
(vector 3 2 1)))]
Some sugar is provided in the name of implementation hiding and readability. The following is equivalent to
the last example:
@schemeblock[(define my-pretty-matrix (matrix (row 6 5 4)
(row 3 2 1)))]
@subsection{Accessors}
YAML follows Scheme/Racket conventions for vectors.
@defproc[(matrix? [m any/c]) boolean?]{
... is not very robust. A well constructed matrix is a vector of vectors of identical size. If
the library's constructors are used, this is garunteed, but @scheme[matrix?] is not yet suspicious
of the creative or the evil.}
@defproc[(matrix-size [m matrix?])(values exact-positive-integer? exact-positive-integer?)]{
returns the numbers of rows and columns of the matrix @scheme[m] via @scheme[values].}
@defproc[(matrix-ref [m matrix?]
[row natural-number/c]
[column natural-number/c])
any]{
returns the contents of @scheme[m].}
@defproc[(matrix-set! [m matrix?]
[row natural-number/c]
[column natural-number/c]
[value any])
any]{
changes the contents of @scheme[m].}
@subsection{Operators}
Again, YAML tries to follow Scheme/Racket convention.
@defproc[(matrix-map [proc procedure?]
[m matrix?]
... )
matrix?]{
works in the same fashion as @scheme[vector-map]. Because it is so straight-forward, no
seperate implementation is offered for addition, subtraction, or scalar multiplication.}
Things start to get complicated when you introduce multiplication. Multipliers have
optional arguments for @racket[*] and @racket[+] for the user to provide appropriate
operators for his/her special type.
@defproc*[([(matrix*vector [m matrix?]
[v vector?])
vector?]
[(matrix*vector [m matrix?]
[v vector?]
[multiply *]
[add +])
vector?])]{
multiplies a matrix by (the transpose of) a vector to produce another vector.}
@defproc*[([(matrix*matrix (m1 matrix?)
(m2 matrix?))
matrix?]
[(matrix*matrix [m1 matrix?]
[m2 matrix?]
[multiply *]
[add +])
matrix?])]{
multiplies two matrices to produce another one.}
@defproc[(transpose (m matrix?)) matrix?]{
returns the transpose of @scheme[m].}