packages feed

computational-algebra-0.4.0.0: examples/ipsolve.hs

{-# LANGUAGE BangPatterns, CPP, DataKinds, ExistentialQuantification    #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances, GADTs, KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses, NoImplicitPrelude, RankNTypes       #-}
{-# LANGUAGE ScopedTypeVariables, TemplateHaskell, TypeOperators        #-}
{-# LANGUAGE UndecidableInstances                                       #-}
module Main where
import           Algebra.Algorithms.Groebner
import           Algebra.Instances            ()
import           Algebra.Prelude
import           Control.Applicative          ((<$>))
import           Control.Lens                 (ix, makeLenses, view, (&), (.~))
import           Data.Reflection
import           Data.Singletons.Prelude      (SList)
import           Data.Singletons.Prelude.List
import qualified Data.Sized.Builtin           as V

newtype IsOrder_ n = IsOrder_ { cmp :: Monomial n -> Monomial n -> Ordering }
data ReifiedOrder a = ReifiedOrder

retrieveOrder :: Proxy (ReifiedOrder a) -> Proxy a
retrieveOrder Proxy = Proxy

instance Reifies a (IsOrder_ n) => IsOrder n (ReifiedOrder a) where
  cmpMonomial pxy = cmp $ reflect (retrieveOrder pxy)

instance Reifies a (IsOrder_ n) => IsMonomialOrder n (ReifiedOrder a)

type IPOrder n vs = ProductOrder n (1 + Length vs)
                      Grevlex
                      (ProductOrder 1 (Length vs) Grevlex (WeightOrder vs Grevlex))
ipOrder :: SNat n -> SList vs -> IPOrder n vs
ipOrder n vs =
  ProductOrder n (sOne %:+ sLength vs)
    Grevlex
    (ProductOrder sOne (sLength vs) Grevlex (WeightOrder vs Proxy))

toMonomial :: forall k n ord .
              (KnownNat n, IsMonomialOrder (n + 1 + k) ord)
           => Sing k -> Monomial n -> OrderedPolynomial (Fraction Integer) ord (S n :+ k)
toMonomial k ds =
  withKnownNat k $
  withKnownNat (sSucc (sing :: SNat n) %:+ k) $
  let !c = foldl' (\a b -> if b < 0 then a + abs b else a) 0 ds
  in toPolynomial' (1,
     -- coerce (symmetry $ sAndPlusOne (V.sLength ds)) $
     V.map (+c) ds `V.append` V.singleton c `V.append` V.replicate' 0)

calcCost :: Sized' n Int -> Sized' m Int -> Int
calcCost ns ms = sum $ V.zipWith (*) ns ms

costCmp :: Sing m -> Sized' n Int -> Monomial m -> Monomial m -> Ordering
costCmp _ cost ns ms =
  comparing (calcCost cost) ns ms <> grevlex ns ms

toReifiedOrder :: Proxy m -> ReifiedOrder m
toReifiedOrder Proxy = ReifiedOrder

-- | Solve integer programming problem with general signature.
solveIP' :: forall n m . (KnownNat n, KnownNat m)
         => Sized' n Int            -- ^ cost vector
         -> Sized' m (Sized' n Int) -- ^ constraint matrix
         -> Sized' m Int            -- ^ constraint
         -> Maybe (Sized' n Int)    -- ^ answer
solveIP' c mat b =
  let n    = sing :: SNat n
      m    = sing :: SNat m
      vlen = sSucc m %:+ n
  in withKnownNat (sSucc m) $
     reify (IsOrder_ $ costCmp n c) $ \pxy ->
     withWitness (plusLeqL (sSucc m) n) $
          withKnownNat vlen $
            let ord  = ProductOrder (sSucc m) n Grevlex (toReifiedOrder pxy)
                !b' = toMonomial n b
                as  = map  (toMonomial n) $
                      V.toList $ sequenceA mat
                (xsw, ys)  = splitAt (sNatToInt m+1) (vars' ord vlen)
                gs  = calcGroebnerBasis $ toIdeal $ product xsw - one : zipWith (-) ys as
                ans = b' `modPolynomial` gs
                (cond, solution) = V.splitAt (sSucc m) $ getMonomial $ leadingMonomial ans
            in if all (== 0) cond
               then Just $ coerceLength (plusMinus' (sSucc m) n) solution
               else Nothing

vars' :: IsPolynomial poly => (MOrder poly) -> SNat (Arity poly) -> [poly]
vars' _ _ = vars

data Cnstr n = (:<=) { _lhs :: Sized' n Int, _rhs :: Int }
             | (:>=) { _lhs :: Sized' n Int, _rhs :: Int }
             | (:==) { _lhs :: Sized' n Int, _rhs :: Int }
             deriving (Show, Eq, Ord)

infix 4 :<=, :>=, :==

data IPProblem n m = IPProblem { objectCnstr :: Sized' n Int
                               , cnstrs      :: Sized' m (Cnstr n)
                               } deriving (Show, Eq)
makeLenses ''Cnstr

solveCnstrs :: forall n m. (KnownNat m, KnownNat n) => IPProblem n m -> Maybe (Sized' n Int)
solveCnstrs ipp =
  let sn = sing :: SNat n
      sm = sing :: SNat m
      (obj, mat, vec) = extractProblem $ nfProblem ipp
  in withWitness (plusLeqL sn sm) $
     withKnownNat (sn %:+ sm) $
     V.take (sing :: SNat n) <$> solveIP' obj mat vec

extractProblem :: IPProblem n m -> (Sized' n Int, Sized' m (Sized' n Int), Sized' m Int)
extractProblem (IPProblem obj css) = (obj, V.map (view lhs) css, V.map (view rhs) css)

nfProblem :: forall n m . KnownNat m => IPProblem n m -> IPProblem (n :+ m) m
nfProblem (IPProblem obj css) =
  IPProblem (obj `V.append` V.replicate (sing :: SNat m) 0)
            (nfCnstrs css)

ordVec :: SNat n -> Sized' n (V.Ordinal n)
ordVec n = generate n id

nfCnstrs :: forall n m. (KnownNat m)
         => Sized' m (Cnstr n) -> Sized' m (Cnstr (n :+ m))
nfCnstrs css = V.zipWithSame conv css (ordVec (sing :: SNat m))
  where
    conv (lh :<= r) nth = (lh `V.append` (V.replicate (sing :: SNat m) 0 & ix nth .~  1)) :== r
    conv (lh :>= r) nth = (lh `V.append` (V.replicate (sing :: SNat m) 0 & ix nth .~ -1)) :== r
    conv (lh :== r) _   = (lh `V.append`  V.replicate (sing :: SNat m) 0) :== r

testC :: Sized' 4 Int
testM :: Sized' 2 (Sized' 4 Int)
testB :: Sized' 2 Int
(testC, testM, testB) =
  (1000 :< 1 :< 1 :< 100 :< NilL,
   (3 :< -2 :< 1 :< -1 :< NilL) :< (4 :< 1 :< -1 :< 0 :< NilL) :< NilL,
   -1 :< 5 :< NilL)

data Rect = Rect { _height :: Int, _width :: Int
                 } deriving (Read, Show, Eq, Ord)
makeLenses ''Rect

data Design = Design { _frame    :: Rect
                     , _pictures :: [Rect]
                     } deriving (Read, Show, Eq, Ord)
makeLenses ''Design

data Department = Department { _area    :: Int
                             , _aspect  :: Int
                             , _maxSide :: Int
                             , _minSide :: Int
                             } deriving (Read, Show, Eq, Ord)

data SomeIPProblem = forall n m. SomeIPProblem (IPProblem n m)

designConstraint :: Design -> SomeIPProblem
designConstraint = undefined

main :: IO ()
main = act

act :: IO ()
act = print $ solveIP' testC testM testB