From 8f1f9e5a067d06bac40133864d3d063f1673a8d4 Mon Sep 17 00:00:00 2001
From: Brandon Rodriguez <brodriguez8774@gmail.com>
Date: Tue, 26 Nov 2019 22:13:01 -0500
Subject: [PATCH] Implement parial "initialize-simplex" algorithm function

---
 resources/simplex/initialize.py       | 108 ++++++++++++++++++++++++++
 resources/simplex/simplex.py          |   5 ++
 tests/resources/simplex/initialize.py |  58 ++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 resources/simplex/initialize.py
 create mode 100644 tests/resources/simplex/initialize.py

diff --git a/resources/simplex/initialize.py b/resources/simplex/initialize.py
new file mode 100644
index 0000000..c2c7baa
--- /dev/null
+++ b/resources/simplex/initialize.py
@@ -0,0 +1,108 @@
+"""
+Date: 11-22-19
+Class: CS5310
+Assignment: Linear Programming Simplex Algorithm
+Author: Brandon Rodriguez
+
+
+Implementation of the "Initialize" function for Linear Programming Simplex problems.
+"""
+
+
+# System Imports.
+import copy
+
+# User Class Imports.
+from resources import logging as init_logging
+
+
+# Initialize Logger.
+logger = init_logging.get_logger(__name__)
+
+
+class Initialize():
+    def __init__(self, parent, *args, **kwargs):
+        # Get calling parent. We use this to pull parent data on __call__.
+        self._parent = parent
+
+        # Parent data values to set later.
+        self._matrix_a = None
+        self._vector_b = None
+        self._vector_c = None
+        self._obj_constant_index = None
+        self._basic_var_indexes = None
+        self._nonbasic_var_indexes = None
+
+    def __call__(self, *args, **kwargs):
+        # Get parent data at the point of call.
+        self._matrix_a = self._parent._matrix_a
+        self._vector_b = self._parent._constants
+        self._vector_c = self._parent._obj_func
+        self._obj_constant_index = self._parent._obj_constant_index
+        self._basic_var_indexes = self._parent._basic_var_indexes
+        self._nonbasic_var_indexes = self._parent._nonbasic_var_indexes
+
+        # Run initialize function.
+        return self.initialize_simplex()
+
+    def initialize_simplex(self):
+        # Determine smallest constant for constraints.
+        smallest_const_index = 0
+        for index in range(len(self._vector_b)):
+            if self._vector_b[index] < self._vector_b[smallest_const_index]:
+                # Found a smaller value. Save index value.
+                smallest_const_index = index
+
+        # Check if our smallest value is not negative.
+        if self._vector_b[smallest_const_index] >= 0:
+            # Smallest value is non-negative. Is feasible.
+            # We already know values so return as-is.
+            return (
+                self._nonbasic_var_indexes,
+                self._basic_var_indexes,
+                self._matrix_a,
+                self._vector_b,
+                self._vector_c,
+                self._obj_constant_index,
+            )
+
+        # If we got this far, then one or more constraint constants are negative.
+        # This means we have an invalid simplex starting point, and want to correct it.
+        return self._correct_simplex(smallest_const_index)
+
+    def _correct_simplex(self, smallest_index):
+        # Get values pulled from parent. We make sure we get a new copy and not a reference.
+        matrix_a = copy.deepcopy(self._matrix_a)
+        vector_b = copy.deepcopy(self._vector_b)
+        vector_c = copy.deepcopy(self._vector_c)
+        obj_const_index = self._obj_constant_index
+        b_array = copy.deepcopy(self._basic_var_indexes)
+        n_array = copy.deepcopy(self._nonbasic_var_indexes)
+
+        # Add a "new" constraint variable. This will be set to 1 for all constants and -1 for obj func.
+        for row_index in range(len(matrix_a)):
+            matrix_a[row_index].append(1)
+
+        # "Reset" objective function to 0 for everything other than our new var.
+        for col_index in range(len(vector_c)):
+            vector_c[col_index] = 0
+
+        # Add our new variable to the objective function as well.
+        vector_c.insert((len(vector_c) - 1), -1)
+
+        # Update our parent values for a pivot.
+        self._parent._matrix_a = copy.deepcopy(matrix_a)
+        self._parent._obj_func = copy.deepcopy(vector_c)
+        self._parent._nonbasic_var_indexes.append(len(vector_c) - 2)
+        # logger.info('Nonbasics: {0}'.format(self._parent._nonbasic_var_indexes))
+        # logger.info('Basics: {0}'.format(self._parent._basic_var_indexes))
+
+        # Now pivot once to get a feasible instance.
+        # self._parent.display_tableau()
+        # logger.info('Smallest Index: {0}'.format(smallest_index))
+        self._parent.pivot(self._parent._basic_var_indexes[smallest_index], (len(vector_c) - 2))
+
+        # Need base "Simplex" function to run further?
+        return None
+
+
diff --git a/resources/simplex/simplex.py b/resources/simplex/simplex.py
index dc91d7f..714a914 100644
--- a/resources/simplex/simplex.py
+++ b/resources/simplex/simplex.py
@@ -36,6 +36,7 @@ Algorithm Variables (At least as far as I can tell):
 import json
 
 # User Class Imports.
+from .initialize import Initialize
 from .pivot import Pivot
 from resources import logging as init_logging
 
@@ -57,6 +58,7 @@ class Simplex():
         self._nonbasic_var_indexes = None
         self._description = None
 
+        self._initialize = Initialize(self)
         self._pivot = Pivot(self)
 
     #region Simplex Read in and Setup
@@ -394,6 +396,9 @@ class Simplex():
         logger.info('      ' + '-' * tableau_length)
         logger.info('')
 
+    def initialize(self):
+        self._initialize()
+
     def pivot(self, old_basic_index, new_basic_index):
         """
         Pivots simplex around provided basic variable indexes.
diff --git a/tests/resources/simplex/initialize.py b/tests/resources/simplex/initialize.py
new file mode 100644
index 0000000..4ff62ca
--- /dev/null
+++ b/tests/resources/simplex/initialize.py
@@ -0,0 +1,58 @@
+"""
+Date: 11-22-19
+Class: CS5310
+Assignment: Linear Programming Simplex Algorithm
+Author: Brandon Rodriguez
+
+
+Tests for "Initialize" function implementation of the Simplex algorithm.
+"""
+
+# System Imports.
+import unittest
+
+# User Class Imports.
+from resources.simplex.simplex import Simplex
+
+
+class TestInitialize(unittest.TestCase):
+    def setUp(self):
+        self.simplex = Simplex()
+
+    def test__initialize(self):
+        # Setup initial simplex.
+        self.simplex.set_simplex_values(
+            [
+                [2, -1],
+                [1, -5],
+            ],
+            [2, -4],
+            [2, -1],
+        )
+        # Test initial values after setup.
+        self.assertEqual(self.simplex._matrix_a, [
+            [2, -1, 1, 0],
+            [1, -5, 0, 1],
+        ])
+        self.assertEqual(self.simplex._constants, [2, -4])
+        self.assertEqual(self.simplex._obj_func, [2, -1, 0, 0, 0])
+        self.assertEqual(self.simplex._obj_constant_index, 4)
+        self.assertEqual(self.simplex._basic_var_indexes, [2, 3])
+        self.assertEqual(self.simplex._nonbasic_var_indexes, [0, 1])
+
+        # Call initialize method to test.
+        # self.simplex.display_tableau()
+        self.simplex.initialize()
+
+        # self.simplex.display_tableau()
+
+        # # Test initial values "initialize" method.
+        # self.assertEqual(self.simplex._matrix_a, [
+        #     [1-5, 1, 0, 1/5],
+        #     [-9/5, 0, 1, 1/5],
+        # ])
+        # self.assertEqual(self.simplex._constants, [4/5, 14/5])
+        # self.assertEqual(self.simplex._obj_func, [9/5, 0, 0, 1/5, -4/5])
+        # self.assertEqual(self.simplex._obj_constant_index, 4)
+        # self.assertEqual(self.simplex._basic_var_indexes, [1, 2])
+        # self.assertEqual(self.simplex._nonbasic_var_indexes, [0, 3])
-- 
GitLab