packages feed

glpk-headers 0.4.1 → 0.5.0

raw patch · 9 files changed

+194/−210 lines, 9 filesdep +hspecdep −tastydep −tasty-discoverdep −tasty-hunitdep ~basedep ~derive-storablePVP ok

version bump matches the API change (PVP)

Dependencies added: hspec

Dependencies removed: tasty, tasty-discover, tasty-hunit

Dependency ranges changed: base, derive-storable

API changes (from Hackage documentation)

- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkBacktrackingTechnique
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkBinarization
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkBranchingTechnique
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkCliqueCuts
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkConstraintOrigin
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkControl
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkCoverCuts
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkCutType
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkFactorizationType
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkFeasibilityPump
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkGomoryCuts
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkMIRCuts
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkMessageLevel
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkPreCholeskyOrdering
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkPreProcessingTechnique
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkPresolve
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkPricing
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkProximitySearch
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkRatioTest
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkSimpleRounding
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkSimplexMethod
- Math.Programming.Glpk.Header: instance Foreign.Storable.Generic.Internal.GStorable Math.Programming.Glpk.Header.GlpkVariableType
+ Math.Programming.Glpk.Header: glp_error_hook :: FunPtr (Ptr a -> IO CInt) -> Ptr a -> IO ()
+ Math.Programming.Glpk.Header: instance GHC.Generics.Generic Math.Programming.Glpk.Header.GlpkConstraintOrigin
+ Math.Programming.Glpk.Header: mkHaskellErrorHook :: (Ptr a -> IO CInt) -> IO (FunPtr (Ptr a -> IO CInt))
+ Math.Programming.Glpk.Header: mkHaskellMIPCallback :: (Ptr (GlpkTree a) -> Ptr a -> IO ()) -> IO (FunPtr (Ptr (GlpkTree a) -> Ptr a -> IO ()))
+ Math.Programming.Glpk.Header: mkHaskellTermHook :: (Ptr a -> IO CInt) -> IO (FunPtr (Ptr a -> IO CInt))

Files

ChangeLog.md view
@@ -1,3 +1,20 @@-# Changelog for GlpkHs+# Changelog for `glpk-headers` -## Unreleased changes+## [Unreleased]++- The `mkHaskellErrorHook` function was added to+  `Math.Programming.Glpk.Headers` to support writing GLPK error+  handlers.++- The `mkHaskellTermHook` function was added to+  `Math.Programming.Glpk.Headers` to support writing GLPK terminal+  callback hooks.++- The `mkHaskellMIPCallback` function was added to+  `Math.Programming.Glpk.Headers` to support writing MIP callback+  functions.++## [0.5.0] -- 2021-03-07++* Switch from Stack to Cabal (and Nix).+* Update to GHC 8.8.4
LICENSE view
@@ -1,4 +1,4 @@-Copyright Patrick Steele (c) 2018+Copyright (c) 2018-2023, Patrick Steele  All rights reserved. 
README.md view
@@ -4,3 +4,7 @@ [GLPK](https://www.gnu.org/software/glpk/). No attempt is made to make these bindings conform to Haskell norms --- they are a direct translation from the GLPK header file.++## Requirements++* GLPK >=4.65
glpk-headers.cabal view
@@ -1,51 +1,37 @@-cabal-version:      1.12-name:               glpk-headers-version:            0.4.1-license:            BSD3-license-file:       LICENSE-copyright:          2018 Patrick Steele-maintainer:         steele.pat@gmail.com-author:             Patrick Steele-homepage:           https://github.com/prsteele/glpk-headers-haskell#readme-bug-reports:        https://github.com/prsteele/glpk-headers-haskell/issues-synopsis:           Low-level Haskell bindings to GLPK.-description:-    Please see the README on GitHub at <https://github.com/prsteele/glpk-headers-haskell#readme>--category:           Math-build-type:         Simple-extra-source-files:-    README.md-    ChangeLog.md+cabal-version:       2.4+name:                glpk-headers+version:             0.5.0+synopsis:            Low-level Haskell bindings to the GLPK library+description:         Please see the README on GitHub at <https://github.com/prsteele/math-programming/blob/main/glpk-headers/README.md>+bug-reports:         https://github.com/prsteele/math-programming/issues+license:             BSD-3-Clause+license-file:        LICENSE+author:              Patrick Steele+maintainer:          steele.pat@gmail.com+copyright:           2018-2023, Patrick Steele+category:            Math+build-type:          Simple+extra-source-files:  README.md, ChangeLog.md  source-repository head-    type:     git-    location: https://github.com/prsteele/glpk-headers-haskell+  type:     git+  location: https://github.com/prsteele/math-programming  library-    exposed-modules:  Math.Programming.Glpk.Header-    hs-source-dirs:   src-    other-modules:    Paths_glpk_headers-    default-language: Haskell2010-    build-depends:-        base >=4.7 && <5,-        derive-storable >=0.1.2.0 && <0.3--test-suite GlpkHs-test-    type:             exitcode-stdio-1.0-    main-is:          Driver.hs-    hs-source-dirs:   test-    other-modules:-        Diet-        Paths_glpk_headers+  default-language:  Haskell2010+  hs-source-dirs:    src+  exposed-modules:   Math.Programming.Glpk.Header+  extra-libraries:   glpk+  build-depends:     base            <5+                   , derive-storable ^>=0.3 -    default-language: Haskell2010-    extra-libraries:  glpk-    ghc-options:      -threaded -rtsopts -with-rtsopts=-N-    build-depends:-        base >=4.7 && <5,-        derive-storable >=0.1.2.0 && <0.3,-        glpk-headers -any,-        tasty >=1.2.3 && <1.3,-        tasty-discover >=4.2.1 && <4.3,-        tasty-hunit >=0.10.0.2 && <0.11+test-suite glpk-headers-haskell-test-suite+  default-language:  Haskell2010+  type:              exitcode-stdio-1.0+  main-is:           Spec.hs+  hs-source-dirs:    test+  ghc-options:       -threaded -with-rtsopts=-N+  other-modules:     Math.Programming.Glpk.HeadersSpec+  build-depends:     glpk-headers+                   , base  <5+                   , hspec ^>=2.8
src/Math/Programming/Glpk/Header.hsc view
@@ -399,6 +399,11 @@   , glp_config   , glp_term_out   , glp_term_hook+  , glp_error_hook+  -- ** Helper functions+  , mkHaskellErrorHook+  , mkHaskellTermHook+  , mkHaskellMIPCallback   ) where  import Data.Typeable@@ -805,7 +810,6 @@   = GlpkVariableType { fromGlpkVariableType :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -929,7 +933,6 @@   = GlpkMessageLevel { fromGlpkMessageLevel :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -951,7 +954,6 @@   = GlpkSimplexMethod { fromGlpkSimplexMethod :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -971,7 +973,6 @@   = GlpkPricing { fromGlpkPricing :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -991,7 +992,6 @@   = GlpkRatioTest { fromGlpkRatioTest :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1010,7 +1010,6 @@   = GlpkPreCholeskyOrdering { fromGlpkPreCholeskyOrdering :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1031,7 +1030,6 @@   = GlpkBranchingTechnique { fromGlpkBranchingTechnique :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1053,7 +1051,6 @@   = GlpkBacktrackingTechnique { fromGlpkBacktrackingTechnique :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1074,7 +1071,6 @@   = GlpkPreProcessingTechnique { fromGlpkPreProcessingTechnique :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1094,7 +1090,6 @@   = GlpkFeasibilityPump { fromGlpkFeasibilityPump :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1113,7 +1108,6 @@   = GlpkProximitySearch { fromGlpkProximitySearch :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1132,7 +1126,6 @@   = GlpkGomoryCuts { fromGlpkGomoryCuts :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1151,7 +1144,6 @@   = GlpkMIRCuts { fromGlpkMIRCuts :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1170,7 +1162,6 @@   = GlpkCoverCuts { fromGlpkCoverCuts :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1189,7 +1180,6 @@   = GlpkCliqueCuts { fromGlpkCliqueCuts :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1208,7 +1198,6 @@   = GlpkPresolve { fromGlpkPresolve :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1227,7 +1216,6 @@   = GlpkBinarization { fromGlpkBinarization :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1246,7 +1234,6 @@   = GlpkSimpleRounding { fromGlpkSimpleRounding :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1265,7 +1252,7 @@   = GlpkConstraintOrigin { fromGlpkConstraintOrigin :: CInt }   deriving     ( Eq-    , GStorable+    , Generic     , Ord     , Read     , Show@@ -1285,7 +1272,6 @@   = GlpkCutType { fromGlpkCutType :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1306,7 +1292,6 @@   = GlpkControl { fromGlpkControl :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -1500,7 +1485,6 @@   = GlpkFactorizationType { fromGlpkFactorizationType :: CInt }   deriving     ( Eq-    , GStorable     , Ord     , Read     , Show@@ -2760,3 +2744,17 @@   :: FunPtr (Ptr a -> CString -> IO CInt)   -> Ptr a   -> IO ()++foreign import ccall "glp_error_hook" glp_error_hook+  :: FunPtr (Ptr a -> IO CInt)+  -> Ptr a+  -> IO ()++foreign import ccall "wrapper"+  mkHaskellErrorHook :: (Ptr a -> IO CInt) -> IO (FunPtr (Ptr a -> IO CInt))++foreign import ccall "wrapper"+  mkHaskellTermHook :: (Ptr a -> IO CInt) -> IO (FunPtr (Ptr a -> IO CInt))++foreign import ccall "wrapper"+  mkHaskellMIPCallback :: (Ptr (GlpkTree a) -> Ptr a -> IO ()) -> IO (FunPtr (Ptr (GlpkTree a) -> Ptr a -> IO ()))
− test/Diet.hs
@@ -1,129 +0,0 @@-module Diet where--import           Control.Monad-import           Foreign.C.Types-import           Foreign.Ptr-import           Test.Tasty-import           Test.Tasty.HUnit-import           Text.Printf--import           Math.Programming.Glpk.Header--test_diet :: TestTree-test_diet = testGroup "Diet problem"-  [ testCase "Basic diet problem" basicDiet-  ]--data Food = Corn | Milk | Bread-  deriving-    ( Eq-    , Ord-    , Read-    , Show-    )--data Nutrient = Calories | VitaminA-  deriving-    ( Eq-    , Ord-    , Read-    , Show-    )--basicDiet :: Assertion-basicDiet =-  let-    cost :: Food -> CDouble-    cost Corn  = 0.18-    cost Milk  = 0.23-    cost Bread = 0.05--    nutrition :: Nutrient -> Food -> CDouble-    nutrition Calories Corn  = 72-    nutrition VitaminA Corn  = 107-    nutrition Calories Milk  = 121-    nutrition VitaminA Milk  = 500-    nutrition Calories Bread = 65-    nutrition VitaminA Bread = 0--    foods :: [Food]-    foods = [Corn, Milk, Bread]--    nutrients :: [Nutrient]-    nutrients = [Calories, VitaminA]--    maxServings :: CDouble-    maxServings = 10--    nutrientBounds :: Nutrient -> (CDouble, CDouble)-    nutrientBounds Calories = (2000, 2250)-    nutrientBounds VitaminA = (5000, 50000)--    numAmountVariables :: CInt-    numAmountVariables = fromIntegral $ length foods--    numNutrientConstraints :: CInt-    numNutrientConstraints = fromIntegral (length nutrients)--    expected :: Food -> CDouble-    expected Corn  = 1.94-    expected Milk  = 10-    expected Bread = 10--    expectedCost :: CDouble-    expectedCost = 3.15-  in do-    _ <- glp_term_out glpkOff-    problem <- glp_create_prob--    -- Create the decision variables-    amountStart <- glp_add_cols problem numAmountVariables-    let amounts = zip foods [amountStart..]--    -- Create the nutrient constraints-    nutrientConStart <- glp_add_rows problem numNutrientConstraints-    let nutrientConstraints = zip nutrients [nutrientConStart..]--    -- Set the objective-    glp_set_obj_dir problem glpkMin-    forM_ amounts $ \(food, column) ->-      glp_set_obj_coef problem column (cost food)--    -- Set the right-hand side of the nutrient constraints-    forM_ nutrientConstraints $ \(nutrient, row) -> do-      let (lower, upper) = nutrientBounds nutrient-      glp_set_row_bnds problem row  glpkBounded lower upper--    -- Set the body of the nutrient constraints-    forM_ nutrientConstraints $ \(nutrient, row) ->-      allocaGlpkArray (map snd amounts) $ \indices ->-        allocaGlpkArray (map (nutrition nutrient . fst) amounts) $ \coefs ->-          glp_set_mat_row problem row numAmountVariables indices coefs--    -- Ensure we don't have too many servings of any one food-    forM_ amounts $ \(_, column) ->-      glp_set_col_bnds problem column glpkBounded 0 maxServings--    -- Check that we reached optimality-    status <- glp_simplex problem nullPtr-    status @?= glpkSimplexSuccess--    -- Check the variable values-    forM_ amounts $ \(food, column) -> do-      x <- glp_get_col_prim problem column--      let correct = expected food-          msg = printf-                "Amount of %s should be about %.2f, but is %.3f"-                (show food)-                (realToFrac correct :: Double)-                (realToFrac x :: Double)-      assertBool msg (abs (x - correct) <= 1e-1)--    -- Check the objective value-    objective <- glp_get_obj_val problem-    let msg = printf-              "Objective should be about %.2f, but is %.3f"-              (realToFrac expectedCost :: Double)-              (realToFrac objective :: Double)-    assertBool msg (abs (objective - expectedCost) < 1e-1)
− test/Driver.hs
@@ -1,10 +0,0 @@-{-# OPTIONS_GHC -F -pgmF tasty-discover #-}-{-|-Module : Main--The entry point for tests. We use tasty-discover to automatically-discover test functions in this directory, so simply preface tasty-tests with test_ for tasty TestTrees, or prop_ for QuickCheck-properties.--}-module Main where
+ test/Math/Programming/Glpk/HeadersSpec.hs view
@@ -0,0 +1,115 @@+module Math.Programming.Glpk.HeadersSpec where++import Control.Monad+import Foreign.C.Types+import Foreign.Ptr+import Math.Programming.Glpk.Header+import Test.Hspec+import Text.Printf++spec :: Spec+spec =+  describe "GPLK bindings" $ do+    it "can solve the diet problem" basicDiet++data Food = Corn | Milk | Bread+  deriving+    ( Eq,+      Ord,+      Read,+      Show+    )++data Nutrient = Calories | VitaminA+  deriving+    ( Eq,+      Ord,+      Read,+      Show+    )++basicDiet :: IO ()+basicDiet =+  let cost :: Food -> CDouble+      cost Corn = 0.18+      cost Milk = 0.23+      cost Bread = 0.05++      nutrition :: Nutrient -> Food -> CDouble+      nutrition Calories Corn = 72+      nutrition VitaminA Corn = 107+      nutrition Calories Milk = 121+      nutrition VitaminA Milk = 500+      nutrition Calories Bread = 65+      nutrition VitaminA Bread = 0++      foods :: [Food]+      foods = [Corn, Milk, Bread]++      nutrients :: [Nutrient]+      nutrients = [Calories, VitaminA]++      maxServings :: CDouble+      maxServings = 10++      nutrientBounds :: Nutrient -> (CDouble, CDouble)+      nutrientBounds Calories = (2000, 2250)+      nutrientBounds VitaminA = (5000, 50000)++      numAmountVariables :: CInt+      numAmountVariables = fromIntegral $ length foods++      numNutrientConstraints :: CInt+      numNutrientConstraints = fromIntegral (length nutrients)++      expected :: Food -> CDouble+      expected Corn = 1.94+      expected Milk = 10+      expected Bread = 10++      expectedCost :: CDouble+      expectedCost = 3.15+   in do+        _ <- glp_term_out glpkOff+        problem <- glp_create_prob++        -- Create the decision variables+        amountStart <- glp_add_cols problem numAmountVariables+        let amounts = zip foods [amountStart ..]++        -- Create the nutrient constraints+        nutrientConStart <- glp_add_rows problem numNutrientConstraints+        let nutrientConstraints = zip nutrients [nutrientConStart ..]++        -- Set the objective+        glp_set_obj_dir problem glpkMin+        forM_ amounts $ \(food, column) ->+          glp_set_obj_coef problem column (cost food)++        -- Set the right-hand side of the nutrient constraints+        forM_ nutrientConstraints $ \(nutrient, row) -> do+          let (lower, upper) = nutrientBounds nutrient+          glp_set_row_bnds problem row glpkBounded lower upper++        -- Set the body of the nutrient constraints+        forM_ nutrientConstraints $ \(nutrient, row) ->+          allocaGlpkArray (map snd amounts) $ \indices ->+            allocaGlpkArray (map (nutrition nutrient . fst) amounts) $ \coefs ->+              glp_set_mat_row problem row numAmountVariables indices coefs++        -- Ensure we don't have too many servings of any one food+        forM_ amounts $ \(_, column) ->+          glp_set_col_bnds problem column glpkBounded 0 maxServings++        -- Check that we reached optimality+        status <- glp_simplex problem nullPtr+        status `shouldBe` glpkSimplexSuccess++        -- Check the variable values+        forM_ amounts $ \(food, column) -> do+          x <- glp_get_col_prim problem column+          abs (x - expected food) `shouldSatisfy` (<= 1e-1)++        -- Check the objective value+        objective <- glp_get_obj_val problem+        abs (objective - expectedCost) `shouldSatisfy` (<= 1e-1)
+ test/Spec.hs view
@@ -0,0 +1,3 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}++module Spec where