packages feed

toysolver-0.0.2: src/Converter/CNF2LP.hs

{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Converter.CNF2LP
-- Copyright   :  (c) Masahiro Sakai 2011-2012
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  experimental
-- Portability :  portable
--
-----------------------------------------------------------------------------
module Converter.CNF2LP
  ( ObjType (..)
  , convert 
  ) where

import Data.Array.IArray
import qualified Data.Set as Set
import qualified Data.Map as Map
import Data.OptDir
import qualified Text.LPFile as LPFile
import qualified Language.CNF.Parse.ParseDIMACS as DIMACS
import qualified SAT.Types as SAT
import Converter.ObjType

convert :: ObjType -> DIMACS.CNF -> (LPFile.LP, Map.Map LPFile.Var Rational -> SAT.Model)
convert objType cnf = (lp, mtrans)
  where
    lp = LPFile.LP
      { LPFile.variables = Set.fromList vs
      , LPFile.dir = dir
      , LPFile.objectiveFunction = (Nothing, obj)
      , LPFile.constraints = cs
      , LPFile.bounds = Map.empty
      , LPFile.integerVariables = Set.empty
      , LPFile.binaryVariables = Set.fromList vs
      , LPFile.semiContinuousVariables = Set.empty
      , LPFile.sos = []
      }
    mtrans m =
      array (1, DIMACS.numVars cnf)
        [ (i, val)
        | i <- [1 .. DIMACS.numVars cnf]
        , let val =
                case m Map.! ("x" ++ show i) of
                  0 -> False
                  1 -> True
                  v0 -> error (show v0 ++ " is neither 0 nor 1")
        ]
  
    dir = if objType == ObjMaxZero then OptMin else OptMax
    obj = if objType == ObjNone then [LPFile.Term 0 (take 1 vs)] else [LPFile.Term 1 [v] | v <- vs]
    vs = if DIMACS.numVars cnf == 0
         then ["x0"]
         else ["x" ++ show i | i <- [1 .. DIMACS.numVars cnf]]
    cs = do
      cl <- DIMACS.clauses cnf      
      let (lhs,n) = foldr f ([], 0) (elems cl)
      return $ LPFile.Constraint
        { LPFile.constrType      = LPFile.NormalConstraint
        , LPFile.constrLabel     = Nothing
        , LPFile.constrIndicator = Nothing
        , LPFile.constrBody      = (lhs, LPFile.Ge, fromIntegral $ 1 - n)
        }
    f :: Int -> (LPFile.Expr,Integer) -> (LPFile.Expr,Integer)
    f lit (es,n) =
      if lit > 0
      then (LPFile.Term 1 [v] : es, n)
      else (LPFile.Term (-1) [v] : es, n+1)
      where v = "x" ++ show (abs lit)