packages feed

grasp-0.1.0.0: src/AM3/Instance.hs

-- | Data type representing an instance of the problem.

{-# LANGUAGE RecordWildCards #-}

module AM3.Instance (
  -- * Types
  Instance
  , CenterId
  , OfficeId
  , SegmentId
    -- * Construction
  , newInstance
    -- * Importing
  , fromFile
    -- * Exporting
  , toDat
  , toFile
    -- * Queries
  , isAllowed
  , numCenters
  , numOffices
  , numSegments
  , officeData
  , centerCost
  , segmentCost
  , segmentThreshold
  , centerCosts
  , segmentCosts
  , segmentThresholds
  , centers
  , offices
  , allowedConnections
  , centerCapacity
  , replications
  , centerCapacities
  , officeDatas
  , index2CO
   ) where

import AM3.DatParser
import Control.Arrow
import Data.Foldable
import Data.Ix
import Data.List.Split (chunksOf)
import Data.Vector.Unboxed (Vector, fromList, (!))
import qualified Data.Vector.Unboxed as V
import GRASP
import Text.Megaparsec hiding (fromFile)

-- | Identifier of a Center.
type CenterId = Int

-- | Identifier of an office.
type OfficeId = Int

-- | Identifier of a segment.
type SegmentId = Int

-- | Container for all parameters of the problem.
data Instance = Instance {
  _nC :: Int -- ^ Number of centers.
  , _nO :: Int -- ^ Number of offices.
  , _nP :: Int -- ^ Number of segments
  , _r :: Int -- ^ Number of replications.
  , _d :: Vector Int -- ^ Needed storage per office.
  , _k :: Vector Int -- ^ Center capacities.
  , _f :: Vector Cost -- ^ Fixed cost of each center.
  , _s :: Vector Cost -- ^ Segment costs.
  , _m :: Vector Int -- ^ Segments thresholds.
  , _u :: Vector Bool -- ^ Allowed connections. Stored by rows (row per center).
  , _us :: [(CenterId, OfficeId)] -- ^ List of allowed connections.
  } deriving (Show, Read)

newInstance ::
  Int -- ^ Number of replications.
  -> [Int] -- ^ Needed storage per office.
  -> [Int] -- ^ Center capacities.
  -> [Cost] -- ^ Fixed costs.
  -> [Cost] -- ^ Segment costs.
  -> [Int] -- ^ Segment thresholds.
  -> [Bool] -- ^ Allowed connections.
  -> Instance
newInstance rr dd kk ff ss mm uu =
  let x =
        Instance {
        _r = rr
        , _nC = length kk
        , _nO = length dd
        , _nP = length mm
        , _d = fromList dd
        , _k = fromList kk
        , _f = fromList ff
        , _s = fromList ss
        , _m = fromList mm
        , _u = fromList uu
        , _us = calcAllowedConnections x
        }
  in x

allowedConnections :: Instance -> [(CenterId, OfficeId)]
allowedConnections = _us

emptyInstance :: Instance
emptyInstance = newInstance 0 [] [] [] [] [] []

replications :: Instance -> Int
replications Instance{..} = _r

-- | List of all 'CenterId'.
centers :: Instance -> [CenterId]
centers Instance{..} = range (0, _nC - 1)

-- | List of all 'OfficeId'.
offices :: Instance -> [OfficeId]
offices Instance{..} = range (0, _nO - 1)


-- | True if the connection is allowed.
isAllowed :: Instance -> CenterId -> OfficeId -> Bool
isAllowed Instance{..} c o = _u ! ix
  where ix = _nO*c + o

-- | Unidimensional to bidimensional index.
index2CO :: Instance -> Int -> (CenterId, OfficeId)
index2CO Instance{..} = (`divMod`_nO)

-- | Cost for using a center.
centerCost :: Instance -> CenterId -> Cost
centerCost Instance{..} = (_f !)

-- | Vector of center costs.
centerCosts :: Instance -> Vector Cost
centerCosts = _f

centerCapacity :: Instance -> CenterId -> Int
centerCapacity Instance{..} = (_k !)

centerCapacities :: Instance -> Vector Int
centerCapacities = _k

officeDatas :: Instance -> Vector Int
officeDatas = _d

-- | Needed storage of an office.
officeData :: Instance -> OfficeId -> Int
officeData Instance{..} = (_d !)

-- | Number of offices.
numOffices :: Instance -> Int
numOffices Instance{..} = _nO

-- | Number of centers.
numCenters :: Instance -> Int
numCenters Instance{..} = _nC

-- | Number of segments
numSegments :: Instance -> Int
numSegments Instance{..} = _nP

-- | Cost of a segment.
segmentCost :: Instance -> SegmentId -> Int
segmentCost Instance{..} = (_s !)

segmentCosts :: Instance -> Vector Int
segmentCosts = _s

-- | Threshold of a segment.
segmentThreshold :: Instance -> SegmentId -> Int
segmentThreshold Instance{..} = (_m !)

segmentThresholds :: Instance -> Vector Int
segmentThresholds = _m

-- | List of all allowed connections.
calcAllowedConnections :: Instance -> [(CenterId, OfficeId)]
calcAllowedConnections i = [ (c, o) | c <- centers i, o <- offices i
                                    , isAllowed i c o ]

mkInstance :: [Assig] -> Instance
mkInstance as = let parsed = foldl' f emptyInstance as
                in parsed {_us = calcAllowedConnections parsed}
  where
    f i (Assig "r" (Num x)) = i {_r = x}
    f i (Assig "NO" (Num x)) = i {_nO = x}
    f i (Assig "NC" (Num x)) = i {_nC = x}
    f i (Assig "NP" (Num x)) = i {_nP = x}
    f i (Assig "d" xs) = i {_d = fromList $ flatten xs}
    f i (Assig "k" xs) = i {_k = fromList $ flatten xs}
    f i (Assig "f" xs) = i {_f = fromList $ flatten xs}
    f i (Assig "s" xs) = i {_s = fromList $ flatten xs}
    f i (Assig "u" xs) = i {_u = fromList $ map toEnum $ flatten xs}
    f i (Assig "m" xs) = i {_m = fromList $ flatten xs}
    f i _ = i


-- | Creates and 'Instance' from a @.dat@ file.
fromFile :: FilePath -> IO (Either ParseError Instance)
fromFile path = right mkInstance <$> parseFromFile pdat path

-- | Shows an 'Instance' in @.dat@ format.
toDat :: Instance -> String
toDat Instance{..} = unlines [
  assigInt "r" _r
  , assigInt "NO" _nO
  , assigInt "NP" _nP
  , assigInt "NC" _nC
  , assigVector "d" _d
  , assigVector "k" _k
  , assigVector "f" _f
  , assigVector "m" _m
  , assigVector "s" _s
  , assigVector2 "u" (V.map fromEnum _u) _nO
  ]
  where
    showAssig name val = name ++ " = " ++ val ++ ";"
    assigInt name x = showAssig name (show x)
    assigVector name v = showAssig name (noCommas $ show v)
    assigVector2 name v2 numCols =
      showAssig name (noCommas $ show (chunksOf numCols (V.toList v2)))
    noCommas = map (\case ',' -> ' '; x -> x)

-- | Exports an 'Instance' to a @.dat@ file.
toFile :: FilePath -> Instance -> IO ()
toFile p = writeFile p . toDat