packages feed

ideas-0.5.8: src/Domain/LinearAlgebra/EquationsRules.hs

-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Domain.LinearAlgebra.EquationsRules where

import Prelude hiding (repeat)
import Common.Context
import Common.Transformation
import Common.Utils
import Common.View hiding (simplify)
import Control.Monad
import Data.List hiding (repeat)
import Data.Maybe
import Domain.Math.Expr
import Domain.Math.Data.Equation
import Domain.Math.Simplification (simplify)
import Domain.LinearAlgebra.LinearView
import Domain.LinearAlgebra.LinearSystem
import Domain.LinearAlgebra.MatrixRules (covered) -- for context
import Test.QuickCheck

equationsRules :: [Rule (Context (LinearSystem Expr))]
equationsRules = 
   [ ruleExchangeEquations, ruleEliminateVar, ruleDropEquation
   , ruleInconsistentSystem
   , ruleScaleEquation, ruleBackSubstitution, ruleIdentifyFreeVariables
   , ruleCoverUpEquation, ruleUncoverEquation, ruleCoverAllEquations 
   ]

ruleExchangeEquations :: Rule (Context (LinearSystem Expr))
ruleExchangeEquations = simplifySystem $ makeRule "Exchange" $ 
   supplyLabeled2 descr args (\x y -> liftSystemTrans $ exchange x y)
 where
   descr  = ("equation 1", "equation 2")
   args c = do mv <- minvar c
               i  <- findIndex (elem mv . getVarsSystem . return) (remaining c)
               return (get covered c, get covered c + i)

ruleEliminateVar :: Rule (Context (LinearSystem Expr))
ruleEliminateVar = simplifySystem $ makeRule "Eliminate variable" $ 
   supplyLabeled3 descr args (\x y z -> liftSystemTrans $ addEquations x y z)
 where
   descr  = ("equation 1", "equation 2", "scale factor")
   args c = do 
      mv <- minvar c
      let hd:rest = remaining c
          getCoef = coefficientOf mv . getLHS
      (i, coef) <- safeHead [ (i, c) | (i, eq) <- zip [0..] rest, let c = getCoef eq, c /= 0 ]
      guard (getCoef hd /= 0)
      let v = negate coef / getCoef hd
      return ( i + get covered c + 1, get covered c, v)

ruleDropEquation :: Rule (Context (LinearSystem Expr))
ruleDropEquation = simplifySystem $ makeSimpleRule "Drop (0=0) equation" $ 
   \c -> do i <- findIndex (fromMaybe False . testConstants (==)) (equations c)
            return $ change covered (\n -> if i < n then n-1 else n)
                   $ fmap (deleteIndex i) c

ruleInconsistentSystem :: Rule (Context (LinearSystem Expr))
ruleInconsistentSystem = simplifySystem $ makeSimpleRule "Inconsistent system (0=1)" $ 
   \c -> do let stop = [0 :==: 1]
            guard $ invalidSystem (equations c) && equations c /= stop
            return $ set covered 1 (fmap (const stop) c)

ruleScaleEquation :: Rule (Context (LinearSystem Expr))
ruleScaleEquation = simplifySystem $ makeRule "Scale equation to one" $ 
   supplyLabeled2 descr args (\x y -> liftSystemTrans $ scaleEquation x y)
 where
   descr  = ("equation", "scale factor")
   args c = do eq <- safeHead $ drop (get covered c) (equations c)
               let expr = getLHS eq
               mv <- minvar c
               guard (coefficientOf mv expr /= 0)
               let coef = 1 / coefficientOf mv expr
               return (get covered c, coef)
   
ruleBackSubstitution :: Rule (Context (LinearSystem Expr))
ruleBackSubstitution = simplifySystem $ makeRule "Back substitution" $ 
   supplyLabeled3 descr args (\x y z -> liftSystemTrans $ addEquations x y z)
 where
   descr  = ("equation 1", "equation 2", "scale factor")
   args c = do eq <- safeHead $ drop (get covered c) (equations c)
               let expr = getLHS eq
               mv <- safeHead (getVars expr)
               i  <- findIndex ((/= 0) . coefficientOf mv . getLHS) (take (get covered c) (equations c))
               let coef = negate $ coefficientOf mv (getLHS (equations c !! i))
               return (i, get covered c, coef)

ruleIdentifyFreeVariables :: IsLinear a => Rule (Context (LinearSystem a))
ruleIdentifyFreeVariables = minorRule $ makeSimpleRule "Identify free variables" $
   \c ->  let vars = [ head ys | ys <- map (getVars . getLHS) (equations c), not (null ys) ]
              change eq =
                 let (e1, e2) = splitLinearExpr (`notElem` vars) (getLHS eq) -- constant ends up in e1
                 in e2 :==: getRHS eq - e1
          in return (fmap (map change) c)

ruleCoverUpEquation :: Rule (Context (LinearSystem a))
ruleCoverUpEquation = minorRule $ makeRule "Cover up first equation" $ changeCover (+1)

ruleUncoverEquation :: Rule (Context (LinearSystem a))
ruleUncoverEquation = minorRule $ makeRule "Uncover one equation" $ changeCover (\x -> x-1)

ruleCoverAllEquations :: Rule (Context (LinearSystem a))
ruleCoverAllEquations = minorRule $ makeSimpleRule "Cover all equations" $ 
   \c -> return (set covered (length $ equations c) c)

-- local helper functions
deleteIndex :: Int -> [a] -> [a]
deleteIndex i xs = ys ++ drop 1 zs
 where (ys, zs) = splitAt i xs

testConstants :: IsLinear a => (a -> a -> Bool) -> Equation a -> Maybe Bool
testConstants f (lhs :==: rhs)
   | isConstant lhs && isConstant rhs = Just (f lhs rhs)
   | otherwise = Nothing

-- simplify a linear system
simplifySystem :: Rule (Context (LinearSystem Expr)) -> Rule (Context (LinearSystem Expr))
simplifySystem = doAfter $ fmap (map (fmap f))
 where f = simplifyWith (fmap simplify) linearView

---------------------------------------------------------------------------------
-- Parameterized transformations

exchange :: Int -> Int -> Transformation [a]
exchange i j 
   | i >  j    = exchange j i
   | otherwise = makeTrans "exchange" $ \xs -> do
        guard (i/=j && validEquation i xs && validEquation j xs)
        let (begin, x:rest) = splitAt i xs
            (middle, y:end) = splitAt (j-i-1) rest
        return $ begin++[y]++middle++[x]++end

scaleEquation :: IsLinear a => Int -> a -> Transformation (LinearSystem a)
scaleEquation i a = makeTrans "scaleEquation" $ \xs -> do
   guard (a `notElem` [0,1] && validEquation i xs)
   let (begin, this:end) = splitAt i xs
   return (begin ++ [fmap (a*) this] ++ end)
      
addEquations :: IsLinear a => Int -> Int -> a -> Transformation (LinearSystem a)
addEquations i j a = makeTrans "addEquations" $ \xs -> do
   guard (i/=j && validEquation i xs && validEquation j xs)
   let (begin, this:end) = splitAt i xs
       exprj = xs!!j
   return $ begin++[combineWith (+) this (fmap (a*) exprj)]++end

changeCover :: (Int -> Int) -> Transformation (Context (LinearSystem a))
changeCover f = makeTrans "changeCover" $ \c -> do
   let new = f (get covered c)
   guard (new >= 0 && new <= length (equations c))
   return (set covered new c)

-- local helper function
validEquation :: Int -> [a] -> Bool
validEquation n xs = n >= 0 && n < length xs
  
--------------------
-- TEMP

equations :: Context (LinearSystem a) -> LinearSystem a
equations = fromContext

-- | The equations that remain to be solved
remaining :: Context (LinearSystem a) -> Equations a
remaining c = drop (get covered c) (equations c)

-- | The minimal variable in the remaining equations
minvar :: IsLinear a => Context (LinearSystem a) -> Maybe String
minvar c | null list = Nothing
         | otherwise = Just (minimum list)
 where
   list = getVarsSystem (remaining c) 
   
liftSystemTrans :: Transformation (LinearSystem a) -> Transformation (Context (LinearSystem a))
liftSystemTrans = lift $ makeLiftPair (return . equations) (fmap . const)

systemInNF :: (Arbitrary a, IsLinear a) => Gen (LinearSystem a)
systemInNF = do
   n <- arbitrary
   replicateM n $ liftM2 (:==:) arbitrary arbitrary

toIntegerSystem :: RealFrac a => LinearSystem a -> LinearSystem Integer
toIntegerSystem = map (fmap round)

fromIntegerSystem :: RealFrac a => LinearSystem Integer -> LinearSystem a
fromIntegerSystem = map (fmap fromInteger)