From bfa56496fc9c109ace07e96e1e759ae77c699f6e Mon Sep 17 00:00:00 2001 From: Junaid Rasheed Date: Fri, 2 Oct 2026 11:35:05 +0100 Subject: [PATCH 1/2] refactor(simplex): extract results directly from dictionaries --- src/Linear/Simplex/Solver/TwoPhase.hs | 51 ++++++--------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/src/Linear/Simplex/Solver/TwoPhase.hs b/src/Linear/Simplex/Solver/TwoPhase.hs index ca148f0..679d1e8 100644 --- a/src/Linear/Simplex/Solver/TwoPhase.hs +++ b/src/Linear/Simplex/Solver/TwoPhase.hs @@ -70,7 +70,6 @@ import Linear.Simplex.Types ) import Linear.Simplex.Util ( combineVarLitMapSums - , dictionaryFormToTableau , foldVarLitMap , insertPivotObjectiveToDict , isMax @@ -331,49 +330,21 @@ optimizeFeasibleSystem objFunction fsys@(FeasibleSystem {dict = phase1Dict, ..}) logMsg LevelInfo "optimizeFeasibleSystem: Objective is unbounded (ratio test failed)" pure Unbounded Just resultDict -> do - let result = displayResults (dictionaryFormToTableau resultDict) + let result = displayResults resultDict logMsg LevelInfo $ "optimizeFeasibleSystem: Found optimal solution: " <> showT result pure result where - -- \| displayResults takes a 'Tableau' and returns an 'OptimisationOutcome'. The 'Tableau' - -- represents the final tableau of a linear program after the simplex - -- algorithm has been applied. The 'OptimisationOutcome' contains the values of all - -- variables appearing in the system. - -- - -- The function first filters out the rows of the tableau that correspond - -- to the slack and artificial variables. It then extracts the values of - -- the remaining variables and stores them in a map. If the objective - -- function is a maximization problem, the map contains the values of the - -- variables as they appear in the final tableau. If the objective function - -- is a minimization problem, the map contains the values of the variables - -- as they appear in the final tableau, except for the objective variable, - -- which is negated. - displayResults :: Tableau -> OptimisationOutcome - displayResults tableau = - Optimal extractVarVals + -- Extract basic-variable values directly from the final dictionary. + -- Omit slack/artificial variables and restore the minimization objective sign. + displayResults :: Dict -> OptimisationOutcome + displayResults resultDict = + Optimal $ M.mapWithKey valueOf originalRows where - extractVarVals = - let tableauWithOriginalVars = - M.filterWithKey - ( \basicVarName _ -> - basicVarName `notElem` slackVars ++ artificialVars - ) - tableau - in case objFunction of - Max _ -> - M.map - ( \tableauRow -> - tableauRow.rhs - ) - tableauWithOriginalVars - Min _ -> - M.mapWithKey -- We maximized -objVar, so we negate the objVar to get the final value - ( \basicVarName tableauRow -> - if basicVarName == objectiveVar - then negate $ tableauRow.rhs - else tableauRow.rhs - ) - tableauWithOriginalVars + originalRows = M.filterWithKey (\var _ -> var `notElem` slackVars ++ artificialVars) resultDict + valueOf var row = + if not (isMax objFunction) && var == objectiveVar + then negate row.constant + else row.constant -- \| Objective to use when optimising the linear program if no artificial -- variables were necessary in the first phase. It is essentially the original From 09b44735f35f85421f8733027d81f8d24b3b61fd Mon Sep 17 00:00:00 2001 From: Junaid Rasheed Date: Fri, 2 Oct 2026 11:46:39 +0100 Subject: [PATCH 2/2] docs(changelog): document changes in PR #22 --- ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog.md b/ChangeLog.md index d47e0a4..2690b2f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,7 @@ ## Unreleased changes +- Extract optimal variable values directly from dictionary constants without an intermediate tableau conversion. ([#22](https://github.com/rasheedja/simplex-method/pull/22)) - `twoPhaseSimplex` now takes a `VarDomainMap` as its first argument - Specify each variable's domain using smart constructors: `nonNegative`, `unbounded`, `lowerBoundOnly`, `upperBoundOnly`, or `boundedRange` - Variables not in the `VarDomainMap` are assumed to be `unbounded`