• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

msakai / haskell-MIP / 351

04 Jan 2026 10:32PM UTC coverage: 77.291% (+0.005%) from 77.286%
351

push

github

msakai
update ChangeLog for v0.2.0.1

1518 of 1964 relevant lines covered (77.29%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.82
/MIP/src/Numeric/Optimization/MIP/Base.hs
1
{-# OPTIONS_GHC -Wall #-}
2
{-# OPTIONS_HADDOCK show-extensions #-}
3
{-# LANGUAGE CPP #-}
4
{-# LANGUAGE FlexibleInstances #-}
5
{-# LANGUAGE MultiParamTypeClasses #-}
6
{-# LANGUAGE PatternSynonyms #-}
7
{-# LANGUAGE TypeFamilies #-}
8
{-# LANGUAGE ViewPatterns #-}
9
-----------------------------------------------------------------------------
10
-- |
11
-- Module      :  Numeric.Optimization.MIP.Base
12
-- Copyright   :  (c) Masahiro Sakai 2011-2019
13
-- License     :  BSD-style
14
--
15
-- Maintainer  :  masahiro.sakai@gmail.com
16
-- Stability   :  provisional
17
-- Portability :  non-portable
18
--
19
-- Mixed-Integer Programming Problems with some commmonly used extensions
20
--
21
-----------------------------------------------------------------------------
22
module Numeric.Optimization.MIP.Base
23
  (
24
  -- * Mixed-Integer Programming (MIP) problem specification
25

26
  -- ** MIP problems
27
    Problem (..)
28

29
  -- *** Set of variables
30
  , variables
31
  , continuousVariables
32
  , integerVariables
33
  , binaryVariables
34
  , semiContinuousVariables
35
  , semiIntegerVariables
36

37
  -- *** Variable's attributes
38
  , varTypes
39
  , varType
40
  , getVarType
41
  , varBounds
42
  , getBounds
43

44
  -- ** Variables
45
  , Var (Var)
46
  , varName
47
  , toVar
48
  , fromVar
49

50
  -- *** Variable types
51
  , VarType (..)
52

53
  -- *** Variable bounds
54
  , BoundExpr
55
  , Extended (..)
56
  , Bounds
57
  , defaultBounds
58
  , defaultLB
59
  , defaultUB
60

61
  -- ** Labels
62
  , Label
63

64
  -- ** Expressions
65
  , Expr (Expr)
66
  , varExpr
67
  , constExpr
68
  , terms
69
  , Term (..)
70

71
  -- ** Objective function
72
  , OptDir (..)
73
  , ObjectiveFunction (..)
74

75
  -- ** Constraints
76

77
  -- *** Linear (or Quadratic or Polynomial) constraints
78
  , Constraint (..)
79
  , (.==.)
80
  , (.<=.)
81
  , (.>=.)
82
  , RelOp (..)
83

84
  -- *** SOS constraints
85
  , SOSType (..)
86
  , SOSConstraint (..)
87

88
  -- * Solutions
89
  , Solution (..)
90
  , Status (..)
91
  , meetStatus
92

93
  -- * Evaluation
94
  , Tol (..)
95
  , zeroTol
96
  , Eval (..)
97

98
  -- * File I/O
99
  , FileOptions (..)
100
  , WriteSetting (..)
101

102
  -- * Utilities
103
  , Default (..)
104
  , Variables (..)
105
  , intersectBounds
106
  ) where
107

108
#if !MIN_VERSION_lattices(2,0,0)
109
import Algebra.Lattice
110
#endif
111
import Algebra.PartialOrd
112
import Control.Arrow ((***))
113
import Control.Monad
114
import Data.Default.Class
115
import Data.Foldable (toList)
116
import Data.Hashable
117
import Data.List (sortBy)
118
import Data.Map (Map)
119
import qualified Data.Map as Map
120
import Data.Ord (comparing)
121
import Data.Sequence (Seq)
122
import qualified Data.Sequence as Seq
123
import Data.Set (Set)
124
import qualified Data.Set as Set
125
import Data.Interned (intern, unintern)
126
import Data.Interned.Text
127
import Data.ExtendedReal
128
import Data.OptDir
129
import Data.String
130
import qualified Data.Text as T
131
import System.IO (TextEncoding)
132

133
infix 4 .<=., .>=., .==.
134

135
-- ---------------------------------------------------------------------------
136

137
-- | A problem instance
138
data Problem c
139
  = Problem
140
  { name :: Maybe T.Text
1✔
141
    -- ^ Problem name
142
  , objectiveFunction :: ObjectiveFunction c
1✔
143
    -- ^ Objective functions of the problem
144
  , constraints :: [Constraint c]
1✔
145
    -- ^ Constraints of the problem
146
    --
147
    -- Indicator constraints and lazy constraints are included in this list.
148
  , sosConstraints :: [SOSConstraint c]
1✔
149
    -- ^ Special ordered sets
150
  , userCuts :: [Constraint c]
1✔
151
    -- ^ User cuts
152
  , varDomains :: Map Var (VarType, Bounds c)
1✔
153
    -- ^ Variable types and their bounds
154
  }
155
  deriving (Show, Eq, Ord)
×
156

157
instance Default (Problem c) where
158
  def = Problem
1✔
159
        { name = Nothing
1✔
160
        , objectiveFunction = def
1✔
161
        , constraints = []
1✔
162
        , sosConstraints = []
1✔
163
        , userCuts = []
1✔
164
        , varDomains = Map.empty
×
165
        }
166

167
instance Functor Problem where
×
168
  fmap f prob =
×
169
    prob
×
170
    { objectiveFunction = fmap f (objectiveFunction prob)
×
171
    , constraints       = map (fmap f) (constraints prob)
×
172
    , sosConstraints    = map (fmap f) (sosConstraints prob)
×
173
    , userCuts          = map (fmap f) (userCuts prob)
×
174
    , varDomains        = fmap (id *** (fmap f *** fmap f)) (varDomains prob)
×
175
    }
176

177
-- | Types of variables
178
--
179
-- This is equivalent to:
180
--
181
-- @
182
-- 'fmap' 'fst' . 'varDomains'
183
-- @
184
varTypes :: Problem c -> Map Var VarType
185
varTypes = fmap fst . varDomains
×
186

187
{-# DEPRECATED varType "Use varTypes instead" #-}
188
-- | Types of variables
189
--
190
-- Deprecated alias of 'varTypes'.
191
varType :: Problem c -> Map Var VarType
192
varType = varTypes
×
193

194
-- | Bounds of variables
195
--
196
-- This is equivalent to:
197
--
198
-- @
199
-- 'fmap' 'snd' . 'varDomains'
200
-- @
201
varBounds :: Problem c -> Map Var (Bounds c)
202
varBounds = fmap snd . varDomains
1✔
203

204
-- | Label used for naming various elements of t'Problem'
205
type Label = T.Text
206

207
-- ---------------------------------------------------------------------------
208

209
-- | Variables used in problems
210
newtype Var = Var' InternedText
211
  deriving Eq
1✔
212

213
pattern Var :: T.Text -> Var
214
pattern Var s <- Var' (unintern -> s) where
1✔
215
  Var s = Var' (intern s)
1✔
216

217
{-# COMPLETE Var #-}
218

219
instance IsString Var where
220
  fromString = Var' . fromString
1✔
221

222
instance Ord Var where
×
223
  compare (Var' a) (Var' b)
1✔
224
    | a == b = EQ
1✔
225
    | otherwise = compare (unintern a) (unintern b)
×
226

227
instance Show Var where
×
228
  showsPrec d (Var x) = showsPrec d x
×
229

230
instance Hashable Var where
×
231
#if MIN_VERSION_intern(0,9,3)
232
  hashWithSalt salt (Var' x) = hashWithSalt salt x
×
233
#else
234
  hashWithSalt salt (Var' x) = hashWithSalt salt (internedTextId x)
235
#endif
236

237
-- | Variable's name
238
varName :: Var -> T.Text
239
varName (Var s) = s
1✔
240

241
{-# DEPRECATED toVar "Use fromString function or Var pattern instead" #-}
242
-- | convert a string into a variable
243
toVar :: String -> Var
244
toVar = fromString
×
245

246
{-# DEPRECATED fromVar "Use varName function or Var pattern instead" #-}
247
-- | convert a variable into a string
248
fromVar :: Var -> String
249
fromVar (Var s) = T.unpack s
×
250

251
-- | Type of variables
252
--
253
-- Variables can take values depending on their types and their bounds ('Bounds').
254
data VarType
255
  = ContinuousVariable     -- ^ can take values from \(\{x \in \mathbb{R} \mid L \le x \le U\}\)
256
  | IntegerVariable        -- ^ can take values from \(\{x \in \mathbb{Z} \mid L \le x \le U\}\)
257
  | SemiContinuousVariable -- ^ can take values from \(\{0\} \cup \{x \in \mathbb{R} \mid L \le x \le U\}\)
258
  | SemiIntegerVariable    -- ^ can take values from \(\{0\} \cup \{x \in \mathbb{Z} \mid L \le x \le U\}\)
259
  deriving (Eq, Ord, Show)
×
260

261
instance Default VarType where
262
  def = ContinuousVariable
×
263

264
-- | looking up bounds for a variable
265
getVarType :: Problem c -> Var -> VarType
266
getVarType mip v =
1✔
267
  case Map.lookup v (varDomains mip) of
1✔
268
    Just (vt, _) -> vt
1✔
269
    Nothing -> def
×
270

271
-- | type for representing lower/upper bound of variables
272
type BoundExpr c = Extended c
273

274
-- | type for representing lower/upper bound of variables
275
type Bounds c = (BoundExpr c, BoundExpr c)
276

277
-- | default bounds
278
defaultBounds :: Num c => Bounds c
279
defaultBounds = (defaultLB, defaultUB)
1✔
280

281
-- | default lower bound (0)
282
defaultLB :: Num c => BoundExpr c
283
defaultLB = Finite 0
1✔
284

285
-- | default upper bound (+∞)
286
defaultUB :: BoundExpr c
287
defaultUB = PosInf
1✔
288

289
-- | looking up bounds for a variable
290
getBounds :: Num c => Problem c -> Var -> Bounds c
291
getBounds mip v =
1✔
292
  case Map.lookup v (varDomains mip) of
1✔
293
    Just (_, bs) -> bs
1✔
294
    Nothing -> defaultBounds
×
295

296
-- | Intersection of two 'Bounds'
297
intersectBounds :: Ord c => Bounds c -> Bounds c -> Bounds c
298
intersectBounds (lb1,ub1) (lb2,ub2) = (max lb1 lb2, min ub1 ub2)
×
299

300
-- ---------------------------------------------------------------------------
301

302
-- | Arithmetic expressions
303
--
304
-- Essentialy an expression is a sequence of t'Term's.
305
newtype Expr c = Expr' (Seq (Term c))
306
  deriving (Eq, Ord)
×
307

308
pattern Expr :: [Term c] -> Expr c
309
pattern Expr ts <- Expr' (toList -> ts) where
1✔
310
  Expr ts = Expr' (Seq.fromList ts)
1✔
311

312
{-# COMPLETE Expr #-}
313

314
instance Show c => Show (Expr c) where
×
315
  showsPrec d (Expr ts) = showParen (d > app_prec) $
×
316
    showString "Expr " . showsPrec (app_prec+1) ts
×
317
    where
318
      app_prec = 10
×
319

320
-- | Variable expression
321
varExpr :: Num c => Var -> Expr c
322
varExpr v = Expr' $ Seq.singleton $ Term 1 [v]
1✔
323

324
-- | Constant expression
325
constExpr :: (Eq c, Num c) => c -> Expr c
326
constExpr 0 = Expr' Seq.empty
1✔
327
constExpr c = Expr' $ Seq.singleton $ Term c []
1✔
328

329
-- | Terms of an expression
330
terms :: Expr c -> [Term c]
331
terms (Expr ts) = ts
1✔
332

333
instance Num c => Num (Expr c) where
1✔
334
  Expr' e1 + Expr' e2 = Expr' (e1 <> e2)
1✔
335
  Expr e1 * Expr e2 = Expr [Term (c1*c2) (vs1 ++ vs2) | Term c1 vs1 <- e1, Term c2 vs2 <- e2]
1✔
336
  negate (Expr' e) = Expr' $ fmap (\(Term c vs) -> Term (-c) vs) e
1✔
337
  abs = id
×
338
  signum _ = 1
×
339
  fromInteger 0 = Expr []
1✔
340
  fromInteger c = Expr [Term (fromInteger c) []]
1✔
341

342
instance Functor Expr where
×
343
  fmap f (Expr' ts) = Expr' $ fmap (fmap f) ts
×
344

345
-- | Split an expression into an expression without constant term and a constant
346
splitConst :: Num c => Expr c -> (Expr c, c)
347
splitConst (Expr' ts) = (e2, c2)
1✔
348
  where
349
    p (Term _ (_:_)) = True
1✔
350
    p _ = False
×
351
    e2 = Expr' $ Seq.filter p ts
1✔
352
    c2 = sum [c | Term c [] <- toList ts]
×
353

354
-- | terms
355
data Term c = Term c [Var]
356
  deriving (Eq, Ord, Show)
×
357

358
instance Functor Term where
×
359
  fmap f (Term c vs) = Term (f c) vs
×
360

361
-- ---------------------------------------------------------------------------
362

363
-- | objective function
364
data ObjectiveFunction c
365
  = ObjectiveFunction
366
  { objLabel :: Maybe Label
1✔
367
  , objDir :: OptDir
1✔
368
  , objExpr :: Expr c
1✔
369
  }
370
  deriving (Eq, Ord, Show)
×
371

372
instance Default (ObjectiveFunction c) where
373
  def =
1✔
374
    ObjectiveFunction
1✔
375
    { objLabel = Nothing
1✔
376
    , objDir = OptMin
1✔
377
    , objExpr = Expr []
1✔
378
    }
379

380
instance Functor ObjectiveFunction where
×
381
  fmap f obj = obj{ objExpr = fmap f (objExpr obj) }
×
382

383
-- ---------------------------------------------------------------------------
384

385
-- | Constraint
386
--
387
-- In the most general case, it is of the form @x = v → L ≤ e ≤ U@.
388
data Constraint c
389
  = Constraint
390
  { constrLabel     :: Maybe Label
1✔
391
    -- ^ name of the constraint
392
  , constrIndicator :: Maybe (Var, c)
1✔
393
    -- ^ @x = v@ (v is either 0 or 1)
394
  , constrExpr      :: Expr c
1✔
395
    -- ^ expression @e@
396
  , constrLB        :: BoundExpr c
1✔
397
    -- ^ lower bound @L@
398
  , constrUB        :: BoundExpr c
1✔
399
    -- ^ upper bound @U@
400
  , constrIsLazy    :: Bool
1✔
401
    -- ^ if it is set to @True@, solver can delay adding the constraint until the constraint is violated.
402
  }
403
  deriving (Eq, Ord, Show)
×
404

405
-- | Equality constraint.
406
(.==.) :: Num c => Expr c -> Expr c -> Constraint c
407
lhs .==. rhs =
×
408
  case splitConst (lhs - rhs) of
×
409
    (e, c) -> def{ constrExpr = e, constrLB = Finite (- c), constrUB = Finite (- c) }
×
410

411
-- | Inequality constraint (≤).
412
(.<=.) :: Num c => Expr c -> Expr c -> Constraint c
413
lhs .<=. rhs =
1✔
414
  case splitConst (lhs - rhs) of
1✔
415
    (e, c) -> def{ constrExpr = e, constrUB = Finite (- c) }
1✔
416

417
-- | Inequality constraint (≥).
418
(.>=.) :: Num c => Expr c -> Expr c -> Constraint c
419
lhs .>=. rhs =
×
420
  case splitConst (lhs - rhs) of
×
421
    (e, c) -> def{ constrExpr = e, constrLB = Finite (- c) }
×
422

423
instance Default (Constraint c) where
424
  def = Constraint
1✔
425
        { constrLabel = Nothing
1✔
426
        , constrIndicator = Nothing
1✔
427
        , constrExpr = Expr []
×
428
        , constrLB = NegInf
1✔
429
        , constrUB = PosInf
×
430
        , constrIsLazy = False
1✔
431
        }
432

433
instance Functor Constraint where
×
434
  fmap f c =
×
435
    c
×
436
    { constrIndicator = fmap (id *** f) (constrIndicator c)
×
437
    , constrExpr = fmap f (constrExpr c)
×
438
    , constrLB = fmap f (constrLB c)
×
439
    , constrUB = fmap f (constrUB c)
×
440
    }
441

442
-- | relational operators
443
data RelOp
444
  = Le  -- ^ (≤)
445
  | Ge  -- ^ (≥)
446
  | Eql -- ^ (=)
447
  deriving (Eq, Ord, Enum, Show)
×
448

449
-- ---------------------------------------------------------------------------
450

451
-- | types of SOS (special ordered sets) constraints
452
data SOSType
453
  = S1 -- ^ Type 1 SOS constraint
454
  | S2 -- ^ Type 2 SOS constraint
455
  deriving (Eq, Ord, Enum, Show, Read)
×
456

457
-- | SOS (special ordered sets) constraints
458
data SOSConstraint c
459
  = SOSConstraint
460
  { sosLabel :: Maybe Label
1✔
461
  , sosType  :: SOSType
1✔
462
  , sosBody  :: [(Var, c)]
1✔
463
  }
464
  deriving (Eq, Ord, Show)
×
465

466
instance Functor SOSConstraint where
×
467
  fmap f c = c{ sosBody = map (id *** f) (sosBody c) }
×
468

469
-- ---------------------------------------------------------------------------
470

471
-- | MIP status with the following partial order:
472
--
473
-- <<doc-images/MIP-Status-diagram.png>>
474
data Status
475
  = StatusUnknown
476
  | StatusFeasible
477
  | StatusOptimal
478
  | StatusInfeasibleOrUnbounded
479
  | StatusInfeasible
480
  | StatusUnbounded
481
  deriving (Eq, Ord, Enum, Bounded, Show)
×
482

483
instance PartialOrd Status where
×
484
  leq a b = (a,b) `Set.member` rel
1✔
485
    where
486
      rel = unsafeLfpFrom rel0 $ \r ->
1✔
487
        Set.union r (Set.fromList [(x,z) | (x,y) <- Set.toList r, (y',z) <- Set.toList r, y == y'])
1✔
488
      rel0 = Set.fromList $
1✔
489
        [(x,x) | x <- [minBound .. maxBound]] ++
1✔
490
        [ (StatusUnknown, StatusFeasible)
1✔
491
        , (StatusUnknown, StatusInfeasibleOrUnbounded)
1✔
492
        , (StatusFeasible, StatusOptimal)
1✔
493
        , (StatusFeasible, StatusUnbounded)
1✔
494
        , (StatusInfeasibleOrUnbounded, StatusUnbounded)
1✔
495
        , (StatusInfeasibleOrUnbounded, StatusInfeasible)
1✔
496
        ]
497

498
-- | /meet/ (greatest lower bound) operator of the partial order of 'Status' type.
499
--
500
-- If the version of @lattices@ is \<2, then @MeetSemiLattice@ instance can also be used.
501
meetStatus :: Status -> Status -> Status
502
StatusUnknown `meetStatus` _b = StatusUnknown
1✔
503
StatusFeasible `meetStatus` b
504
  | StatusFeasible `leq` b = StatusFeasible
1✔
505
  | otherwise = StatusUnknown
×
506
StatusOptimal `meetStatus` StatusOptimal = StatusOptimal
1✔
507
StatusOptimal `meetStatus` b
508
  | StatusFeasible `leq` b = StatusFeasible
1✔
509
  | otherwise = StatusUnknown
×
510
StatusInfeasibleOrUnbounded `meetStatus` b
511
  | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded
1✔
512
  | otherwise = StatusUnknown
×
513
StatusInfeasible `meetStatus` StatusInfeasible = StatusInfeasible
1✔
514
StatusInfeasible `meetStatus` b
515
  | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded
1✔
516
  | otherwise = StatusUnknown
×
517
StatusUnbounded `meetStatus` StatusUnbounded = StatusUnbounded
1✔
518
StatusUnbounded `meetStatus` b
519
  | StatusFeasible `leq` b = StatusFeasible
1✔
520
  | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded
1✔
521
  | otherwise = StatusUnknown
×
522

523
#if !MIN_VERSION_lattices(2,0,0)
524

525
instance MeetSemiLattice Status where
526
  meet = meetStatus
527

528
#endif
529

530

531
-- | Type for representing a solution of MIP problem.
532
data Solution r
533
  = Solution
534
  { solStatus :: Status
1✔
535
    -- ^ status
536
  , solObjectiveValue :: Maybe r
1✔
537
    -- ^ value of the objective function
538
  , solVariables :: Map Var r
1✔
539
    -- ^ variable assignments
540
  }
541
  deriving (Eq, Ord, Show)
×
542

543
instance Functor Solution where
×
544
  fmap f (Solution status obj vs) = Solution status (fmap f obj) (fmap f vs)
1✔
545

546
instance Default (Solution r) where
547
  def = Solution
1✔
548
        { solStatus = StatusUnknown
×
549
        , solObjectiveValue = Nothing
×
550
        , solVariables = Map.empty
×
551
        }
552

553
-- ---------------------------------------------------------------------------
554

555
-- | Tolerance for evaluating solutions against t'Problem'.
556
data Tol r
557
  = Tol
558
  { integralityTol :: r
1✔
559
    -- ^ If a value of integer variable is within this amount from its nearest
560
    -- integer, it is considered feasible.
561
  , feasibilityTol :: r
1✔
562
    -- ^ If the amount of violation of constraints is within this amount, it is
563
    -- considered feasible.
564
  , optimalityTol :: r
1✔
565
    -- ^ Feasiblity tolerance of dual constraints.
566
  }
567

568
-- | Defautl is @1e-6@ for the feasibility and optimality tolerances, and @1e-5@ for the integrality tolerance.
569
instance Fractional r => Default (Tol r) where
570
  def =
1✔
571
    Tol
1✔
572
    { integralityTol = 1e-5
1✔
573
    , feasibilityTol = 1e-6
1✔
574
    , optimalityTol = 1e-6
1✔
575
    }
576

577
-- | t'Tol' value with all tolerances are zero
578
zeroTol :: Fractional r => Tol r
579
zeroTol =
×
580
  Tol
×
581
  { integralityTol = 1e-5
×
582
  , feasibilityTol = 1e-6
×
583
  , optimalityTol = 1e-6
×
584
  }
585

586
-- | Type class for evaluation various elements of t'Problem' under
587
-- the given variable assignments.
588
class Eval r a where
589
  -- | Result type of 'eval'
590
  type Evaluated r a
591

592
  -- | Evaluate a value of type @a@ under given assignments and the tolerance
593
  eval :: Tol r -> Map Var r -> a -> Evaluated r a
594

595
instance Num r => Eval r Var where
596
  type Evaluated r Var = r
597
  eval _tol sol v =
1✔
598
    case Map.lookup v sol of
1✔
599
      Just val -> val
1✔
600
      Nothing -> 0
×
601

602
instance Num r => Eval r (Term r) where
603
  type Evaluated r (Term r) = r
604
  eval tol sol (Term c vs) = product (c : [eval tol sol v | v <- vs])
×
605

606
instance Num r => Eval r (Expr r) where
607
  type Evaluated r (Expr r) = r
608
  eval tol sol expr = sum [eval tol sol t | t <- terms expr]
×
609

610
instance Num r => Eval r (ObjectiveFunction r) where
611
  type Evaluated r (ObjectiveFunction r) = r
612
  eval tol sol obj = eval tol sol (objExpr obj)
×
613

614
instance (Num r, Ord r) => Eval r (Constraint r) where
615
  type Evaluated r (Constraint r) = Bool
616
  eval tol sol constr =
1✔
617
    not (evalIndicator (constrIndicator constr)) ||
1✔
618
    isInBounds tol (constrLB constr, constrUB constr) (eval tol sol (constrExpr constr))
×
619
    where
620
      evalIndicator Nothing = True
1✔
621
      evalIndicator (Just (v, val')) = isInBounds tol (Finite val', Finite val') (eval tol sol v)
×
622

623
instance (Num r, Ord r) => Eval r (SOSConstraint r) where
624
  type Evaluated r (SOSConstraint r) = Bool
625
  eval tol sol sos =
1✔
626
    case sosType sos of
1✔
627
      S1 -> length [() | val <- body, val] <= 1
×
628
      S2 -> f body
1✔
629
    where
630
      body = map (not . isInBounds tol (0, 0) . eval tol sol . fst) $ sortBy (comparing snd) $ (sosBody sos)
×
631
      f [] = True
×
632
      f [_] = True
1✔
633
      f (x1 : x2 : xs)
634
        | x1 = all not xs
1✔
635
        | otherwise = f (x2 : xs)
×
636

637
instance (RealFrac r) => Eval r (Problem r) where
638
  type Evaluated r (Problem r) = Maybe r
639
  eval tol sol prob = do
1✔
640
    forM_ (Map.toList (varDomains prob)) $ \(v, (vt, bounds)) -> do
1✔
641
      let val = eval tol sol v
×
642
      case vt of
1✔
643
        ContinuousVariable -> return ()
×
644
        SemiContinuousVariable -> return ()
×
645
        IntegerVariable -> guard $ isIntegral tol val
1✔
646
        SemiIntegerVariable -> guard $ isIntegral tol val
1✔
647
      case vt of
1✔
648
        ContinuousVariable -> guard $ isInBounds tol bounds val
1✔
649
        IntegerVariable -> guard $ isInBounds tol bounds val
1✔
650
        SemiIntegerVariable -> guard $ isInBounds tol (0,0) val || isInBounds tol bounds val
1✔
651
        SemiContinuousVariable -> guard $ isInBounds tol (0,0) val || isInBounds tol bounds val
1✔
652
    forM_ (constraints prob) $ \constr -> do
×
653
      guard $ eval tol sol constr
×
654
    forM_ (sosConstraints prob) $ \constr -> do
×
655
      guard $ eval tol sol constr
×
656
    return $ eval tol sol (objectiveFunction prob)
×
657

658
isIntegral :: RealFrac r => Tol r -> r -> Bool
659
isIntegral tol x = abs (x - fromIntegral (floor (x + 0.5) :: Integer)) <= integralityTol tol
1✔
660

661
isInBounds :: (Num r, Ord r) => Tol r -> Bounds r -> r -> Bool
662
isInBounds tol (lb, ub) x =
1✔
663
  lb - Finite (feasibilityTol tol) <= Finite x &&
1✔
664
  Finite x <= ub + Finite (feasibilityTol tol)
1✔
665

666
-- ---------------------------------------------------------------------------
667

668
-- | Type class for types that contain variables.
669
class Variables a where
670
  vars :: a -> Set Var
671

672
instance Variables a => Variables [a] where
673
  vars = Set.unions . map vars
1✔
674

675
instance (Variables a, Variables b) => Variables (Either a b) where
676
  vars (Left a)  = vars a
1✔
677
  vars (Right b) = vars b
1✔
678

679
instance Variables (Problem c) where
680
  vars = variables
1✔
681

682
instance Variables (Expr c) where
683
  vars (Expr e) = vars e
1✔
684

685
instance Variables (Term c) where
686
  vars (Term _ xs) = Set.fromList xs
1✔
687

688
instance Variables Var where
689
  vars v = Set.singleton v
×
690

691
instance Variables (ObjectiveFunction c) where
692
  vars ObjectiveFunction{ objExpr = e } = vars e
1✔
693

694
instance Variables (Constraint c) where
695
  vars Constraint{ constrIndicator = ind, constrExpr = e } = Set.union (vars e) vs2
1✔
696
    where
697
      vs2 = maybe Set.empty (Set.singleton . fst) ind
1✔
698

699
instance Variables (SOSConstraint c) where
700
  vars SOSConstraint{ sosBody = xs } = Set.fromList (map fst xs)
1✔
701

702
-- ---------------------------------------------------------------------------
703

704
-- | Set of variables of a t'Problem'
705
variables :: Problem c -> Set Var
706
variables mip = Map.keysSet $ varDomains mip
1✔
707

708
-- | Set of continuous variables of a t'Problem'
709
continuousVariables :: Problem c -> Set Var
710
continuousVariables mip = Map.keysSet $ Map.filter ((ContinuousVariable ==) . fst) (varDomains mip)
×
711

712
-- | Set of integer variables of a t'Problem'
713
integerVariables :: Problem c -> Set Var
714
integerVariables mip = Map.keysSet $ Map.filter ((IntegerVariable ==) . fst) (varDomains mip)
1✔
715

716
-- | Set of binary variables (integers variables with lower bound 0 and upper bound 1) of a t'Problem'
717
binaryVariables :: (Num c, Eq c) => Problem c -> Set Var
718
binaryVariables mip = Map.keysSet $ Map.filter p (varDomains mip)
×
719
  where
720
    p (IntegerVariable, (Finite 0, Finite 1)) = True
×
721
    p (_, _) = False
×
722

723
-- | Set of semi-continuous variables of a t'Problem'
724
semiContinuousVariables :: Problem c -> Set Var
725
semiContinuousVariables mip = Map.keysSet $ Map.filter ((SemiContinuousVariable ==) . fst) (varDomains mip)
1✔
726

727
-- | Set of semi-integer variables of a t'Problem'
728
semiIntegerVariables :: Problem c -> Set Var
729
semiIntegerVariables mip = Map.keysSet $ Map.filter ((SemiIntegerVariable ==) . fst) (varDomains mip)
1✔
730

731
-- ---------------------------------------------------------------------------
732

733
-- | Options for reading/writing problem files
734
data FileOptions
735
  = FileOptions
736
  { optFileEncoding :: Maybe TextEncoding
1✔
737
    -- ^ Text encoding used for file input/output
738
  , optMPSWriteObjSense :: WriteSetting
1✔
739
    -- ^ @OBJSENSE@ section in MPS file is an extention of MPS file
740
    -- format for specifying the direction of the objective function
741
    -- in MPS file. But not all solvers support it (e.g. GLPK-4.48
742
    -- does not support it).
743
    --
744
    -- This option controls whether the @OBJSENSE@ sections is written.
745
    -- If 'WriteIfNotDefault' is used, @OBJSENSE@ is written when the
746
    -- objective is maximization and @OBJSENSE@ is not written written
747
    -- when the objective is minimizing.
748
    --
749
    -- (Default: 'WriteIfNotDefault')
750
  , optMPSWriteObjName :: Bool
1✔
751
    -- ^ @OBJNAME@ section is an extention of MPS file format for
752
    -- selecting an objective function from among the free rows within
753
    -- a MPS file. Not all solver support it (e.g. GLPK-4.48
754
    -- does not support @OBJNAME@ it).
755
    --
756
    -- This option controls whether the @OBJNAME@ section is written.
757
    --
758
    -- (Default: 'True')
759
  } deriving (Show)
×
760

761
instance Default FileOptions where
762
  def =
1✔
763
    FileOptions
1✔
764
    { optFileEncoding = Nothing
1✔
765
    , optMPSWriteObjSense = WriteIfNotDefault
1✔
766
    , optMPSWriteObjName = True
1✔
767
    }
768

769
-- | Options for writing something of not
770
data WriteSetting
771
  = WriteAlways
772
  | WriteIfNotDefault
773
  | WriteNever
774
  deriving (Eq, Ord, Enum, Bounded, Show, Read)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc