{-
   Haskell Dynamics Engine, Copyright (C) 2007 Ruben Henner Zilibowitz
   All rights reserved. Email: rubenz@cse.unsw.edu.au Web: www.cse.unsw.edu.au/~rubenz
   
   This is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License that is include with this
   package in the file LICENSE-GPL.TXT.
-}

{-
Code for solving the Linear Complementarity Problem.

Given a matrix A and a vector b find a vector f such that:
a = Af + b
a >= 0
f >= 0
a.f = 0

This algorithm should succeed provided A is positive definite. This may not be a necessary condition however.
The algorithm comes from the paper "Fast Contact Force Computation for Nonpenetrating Rigid Bodies" by David Baraff.

Author: Ruben Henner Zilibowitz
-}

module LCP where

import Data.Set hiding (null,elems,map)
import Gausselim
import Data.Array

{-
Given A and b find an f such that:
a = Af + b
a >= 0
f >= 0
a.f = 0
-}

{- The difference between 1 and the least value greater than 1 that is
   representable in the given floating point type, b**1-p.  -}
epsilon :: Double
epsilon = 1e-9

{- Maximum representable finite floating-point number,

	(1 - b**-p) * b**emax
-}
infinity :: Double
infinity = 1e9

-- max number of runs through while_d_drive_to_zero_d or drive_to_zero
max_depth :: Int
max_depth = 25

-- compute_forces
compute_forces :: Matrix Double -> Vector Double -> Vector Double
compute_forces matA vecb = let (f,_,_,_) = solve_lcp matA vecb in f

-- compute_forces_with_holonomic
compute_forces_with_holonomic :: Int -> Matrix Double -> Vector Double -> Vector Double
compute_forces_with_holonomic n matA vecb = let (f,_,_,_) = while_d_drive_to_zero_d max_depth (vecf ++ (replicate m 0),(replicate n 0) ++ (drop n vecb),fromList [1..n],empty) matA vecb in f
   where
      matA' = take n (map (take n) matA)
      vecb' = take n vecb
      vecf = gauss matA' vecb'
      m = (length vecb) - n

-- solve_lcp
solve_lcp :: Matrix Double -> Vector Double -> (Vector Double, Vector Double, Set Int, Set Int)
solve_lcp matA vecb = while_d_drive_to_zero_d max_depth (replicate n 0,vecb,empty,empty) matA vecb
   where n = length vecb

while_d_drive_to_zero_d :: Int -> (Vector Double,Vector Double,Set Int,Set Int) -> Matrix Double -> Vector Double -> (Vector Double,Vector Double,Set Int,Set Int)
while_d_drive_to_zero_d 0 _ matA vecb = error ("LCP solver: while_d_drive_to_zero_d max depth exceeded with: A=" ++ (show matA) ++ " b=" ++ (show vecb))
while_d_drive_to_zero_d n dat@(f,a,c,nc) matA vecb
   | (null ds) = dat
   | otherwise = while_d_drive_to_zero_d (n-1) (drive_to_zero max_depth (head ds) dat matA vecb) matA vecb
   where ds = [d | (d,x) <- zip [0..] a, -x > epsilon]

-- drive_to_zero
drive_to_zero :: Int -> Int -> (Vector Double,Vector Double,Set Int,Set Int) -> Matrix Double -> Vector Double -> (Vector Double,Vector Double,Set Int,Set Int)
drive_to_zero 0 _ _ matA vecb = error ("LCP solver: drive_to_zero max depth exceeded with: A=" ++ (show matA) ++ " b=" ++ (show vecb))
drive_to_zero n d (f,a,c,nc) matA vecb
   | (j == (-1)) = error ("LCP solver: drive_to_zero infinite force with: A=" ++ (show matA) ++ " b=" ++ (show vecb) ++ " d=" ++ (show d))
   | (j `member` c) = drive_to_zero (n-1) d (f',a',delete j c,insert j nc) matA vecb
   | (j `member` nc) = drive_to_zero (n-1) d (f',a',insert j c,delete j nc) matA vecb
   | otherwise = (f',a',insert j c,nc)
   where
      df = fdirection d c nc matA vecb
      da = matA `matXvec` df
      (s,j) = maxstep (f,a,c,nc) df da d
      f' = f `vecsum` (s `scalarXvec` df)
      a' = a `vecsum` (s `scalarXvec` da)

-- fdirection
fdirection :: Int -> Set Int -> Set Int -> Matrix Double -> Vector Double -> Vector Double
fdirection d c nc matA vecb = df
   where
      cList = toList c
      matA11 = [[(matA!!j)!!k | k <- cList] | j <- cList]
      vecv1 = [-((matA!!j)!!d) | j <- cList]
      x = gauss matA11 vecv1
      dfArr = accumArray (flip const) 0 (0,n-1) ((d,1):(zip cList x))
      n = length vecb
      df = elems dfArr

-- maxstep
maxstep :: (Vector Double,Vector Double,Set Int,Set Int) -> Vector Double -> Vector Double -> Int -> (Double,Int)
maxstep (f,a,c,nc) df da d = result
   where
      (s0,j0) = if (da!!d > 0) then (-(a!!d)/(da!!d),d) else (infinity,-1)
      cList = toList c
      ncList = toList nc
      tests = (s0,j0) : ([(-(f!!i)/(df!!i),i) | i <- cList, df!!i < 0] ++
                         [(-(a!!i)/(da!!i),i) | i <- ncList, df!!i < 0])
      result = minimum tests

-- matrix and vector functions
matXvec :: Matrix Double -> Vector Double -> Vector Double
matXvec mat vec = map (sum.(zipWith (*) vec)) mat

scalarXvec :: Double -> Vector Double -> Vector Double
scalarXvec x = map (x*)

vecsum :: Vector Double -> Vector Double -> Vector Double
vecsum = zipWith (+)

{-
--- tests
matM0 = [[2,1],[1,2 :: Double]]
q0 = [-5,-6 :: Double]

matM1 = [[1,0,0],[2,1,0],[2,2,1]] :: [[Double]]
q1 = [-8,-12,-14] :: [Double]

-- cycling occurs on this example
matM2 = [[1,2,0],[0,1,2],[2,0,1]] :: [[Double]]
q2 = [-1,-1,-1] :: [Double]

matM3 = [[2,1,1],[1,2,1],[1,1,2]] :: [[Double]]
q3 = [-4,-5,-1] :: [Double]

matM4 = [[1,-1,0,1],[1,0,0,1],[0,0,1,-3],[1,0,1,1]] :: [[Double]]
q4 = [-1,-2,-3,-4] :: [Double]

matM5 = [[10,0,-2],[2,0.1,-0.4],[0,0.2,0.1]] :: [[Double]]
q5 = [10,1,-1] :: [Double]

matM6 = [[1,0,2],[-2,1,4],[-4,2,9]] :: [[Double]]
q6 = [1,-1,3] :: [Double]

matM7 = [[1,2,-1,2],[-1,1,2,-1],[2,-1,1,2],[-1,2,-1,1]] :: [[Double]]
q7 = [-2,1,-1,2] :: [Double]
-}
{-
matM1 = [[34,16,4,1],
         [16,34,16,1],
         [4,16,8,1],
         [-1,-1,-1,0]] :: [[Double]]
q1 = [-66,-54,-20,1] :: [Double]

matM2 = [[0,0,2,2,1],
         [0,0,1,2,2],
         [1,2,0,0,0],
         [3,1,0,0,0],
         [2,3,0,0,0]] :: [[Double]]
q2 = [-1,-1,-1,-1,-1] :: [Double]

matM3 = [[-2,1],[1,-2]] :: [[Double]]
q3 = [4,-1] :: [Double]
-}
