packages feed

cplex-hs-0.3.0.0: src/LSolver/Problems/MinCostMulticom.hs

module LSolver.Problems.MinCostMulticom(generateLinearProblem, getAllPaths, buildAdjacency) where

import qualified Data.Vector as V
import qualified Data.Map as M
import Data.Vector((!),(//))
import Control.Monad
import LSolver.Bindings
import LSolver.Backend.Cplex

generateLinearProblem :: [(Int, Int)] -> [Double] -> [Double] -> [(Int, Int)] -> [Double]
            -> LinearProblem Int
generateLinearProblem edges costs capacity commodities demand =
    let
        n = length costs
        varRange = (0,n-1)
        adj = buildAdjacency edges
        (pathsAll, pathsCom) = genPaths adj commodities
        edgeCosts = genEdgeCosts edges costs
        edgeCapacity = genEdgeCapacity edges capacity
        objFunc = genObjectiveFunction pathsAll edgeCosts
        constraints = genConstraints pathsAll pathsCom edges edgeCapacity demand
    in LP objFunc constraints (standardBounds varRange)

loadFromFile :: String -> IO (LinearProblem Int)
loadFromFile fileName = do
    contents <- readFile fileName
    let [n, e, cost, cap, k, d] = lines $ contents :: [String]
    return $ generateLinearProblem (read e) (read cost) (read cap) (read k) (read d)

genObjectiveFunction pathsAll edgeCosts = Minimize $ zipWith (:#) (V.toList $ (\p -> sum $
                    (\a -> edgeCosts M.! a) <$> p) <$> pathsAll) [0..]

genEdgeCosts edges costs = M.fromList $ zip edges costs
genEdgeCapacity edges capacity = M.fromList $ zip edges capacity
genPaths adj commodities =
    let pathsCom = V.fromList $ map V.fromList $ map (\(s,t) -> getAllPaths s t adj) commodities
        pathsAll = V.foldr (V.++) V.empty pathsCom
    in (pathsAll, pathsCom)

genConstraints pathsAll pathsCom edges edgeCapacity demand =
    Constraints $ (genConstraints1 pathsCom pathsAll) ++ (genConstraints2 pathsAll edges)
    where
        genConstraints1 pathsCom pathsAll = zipWith (:=) (createOneArrs pathsCom pathsAll) demand

        genConstraints2 pathsAll edges = let pathsEdges = genPathEdges pathsAll edges in
                 map (\a ->
                    (map (\p -> 1:#p) $
                  pathsEdges M.! a) :< (edgeCapacity M.! a)   ) edges
-- helper, generates ones and zeroes vector to indicate active path
createOneArrs pathsCom pathsAll = map (\xs -> map (\i -> 1:#i) xs) $ createOneArrs' (V.toList pathsCom) 0 (length pathsAll)
createOneArrs' [] _ _ = []
createOneArrs' (p:ps) i n = let pn = V.length p in
    [i..i+pn-1]:createOneArrs' ps (pn+i) n


-- Hilfe functions
genPathEdges paths edges = foldr (\p m -> genPathEdges' p edges m) M.empty [0..V.length paths-1]
    where
        genPathEdges' pid [] m = m
        genPathEdges' pid (e:edges) m
            | e `elem` (paths ! pid) = genPathEdges' pid edges $ M.insertWith (\new old -> if old /= new then old ++ new else new) e [pid] m
            | otherwise = genPathEdges' pid edges m


getBounds :: Ord a => [(a,a)] -> (a,a)
getBounds edges =   let lst = [fst,snd] <*> edges
                    in (minimum lst, maximum lst)


getAllPaths s e adj =   let paths = filter (\xs -> e == last xs) $ filter (\(x:xs) -> x == s) $ getAllPaths' s e adj []
                        in (\p -> tail $ zip (0:p) p) <$> paths
getAllPaths' s e _ paths
    | s == e = map (\a -> reverse $ e:a) paths
getAllPaths' s e adj paths =
                        let startedPaths = liftM (s:) ([]:paths)
                            ps = filter (/= startedPaths) $ (\n -> getAllPaths' n e adj startedPaths) <$> neighbors
                        in concat $ ps
    where neighbors = adj ! s


--buildAdjacency :: [(Int, Int)] -> Array (Int, Int) Int
buildAdjacency edges = let n = length edges
    in buildAdjacency' edges $ V.replicate n []
buildAdjacency' [] arr = arr
buildAdjacency' ((i,j):cs) arr =
    let cur = (arr ! i)
        added = if j `elem` cur then cur else j  : cur
    in buildAdjacency' cs (arr // [(i,added)])