[comp.lang.functional] Matrix manipulation programs

sph@doc.ic.ac.uk (Simon Hughes) (12/04/90)

   Can anybody email me (a reference for) functional programs for
matrix manipulation?  E.g. transposition, multiplication etc.
Preferably in a form in which the matrices are represented by lists of
lists.
   My research involves static analysis of functional programs to
improve their store use efficiency.  I suspect a matrix manipulation
package would be a good application of the analyses.  Thanks.
--
------------------------------------------------------------------------------
 Simon Hughes, Dept. of Computing, Imperial College.  Email: sph@doc.ic.ac.uk

sph@iti.org (Simon Hughes) (01/11/91)

   Some time ago I posted to this newsgroup, asking people to
email me matrix manipulation programs.  Here's what I received.
Thanks to everyone who responded (including those not detailed
below).
					  Simon

P.S.  Larry's programs will appear in the following book.
Lawrence C Paulson.
ML for the Working Programmer.
(Cambridge University Press, 1991, in press).
------------------------------------------------------------------






From jeroen%ruuinf.cs.ruu.nl@nsf.ac.uk Wed Dec  5 09:19:17 1990

This is my version, written in Miranda.
It stores matrices as lists of lists.
It avoids the list-indexing operator (!); instead
it uses functions like map, inits and tails as much as possible.

This satisfies my sense of "functional beauty", although
I must admit that functions like  inv  may be difficult to
read because of it.

--
Jeroen Fokker                                 |  jeroen@cs.ruu.nl
dept.of Computer Science, Utrecht University  |  tel.+31-30-534129
PObox 80089, 3508TB Utrecht, the Netherlands  |  fax.+31-30-513791
--



scalar == num
vector == [scalar]
matrix == [vector]

inprod :: vector -> vector -> scalar 
apply  :: matrix -> vector -> vector
mul    :: matrix -> matrix -> matrix
diag   :: matrix -> vector
transp :: matrix -> matrix
det    :: matrix -> scalar
inv    :: matrix -> matrix
solve  :: matrix -> vector -> vector

inprod a b = sum (map2 (*) a b)
apply  m v = map (inprod v) m

mul as bs = map f as
            where f a = map (inprod a)(transp bs)

diag []       =  []
diag (l:ls)   =  hd l :  diag (map tl ls)

antidiag = diag . reverse

transp [xs]     = map single xs
transp (xs:xss) = map2 (:) xs (transp xss)

gaps xs = map2 (++) (init(inits xs)) (tl(tails xs))
altern  = inprod plusmin

det [[x]]  =  x
det (v:m)  =  altern (map2 (*) v (map det (gaps (transp m))))

inv [[x]] = [[1/x]]
inv m = transp (zipp (*) c (mapp det (map gaps (map transp (gaps m)))))
        where c = mapp (/(det m)) (iterate tl plusmin)

solve m v = map ((/(det m)).det) (repls v (transp m))
            where repls v m = map2 (insert v) (init(inits m)) (tl(tails m))
                  insert v a b = a++ [v] ++b

m = [[1,3,1,1],[2,1,5,2],[1,-1,2,3],[4,1,-3,7]]
n=  [[2,1],[4,3]]

mapp f = map (map f)
zipp f = map2 (map2 f)

inits []     =  [[]]
inits (x:xs) =  [] : map (x:) (inits xs)

tails []     =  [[]]
tails (x:xs) =  (x:xs) : (tails xs)

plusmin = 1 : -1 : plusmin
single x = [x]

negvec = map neg
a $vecminus b = map2 (-) a b





From Larry.Paulson%uk.ac.cam.cl@cl.cam.ac.uk Wed Dec  5 10:37:03 1990

I enclose ML code for matrix transpose, multiplication, and Gaussian
Elimination.  It comes from my forthcoming book on Standard ML.  (The code is
entirely first-order because higher-order functions come later in the book!) 
Please report any errors you find; other comments are also welcome.  Hope you
find the code useful!
								Larry Paulson
------
(** Matrix transpose **)

fun headcol []    = []
  | headcol ((x::_) :: rows) = x :: headcol rows;

fun tailcols []    = []
  | tailcols ((_::xs) :: rows) = xs :: tailcols rows;

val matrix =
    [ ["a","b","c"],
      ["d","e","f"] ];

headcol matrix;
tailcols matrix;

fun transp ([]::rows) = []
  | transp rows = headcol rows :: transp (tailcols rows);

transp matrix;

(** Matrix product **)

fun dotprod([], []) = 0.0
  | dotprod(x::xs,y::ys) = x*y + dotprod(xs,ys);

fun rowprod(row, []) = []
  | rowprod(row, col::cols) =
        dotprod(row,col) :: rowprod(row,cols);

fun rowlistprod([], cols) = []
  | rowlistprod(row::rows, cols) =
        rowprod(row,cols) :: rowlistprod(rows,cols);

fun matprod(Arows,Brows) = rowlistprod(Arows, transp Brows);

val Arows = [ [2.0, 0.0], 
              [3.0, ~1.0],
              [0.0, 1.0],
              [1.0, 1.0] ];

val Brows = [ [1.0,  0.0, 2.0],
              [4.0, ~1.0, 0.0] ];

matprod(Arows,Brows);


(** Gaussian Elimination (Sedgewick Chapter 5) **)

(*the first row with absolutely greatest head*)
fun pivotrow [row] = row : real list
  | pivotrow (row1::row2::rows) =
      if abs(hd row1) >= abs(hd row2)
      then pivotrow(row1::rows)
      else pivotrow(row2::rows);

(*the matrix excluding the first row with head p*)
fun delrow (p, []) = []
  | delrow (p, row::rows) =
      if p = hd row then rows
      else row :: delrow(p, rows);

fun scalarprod(k, []) = [] : real list
  | scalarprod(k, x::xs) = k*x :: scalarprod(k,xs);

fun vectorsum ([], []) = [] : real list
  | vectorsum (x::xs,y::ys) = x+y :: vectorsum(xs,ys);

fun gausselim [row] = [row]
  | gausselim rows =
      let val p::prow = pivotrow rows
          fun elimcol [] = []
            | elimcol ((x::xs)::rows) =
                  vectorsum(xs, scalarprod(~x/p, prow))
                  :: elimcol rows
      in  (p::prow) :: gausselim(elimcol(delrow(p,rows)))
      end;

fun solutions [] = [~1.0]
  | solutions((x::xs)::rows) =
      let val solns = solutions rows
      in ~(dotprod(solns,xs)/x) :: solns  end;

(*Sedgewick's example*)
gausselim [[ 1.0,  3.0, ~4.0,  8.0], 
           [ 0.0,  2.0, ~2.0,  6.0], 
           [~1.0, ~2.0,  5.0, ~1.0]];
solutions it;
(*should be [1,5,2] with ~1 at end*)

gausselim [[ 0.0,  1.0,  2.0,  7.0,  7.0],
           [~4.0,  0.0,  0.0, ~5.0, ~17.0],
           [ 6.0, ~1.0,  0.0,  0.0,  28.0],
           [~2.0,  0.0,  2.0,  8.0,  12.0]];
solutions it;
(*should be [3,~10,5,1] with ~1 at end*)

------





From ahsnippe%praxis.cs.ruu.nl@nsf.ac.uk Fri Dec  7 08:20:10 1990

I've done some programming in Miranda which resulted in a library
for wire-frame-graphics. Part of it is a matrix-manipulation program.
A better version is owned by jeroen@cs.ruu.nl (Jeroen Fokker).


:
vector == [num]
matrix == [vector]



 dotprodukt [] [] = 0
 dotprodukt (a:l) (b:m) = a*b + dotprodukt l m

 apply xs y = map (dotprodukt y) xs

 ident n = map (line n) [0..(n-1)]   
            where line n i = (rep i 0)++[1]++(rep (n-i-1) 0)

 scalevec n x = map (*n) x

 scalemat n = map (scalevec n)

 mul x y = transpose (map (apply x) (transpose y))

 negvec = scalevec (-1)

 vecminus x y = map2 (-) x y

++++++++++++++++++++++++++

This is the one used in the graphics package

Next has some additonal functions :

++++++++++++++++++++++++++++++++

vector == [num]
matrix == [vector]

inprodukt :: vector -> vector -> num

 inprodukt [] [] = 0
 inprodukt (a:l) (b:m) = a*b + inprodukt l m


matapply :: matrix -> vector -> vector

 matapply xs y = map (inprodukt y) xs

transp :: matrix -> matrix

 transp m = (kop m) : (transp (rest m)) , if (vol m)
	 = []                          , otherwise

     vol []   = False
     vol (x:xs) = (x~=[])  
     
     kop  = map (hd) 
     
     rest = map (tl) 

ident :: num -> matrix

 ident n = map (line n) [0..(n-1)]   

 line n i = (rep i 0)++[1]++(rep (n-i-1) 0)


scalvec :: num -> vector -> vector

 scalvec n x = map (*n) x

scalmat :: num -> matrix -> matrix

 scalmat n = map (scalvec n)

matmul  :: matrix -> matrix -> matrix

 matmul x = map (matapply x)
det :: matrix -> num

 det  m = 0                      , if  ~(vol m) 
        = hd (hd m)              , if #m=1 
        = som (#m) m               , otherwise 

     som i m = (-1)^i * (((m)!0)!(i-1)) * (det (slicedmat i m)) + (som (i-1) m)  , if i >0
             = 0                                                             , otherwise

         slicedmat n []     = []
         slicedmat n (m:ms) = plak (stuk n ms) (rest2 n ms)
    
             stuk n []     = []
             stuk n (m:ms) = (take (n-1) m ) : stuk (n-1) ms 
            
             rest2 n []     = []
             rest2 n (m:ms) = (drop n m)  : rest2 n ms 
            
             plak [] [] = []
             plak (m:ms) (n:ns) = (m ++ n) : plak ms ns 

I hope the program is bug-free since I haven't used it for a long time.
If you have any questions, feel free to ask.


			  --[Arnold]--






From S.Clayman@cs.ucl.ac.uk Fri Dec  7 18:57:24 1990

I suspect you've had a bundle of replies. Anyway here's
a bit of Miranda I wrote ages ago
______________________________________________________________
|| some matrix things

%export scalar_mult_vector scalar_mult_matrix multmatrix

scalar == num
vector == [num]
matrix == [[num]]

|| this does s.v => [s.v1,s.v2 ...]
scalar_mult_vector::scalar->vector->vector
scalar_mult_vector s v = map ((*)s) v

|| this does s.M => [[s.m11,s.m12...],[s.m21,s.m22...]...]
scalar_mult_matrix::scalar->matrix->matrix
scalar_mult_matrix s m = map (scalar_mult_vector s) m

|| this is used in multmatrix to multiply
|| each row of matrix1 by matrix2
vector_mult_matrix::vector->matrix->vector
vector_mult_matrix v m
	= domult v m, #v=#m
	= error "vector_mult_matrix: vector and matrix have different lengths", otherwise
	  where
	  domult v ([]:any) = []
	  domult v m = sum (multi (map (*) v) (map hd m)) : domult v (map tl m)

multmatrix::matrix->matrix->matrix
multmatrix m1 m2 = multi (map vector_mult_matrix m1) (repeat m2)

|| multi - applies a list of functions to
|| a list of args 
|| multi [(*)1.5,(*)2,(+)3] [2,4,6]
|| should return [3,8,9]

multi::[*->**]->[*]->[**]
multi = map2 apply
	where
	apply f x = f x

______________________________________________________________

I hope it's not too obscure

Stuart






From pierre%ra.mcrcim.mcgill.edu@nsf.ac.uk Sat Dec  8 00:48:38 1990

I have done some work using MIT's eager functional language Id, to
perform LU decomposition.  My code is pretty simple-minded, and thus
inefficient, but code given by Arvind and Nikhil in a draft of a
forthcoming book on Id is far better written.  Perhaps they could be
convinced to mail you a copy.  One of their e-mail address is:

bug-id-world@mc.mit.edu

You can also look in J. Parallel and Distrib. Computing, vol 5 (no 5),
p. 460--493, for some of their earlier work.

		Pierre
--
------------------------------------------------------------------------------
 Simon Hughes, Dept. of Computing, Imperial College.  Email: sph@doc.ic.ac.uk