packages feed

np-linear-0.1.1.1: src/Algebra/Linear/Integral.hs

{-# LANGUAGE NoImplicitPrelude,RebindableSyntax #-}
module Algebra.Linear.Integral
  (
    divModUpperTriangular
  , divModLowerTriangular
  , hermite
  ) where


import           Algebra.Linear
import           Auxiliary     (minimumAmong,adorn)

import qualified Algebra.Ring
import           NumericPrelude

import           Control.Arrow (first,second,(***))
import           Data.Function (on)
import           Data.List     (partition)


divModUpperTriangular :: Vector Integer -> Matrix Integer -> (Vector Integer,Vector Integer)
divModUpperTriangular x u = reverse *** reverse $ divModLowerTriangular (reverse x) (reverse . map reverse $ u)

divModLowerTriangular :: Vector Integer -> Matrix Integer -> (Vector Integer,Vector Integer)
divModLowerTriangular x l = go l x [] [] where
  go []        []        q r = (q,r)
  go (lᵢ : l') (xᵢ : x') q r = go l' x' (q ++ [qᵢ]) (r ++ [rᵢ]) where
    (lFirst,lᵢᵢ : _) = splitAt (length q) lᵢ
    y = xᵢ - innerProduct q lFirst
    (qᵢ,rᵢ)
      | lᵢᵢ == 0  = error "Algebra.Linear.divModLowerTriangular: zero on diagonal"
      | otherwise = y `divMod` lᵢᵢ

-- Compute the Hermite normal form of the matrix,
-- together with the unimodular basis transformation matrix.
hermite :: Matrix Integer -> (Matrix Integer,Matrix Integer)
hermite []          = ([],[])
hermite xs@([] : _) = (xs,identity (length xs))
hermite vs          = case minimumAmong (compare `on` (abs . head . fst)) nonZero of
  Nothing                    -> first (map (0 :)) $ hermite (map tail vs)
  Just ((v,i),[])            -> case hermite (map (tail . fst) startZero) of
    (h,u) -> ((positive v :) . map (0 :) $ h,changeSign v `matrixProduct` rowSwap n (1,i) `matrixProduct` shift u)
  Just ((v@(v₀ : _),i),rest) -> let
    (reduced,translates) = unzip . flip map rest $ \ (x@(x₀ : _),j) -> let
      (c,_) = x₀ `divMod` v₀ in ((zipWith (\ vᵢ xᵢ -> xᵢ - c * vᵢ) v x,j),(j,c))
    (h,u) = hermite $ v : map fst reduced ++ map fst startZero
      in (h,
          u
            `matrixProduct` permute (i : map snd reduced ++ map snd startZero)
            `matrixProduct` affine n i (map (second negate) translates)
          )
 where
  (startZero,nonZero) = partition ((==) 0 . head . fst) . flip zip [1 ..] $ vs
  positive v = map (* signum (head v)) v
  changeSign v = matrixFromFunction n n f where
    f i j
      | i == j && i == 1 = signum (head v)
      | i == j           = 1
      | otherwise        = 0
  shift u = (1 : replicate (n - 1) 0) : map (0 :) u
  n = length vs


-- adjoint :: Matrix Integer -> Matrix Integer
-- adjoint m = strip . fst . hermite . adorn $ m where
--   n = length m
--   strip = map (drop n)

-- Alternative implementation of Hermite normal form
{-
hermite' :: Matrix Integer -> Matrix Integer
hermite' []          = []
hermite' xs@([] : _) = xs
hermite' vs          = case minimumAmong (compare `on` (abs . head)) nonZero of
  Nothing                -> map (0 :) $ hermite' (map tail vs)
  Just (v,[])            -> (positive v :) . map (0 :) $ hermite' (map tail startZero)
  Just (v@(v₀ : _),rest) -> hermite' $ v : reduced ++ startZero where
    reduced = map (\ x@(x₀ : _) -> let (c,_) = x₀ `divMod` v₀ in zipWith (\ vᵢ xᵢ -> xᵢ - c * vᵢ) v x) rest
 where
  (startZero,nonZero) = partition ((==) 0 . head) vs
  positive v = map (* signum (head v)) v
-}

-- Examples

la :: Matrix Integer
la = 
  [
    [ 6, 2,-4,-4, 2,-2,-2]
  , [ 2, 6,-4,-4, 2,-2,-2]                                                                                                                                                                
  , [-4,-4, 8, 4,-4, 0, 4]                                                                                                                                                                 
  , [-4,-4, 4, 8,-4, 4, 0]                                                                                                                                                                 
  , [ 2, 2,-4,-4, 6,-2,-2]                                                                                                                                                                
  , [-2,-2, 0, 4,-2, 6,-2]                                                                                                                                                                
  , [-2,-2, 4, 0,-2,-2, 6]
  ]

l :: Matrix Integer
l = 
  [
    [2,1,1,1,1,1,1]                                                                                                                                                                    
  , [1,2,1,1,1,1,1]                                                                                                                                                                    
  , [1,1,2,0,1,1,0]                                                                                                                                                                    
  , [1,1,0,2,1,0,1]                                                                                                                                                                    
  , [1,1,1,1,2,1,1]                                                                                                                                                                   
  , [1,1,1,0,1,2,1]                                                                                                                                                                    
  , [1,1,0,1,1,1,2]
  ]