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

Update docs #11

Merged
merged 1 commit into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
106 changes: 58 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,97 +11,105 @@ The `Linear.Simplex.Solver.TwoPhase` module contain both phases of the two-phase
Phase one is implemented by `findFeasibleSolution`:

```haskell
findFeasibleSolution :: [PolyConstraint] -> Maybe (DictionaryForm, [Integer], [Integer], Integer)
findFeasibleSolution :: (MonadIO m, MonadLogger m) => [PolyConstraint] -> m (Maybe FeasibleSystem)
```

`findFeasibleSolution` takes a list of `PolyConstraint`s.
The `PolyConstraint` type, as well as other custom types required by this library, are defined in the `Linear.Simplex.Types` module.
`PolyConstraint` is defined as:

```haskell
data PolyConstraint =
LEQ Vars Rational |
GEQ Vars Rational |
EQ Vars Rational deriving (Show, Eq);
data PolyConstraint
= LEQ {lhs :: VarLitMapSum, rhs :: SimplexNum}
| GEQ {lhs :: VarLitMapSum, rhs :: SimplexNum}
| EQ {lhs :: VarLitMapSum, rhs :: SimplexNum}
deriving (Show, Read, Eq, Generic)
```

And `Vars` is defined as:
`SimplexNum` is an alias for `Rational`, and `VarLitMapSum` is an alias for `VarLitMap`, which is an alias for `Map Var SimplexNum`.
`Var` is an alias of `Int`.

```haskell
type Vars = [(Integer, Rational)]
```
A `VarLitMapSum` is read as `Integer` variables mapped to their `Rational` coefficients, with an implicit `+` between each entry.
For example: `Map.fromList [(1, 2), (2, (-3)), (1, 3)]` is equivalent to `(2x1 + (-3x2) + 3x1)`.

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 `VarLitMapSum` and the RHS is a `Rational`.
For example: `LEQ (Map.fromList [(1, 2), (2, (-3)), (1, 3)] 60)` is equivalent to `(2x1 + (-3x2) + 3x1) <= 60`.

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 `FeasibleSystem` if a feasible solution exists:

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
data FeasibleSystem = FeasibleSystem
{ dict :: Dict
, slackVars :: [Var]
, artificialVars :: [Var]
, objectiveVar :: Var
}
deriving (Show, Read, Eq, Generic)
```

```haskell
type DictionaryForm = [(Integer, Vars)]
type Dict = M.Map Var DictValue

data DictValue = DictValue
{ varMapSum :: VarLitMapSum
, constant :: SimplexNum
}
deriving (Show, Read, Eq, Generic)
```

`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.
`Dict` can be thought of as a set of equations, where the key represents a basic variable on the LHS of the equation
that is equal to the RHS represented as a `DictValue` value.

### Phase Two

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

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

optimizeFeasibleSystem :: ObjectiveFunction -> DictionaryForm -> [Integer] -> [Integer] -> Integer -> Maybe (Integer, [(Integer, Rational)])
optimizeFeasibleSystem :: (MonadIO m, MonadLogger m) => ObjectiveFunction -> FeasibleSystem -> m (Maybe Result)

data ObjectiveFunction = Max {objective :: VarLitMapSum} | Min {objective :: VarLitMapSum}

data Result = Result
{ objectiveVar :: Var
, varValMap :: VarLitMap
}
deriving (Show, Read, Eq, Generic)
```

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 `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.
We give `optimizeFeasibleSystem` an `ObjectiveFunction` along with a `FeasibleSystem`.

### Two-Phase Simplex

`twoPhaseSimplex` performs both phases of the simplex method.
It has the type:

```haskell
twoPhaseSimplex :: ObjectiveFunction -> [PolyConstraint] -> Maybe (Integer, [(Integer, Rational)])
twoPhaseSimplex :: (MonadIO m, MonadLogger m) => ObjectiveFunction -> [PolyConstraint] -> m (Maybe Result)
```

The return type is the same as that of `optimizeFeasibleSystem`

### Extracting Results

The result of the objective function is present in the return type of both `twoPhaseSimplex` and `optimizeFeasibleSystem`, but this can be difficult to grok in systems with many variables, so the following function will extract the value of the objective function for you.
The result of the objective function is present in the returned `Result` type of both `twoPhaseSimplex` and `optimizeFeasibleSystem`, but this can be difficult to grok in systems with many variables, so the following function will extract the value of the objective function for you.

```haskell
extractObjectiveValue :: Maybe (Integer, [(Integer, Rational)]) -> Maybe Rational
dictionaryFormToTableau :: Dict -> Tableau
```

There are similar functions for `DictionaryForm` as well as other custom types in the module `Linear.Simplex.Util`.

## Usage notes

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

```haskell
exampleFunction :: (ObjectiveFunction, [PolyConstraint])
exampleFunction =
(
Max [(1, 3), (2, 5)], -- 3x1 + 5x2
Max {objective = Map.fromList [(1, 3), (2, 5)]}, -- 3x1 + 5x2
[
LEQ [(1, 3), (2, 1)] 15, -- 3x1 + x2 <= 15
LEQ [(1, 1), (2, 1)] 7, -- x1 + x2 <= 7
LEQ [(2, 1)] 4, -- x2 <= 4
LEQ [(1, -1), (2, 2)] 6 -- -x1 + 2x2 <= 6
LEQ {lhs = Map.fromList [(1, 3), (2, 1)], rhs = 15}, -- 3x1 + x2 <= 15
LEQ {lhs = Map.fromList [(1, 1), (2, 1)], rhs = 7}, -- x1 + x2 <= 7
LEQ {lhs = Map.fromList [(2, 1)], rhs = 4}, -- x2 <= 4
LEQ {lhs = Map.fromList [(1, -1), (2, 2)], rhs = 6} -- -x1 + 2x2 <= 6
]
)

Expand All @@ -111,13 +119,15 @@ twoPhaseSimplex (fst exampleFunction) (snd exampleFunction)
The result of the call above is:

```haskell
Just
(7, -- Integer representing objective function
[
(7,29 % 1), -- Value for variable 7, so max(3x1 + 5x2) = 29.
(1,3 % 1), -- Value for variable 1, so x1 = 3
(2,4 % 1) -- Value for variable 2, so x2 = 4
]
Just
(Result
{ objectiveVar = 7 -- Integer representing objective function
, varValMap = M.fromList
[ (7, 29) -- Value for variable 7, so max(3x1 + 5x2) = 29.
, (1, 3) -- Value for variable 1, so x1 = 3
, (2, 4) -- Value for variable 2, so x2 = 4
]
}
)
```

Expand Down
2 changes: 1 addition & 1 deletion src/Linear/Simplex/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ data TableauRow = TableauRow
-- Each entry in the map is a row.
type Tableau = M.Map Var TableauRow

-- | Values for a 'DictEntry'.
-- | Values for a 'Dict'.
data DictValue = DictValue
{ varMapSum :: VarLitMapSum
, constant :: SimplexNum
Expand Down
Loading