toysolver-0.0.2: src/ContiTraverso.hs
{-
http://posso.dm.unipi.it/users/traverso/conti-traverso-ip.ps
http://madscientist.jp/~ikegami/articles/IntroSequencePolynomial.html
http://www.kurims.kyoto-u.ac.jp/~kyodo/kokyuroku/contents/pdf/1295-27.pdf
-}
module ContiTraverso where
import Data.Function
import Data.Monoid
import qualified Data.IntMap as IM
import qualified Data.IntSet as IS
import qualified Data.LA as LA
import Data.Expr (Variables (..))
import Data.Polynomial
import Data.Polynomial.GBase
type Var = Int
type Model = IM.IntMap Integer
solve :: MonomialOrder Var -> [Var] -> [(LA.Expr Integer, Integer)] -> LA.Expr Integer -> Maybe Model
solve cmp vs cs obj =
if IM.keysSet (IM.filter (/= 0) m) `IS.isSubsetOf` vs'
then Just $ IM.filterWithKey (\y _ -> y `IS.member` vs') m
else Nothing
where
vs' :: IS.IntSet
vs' = IS.fromList vs
v2 :: Var
v2 = if null vs then 0 else maximum vs + 1
vs2 :: [Var]
vs2 = [v2 .. v2 + length cs - 1]
vs2' :: IS.IntSet
vs2' = IS.fromList vs2
t :: Var
t = v2 + length cs
cmp2 :: MonomialOrder Var
cmp2 = elimOrdering (IS.fromList vs2) `mappend` elimOrdering (IS.singleton t) `mappend` costOrdering obj `mappend` cmp
gbase :: [Polynomial Rational Var]
gbase = buchberger cmp2 (product (map var (t:vs2)) - 1 : phi)
where
phi = do
xj <- vs
let aj = [(yi, aij) | (yi,(ai,_)) <- zip vs2 cs, let aij = LA.coeff xj ai]
return $ product [var yi ^ aij | (yi, aij) <- aj, aij > 0]
- product [var yi ^ (-aij) | (yi, aij) <- aj, aij < 0] * var xj
yb = product [var yi ^ bi | ((_,bi),yi) <- zip cs vs2]
[(_,z)] = terms (reduce cmp2 yb gbase)
m = mkModel (vs++vs2++[t]) z
mkModel :: [Var] -> MonicMonomial Var -> Model
mkModel vs xs = mmToIntMap xs `IM.union` IM.fromList [(x, 0) | x <- vs]
-- IM.union is left-biased
costOrdering :: LA.Expr Integer -> MonomialOrder Var
costOrdering obj = compare `on` f
where
vs = vars obj
f xs = LA.evalExpr (mkModel (IS.toList vs) xs) obj
elimOrdering :: IS.IntSet -> MonomialOrder Var
elimOrdering xs = compare `on` f
where
f ys = not (IS.null (xs `IS.intersection` IM.keysSet (mmToIntMap ys)))
-- http://madscientist.jp/~ikegami/articles/IntroSequencePolynomial.html
-- optimum is (3,2,0)
test_ikegami = solve grlex vs cs obj
where
[x,y,z] = vs
vs = [1..3]
cs = [ (LA.fromTerms [(2,x),(2,y),(2,z)], 10)
, (LA.fromTerms [(3,x),(1,y),(1,z)], 11)
]
obj = LA.fromTerms [(1,x),(2,y),(3,z)]
-- http://posso.dm.unipi.it/users/traverso/conti-traverso-ip.ps
-- optimum is (39, 75, 1, 8, 122)
test_1 = solve grlex vs cs obj
where
[x1,x2,x3,x4,x5] = vs
vs = [1..5]
cs = [ (LA.fromTerms [(2, x1), ( 5, x2), (-3, x3), ( 1,x4), (-2, x5)], 214)
, (LA.fromTerms [(1, x1), ( 7, x2), ( 2, x3), ( 3,x4), ( 1, x5)], 712)
, (LA.fromTerms [(4, x1), (-2, x2), (-1, x3), (-5,x4), ( 3, x5)], 331)
]
obj = LA.fromTerms [(1,x1),(1,x2),(1,x3),(1,x4),(1,x5)]
-- optimum is (0,2,2)
test_2 = solve grlex vs cs obj
where
[x1,x2,x3] = vs
vs = [1..3]
cs = [ (LA.fromTerms [(2, x1), (3, x2), (-1, x3)], 4) ]
obj = LA.fromTerms [(2,x1),(1,x2)]
-- infeasible
test_3 = solve grlex vs cs obj
where
[x1,x2,x3] = vs
vs = [1..3]
cs = [ (LA.fromTerms [(2, x1), (2, x2), (2, x3)], 3) ]
obj = LA.fromTerms [(1,x1)]