module DDC.Core.Flow.Transform.Rates.Clusters.Linear
(solve_linear)
where
import DDC.Base.Pretty
import DDC.Core.Flow.Transform.Rates.Graph
import DDC.Core.Flow.Transform.Rates.Clusters.Base
import qualified Data.Map as Map
import Numeric.Limp.Program.ResultKind
import Numeric.Limp.Program
import Numeric.Limp.Rep
import Numeric.Limp.Solvers.Cbc.Solve
import qualified Numeric.Limp.Canon.Convert as Conv
import qualified Numeric.Limp.Canon.Simplify as CSimp
data ZVar n
= SameCluster n n
| C n
deriving (Eq, Show, Ord)
instance Pretty n => Pretty (ZVar n) where
ppr (SameCluster a b) = text "SC" <+> ppr a <+> ppr b
ppr (C a) = text "C" <+> ppr a
data RVar n
= Pi n
deriving (Eq, Show, Ord)
instance Pretty n => Pretty (RVar n) where
ppr (Pi a) = text "O" <+> ppr a
mkSameCluster :: Ord n => n -> n -> ZVar n
mkSameCluster m n
= SameCluster (min m n) (max m n)
gobjective :: Ord n => [n] -> [(Int,n,n)] -> Linear (ZVar n) (RVar n) IntDouble 'KZ
gobjective ns ws
= foldl (.+.) c0
( map (\(w,i,j) -> z (mkSameCluster i j) (Z w)) ws
++ map (\n -> z (C n) (Z $ length ns)) ns)
getBounds :: Ord n => [n] -> [(Int,n,n)] -> [Bounds (ZVar n) (RVar n) IntDouble]
getBounds ns ws
= map boundC ns
++ map boundSC ws
where
boundC n
= binary (C n)
boundSC (_,i,j)
= binary (mkSameCluster i j)
getConstraints :: (Ord n, Eq t, Show n)
=> Int -> Graph n t
-> [((n,n),Bool)]
-> [(Int,n,n)]
-> TransducerMap n
-> Constraint (ZVar n) (RVar n) IntDouble
getConstraints bigN g arcs ws trans
= mconcat $ map edgeConstraint arcs
++ map weightConstraint ws
where
piDiff u v = r1 (Pi v) .-. r1 (Pi u)
sc u v = z1 (mkSameCluster u v)
edgeConstraint ((v,u), fusible)
| fusible && typeComparable g trans u v && noFusionPreventingPath arcs u v
= let x = sc u v
in Between x (piDiff u v) (Z bigN *. x)
:&& x :<= z1 (C u)
| otherwise
= piDiff u v :>= c1
:&& z1 (C u) :== c1
weightConstraint (_,u,v)
| not $ any (\((i,j),_) -> (u,v) == (i,j) || (v,u) == (i,j)) arcs
= let x = sc u v
in Between (Z (bigN) *. x) (piDiff u v) (Z bigN *. x)
:&& checkTypes u v
| otherwise
= CTrue
checkTypes u v
| Just uT <- nodeType g u
, Just vT <- nodeType g v
, uT /= vT
, Just (u',v') <- trans u v
= filtConstraint v' v u v
:&& filtConstraint u' u u v
:&& filtConstraint u' v' u v
| otherwise
= CTrue
filtConstraint a b c d
| a == b
= CTrue
| checkFusible a b
= sc a b :<= sc c d
| otherwise
= sc c d :== c1
checkFusible a b
= any (\(_, i,j) -> (i,j) == (a,b) || (i,j) == (b,a)) ws
clusterings :: (Ord n, Eq t) => [((n,n),Bool)] -> [n] -> Int -> Graph n t -> TransducerMap n -> [(Int, n,n)]
clusterings arcs ns bigN g trans
= go ns
where
go (u:rest)
= [ (w,u,v)
| v <- rest
, noFusionPreventingPath arcs u v
, cmp u v
, let w = weight u v
, w > 0]
++ go rest
go []
= []
cmp = typeComparable g trans
weight u v
| (_:_) <- filter (\((i,j),_) -> (u,v) == (i,j) || (v,u) == (i,j)) arcs
= bigN * bigN
| ius <- map (fst.fst) $ filter (\((_,j),_) -> j == u) arcs
, ivs <- map (fst.fst) $ filter (\((_,j),_) -> j == v) arcs
, _:_ <- filter (flip elem ius) ivs
= bigN * bigN
| otherwise
= 1
lp :: (Ord n, Eq t, Show n) => Graph n t -> TransducerMap n -> Program (ZVar n) (RVar n) IntDouble
lp g trans
= minimise (gobjective names weights)
(getConstraints nNodes g arcs weights trans)
(getBounds names weights)
where
g' = listOfGraph g
names = map fst $ fst g'
arcs = snd g'
weights = clusterings arcs names nNodes g trans
nNodes
= numNodes g
solve_linear :: (Ord n, Eq t, Show n, Pretty n) => Graph n t -> TransducerMap n -> [[n]]
solve_linear g trans
= case solve lp's of
Left e -> error (show e)
Right ass -> Map.elems
$ fixMap (sub `mappend` ass)
where
lp' = lp g trans
lp'c = Conv.program lp'
Right (sub, lp's) = CSimp.simplify lp'c
fixMap ass@(Assignment mz _r)
= reorder ass $ snd $ fillMap $ Map.foldWithKey go (0 :: Int, Map.empty) mz
go k v (n, m)
| SameCluster i j <- k
, v == 0
= case (Map.lookup i m, Map.lookup j m) of
(Just iC, Just jC)
-> if iC == jC
then (n, m)
else (n, Map.map (\x -> if x == iC then jC else x) m)
(Just iC, Nothing)
-> (n, Map.insert j iC m)
(Nothing, Just jC)
-> (n, Map.insert i jC m)
(Nothing, Nothing)
-> ( n + 1
, Map.insert i n
$ Map.insert j n m)
| otherwise
= (n, m)
fillMap (n, m)
= foldr goFill (n, m) (fst $ listOfGraph g)
goFill (k,_ty) (n, m)
| Map.member k m
= (n, m)
| otherwise
= ( n + 1
, Map.insert k n m)
reorder ass m
= Map.fromList
$ map (reorder' ass)
$ Map.toList $ invertMap m
reorder' ass (k,v:vs)
= let k' = rOf ass (Pi v)
in ((truncate k' :: Int, k), v:vs)
reorder' _ (_, [])
= error "ddc-core-flow:DDC.Core.Flow.Transform.Rates.Linear: impossible, empty list in inverted map"