Skip to content

In comment test facility crashes with foreign library. #2744

@MarkHorwedel

Description

@MarkHorwedel

Your environment

Which OS do you use:
MacBook Pro 14" (2021) with 16GB ram, Apple M1 pro, serial # VGWRXQ2KN7
Which LSP client (editor/plugin) do you use:
VSCode with Haskell-language-server-wrapper.
Describe your project (alternative: link to the project):

stack.yaml:

# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/

# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-18.24

# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
#   subdirs:
#   - auto-update
#   - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
# extra-deps:
# - acme-missiles-0.3
# - git: https://github.com/commercialhaskell/stack.git
#   commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
extra-deps:
- hmatrix-glpk-0.19.0.0@sha256:cf46c54bc3878bd207674ad88c3389083809f4df862df68ee64c6708dc517a52,1683
- Extra-1.46.3@sha256:eae163f553ba2f3252e2daf36c8ba3790c0b1435b359132f4e4a14976087827c,1412
- Unixutils-1.54.2@sha256:8061890ca05f13ee3bfb058b8a9b55eeb68410940c929b84f6086fe4750c19e6,1218

# Override default flag values for local packages and extra-deps
# flags: {}

# Extra package databases containing global packages
# extra-package-dbs: []

# Control whether we use the GHC we find on the path
system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.7"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
extra-include-dirs: [/opt/homebrew/Cellar/glpk/5.0/include]
extra-lib-dirs: [/opt/homebrew/Cellar/glpk/5.0/lib]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

package.yaml:

name:                Problem103
version:             0.1.0.0
github:              "githubuser/Problem103"
license:             BSD3
author:              "Author name here"
maintainer:          "example@example.com"
copyright:           "2022 Author name here"

extra-source-files:
- README.md
- ChangeLog.md

# Metadata used when publishing your package
# synopsis:            Short description of your package
# category:            Web

# To avoid duplicated efforts in documentation and dealing with the
# complications of embedding Haddock markup inside cabal files, it is
# common to point users to the README.md file.
description:         Please see the README on GitHub at <https://github.com/githubuser/Problem103#readme>

dependencies:
- base >= 4.7 && < 5
- hspec
- hmatrix-glpk
- containers
- data-ordlist

library:
  source-dirs: src

executables:
  Problem103-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    - -Wall
    dependencies:
    - Problem103

tests:
  Problem103-test:
    main:                Spec.hs
    source-dirs:         test
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    - -Wall
    dependencies:
    - Problem103

Lib.hs:

{-# LANGUAGE TupleSections #-}

module Lib (optimalSetOfLength, isSpecialSumSet, sumString) where

import Data.Function (on)
import Data.List
  ( elemIndex,
    find,
    minimumBy,
    sort,
    subsequences,
  )
import Data.List.Ordered (isSortedBy)
import Data.Maybe (catMaybes)
import Data.Tree (flatten, unfoldTree)
import GHC.Float (double2Int)
import Numeric.LinearProgramming
  ( Bound ((:>=:)),
    Constraints (General),
    Optimization (Minimize),
    Solution (Feasible, Optimal),
    exact,
  )

-- | Build string encoding of sum string.
-- >>> sumString [1,2,3]
-- <BLANKLINE>
-- ByteCodeLink.lookupIE
-- During interactive linking, GHCi couldn't find the following symbol:
--   hmatrixzmglpkzm0zi19zi0zi0zm4WWw7FtlyHzz9Q9FBl9Okvq_NumericziLinearProgramming_ZCzgzeZC_con_info or hmatrixzmglpkzm0zi19zi0zi0zm4WWw7FtlyHzz9Q9FBl9Okvq_NumericziLinearProgramming_ZCzgzeZC_static_info
-- This may be due to you not asking GHCi to load extra object files,
-- archives or DLLs needed by your current session.  Restart GHCi, specifying
-- the missing library using the -L/path/to/object/dir and -lmissinglibname
-- flags, or simply by naming the relevant files on the GHCi command line.
-- Alternatively, this link failure might indicate a bug in GHCi.
-- If you suspect the latter, please report this as a GHC bug:
--   https://www.haskell.org/ghc/reportabug
sumString ::
  -- | sum string to encode.
  [Int] ->
  String
sumString = foldr (\int str -> show int <> str) "" 

-- | Compute the integer set which minimizes the set sum,
-- subject to the constraints on a special subset set.
optimalSetOfLength ::
  -- | The cardinality of the desired set.
  Int ->
  Maybe [Int]
optimalSetOfLength cardinality
  | cardinality < 0 = Nothing
  | cardinality == 1 = Just [1]
  | cardinality == 2 = Just [1, 2]
  | otherwise =
    ( solutionToSet
        . minimumBy solutionCompare
        . catMaybes
        . flatten
        . unfoldTree (simplexSolutionUnfolder cardinality)
    )
      (orderBounds cardinality <> borderBounds cardinality)

-- | Bounds to ensure that n1 < n2 < ... < nn given cardinality
orderBounds ::
  -- | Cardinality
  Int ->
  [[(Double, Int)]]
orderBounds cardinality
  | cardinality < 1 = error "This shouldn't have happened"
  | cardinality == 1 = []
  | otherwise =
    (\c -> [(1, c), (negate 1, pred c)]) <$> [2 .. cardinality]

-- | Comparison of solutions.
solutionCompare ::
  -- | x
  Solution ->
  -- | y
  Solution ->
  Ordering
solutionCompare (Optimal o) (Optimal o') = compare o o'
solutionCompare (Optimal o) (Feasible f) = compare o f
solutionCompare (Feasible f) (Optimal o) = compare f o
solutionCompare (Feasible f) (Feasible f') = compare f f'
solutionCompare (Optimal _) _ = LT
solutionCompare _ (Optimal _) = GT
solutionCompare (Feasible _) _ = LT
solutionCompare _ (Feasible _) = GT
solutionCompare _ _ = EQ

-- | Build the tree of simplex solutions.
simplexSolutionUnfolder ::
  -- | Cardinality
  Int ->
  -- | Sparse matrix encoding the constraints for the simplex
  -- solver.
  [[(Double, Int)]] ->
  (Maybe Solution, [[[(Double, Int)]]])
simplexSolutionUnfolder cardinality constraints =
  case nextConstraintPair sol of
    [[]] ->
      ( case sol of
          Feasible _ -> Just sol
          Optimal _ -> Just sol
          _ -> Nothing,
        []
      )
    cons -> (Nothing, (: constraints) <$> cons)
  where
    sol :: Solution
    sol =
      exact
        (Minimize $ replicate cardinality 1)
        (General ((:>=: 1) <$> constraints))
        [1 :>=: 1]

-- | Is xs a special sum set?
isSpecialSumSet ::
  -- | xs:  Set to test
  [Int] ->
  Bool
isSpecialSumSet =
  isSortedBy ((<) `on` snd)
    . sort
    . ((\ys -> (length ys, sum ys)) <$>)
    . subsequences

-- | Next pair of constraints to force special set conditions.
nextConstraintPair ::
  -- | Solution for current set of constraints.
  Solution ->
  [[(Double, Int)]]
nextConstraintPair (Optimal (_, set)) =
  nextConstraintPairForSet set
nextConstraintPair (Feasible (_, set)) =
  nextConstraintPairForSet set
nextConstraintPair _ = []

-- | Compute next pair of constraints to drive solution to
-- special set.
nextConstraintPairForSet ::
  -- | Current solution set members
  [Double] ->
  [[(Double, Int)]]
nextConstraintPairForSet solution =
  case pairPairs of
    Nothing -> [[]]
    Just ((_, xs), (_, ys)) ->
      [ ((1,) <$> indices xs) <> ((negate 1,) <$> indices ys),
        ((negate 1,) <$> indices xs) <> ((1,) <$> indices ys)
      ]
  where
    pairs :: [(Double, [Double])]
    pairs =
      sort $ (\xs -> (sum xs, xs)) <$> subsequences solution
    pairPairs :: Maybe ((Double, [Double]), (Double, [Double]))
    pairPairs =
      find
        (\((s1, _), (s2, _)) -> s1 == s2)
        (zip pairs (tail pairs))
    indices :: [Double] -> [Int]
    indices xs =
      (catMaybes . (((1 +) <$>) . (`elemIndex` solution) <$>)) xs

-- | Unpack solution if it is optimal or feasible.
solutionToSet ::
  -- | Solution to unpack.
  Solution ->
  Maybe [Int]
solutionToSet (Optimal (_, xs)) = Just $ double2Int <$> xs
solutionToSet (Feasible (_, xs)) = Just $ double2Int <$> xs
solutionToSet _ = Nothing

-- | Build the constraints requiring subsets of greater
-- cardinality to have greater sums than those of lesser
-- cardinality.
borderBounds ::
  -- | Cardinality of total set
  Int ->
  [[(Double, Int)]]
borderBounds cardinality =
  borderBoundForLesserLength indices <$> [1 .. pred cardinality]
  where
    --  | List of (length, subset index) pairs
    indices :: [(Int, [Int])]
    indices =
      ( ((\xs -> (length xs, xs)) <$>)
          . subsequences
      )
        [1 .. cardinality]

-- | Return a constraint to ensure that the maximum sum among
-- sets of a given cardinality is less than the minimum sum of
-- the next higher cardinality.
borderBoundForLesserLength ::
  --  | List of (length, subset index) pairs
  [(Int, [Int])] ->
  -- | Cardinality for the lesser subset.
  Int ->
  [(Double, Int)]
borderBoundForLesserLength indices lesserCardinality =
  constraint
    ( minimum $
        filter
          (\(len, _) -> len == succ lesserCardinality)
          indices
    )
    ( maximum $
        filter
          (\(len, _) -> len == lesserCardinality)
          indices
    )

-- | form constraint coefficient set for greater and
-- lesser (length, index subset)
constraint ::
  -- | Greater subset characterization.
  (Int, [Int]) ->
  -- | Lesser subset characterization.
  (Int, [Int]) ->
  [(Double, Int)]
constraint (_, xs) (_, ys) =
  ((1,) <$> xs) <> ((negate 1,) <$> ys)

Steps to reproduce

Open Lib.hs, search for -- >>> in commentary for function sumString. Click on "Evaluate" above line.

Expected behaviour

You should get a result "123"

Actual behaviour

Get error message
--
-- ByteCodeLink.lookupIE
-- During interactive linking, GHCi couldn't find the following symbol:
-- hmatrixzmglpkzm0zi19zi0zi0zm4WWw7FtlyHzz9Q9FBl9Okvq_NumericziLinearProgramming_ZCzgzeZC_con_info or hmatrixzmglpkzm0zi19zi0zi0zm4WWw7FtlyHzz9Q9FBl9Okvq_NumericziLinearProgramming_ZCzgzeZC_static_info
-- This may be due to you not asking GHCi to load extra object files,
-- archives or DLLs needed by your current session. Restart GHCi, specifying
-- the missing library using the -L/path/to/object/dir and -lmissinglibname
-- flags, or simply by naming the relevant files on the GHCi command line.
-- Alternatively, this link failure might indicate a bug in GHCi.
-- If you suspect the latter, please report this as a GHC bug:
-- https://www.haskell.org/ghc/reportabug

Include debug information

I don't understand where to go for this. The project builds and runs with stack run, with Main.hs as follows:

module Main where

import Data.Maybe (fromJust)
import Lib (optimalSetOfLength, sumString)

main :: IO ()
main = do
  putStrLn "The encoding string for the minimum sum special sum set of length 7 is:"
  putStrLn $ sumString $ fromJust $ optimalSetOfLength 7


It also passes the test suite defined by Spec.hs:
import Data.Maybe (catMaybes)
import Lib (isSpecialSumSet, optimalSetOfLength, sumString)
import Test.Hspec (describe, hspec, it, shouldBe)

main :: IO ()
main =
  hspec $
    describe
      "Tests of optimalSetOfLength"
      $ do
        it
          "first few are correct"
          $ catMaybes (optimalSetOfLength <$> [1 .. 6])
            `shouldBe` firstFewSpecials
        it
          "isSpecialSumSet works on first few"
          $ all
            isSpecialSumSet
            firstFewSpecials
            `shouldBe` True
        it
          "Negative value for isSpecialSumSet"
          $ isSpecialSumSet [1, 2, 3] `shouldBe` False
        it
          "sumString works for first few values"
          $ sumString
            <$> firstFewSpecials
            `shouldBe` [ "1",
                         "12",
                         "234",
                         "3567",
                         "69111213",
                         "111819202225"
                       ]
  where
    firstFewSpecials :: [[Int]]
    firstFewSpecials =
      [ [1],
        [1, 2],
        [2 .. 4],
        [3, 5, 6, 7],
        [6, 9, 11, 12, 13],
        [11, 18, 19, 20, 22, 25]
      ]

I suspect it is some interaction with the gnu integer programming library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs infoNot actionable, because there's missing informationtype: bugSomething isn't right: doesn't work as intended, documentation is missing/outdated, etc..type: supportUser support tickets, questions, help with setup etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions