Skip to content

Commit

Permalink
Update docs (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
rasheedja authored Dec 2, 2023
1 parent 84c89e7 commit b377658
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 49 deletions.
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

0 comments on commit b377658

Please sign in to comment.