ideas-1.0: src/Domain/LinearAlgebra/EquationsRules.hs
-----------------------------------------------------------------------------
-- Copyright 2011, 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 Common.Library hiding (simplify)
import Common.Utils
import Control.Monad
import Data.List
import Data.Maybe
import Domain.LinearAlgebra.LinearSystem
import Domain.LinearAlgebra.LinearView
import Domain.LinearAlgebra.MatrixRules (covered) -- for context
import Domain.Math.Data.Relation
import Domain.Math.Expr
import Domain.Math.Simplification (simplify)
import Prelude
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 = describe "Exchange two equations" $
simplifySystem $ makeRule "linearalgebra.linsystem.exchange" $
supply2 descr args (\x y -> liftTransContext $ exchange x y)
where
descr = ("equation 1", "equation 2")
args = evalCM $ \ls -> do
mv <- minvar ls
eqs <- remaining ls
i <- findIndexM (elem mv . getVarsSystem . return) eqs
cov <- readVar covered
return (cov, cov + i)
ruleEliminateVar :: Rule (Context (LinearSystem Expr))
ruleEliminateVar = describe "Eliminate a variable (using addition)" $
simplifySystem $ makeRule "linearalgebra.linsystem.eliminate" $
supply3 descr args (\x y z -> liftTransContext $ addEquations x y z)
where
descr = ("equation 1", "equation 2", "scale factor")
args = evalCM $ \ls -> do
mv <- minvar ls
hd:rest <- remaining ls
let getCoef = coefficientOf mv . leftHandSide
(i, coef) <- maybeCM $ safeHead [ (i, c) | (i, eq) <- zip [0..] rest, let c = getCoef eq, c /= 0 ]
guard (getCoef hd /= 0)
let v = negate coef / getCoef hd
cov <- readVar covered
return (i + cov + 1, cov, v)
ruleDropEquation :: Rule (Context (LinearSystem Expr))
ruleDropEquation = describe "Drop trivial equations (such as 0=0)" $
simplifySystem $ makeSimpleRule "linearalgebra.linsystem.trivial" $ withCM $ \ls -> do
i <- findIndexM (fromMaybe False . testConstants (==)) ls
modifyVar covered (\n -> if i < n then n-1 else n)
return (deleteIndex i ls)
ruleInconsistentSystem :: Rule (Context (LinearSystem Expr))
ruleInconsistentSystem = describe "Inconsistent system (0=1)" $
simplifySystem $ makeSimpleRule "linearalgebra.linsystem.inconsistent" $ withCM $ \ls -> do
let stop = [0 :==: 1]
guard (invalidSystem ls && ls /= stop)
writeVar covered 1
return stop
ruleScaleEquation :: Rule (Context (LinearSystem Expr))
ruleScaleEquation = describe "Scale equation to one" $
simplifySystem $ makeRule "linearalgebra.linsystem.scale" $
supply2 descr args (\x y -> liftTransContext $ scaleEquation x y)
where
descr = ("equation", "scale factor")
args = evalCM $ \ls -> do
cov <- readVar covered
eq <- maybeCM $ safeHead $ drop cov ls
let expr = leftHandSide eq
mv <- minvar ls
guard (coefficientOf mv expr /= 0)
let coef = 1 / coefficientOf mv expr
return (cov, coef)
ruleBackSubstitution :: Rule (Context (LinearSystem Expr))
ruleBackSubstitution = describe "Back substitution" $
simplifySystem $ makeRule "linearalgebra.linsystem.subst" $
supply3 descr args (\x y z -> liftTransContext $ addEquations x y z)
where
descr = ("equation 1", "equation 2", "scale factor")
args = evalCM $ \ls -> do
cov <- readVar covered
eq <- maybeCM $ safeHead $ drop cov ls
let expr = leftHandSide eq
mv <- maybeCM $ safeHead (vars expr)
i <- findIndexM ((/= 0) . coefficientOf mv . leftHandSide) (take cov ls)
let coef = negate $ coefficientOf mv (leftHandSide (ls !! i))
return (i, cov, coef)
ruleIdentifyFreeVariables :: IsLinear a => Rule (Context (LinearSystem a))
ruleIdentifyFreeVariables = describe "Identify free variables" $
minorRule $ makeSimpleRule "linearalgebra.linsystem.freevars" $ withCM $ \ls ->
let vs = [ head ys | ys <- map (vars . leftHandSide) ls, not (null ys) ]
f eq =
let (e1, e2) = splitLinearExpr (`notElem` vs) (leftHandSide eq) -- constant ends up in e1
in e2 :==: rightHandSide eq - e1
in return (map f ls)
ruleCoverUpEquation :: Rule (Context (LinearSystem a))
ruleCoverUpEquation = describe "Cover up first equation" $
minorRule $ makeRule "linearalgebra.linsystem.coverup" $ changeCover succ
ruleUncoverEquation :: Rule (Context (LinearSystem a))
ruleUncoverEquation = describe "Uncover one equation" $
minorRule $ makeRule "linearalgebra.linsystem.uncover" $ changeCover pred
ruleCoverAllEquations :: Rule (Context (LinearSystem a))
ruleCoverAllEquations = describe "Cove all equations" $
minorRule $ makeSimpleRule "linearalgebra.linsystem.coverall" $ withCM $ \ls -> do
writeVar covered (length ls)
return ls
-- 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)
| hasNoVar lhs && hasNoVar rhs = Just (f lhs rhs)
| otherwise = Nothing
-- simplify a linear system
simplifySystem :: Rule (Context (LinearSystem Expr)) -> Rule (Context (LinearSystem Expr))
simplifySystem = doAfter $ change (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 $ \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 $ \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 $ \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 $ withCM $ \ls -> do
new <- liftM f (readVar covered)
guard (new >= 0 && new <= length ls)
writeVar covered new
return ls
-- local helper function
combineWith :: (a -> a -> a) -> Equation a -> Equation a -> Equation a
combineWith f (x1 :==: x2) (y1 :==: y2) = f x1 y1 :==: f x2 y2
validEquation :: Int -> [a] -> Bool
validEquation n xs = n >= 0 && n < length xs
--------------------
-- TEMP
-- | The equations that remain to be solved
remaining :: LinearSystem a -> ContextMonad (Equations a)
remaining ls = do
cov <- readVar covered
return (drop cov ls)
-- | The minimal variable in the remaining equations
minvar :: IsLinear a => LinearSystem a -> ContextMonad String
minvar ls = do
list <- liftM getVarsSystem (remaining ls)
guard (not $ null list)
return (minimum list)
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)
findIndexM :: MonadPlus m => (a -> Bool) -> [a] -> m Int
findIndexM p = maybe mzero return . findIndex p