-- | Convert linear constraints that only mention one variable to bounds module Numeric.Limp.Canon.Simplify.Bounder where import Numeric.Limp.Canon.Constraint import Numeric.Limp.Canon.Linear import Numeric.Limp.Canon.Program import Numeric.Limp.Rep import Numeric.Limp.Error import Control.Applicative import Data.Either import qualified Data.Map as M type Bound z r c = (Either z r, (Maybe (R c), Maybe (R c))) -- | Convert a single constraint into a bound, if possible. -- -- > bounder $ Constraint (5 <= y <= 10) -- > == Bound (Just 5) y (Just 10) -- -- > bounder $ Constraint (5 <= 2y <= 10) -- > == Bound (Just 2.5) y (Just 5) -- -- > bounder $ Constraint (10 <= 2y <= 5) -- > == Left InfeasibleBoundEmpty -- bounderConstraint1 :: (Ord z, Ord r, Rep c) => Constraint1 z r c -> Either Infeasible (Maybe (Bound z r c)) bounderConstraint1 (C1 low (Linear mf) upp) | M.size mf == 1 , [(k,c)] <- M.toList mf , c /= 0 = let fixup = (/ c) low' = fmap fixup low upp' = fmap fixup upp bounds | c >= 0 = (low',upp') | otherwise = (upp',low') valid | (Just lo, Just hi) <- bounds = lo <= hi | otherwise = True in if valid then Right $ Just (k, bounds) else Left InfeasibleNotIntegral | otherwise = Right Nothing bounderConstraint :: (Ord z, Ord r, Rep c) => Constraint z r c -> Either Infeasible (Constraint z r c, [Bound z r c]) bounderConstraint (Constraint cs) = do (cs', bs) <- partitionEithers <$> mapM bounderC cs return (Constraint cs', bs) where bounderC c = do c' <- bounderConstraint1 c return $ case c' of Nothing -> Left c Just b -> Right b -- bounderProgram :: (Ord z, Ord r, Rep c) => Program z r c -> Either Infeasible (Program z r c) bounderProgram p = do (c',bs) <- bounderConstraint $ _constraints p return $ p { _constraints = c' , _bounds = foldl merge (_bounds p) bs } where merge m (k,v) = case M.lookup k m of Just v' -> M.insert k (mergeBounds v' v) m Nothing -> M.insert k v m