Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial refactor - much better types #9

Merged
merged 36 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ad4d8e3
Initial refactor - much better types
rasheedja Jul 14, 2023
9f3e54c
Run formatter
rasheedja Jul 14, 2023
2e6845a
`FeasibleSystem` instances
rasheedja Jul 29, 2023
785283f
Re-add stack
rasheedja Jul 29, 2023
d8f1929
(wip) fix pivoting issues
rasheedja Jul 29, 2023
33ad19f
Switch CI to stack
rasheedja Aug 11, 2023
9a322d6
Use stack to build haddocks
rasheedja Aug 11, 2023
160c253
Run formatter
rasheedja Aug 11, 2023
fbd8dc5
Matrix test windows and macos
rasheedja Aug 11, 2023
1cf5307
Remove ghc 8.10 from CI
rasheedja Aug 11, 2023
e4f1c66
Make CI fail when tests fail
rasheedja Aug 12, 2023
0bc9d61
Run formatter
rasheedja Aug 12, 2023
de15759
Fix broken tests
rasheedja Aug 12, 2023
c511b04
polishing
rasheedja Aug 12, 2023
eeed31c
Lens getters -> RecordDot getters
rasheedja Aug 12, 2023
5b8327d
Add logging, improve docs, more tests, handle edge cases
rasheedja Sep 9, 2023
eb5ac07
fixme
rasheedja Sep 9, 2023
28aa9c5
Improve logging
rasheedja Sep 30, 2023
f2009ea
Fourmolu upgrade: limit lines to 120 chars
rasheedja Sep 30, 2023
18c497b
Upgrade fourmolu action, specify fourmolu version
rasheedja Sep 30, 2023
de2a7bf
Bump package version
rasheedja Sep 30, 2023
c2f60d2
Bump lts
rasheedja Sep 30, 2023
ebd2099
some helper functions
rasheedja Nov 24, 2023
af6e364
Rename Linaer.Simplex.Simplex to Linear.Simplex.Solver.TwoPhase
rasheedja Nov 24, 2023
9bd3cc0
Fix caching
rasheedja Nov 24, 2023
fb1cc40
Update lts
rasheedja Nov 24, 2023
45cee63
Diagnose caching issues
rasheedja Nov 24, 2023
4aec1cd
Try fixing caching
rasheedja Nov 24, 2023
0453866
Remove windows from CI
rasheedja Nov 24, 2023
cd8d399
Update workflow step labels
rasheedja Nov 24, 2023
f4407a6
Update stack yaml lock
rasheedja Nov 25, 2023
8625413
Add windows + caching to ci
rasheedja Nov 25, 2023
38ddffa
Save .stack-work for windows too
rasheedja Nov 25, 2023
542fb3d
Only save when cache is not hit
rasheedja Nov 25, 2023
b024b6b
Update ChangeLog
rasheedja Nov 25, 2023
38b55a2
Update copyright dates
rasheedja Nov 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 70 additions & 34 deletions .github/workflows/haskell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,17 @@ jobs:

steps:
- uses: actions/checkout@v3
- uses: haskell-actions/run-fourmolu@v7
- uses: haskell-actions/run-fourmolu@v9
with:
version: "0.14.0.0"
build:
name: GHC ${{ matrix.ghc-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
ghc-version: ['9.6', '9.4', '9.2', '9.0', '8.10']

include:
- os: windows-latest
ghc-version: '9.6'
- os: macos-latest
ghc-version: '9.6'
os: [windows-latest, macos-latest, ubuntu-latest]
ghc-version: ['9.6', '9.4', '9.2', '9.0']

steps:
- uses: actions/checkout@v3
Expand All @@ -41,54 +37,94 @@ jobs:
id: setup
with:
ghc-version: ${{ matrix.ghc-version }}
# Defaults, added for clarity:
cabal-version: 'latest'
cabal-update: true
enable-stack: true

- name: Installed minor versions of GHC and Cabal
- name: Installed minor versions of GHC, Cabal, and Stack
shell: bash
run: |
GHC_VERSION=$(ghc --numeric-version)
CABAL_VERSION=$(cabal --numeric-version)
STACK_VERSION=$(stack --numeric-version)
echo "GHC_VERSION=${GHC_VERSION}" >> "${GITHUB_ENV}"
echo "CABAL_VERSION=${CABAL_VERSION}" >> "${GITHUB_ENV}"
echo "STACK_VERSION=${STACK_VERSION}" >> "${GITHUB_ENV}"

- name: Configure the build
run: |
cabal configure --enable-tests --enable-benchmarks --disable-documentation
cabal build --dry-run
# cabal configure --enable-tests --enable-benchmarks --disable-documentation
# cabal build --dry-run
stack build --test --bench --no-haddock --dry-run
# The last step generates dist-newstyle/cache/plan.json for the cache key.

- name: Restore cached dependencies
- name: Restore .stack-work cache
uses: actions/cache/restore@v3
id: cache-restore-stack-work
with:
path: .stack-work
key: ${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-work-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}-${{ hashFiles('**/*.hs') }}
restore-keys: |
${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-work-

- name: Restore ~/.stack cache (Unix)
uses: actions/cache/restore@v3
id: cache-restore-stack-global-unix
if: runner.os == 'Linux' || runner.os == 'macOS'
with:
path: ~/.stack
key: ${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}
restore-keys: |
${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-global-

- name: Restore %APPDATA%\stack, %LOCALAPPDATA%\Programs\stack cache (Windows)
uses: actions/cache/restore@v3
id: cache
id: cache-restore-stack-global-windows
if: runner.os == 'Windows'
with:
path: ${{ steps.setup.outputs.cabal-store }}
key: ${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-cabal-${{ env.CABAL_VERSION }}-plan-${{ hashFiles('**/plan.json') }}
path: |
~\AppData\Roaming\stack
~\AppData\Local\Programs\stack
key: ${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}
restore-keys: |
${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-cabal-${{ env.CABAL_VERSION }}-
${{ runner.os }}-ghc-${{ env.GHC_VERSION }}-stack-${{ env.STACK_VERSION }}-stack-global-

- name: Build dependencies
run: stack build --only-dependencies

- name: Install dependencies
run: cabal build all --only-dependencies
- name: Build the package
run: stack build

# Cache dependencies already here, so that we do not have to rebuild them should the subsequent steps fail.
- name: Save cached dependencies
- name: Save .stack-work cache
uses: actions/cache/save@v3
# Caches are immutable, trying to save with the same key would error.
if: ${{ !steps.cache.outputs.cache-hit
|| steps.cache.outputs.cache-primary-key != steps.cache.outputs.cache-matched-key }}
id: cache-save-stack-work
if: steps.cache-restore-stack-work.outputs.cache-hit != 'true'
with:
path: ${{ steps.setup.outputs.cabal-store }}
key: ${{ steps.cache.outputs.cache-primary-key }}

- name: Build
run: cabal build all
path: .stack-work
key: ${{ steps.cache-restore-stack-work.outputs.cache-primary-key }}

- name: Save %APPDATA%\stack, %LOCALAPPDATA%\Programs\stack cache (Windows)
uses: actions/cache/save@v3
if: runner.os == 'Windows'
&& steps.cache-restore-stack-global-windows.outputs.cache-hit != 'true'
with:
path: |
~\AppData\Roaming\stack
~\AppData\Local\Programs\stack
key: ${{ steps.cache-restore-stack-global-windows.outputs.cache-primary-key }}

- name: Save ~/.stack cache (Unix)
uses: actions/cache/save@v3
id: cache-save-stack-global
if: (runner.os == 'Linux' || runner.os == 'macOS')
&& steps.cache-restore-stack-global-unix.outputs.cache-hit != 'true'
with:
path: ~/.stack
key: ${{ steps.cache-restore-stack-global-unix.outputs.cache-primary-key }}

- name: Run tests
run: cabal test all
run: stack test

- name: Check cabal file
run: cabal check

- name: Build documentation
run: cabal haddock all
run: stack haddock
11 changes: 10 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

## Unreleased changes

## [v0.2.0.0](https://github.com/rasheedja/LPPaver/tree/v0.2.0.0)

- Setup CI
- Use fourmolu formatter
- Switch to Cabal
- Add better types
- Use lens
- Use RecordDot syntax
- Add logging
- Improve Docs
- More Tests
- Bump Stackage LTS
- Rename Linear.Simplex.Simplex -> Linear.Simplex.TwoPhase.Simplex

## [v0.1.0.0](https://github.com/rasheedja/LPPaver/tree/v0.1.0.0)

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright Junaid Rasheed (c) 2020-2022
Copyright Junaid Rasheed (c) 2020-2023

All rights reserved.

Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Quick Overview

The `Linear.Simplex.Simplex` module contain both phases of the simplex method.
The `Linear.Simplex.Solver.TwoPhase` module contain both phases of the two-phase simplex method.

### Phase One

Expand All @@ -20,46 +20,46 @@ The `PolyConstraint` type, as well as other custom types required by this librar

```haskell
data PolyConstraint =
LEQ VarConstMap Rational |
GEQ VarConstMap Rational |
EQ VarConstMap Rational deriving (Show, Eq);
LEQ Vars Rational |
GEQ Vars Rational |
EQ Vars Rational deriving (Show, Eq);
```

And `VarConstMap` is defined as:
And `Vars` is defined as:

```haskell
type VarConstMap = [(Integer, Rational)]
type Vars = [(Integer, Rational)]
```

A `VarConstMap` is treated as a list of `Integer` variables mapped to their `Rational` coefficients, with an implicit `+` between each element in the list.
A `Vars` is treated as a list of `Integer` variables mapped to their `Rational` coefficients, with an implicit `+` between each element in the list.
For example: `[(1, 2), (2, (-3)), (1, 3)]` is equivalent to `(2x1 + (-3x2) + 3x1)`.

And a `PolyConstraint` is an inequality/equality where the LHS is a `VarConstMap` and the RHS is a `Rational`.
And a `PolyConstraint` is an inequality/equality where the LHS is a `Vars` and the RHS is a `Rational`.
For example: `LEQ [(1, 2), (2, (-3)), (1, 3)] 60` is equivalent to `(2x1 + (-3x2) + 3x1) <= 60`.

Passing a `[PolyConstraint]` to `findFeasibleSolution` will return a feasible solution if it exists as well as a list of slack variables, artificial variables, and a variable that can be safely used to represent the objective for phase two.
`Nothing` is returned if the given `[PolyConstraint]` is infeasible.
The feasible system is returned as the type `DictionaryForm`:

```haskell
type DictionaryForm = [(Integer, VarConstMap)]
type DictionaryForm = [(Integer, Vars)]
```

`DictionaryForm` can be thought of as a list of equations, where the `Integer` represents a basic variable on the LHS that is equal to the RHS represented as a `VarConstMap`. In this `VarConstMap`, the `Integer` -1 is used internally to represent a `Rational` number.
`DictionaryForm` can be thought of as a list of equations, where the `Integer` represents a basic variable on the LHS that is equal to the RHS represented as a `Vars`. In this `Vars`, the `Integer` -1 is used internally to represent a `Rational` number.

### Phase Two

`optimizeFeasibleSystem` performs phase two of the simplex method, and has the type:

```haskell
data ObjectiveFunction = Max VarConstMap | Min VarConstMap deriving (Show, Eq)
data ObjectiveFunction = Max Vars | Min Vars deriving (Show, Eq)

optimizeFeasibleSystem :: ObjectiveFunction -> DictionaryForm -> [Integer] -> [Integer] -> Integer -> Maybe (Integer, [(Integer, Rational)])
```

We first pass an `ObjectiveFunction`.
Then we give a feasible system in `DictionaryForm`, a list of slack variables, a list of artificial variables, and a variable to represent the objective.
`optimizeFeasibleSystem` Maximizes/Minimizes the linear equation represented as a `VarConstMap` in the given `ObjectiveFunction`.
`optimizeFeasibleSystem` Maximizes/Minimizes the linear equation represented as a `Vars` in the given `ObjectiveFunction`.
The first item of the returned pair is the `Integer` variable representing the objective.
The second item is a list of `Integer` variables mapped to their optimized values.
If a variable is not in this list, the variable is equal to 0.
Expand Down Expand Up @@ -87,7 +87,7 @@ There are similar functions for `DictionaryForm` as well as other custom types i

## Usage notes

You must only use positive `Integer` variables in a `VarConstMap`.
You must only use positive `Integer` variables in a `Vars`.
This implementation assumes that the user only provides positive `Integer` variables; the `Integer` -1, for example, is sometimes used to represent a `Rational` number.

## Example
Expand Down
12 changes: 6 additions & 6 deletions fourmolu.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
indentation: 2
column-limit: none
column-limit: 120
function-arrows: trailing
comma-style: leading
import-export-style: diff-friendly
import-export-style: leading
indent-wheres: true
record-brace-space: true
newlines-between-decls: 1
haddock-style: multi-line
haddock-style-module:
let-style: auto
in-style: right-align
haddock-style: single-line
haddock-style-module: single-line
let-style: inline
in-style: left-align
single-constraint-parens: always
unicode: never
respectful: true
Expand Down
56 changes: 56 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: simplex-method
version: 0.2.0.0
github: "rasheedja/simplex-method"
license: BSD3
author: "Junaid Rasheed"
maintainer: "[email protected]"
copyright: "BSD-3"

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

# Metadata used when publishing your package
synopsis: Implementation of the two-phase simplex method in exact rational arithmetic
category: Math, Maths, Mathematics, Optimisation, Optimization, Linear Programming

# 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/rasheedja/simplex-method#readme>

dependencies:
- base >= 4.14 && < 5
- containers >= 0.6.5.1 && < 0.7
- generic-lens >= 2.2.0 && < 2.3
- lens >= 5.2.2 && < 5.3
- monad-logger >= 0.3.40 && < 0.4
- text >= 2.0.2 && < 2.1
- time

default-extensions:
DataKinds
DeriveFunctor
DeriveGeneric
DisambiguateRecordFields
DuplicateRecordFields
FlexibleContexts
LambdaCase
OverloadedLabels
OverloadedRecordDot
OverloadedStrings
RecordWildCards
TemplateHaskell
TupleSections
TypeApplications
NamedFieldPuns

library:
source-dirs: src

tests:
simplex-haskell-test:
main: Spec.hs
source-dirs: test
dependencies:
- simplex-method
Loading
Loading