{- Tock: a compiler for parallel languages Copyright (C) 2007 University of Kent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . -} module ArrayUsageCheck where import Control.Monad.State import Data.Array.IArray import Data.List import Data.Maybe import qualified AST as A import FlowGraph import Utils --TODO fix this tangle of code to make it work with the code at the bottom of the file data Constraint = Equality [CoeffVar] Integer type Problem = [Constraint] data CoeffVar = CV { coeff :: Integer, var :: A.Variable } type CoeffExpr = [CoeffVar] --type IndicesUsed = Map.Map Variable [[ makeProblems :: [[CoeffExpr]] -> [Problem] makeProblems indexLists = map checkEq zippedPairs where allPairs :: [([CoeffExpr],[CoeffExpr])] allPairs = [(a,b) | a <- indexLists, b <- indexLists] zippedPairs :: [[(CoeffExpr,CoeffExpr)]] zippedPairs = map (uncurry zip) allPairs checkEq :: [(CoeffExpr,CoeffExpr)] -> Problem checkEq = map checkEq' checkEq' :: (CoeffExpr, CoeffExpr) -> Constraint checkEq' (cv0,cv1) = Equality (cv0 ++ map negate cv1) 0 negate :: CoeffVar -> CoeffVar negate cv = cv {coeff = - (coeff cv)} makeProblem1Dim :: [CoeffExpr] -> [Problem] makeProblem1Dim ces = makeProblems [[c] | c <- ces] type CoeffIndex = Int type EqualityConstraintEquation = Array CoeffIndex Integer type EqualityProblem = [EqualityConstraintEquation] -- Assumed to be >= 0 type InequalityConstraintEquation = Array CoeffIndex Integer type InequalityProblem = [InequalityConstraintEquation] -- | Solves all the constraints in the Equality Problem (taking them to be == 0), -- and transforms the InequalityProblems appropriately. -- TODO the function currently doesn't record the relation between the transformed variables -- (e.g. sigma for x_k) and the original variables (x_k). In order to feed back useful -- information to the user, we should do this at some point in future. solveConstraints :: EqualityProblem -> InequalityProblem -> Maybe InequalityProblem solveConstraints p ineq = normaliseEq p >>= (\p' -> execStateT (solve p') ineq) where -- | Normalises an equation by dividing all coefficients by their greatest common divisor. -- If the unit coefficient (a_0) doesn't divide by this GCD, Nothing will be returned -- (the constraints do not have an integer solution) normaliseEq :: EqualityProblem -> Maybe EqualityProblem normaliseEq = mapM normaliseEq' --Note the mapM; if any calls to normalise' fail, so will normalise where normaliseEq' :: EqualityConstraintEquation -> Maybe EqualityConstraintEquation normaliseEq' e | g == 0 = Just e | ((e ! 0) `mod` g) /= 0 = Nothing | otherwise = Just $ amap (\x -> x `div` g) e where g = foldl1 mygcd (map abs $ tail $ elems e) -- g is the GCD of a_1 .. a_n (not a_0) mygcd :: Integer -> Integer -> Integer mygcd 0 0 = 0 mygcd x y = gcd x y -- | Solves all equality problems in the given list. -- Will either succeed (Just () in the Error/Maybe monad) or fail (Nothing) solve :: EqualityProblem -> StateT InequalityProblem Maybe () solve [] = return () solve p = (solveUnits p >>* removeRedundant) >>= liftF checkFalsifiable >>= solveNext >>= solve -- | Checks if any of the coefficients in the equation have an absolute value of 1. -- Returns either Just or Nothing (there are no such coefficients in the equation). -- This function only looks at a_1 .. a_n. That is, a_0 is ignored. checkForUnit :: EqualityConstraintEquation -> Maybe CoeffIndex checkForUnit = listToMaybe . map fst . filter coeffAbsVal1 . tail . assocs where coeffAbsVal1 :: (a, Integer) -> Bool coeffAbsVal1 (_,x) = (abs x) == 1 -- | Finds the first unit coefficient (|a_k| == 1) in a set of equality constraints. -- Returns Nothing if there are no unit coefficients. Otherwise it returns -- (Just (equation, indexOfUnitCoeff), otherEquations); that is, the specified equation is not -- present in the list of equations. findFirstUnit :: EqualityProblem -> (Maybe (EqualityConstraintEquation,CoeffIndex),EqualityProblem) findFirstUnit [] = (Nothing,[]) findFirstUnit (e:es) = case checkForUnit e of Just ci -> (Just (e,ci),es) Nothing -> let (me,es') = findFirstUnit es in (me,e:es') -- | Substitutes a value for x_k into an equation. Given k, the value for x_k in terms -- of coefficients of other variables (let's call it x_k_val), it subsitutes this into -- all the equations in the list by adding x_k_val (scaled by a_k) to each equation and -- then zeroing out the a_k value. Note that the (x_k_val ! k) value will be ignored; -- it should be zero, in any case (otherwise x_k would be defined in terms of itself!). substIn :: CoeffIndex -> Array CoeffIndex Integer -> EqualityProblem -> EqualityProblem substIn k x_k_val = map substIn' where substIn' eq = (arrayZipWith (+) eq scaled_x_k_val) // [(k,0)] where scaled_x_k_val = amap (* (eq ! k)) x_k_val -- | Solves (i.e. removes by substitution) all unit coefficients in the given list of equations. solveUnits :: EqualityProblem -> StateT InequalityProblem Maybe EqualityProblem solveUnits p = case findFirstUnit p of (Nothing,p') -> return p' -- p' should equal p anyway (Just (eq,ind),p') -> modify change >> ((lift $ normaliseEq $ change p') >>= solveUnits) where change = substIn ind (arrayMapWithIndex (modifyOthersZeroSpecific ind) eq) origVal = eq ! ind -- Zeroes a specific coefficient, modifies the others as follows: -- If the coefficient of x_k is 1, we need to negate the other coefficients -- to get its definition. However, if the coefficient is -1, we don't need to -- do this. For example, consider 2 + 3x_1 + x_2 - 4x_3 = 0. In this case -- x_2 = -2 - 3x_1 + 4x_3; the negation of the original equation (ignoring x_2). -- If however, it was 2 + 3x_1 - x_2 - 4x_3 = 0 then x_2 = 2 + 3x_1 - 4x_3; -- that is, identical to the original equation if we ignore x_2. modifyOthersZeroSpecific :: CoeffIndex -> (CoeffIndex -> Integer -> Integer) modifyOthersZeroSpecific match ind | match == ind = const 0 -- The specific value to zero out | origVal == 1 = negate -- Original coeff was 1; negate | otherwise = id -- Original coeff was -1; don't do anything -- | Finds the coefficient with the smallest absolute value of a_1 .. a_n (i.e. not a_0) -- that is non-zero (i.e. zero coefficients are ignored). findSmallestAbsCoeff :: EqualityConstraintEquation -> CoeffIndex findSmallestAbsCoeff = fst . minimumBy cmpAbsSnd . filter ((/= 0) . snd) . tail . assocs where cmpAbsSnd :: (a,Integer) -> (a,Integer) -> Ordering cmpAbsSnd (_,x) (_,y) = compare (abs x) (abs y) -- | Solves the next equality and returns the new set of equalities. solveNext :: EqualityProblem -> StateT InequalityProblem Maybe EqualityProblem solveNext [] = return [] solveNext (e:es) = -- We transform the kth variable into sigma, effectively -- So once we have x_k = ... (in terms of sigma) we add a_k * RHS -- to all other equations, AFTER zeroing the a_k coefficient (so -- that the multiple of sigma is added on properly) modify (map alterEquation) >> (lift $ (normaliseEq . map alterEquation) (e:es)) where -- | Adds a scaled version of x_k_eq onto the current equation, after zeroing out -- the a_k coefficient in the current equation. alterEquation :: EqualityConstraintEquation -> EqualityConstraintEquation alterEquation eq = arrayZipWith (+) (eq // [(k,0)]) (amap (\x -> x * (eq ! k)) x_k_eq) k = findSmallestAbsCoeff e a_k = e ! k m = (abs a_k) + 1 sign_a_k = signum a_k x_k_eq = amap (\a_i -> sign_a_k * (a_i `mymod` m)) e // [(k,(- sign_a_k) * m)] -- I think this is probably equivalent to mod, but let's follow the maths: mymod :: Integer -> Integer -> Integer mymod x y = x - (y * (floordivplushalf x y)) -- This is floor (x/y + 1/2). Probably a way to do it without reverting to float arithmetic: floordivplushalf :: Integer -> Integer -> Integer floordivplushalf x y = floor ((fromInteger x / fromInteger y) + (0.5 :: Double)) -- Removes all equations where the coefficients are all zero removeRedundant :: EqualityProblem -> EqualityProblem removeRedundant = mapMaybe (boolToMaybe (not . isRedundant)) where isRedundant :: EqualityConstraintEquation -> Bool isRedundant = all (== 0) . elems -- Searches for all equations where only the a_0 coefficient is non-zero; this means the equation cannot be satisfied checkFalsifiable :: EqualityProblem -> Maybe EqualityProblem checkFalsifiable = boolToMaybe (not . any checkFalsifiable') where -- | Returns True if the equation is definitely unsatisfiable checkFalsifiable' :: EqualityConstraintEquation -> Bool checkFalsifiable' e = (e ! 0 /= 0) && (all (== 0) . tail . elems) e