ideas-math-1.0: src/Domain/LinearAlgebra/EquationsRules.hs
-----------------------------------------------------------------------------
-- Copyright 2013, 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
( ruleCoverAllEquations, ruleUncoverEquation, ruleScaleEquation
, ruleBackSubstitution, ruleIdentifyFreeVariables, ruleExchangeEquations
, ruleEliminateVar, ruleCoverUpEquation, ruleInconsistentSystem
, ruleDropEquation, equationsRules
, simplifySystem, remaining, linId
) where
import Control.Monad
import Data.List
import Data.Maybe
import Domain.LinearAlgebra.LinearSystem
import Domain.LinearAlgebra.LinearView
import Domain.LinearAlgebra.MatrixRules (covered, getCovered) -- for context
import Domain.Math.Data.Relation
import Domain.Math.Expr
import Domain.Math.Simplification (simplify)
import Ideas.Common.Library hiding (simplify)
import Ideas.Common.Utils
import Prelude
linId :: Id
linId = newId "linearalgebra.linsystem"
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 $ ruleTrans (linId, "exchange") $
supplyContextParameters exchangeEquations $ \ls -> do
mv <- minvar ls
eqs <- remaining ls
i <- findIndexM (elem mv . getVarsSystem . return) eqs
cov <- getCovered
return (cov, cov + i)
ruleEliminateVar :: Rule (Context (LinearSystem Expr))
ruleEliminateVar = describe "Eliminate a variable (using addition)" $
simplifySystem $ ruleTrans (linId, "eliminate") $
supplyContextParameters addEquations $ \ls -> do
mv <- minvar ls
hd:rest <- remaining ls
let getCoef = coefficientOf mv . leftHandSide
(i, coef) <- msum [ return (i, c) | (i, eq) <- zip [0..] rest, let c = getCoef eq, c /= 0 ]
guard (getCoef hd /= 0)
let v = negate coef / getCoef hd
cov <- getCovered
return (i + cov + 1, cov, v)
ruleDropEquation :: Rule (Context (LinearSystem Expr))
ruleDropEquation = describe "Drop trivial equations (such as 0=0)" $
simplifySystem $ ruleTrans (linId, "trivial") $ makeTransLiftContext $ \ls -> do
i <- findIndexM (fromMaybe False . testConstants (==)) ls
cov <- getCovered
let f n = if i < n then n-1 else n
covered := f cov
return (deleteIndex i ls)
ruleInconsistentSystem :: Rule (Context (LinearSystem Expr))
ruleInconsistentSystem = describe "Inconsistent system (0=1)" $
simplifySystem $ ruleTrans (linId, "inconsistent") $ makeTransLiftContext $ \ls -> do
let stop = [0 :==: 1]
guard (invalidSystem ls && ls /= stop)
covered := 1
return stop
ruleScaleEquation :: Rule (Context (LinearSystem Expr))
ruleScaleEquation = describe "Scale equation to one" $
simplifySystem $ ruleTrans (linId, "scale") $
supplyContextParameters scaleEquation $ \ls -> do
cov <- getCovered
eq <- elementAt 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 $ ruleTrans (linId, "subst") $
supplyContextParameters addEquations $ \ls -> do
cov <- getCovered
eq <- elementAt cov ls
let expr = leftHandSide eq
mv <- headM (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" $
liftToContext $ minorRule (linId, "freevars") $ \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 Just (map f ls)
ruleCoverUpEquation :: Rule (Context (LinearSystem a))
ruleCoverUpEquation = describe "Cover up first equation" $
minor $ ruleTrans (linId, "coverup") $ changeCover succ
ruleUncoverEquation :: Rule (Context (LinearSystem a))
ruleUncoverEquation = describe "Uncover one equation" $
minor $ ruleTrans (linId, "uncover") $ changeCover pred
ruleCoverAllEquations :: Rule (Context (LinearSystem a))
ruleCoverAllEquations = describe "Cove all equations" $
minorRule (linId, "coverall") $ \cls -> do
ls <- fromContext cls
Just (insertRef covered (length ls) cls)
-- 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 $ changeInContext (map (fmap f))
where f = simplifyWith (fmap simplify) linearView
---------------------------------------------------------------------------------
-- Parameterized transformations
exchangeEquations :: ParamTrans (Int, Int) (LinearSystem a)
exchangeEquations = parameter2 "equation 1" "equation 2" exchange
where
exchange i j
| i > j = exchange j i
| otherwise = transMaybe $ \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 :: (Eq a, Reference a, IsLinear a) => ParamTrans (Int, a) (LinearSystem a)
scaleEquation = parameter2 "equation" "scale factor" $ \i a -> transMaybe $ \xs -> do
guard (a `notElem` [0,1])
changeAt i (fmap (a*)) xs
addEquations :: (Reference a, IsLinear a) => ParamTrans (Int, Int, a) (LinearSystem a)
addEquations = parameter3 "equation 1" "equation 2" "scale factor" $ \i j a -> transMaybe $ \xs -> do
guard (i/=j)
j1 :==: j2 <- liftM (fmap (a*)) (elementAt j xs)
let f (i1 :==: i2) = i1+j1 :==: i2+j2
changeAt i f xs
changeCover :: (Int -> Int) -> Transformation (Context (LinearSystem a))
changeCover f = makeTransLiftContext_ $ \ls -> do
new <- liftM f getCovered
guard (new >= 0 && new <= length ls)
covered := new
-- local helper function
validEquation :: Int -> [a] -> Bool
validEquation n xs = n >= 0 && n < length xs
-- | The equations that remain to be solved
remaining :: LinearSystem a -> EnvMonad (Equations a)
remaining ls = do
cov <- getCovered
return (drop cov ls)
-- | The minimal variable in the remaining equations
minvar :: IsLinear a => LinearSystem a -> EnvMonad String
minvar ls = do
xs <- liftM getVarsSystem (remaining ls)
guard (not $ null xs)
return (minimum xs)