{-
New implementation of minimum degree ordering (more
efficient).
Algorithm from Duff86.
XZ, 19/2/92
-}
module Min_degree (min_degree) where
import Defs
import S_Array -- not needed w/ proper module handling
import Norm -- ditto
import List(nub,partition)--1.3
-- minimum degree ordering
-- the entry lists in old_rows must be in assending order
min_degree :: (My_Array Int [Int]) -> [Int]
min_degree old_rows = find_min init_counts [] [] []
where
-- initial row degree counts
init_counts =
s_accumArray (++) ([]::[Int]) (s_bounds old_rows)
(map (\(x,y)->(length y,[x])) (s_assocs old_rows))
-- find rows with minimum degrees (recursive)
find_min counts cliques pro res =
if remaining == []
then res
else find_min new_counts new_cliques processed new_pivots
where
-- updated result
new_pivots = res ++ [pivot_i]
-- processed rows
processed = mg_line pro [pivot_i]
-- updated row counts
new_counts =
s_accumArray mg_line ([]::[Int]) (s_bounds counts)
((map (\(i,js)->(i,rm_list chgd js)) (sparse_assocs counts)) ++ updt)
where
chgd = mg_lines ([pivot_i]:[ js | (_, js) <- updt ])
updt = count_update new_cols []
-- counts of remaining rows
remaining = sparse_assocs counts
(_, (pivot_i:_)) = head remaining
-- (List of) cliques with the processed column removed.
-- Also, whole clique is removed if there is less
-- 2 entries in it.
rmed = do_rm cliques []
-- the function does the removal
do_rm (cli:clis) rmd =
do_rm clis
(
if (l2 == []) || (head l2) /= pivot_i
then cli:rmd
else
case r of
(r1:r2:_) -> r:rmd
_ -> rmd
)
where
r = l1 ++ (tail l2)
(l1,l2) = partition ((<) pivot_i) cli
do_rm _ res = res
-- new cliques
new_cliques = nub (new_cols:rmed)
-- new clique
new_cols = remove pivot_i (get_cols pivot_i cliques)
where
remove x = filter ((/=) x) -- old haskell 1.0 function
-- the function which updates the row counts
count_update (r:rs) res =
count_update rs
(((length (get_cols r (new_cols:cliques)))-1,[r]):res)
count_update _ res = res
-- find nonzero entries
get_cols = \i cli ->
rm_list pro (mg_lines ((old_rows!^i):(filter (elem i) cli)))
-- the following functions assum lists are in assending order
-- check if two lists have something in common
inter_sec x@(x1:xs) y@(y1:ys)
| x1 == y1 = True
| x1 < y1 = inter_sec xs y
| otherwise = inter_sec x ys
inter_sec _ _ = False
-- remove entries in the 1st list from the 2nd list
rm_list x@(x1:xs) y@(y1:ys)
| x1 == y1 = rm_list xs ys
| x1 < y1 = rm_list xs y
| otherwise = y1:rm_list x ys
rm_list _ y = y
-- morge two lists
mg_line x@(x1:xs) y@(y1:ys)
| x1 == y1 = x1:mg_line xs ys
| x1 < y1 = x1:mg_line xs y
| otherwise = y1:mg_line x ys
mg_line x y = x ++ y
-- merge many lists
mg_lines :: Ord a => [[a]] -> [a]
mg_lines = foldl1 mg_line