I would like to put two rather prosaic things into Haskell 1.3.  They almost
fall into the "syntactic sugar" class, but they would make my life easier.

The first is that I would like to see arrays be a class instead of whatever
they are.  I wanted to construct a subclass of arrays that were constrained
to have lower bounds equal to one, but after fooling around for some time I
just gave up.  Maybe it's easy, and I just don't know the right way to hold
my mouth.  I would also like to be able to construct a sub-class of one-
dimensional array that is a vector, and a sub-class of two-dimensional
array that is a matrix, and overload "*" to mean "inner product".

The second thing I would like is an array section notation.  In many operations
of linear algebra, one needs to view a matrix sometimes as an array of row
vectors, and sometimes as an array of column vectors.  This arose in development
of a function that implements Crout's method to factor a matrix.  (Crout's
method is especially attractive for functional languages because each element
of the factor is written exactly once.  That is not the case with Gauss-like
methods.)  I ended up writing three functions, one that computes the inner
product of two vectors, another that computes the inner product of a row and
column of a single matrix, and another that computes the inner product of a
row of one matrix with a column of another.  Others would need functions that
compute the inner product of a vector with a row or column of a matrix.  It
would be easier to write one function that computes the inner product of two
vectors, and create vectors out of pieces of a matrix by using a section
notation.  For example, I might write a Crout reduction with no pivoting as:

lu = array b
    ([(i,1) := a!(i,1) | i <- [1..m]] ++
     [(1,j) := a!(1,j)/a!(1,1) | j <- [2..n]] ++
     [(i,j) := (a!(i,j) - dot lu!(i,1..j-1) lu!(1..j-1,j))
               | i <- [2..m], j <- [2..i]] ++
     [(i,j) := (a!(i,j) - dot lu!(i,1..i-1) lu!(1..i-1,j)) / lu!(i,i)
               | i <- [2..m], j <- [i+1,m]])

where ((_,_),(m,n)) = b = bounds a.

BTW, I have developed a Crout reduction that uses pivoting, but I _think_
it's hitting something that's a little too strict -- the run-time system
insists there's a black hole, but if I run the code "by hand" I'm always able
to find an order such that data are available -- there aren't any circular
dependencies on un-computed data.  Maybe somebody can tell me where I've
gone wrong, or recommend a change in Haskell 1.3 to cope with the problem if
it's real.  The Crout reduction with pivoting follows.  If anybody wants to try
it through the compiler. you'll need a test harness, which I'll be happy to
send, but I don't think I ought to waste net bandwidth to post it.

It's also unfortunate that array bounds were defined to be ((array of low
bounds),(array of high bounds)) [I know the arrays are really tuples] instead
of array of tuples (low,high).  The latter could be used with the inRange
function from the Prelude, while the former cannot.  But it'd probably be
_really_ hard on a lot of people to change this now.

Best regards,
Van Snyder
------------------------ Crout reduction with pivoting ----------------------

module Croutp (croutp) where
-- Crout method for LU factorization

import Linalg (ipamaxc,rcdot2)
-- ipamaxc a j p m n returns the index x of the element of p(m..n) such that
--            a!(p!(x),j) is the element of column j of a having the largest
--            absolute value.
-- rcdot2 a b i j m n computes the inner product of a(i,m..n) and b(m..n,j)

-- croutp takes matrix a and returns l and u factors in one matrix lu.
-- performs pivoting.
-- calculates values of lu from values of a and lu.

croutp :: (RealFrac v) => Array (Int,Int) v -> (Array (Int,Int) v,
           Array Int (Array Int Int), Array Int Int, Array Int Int)
croutp a = if k==1 && l==1 && m<=n then (lu,p,mx,mk)
          else error "crout: lower bounds not 1 or #rows > #columns"
  where
  b = bounds a
  ((k,l),(m,n)) = b

--t :: (RealFrac v) => Array (Int,Int) v
  t = array ((1,1),(m,m))
   ([let k = p!1!i in (k,1) := a!(k,1) | i <- [1..m]] ++
    [let k = p!s!i
     in (k,s) := a!(k,s) - rcdot2 t lu k s 1 (s-1)
    | s <- [2..m], i <- [s..m]])

--p :: Array Int (Array Int Int)
  p = array (1,m)
   ([1 := array (1,m) [i := i | i <- [1..m]]]++
    [s := let u = s-1
              k = mk!u in
          if u == k then p!u
          else p!u // [u := mx!u, k := (p!u)!u] | s <- [2..m]])

--mk :: Array Int Int
--With the first definition of mk active, run-time insists there's a black hole.
--With the second, things work, but the function does no pivoting.
  mk = array (1,m) [s := ipamaxc t s (p!s) s m | s <- [1..m]]
--mk = array (1,m) [s := s | s <- [1..m]]

  mx :: Array Int Int
  mx = array (1,m) [s := (p!s)!(mk!s) | s <- [1..m]]

--lu :: (RealFrac v) => Array (Int,Int) v
  lu = array b
   ([(s,j) := t!(mx!s,j) | s <- [1..m], j <- [1..s]]++
    [(s,j) := let k = mx!s in 
               (a!(k,j) - rcdot2 t lu k j 1 (s-1)) / lu!(s,s)
     | s <- [1..m], j <- [s+1..n]])

module Linalg (iamax,iamaxc,iamaxr,ipamax,ipamaxc,ipamaxr,rcdot,rcdot2) where

-- iamax a returns the index of the element of a of maximum absolute value.
iamax :: (Real v) => Array Int v -> Int
iamax a = kamax l
 where (l,h) = bounds a
       kamax k = if k >= h then h else
                  let u = kamax (k+1) in if abs(a!u)>abs(a!k) then u else k

-- ipamax a p m n returns the index x of the element of p(m..n) such that
--  a!(p!(x)) is the element of a having largest absolute value.
ipamax :: (Real v) => Array Int v -> Array Int Int -> Int -> Int -> Int
ipamax a p m n = kamax m
 where kamax k = if k >= n then n else
                  let u = kamax (k+1)
                   in if abs(a!(p!u)) > abs(a!(p!k)) then u else k

-- iamaxc a j returns the index of the element of
-- column j of a of maximum absolute value.
iamaxc :: (Real v) => Array (Int,Int) v -> Int -> Int
iamaxc a j = if inRange (l2,h2) j then kamax l1
             else error "iamaxc: column number out of range"
 where ((l1,l2),(h1,h2)) = bounds a
       kamax k = if k >= h1 then h1 else
                  let u = kamax (k+1)
                   in if abs(a!(u,j)) > abs(a!(k,j)) then u else k

-- ipamaxc a j p m n returns the index x of the element of p(m..n) such that
--            a!(p!(x),j) is the element of column j of a having the largest
--            absolute value.
ipamaxc :: (Real v) =>
           Array (Int,Int) v -> Int -> Array Int Int -> Int -> Int -> Int
ipamaxc a j p m n = kamax m
 where kamax k = if k >= n then n else
                  let u = kamax (k+1)
                   in if abs(a!(p!(u),j)) > abs(a!(p!(k),j)) then u else k

-- iamaxr a i returns the index of the element of
-- row i of a of maximum absolute value.
iamaxr :: (Real v) => Array (Int,Int) v -> Int -> Int
iamaxr a i = if inRange (l1,h1) i then kamax l2
             else error "iamaxr: row number out of range"
 where ((l1,l2),(h1,h2)) = bounds a
       kamax k = if k >= h2 then h2 else
                  let u = kamax (k+1)
                   in if abs(a!(i,u)) > abs(a!(i,k)) then u else k

-- ipamaxr a j p m n returns the index x of the element of p(m..n) such that
--            a!(j,p!(x)) is the element of row j of a having the largest
--            absolute value.
ipamaxr :: (Real v) => 
           Array (Int,Int) v -> Int -> Array Int Int -> Int -> Int -> Int
ipamaxr a j p m n = kamax m
 where kamax k = if k >= n then n else
                  let u = kamax (k+1)
                   in if abs(a!(j,p!(u))) > abs(a!(j,p!(k))) then u else k

-- rcdot a i j m n computes the inner product of a(i,m..n) and a(m..n,j)
rcdot :: (RealFrac v) => Array (Int,Int) v -> Int -> Int -> Int -> Int -> v
rcdot a i j m n = let ((l1,l2),(h1,h2)) = bounds a
                      d1 = (l1,h1); d2 = (l2,h2) in
 if m > n then fromInteger 0 else
 if not (inRange d1 i && inRange d1 m && inRange d1 n
      && inRange d2 j && inRange d2 m && inRange d2 n)
 then error ("rcdot: index out of range\n"++
       "d1 d2 i j m n = "++(show d1)++" "++(show d2)++" "++
        (show i)++" "++(show j)++" "++(show m)++" "++(show n)++"\n")
 else
 if m == n then a!(i,m) * a!(m,j)
 else let k = (m+n) `div` 2 in
      (rcdot a i j m k) + (rcdot a i j (k+1) n)

-- rcdot2 a b i j m n computes the inner product of a(i,m..n) and b(m..n,j)
rcdot2 :: (RealFrac v) =>
          Array (Int,Int) v -> Array (Int,Int) v -> Int -> Int -> Int -> Int -> v
rcdot2 a b i j m n = let ((la1,la2),(ha1,ha2)) = bounds a
                         ((lb1,lb2),(hb1,hb2)) = bounds b
                         da1 = (la1,ha1); da2 = (la2,ha2)
                         db1 = (lb1,hb1); db2 = (lb2,hb2)
                     in
 if m > n then fromInteger 0 else
 if not (inRange da1 i && inRange db1 m && inRange db1 n
      && inRange db2 j && inRange da2 m && inRange da2 n)
 then error "rcdot2: index out of range" else
 if m == n then a!(i,m) * b!(m,j)
 else let k = (m+n) `div` 2 in
      (rcdot2 a b i j m k) + (rcdot2 a b i j (k+1) n)

Reply via email to