From a560d4c0097d66bed34e832dc70fa0ef4be86006 Mon Sep 17 00:00:00 2001 From: Brandon Rodriguez <brodriguez8774@gmail.com> Date: Sat, 30 Nov 2019 01:37:18 -0500 Subject: [PATCH] Add various small bugfixes for "pivot" function It now properly handles more cases. --- resources/simplex/pivot.py | 135 +++++++++++++++++++++++++++++-------- 1 file changed, 108 insertions(+), 27 deletions(-) diff --git a/resources/simplex/pivot.py b/resources/simplex/pivot.py index 875cf7e..3a25f2b 100644 --- a/resources/simplex/pivot.py +++ b/resources/simplex/pivot.py @@ -34,7 +34,12 @@ class Pivot(): self._basic_var_indexes = None self._nonbasic_var_indexes = None - def __call__(self, old_basic_col, new_basic_col, *args, **kwargs): + def __call__(self, old_basic_col, new_basic_col, *args, debug=False, **kwargs): + # Optionally override parent debug setting on call. + orig_debug = self._debug + if debug is True: + self._debug = debug + # Get parent data at the point of call. self._matrix_a = self._parent._matrix_a self._vector_b = self._parent._vector_b @@ -44,7 +49,12 @@ class Pivot(): self._nonbasic_var_indexes = self._parent._n_array # Run pivot function. - return self.pivot(old_basic_col, new_basic_col) + results = self.pivot(old_basic_col, new_basic_col) + + # Reset original debug settings. + self._debug = orig_debug + + return results def pivot(self, old_basic_col, new_basic_col): """ @@ -92,6 +102,8 @@ class Pivot(): # Compute updated objective function. vector_c, obj_const_index = self._update_objective( n_array, + matrix_a, + vector_b, vector_c, obj_const_index, old_basic, @@ -126,32 +138,38 @@ class Pivot(): logger.info(' vector_b: {0}'.format(vector_b)) logger.info(' old_basic: {0}'.format(old_basic)) logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') # Calculate our new constraint constant. - vector_b[new_basic[0]] = self._vector_b[new_basic[0]] / self._matrix_a[old_basic[0]][new_basic[1]] - - # Truncate if can be represented as int. - if vector_b[new_basic[0]] % 1 == 0: - vector_b[new_basic[0]] = int(vector_b[new_basic[0]]) + vector_b[new_basic[0]] = self._truncate_if_int( + self._vector_b[new_basic[0]] / self._matrix_a[old_basic[0]][new_basic[1]] + ) # Loop through all nonbasic indexes (minus the one we're adding). Adjust coefficients in matrix. for col_index in n_array: - - # Make sure to skip the one we're adding. - if col_index != new_basic[1]: + # Make sure to skip the one we're removing. + if col_index != old_basic[1]: # Adjust given coefficient. - matrix_a[new_basic[0]][col_index] = self._matrix_a[old_basic[0]][col_index] / self._matrix_a[old_basic[0]][new_basic[1]] - - # Truncate if can be represented as int. - if matrix_a[new_basic[0]][col_index] % 1 == 0: - matrix_a[new_basic[0]][col_index] = int(matrix_a[new_basic[0]][col_index]) + matrix_a[new_basic[0]][col_index] = self._truncate_if_int( + self._matrix_a[old_basic[0]][col_index] / self._matrix_a[old_basic[0]][new_basic[1]] + ) # Separately adjust the index of the newly nonbasic variable. - matrix_a[new_basic[0]][old_basic[1]] = self._matrix_a[old_basic[0]][old_basic[1]] / self._matrix_a[old_basic[0]][new_basic[1]] + matrix_a[new_basic[0]][old_basic[1]] = self._truncate_if_int(1 / self._matrix_a[old_basic[0]][new_basic[1]]) + + # Check if new basic is negative. If so, flip. + if matrix_a[new_basic[0]][new_basic[1]] == -1: + matrix_a[new_basic[0]][new_basic[1]] = 1 - # Truncate if can be represented as int. - if matrix_a[new_basic[0]][old_basic[1]] % 1 == 0: - matrix_a[new_basic[0]][old_basic[1]] = int(matrix_a[new_basic[0]][old_basic[1]]) + # Optional debugging printout. + if self._debug: + logger.info('Updated Pivot._calculate_new_basic()') + logger.info(' n_array: {0}'.format(n_array)) + logger.info(' matrix_a: {0}'.format(matrix_a)) + logger.info(' vector_b: {0}'.format(vector_b)) + logger.info(' old_basic: {0}'.format(old_basic)) + logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') return (matrix_a, vector_b) @@ -173,6 +191,7 @@ class Pivot(): logger.info(' vector_b: {0}'.format(vector_b)) logger.info(' old_basic: {0}'.format(old_basic)) logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') # Loop through all basic indexes (minus the one we're removing). Adjust coefficients in matrix. for row_index in range(len(vector_b)): @@ -180,23 +199,41 @@ class Pivot(): # Make sure to skip the one we're removing. if row_index != old_basic[0]: # Adjust constraint constants. - vector_b[row_index] = self._vector_b[row_index] - (self._matrix_a[row_index][new_basic[1]] * self._vector_b[old_basic[0]]) + vector_b[row_index] = self._truncate_if_int( + self._vector_b[row_index] - (self._matrix_a[row_index][new_basic[1]] * vector_b[old_basic[0]]) + ) # Loop through all nonbasic indexes. for col_index in n_array: # Adjust coefficient. - matrix_a[row_index][col_index] = self._matrix_a[row_index][col_index] - (self._matrix_a[new_basic[0]][col_index] * self._matrix_a[row_index][new_basic[1]]) + matrix_a[row_index][col_index] = self._truncate_if_int( + self._matrix_a[row_index][col_index] - (matrix_a[new_basic[0]][col_index] * self._matrix_a[row_index][new_basic[1]]) + ) # Separately adjust the index of the newly nonbasic variable. Make sure to skip the new basic variable. if row_index != new_basic[0]: - matrix_a[row_index][old_basic[1]] = self._matrix_a[row_index][old_basic[1]] - (self._matrix_a[new_basic[0]][old_basic[1]] * self._matrix_a[row_index][new_basic[1]]) + matrix_a[row_index][old_basic[1]] = self._truncate_if_int( + self._matrix_a[row_index][old_basic[1]] - (matrix_a[new_basic[0]][old_basic[1]] * self._matrix_a[row_index][new_basic[1]]) + ) + + # Optional debugging printout. + if self._debug: + logger.info('Updated Pivot._compute_coefficients()') + logger.info(' n_array: {0}'.format(n_array)) + logger.info(' matrix_a: {0}'.format(matrix_a)) + logger.info(' vector_b: {0}'.format(vector_b)) + logger.info(' old_basic: {0}'.format(old_basic)) + logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') return (matrix_a, vector_b) - def _update_objective(self, n_array, vector_c, obj_const_index, old_basic, new_basic): + def _update_objective(self, n_array, matrix_a, vector_b, vector_c, obj_const_index, old_basic, new_basic): """ Computes updated objective function. :param n_array: Array of all non-basic variable col indexes. + :param matrix_a: Matrix of variable coefficients for constraint equations. + :param vector_b: Vector of constants for constraint equations. :param vector_c: Vector of objective function coefficients and constant. :param obj_const_index: Index of constant in vector_c. :param old_basic: Tuple of (row, col) index values for old basic variable. @@ -207,21 +244,40 @@ class Pivot(): logger.info('') logger.info('Starting Pivot._update_objective()') logger.info(' n_array: {0}'.format(n_array)) + logger.info(' matrix_a: {0}'.format(matrix_a)) + logger.info(' vector_b: {0}'.format(vector_b)) logger.info(' vector_c: {0}'.format(vector_c)) logger.info(' obj_const_index: {0}'.format(obj_const_index)) logger.info(' old_basic: {0}'.format(old_basic)) logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') # Update objective constant. - vector_c[self._obj_constant_index] = self._vector_c[self._obj_constant_index] - (self._vector_b[new_basic[0]] * self._vector_c[new_basic[1]]) + vector_c[self._obj_constant_index] = self._truncate_if_int( + self._vector_c[self._obj_constant_index] - (vector_b[new_basic[0]] * self._vector_c[new_basic[1]]) + ) # Loop through all nonbasic indexes. for col_index in n_array: # Adjust objective function value. - vector_c[col_index] = self._vector_c[col_index] - (self._matrix_a[new_basic[0]][col_index] * self._vector_c[new_basic[1]]) + vector_c[col_index] = self._truncate_if_int( + self._vector_c[col_index] - (matrix_a[new_basic[0]][col_index] * self._vector_c[new_basic[1]]) + ) # Separately adjust the index of the newly nonbasic variable. - vector_c[old_basic[1]] = self._vector_c[old_basic[1]] - (self._matrix_a[new_basic[0]][old_basic[1]] * self._vector_c[new_basic[1]]) + vector_c[old_basic[1]] = self._truncate_if_int( + self._vector_c[old_basic[1]] - (matrix_a[new_basic[0]][old_basic[1]] * self._vector_c[new_basic[1]]) + ) + + # Optional debugging printout. + if self._debug: + logger.info('Updated Pivot._update_objective()') + logger.info(' n_array: {0}'.format(n_array)) + logger.info(' vector_c: {0}'.format(vector_c)) + logger.info(' obj_const_index: {0}'.format(obj_const_index)) + logger.info(' old_basic: {0}'.format(old_basic)) + logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') return (vector_c, obj_const_index) @@ -236,11 +292,12 @@ class Pivot(): # Optional debugging printout. if self._debug: logger.info('') - logger.info('Starting Pivot._update_objective()') + logger.info('Starting Pivot._compute_basic_sets()') logger.info(' n_array: {0}'.format(n_array)) logger.info(' b_array: {0}'.format(b_array)) logger.info(' old_basic: {0}'.format(old_basic)) logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') # Remove new basic variable from set of nonbasics. n_array.remove(new_basic[1]) @@ -255,4 +312,28 @@ class Pivot(): # Add new basic variable to set of basics, making sure we put it in the proper equation index. b_array.insert(new_basic[0], new_basic[1]) + # Optional debugging printout. + if self._debug: + logger.info('Updated Pivot._compute_basic_sets()') + logger.info(' n_array: {0}'.format(n_array)) + logger.info(' b_array: {0}'.format(b_array)) + logger.info(' old_basic: {0}'.format(old_basic)) + logger.info(' new_basic: {0}'.format(new_basic)) + logger.info('') + return (n_array, b_array) + + def _truncate_if_int(self, value): + """ + Truncates value if it can be properly represented as an int. + + Necessary since we're dealing with multiplication and division, which will return floats by default. + :param value: Possible float value to truncate to an int. + :return: Proper representation of value. + """ + if value == 0.0 or value % 1 == 0: + # Can be represented as an int. Return that. + return int(value) + else: + # Return original representation. + return value -- GitLab