From 800727570937e63acebb2140f6b5b4a3a32df88f Mon Sep 17 00:00:00 2001 From: Ilya Grigorev Date: Tue, 8 Oct 2024 00:57:25 +0500 Subject: [PATCH] add test coverage and fix input --- .vscode/settings.json | 9 -- Makefile.txt => Makefile | 7 +- main.cpp | 246 ++++++++++++++++++++++++++++++++++----- simplex.cpp | 202 +++++++++++--------------------- simplex.h | 11 +- tools/elimination.cpp | 67 +++++++++-- tools/math.cpp | 3 +- tools/matrix.cpp | 86 ++++++-------- tools/matrix.h | 2 - 9 files changed, 389 insertions(+), 244 deletions(-) delete mode 100644 .vscode/settings.json rename Makefile.txt => Makefile (85%) diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index eeb0991..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "makefile.launchConfigurations": [ - { - "cwd": "/home/emil/Coding/Assignments/SimplexTASK", - "binaryPath": "/home/emil/Coding/Assignments/SimplexTASK/simplex.out", - "binaryArgs": [] - } - ] -} \ No newline at end of file diff --git a/Makefile.txt b/Makefile similarity index 85% rename from Makefile.txt rename to Makefile index e02affa..4fe2380 100644 --- a/Makefile.txt +++ b/Makefile @@ -4,7 +4,7 @@ TOOLS := tools BUILD := build build: - mkdir $(BUILD) + mkdir -p $(BUILD) $(GXX) -c $(flags) $(TOOLS)/elimination.cpp -o $(BUILD)/elimination.obj $(GXX) -c $(flags) $(TOOLS)/math.cpp -o $(BUILD)/math.obj $(GXX) -c $(flags) $(TOOLS)/matrix.cpp -o $(BUILD)/matrix.obj @@ -28,8 +28,11 @@ $(BUILD)/simplex.obj: simplex.cpp $(BUILD)/main.obj: main.cpp $(GXX) -c $(flags) main.cpp -o $(BUILD)/main.obj -$(BUILD)/simplex.out: $(BUILD)/elimination.obj $(BUILD)/math.obj $(BUILD)/matrix.obj $(BUILD)/simplex.obj $(BUILD)/main.obj +simplex.out: $(BUILD)/elimination.obj $(BUILD)/math.obj $(BUILD)/matrix.obj $(BUILD)/simplex.obj $(BUILD)/main.obj $(GXX) $(BUILD)/*.obj $(flags) -o simplex.out +test: simplex.out + ./simplex.out + clean: rm -rf $(BUILD) diff --git a/main.cpp b/main.cpp index 237d0a6..e91beae 100644 --- a/main.cpp +++ b/main.cpp @@ -1,57 +1,243 @@ #include +#include #include "tools/matrix.h" #include "tools/math.h" #include "simplex.h" +bool check_eq(double a, double b, + double relativeEpsilon = 0.0001) { + double diff = std::abs(a - b); + a = std::abs(a); + b = std::abs(b); + double largest = (b > a) ? b : a; -void printInitialInputs(Vector C, Matrix A, Vector b) { - + return diff <= largest * relativeEpsilon; } -int main() { - // TODO: Initially should be positive - Vector C = {5, 4, 0, 0, 0, 0}; +int TEST_GENERAL_CASE() { + std::cout << "----------------------------RUNNING_TEST_GENERAL_CASE----------------------------" << std::endl; + Vector C = {5, 4}; Matrix A = { - {6, 4, 1, 0, 0, 0}, - {1, 2, 0, 1, 0, 0}, - {-1, 1, 0, 0, 1, 0}, - {0, 1, 0, 0, 0, 1} - }; - + {6, 4}, + {1, 2}, + {-1, 1}, + {0, 1} + }; Vector b = {24, 6, 1, 2}; - for (int i = 0; i < b.size(); i++) { - if (b[i] < 0) { - std::cout << "Error: method is not applicable" << std::endl; - return 1; + auto result = simplex(C, A, b); + + if (!(result.state == bounded)) { + std::string state_name; + switch (result.state) { + case unsolvable: + state_name = "unsolvable"; + break; + case unbounded: + state_name = "unbounded"; + break; + default: + state_name = "bounded"; + break; } + std::cout << "Incorrect state type. Expected bounded. Got " << state_name << std::endl; + return 0; } - //Matrix test = {{1, -1, -2}, {1, 1, -2}, {1, -1, 2}}; + if (!check_eq(result.objective_function_value, 21)) { + std::cout << "Incorrect objective function value. Expected 21. Got " + << result.objective_function_value << std::endl; + return 0; + } - //showMatrix(test); - + if (!( check_eq(result.solution[0],3) && check_eq(result.solution[1], 1.5) )) { + std::cout << "Incorrect desire variables. Expected 3 and 1.5. Got " + << result.solution; + return 0; + } - Result result = Simplex(C, A, b, 0.1, true); + return 1; +} +int TEST_MINIMIZE_CASE() { + std::cout << "----------------------------RUNNING_TEST_MINIMIZE_CASE----------------------------" << std::endl; + Vector C = {-2, 2, -6}; + Matrix A = { + {2, 1, -2}, + {1, 2, 4}, + {1, -1, 2} + }; + Vector b = {24, 23, 10}; - if(result.state == bounded) { - if(result.solution == nullptr) { - std::cout << "Error: no solution value is returned" << std::endl; - return 1; + auto result = simplex(C, A, b, 0.01, false); + + if (!(result.state == bounded)) { + std::string state_name; + switch (result.state) { + case unsolvable: + state_name = "unsolvable"; + break; + case unbounded: + state_name = "unbounded"; + break; + default: + state_name = "bounded"; + break; } - std::cout << *result.solution << std::endl; - std::cout << result.objective_fucntion_value << std::endl; + std::cout << "Incorrect state type. Expected bounded. Got " << state_name << std::endl; + return 0; + } - delete result.solution; + if (!check_eq(result.objective_function_value, -30.75)) { + std::cout << "Incorrect objective function value. Expected -30.75. Got " + << result.objective_function_value << std::endl; + return 0; } - else { - std::cout << "Error: unbounded" << std::endl; - return 1; + + if (!( check_eq(result.solution[0], 0) && check_eq(result.solution[1], 0.75) + && check_eq(result.solution[2],5.375) + )) { + std::cout << "Incorrect desire variables. Expected 3 and 1.5. Got " + << result.solution; + return 0; } - //std::cout << "asdasd" << std::endl; + + return 1; +} + +int TEST_WITH_SLACK_CASE() { + std::cout << "----------------------------RUNNING_TEST_WITH_SLACK_CASE----------------------------" << std::endl; + + Vector C = {5, 4}; + Matrix A = { + {6, 4, 1}, + {1, 2, 0}, + {-1, 1, 0}, + {0, 1, 0} + }; + Vector b = {24, 6, 1, 2}; + + auto result = simplex(C, A, b); + + if (!(result.state == bounded)) { + std::string state_name; + switch (result.state) { + case unsolvable: + state_name = "unsolvable"; + break; + case unbounded: + state_name = "unbounded"; + break; + default: + state_name = "bounded"; + break; + } + std::cout << "Incorrect state type. Expected bounded. Got " << state_name << std::endl; + return 0; + } + + if (result.objective_function_value != 21) { + std::cout << "Incorrect objective function value. Expected 21. Got " + << result.objective_function_value << std::endl; + return 0; + } + + if (!((result.solution[0] == 3) || (result.solution[1] == 1.5))) { + std::cout << "Incorrect desire variables. Expected 3 and 1.5. Got " + << result.solution[0] << result.solution[1]; + return 0; + } + + return 1; +} + +int TEST_UNBOUNDED_CASE() { + std::cout << "----------------------------RUNNING_TEST_UNBOUNDED_CASE----------------------------" << std::endl; + + Vector C = {2, 1}; + Matrix A = { + {1, -1}, + {2, 0} + }; + Vector b = {10, 40}; + + auto result = simplex(C, A, b); + + if (!(result.state == unbounded)) { + std::string state_name; + switch (result.state) { + case unsolvable: + state_name = "unsolvable"; + break; + case unbounded: + state_name = "unbounded"; + break; + default: + state_name = "bounded"; + break; + } + std::cout << "Incorrect state type. Expected unbounded. Got " << state_name << std::endl; + return 0; + } + + return 1; +} + +int TEST_UNSOLVABLE_CASE() { + std::cout << "----------------------------RUNNING_TEST_UNSOLVABLE_CASE----------------------------" << std::endl; + + Vector C = {5, 4}; + Matrix A = { + {6, 4}, + {1, 2}, + {-1, 1}, + {0, 1} + }; + Vector b = {-24, 6, 1, 2}; + + auto result = simplex(C, A, b); + + if (!(result.state == unsolvable)) { + std::string state_name; + switch (result.state) { + case unsolvable: + state_name = "unsolvable"; + break; + case unbounded: + state_name = "unbounded"; + break; + default: + state_name = "bounded"; + break; + } + std::cout << "Incorrect state type. Expected unsolvable. Got " << state_name << std::endl; + return 0; + } + + return 1; +} + +int main() { + + std::vector> tests = { + TEST_GENERAL_CASE, + TEST_MINIMIZE_CASE, + TEST_WITH_SLACK_CASE, + TEST_UNBOUNDED_CASE, + TEST_UNSOLVABLE_CASE + }; + + int counter = 0; + + for (auto& test : tests) { + counter += test(); + } + + std::cout << "----------------------------RESULTS----------------------------" << std::endl; + std::cout << "Total number of tests: " << tests.size() << std::endl; + std::cout << "Total number of passed tests: " << counter << std::endl; return 0; } diff --git a/simplex.cpp b/simplex.cpp index d0dbf38..f55f4ef 100644 --- a/simplex.cpp +++ b/simplex.cpp @@ -6,30 +6,68 @@ enum solver_state { unbounded, - bounded + bounded, + unsolvable }; struct Result { solver_state state; - Vector *solution; + Vector solution; double objective_function_value; }; +void _printInitialInputs(Vector& C, Matrix& A, Vector& b) { +} + +void _stopIterating(Matrix& generalMatrix, Vector& C, std::vector& basicVars, solver_state state, Result& result) { + DestroyMatrix destroyedGeneralMatrix = disassembleGeneralMatrix(generalMatrix); + Matrix _A = destroyedGeneralMatrix.A; + Vector _C = destroyedGeneralMatrix.C; + Vector _b = destroyedGeneralMatrix.b; + + result.state = state; + if (state == bounded) { + result.solution = Vector(C.size()); + for (int i = 0; i < C.size(); i++) { + result.solution[i] = 0; + } + for (size_t i = 1; i < basicVars.size(); i++) { + if (basicVars[i] < C.size()) { + result.solution[basicVars[i]] = _b[i]; + } + } + + result.objective_function_value = _b[0]; + } else { + result.solution = Vector({0}); + result.objective_function_value = 0; + } +} + +/* + Implementation of the Simplex method. +*/ +Result simplex(Vector& C, Matrix& A, Vector& b, double eps = 0.01, bool maximize=true) { -Result Simplex(Vector C, Matrix A, Vector b, double eps = 0.01, bool maximize=true) { if (maximize == true) { for (int i = 0; i < C.size(); i++) { C[i] = -C[i]; - } + } } Result result{}; Matrix generalMatrix = createGeneralMatrix(A, C, b); - std::cout << "Before:" << std::endl; - showMatrix(generalMatrix); + std::cout << "Before:" << std::endl << generalMatrix; std::vector basicVars(generalMatrix.getRows()); basicVars[0] = -1; + for (int i = 0; i < b.size(); ++i) { + if (b[i] < 0) { + _stopIterating(generalMatrix, C, basicVars, unsolvable, result); + return result; + } + } + for (size_t i = 1; i < basicVars.size(); i++) { basicVars[i] = static_cast(basicVars.size()) + i; } @@ -37,65 +75,30 @@ Result Simplex(Vector C, Matrix A, Vector b, double eps = 0.01, bool maximize=tr while (true) { //3 iterationCount++; + int pivot_column_index = 0; + + pivot_column_index = min_index(generalMatrix[0]); + + if (generalMatrix[0][pivot_column_index] >= 0) { + _stopIterating(generalMatrix, C, basicVars, bounded, result); + if (!maximize) { + result.objective_function_value = -result.objective_function_value; + } + return result; + } + std::cout << "Iteration: "; std::cout << iterationCount << std::endl; - int pivot_column_index = 0; - if (maximize) { - - pivot_column_index = min_index(generalMatrix[0]); - - if (generalMatrix[0][pivot_column_index] >= 0) { - DestroyMatrix destroyedGeneralMatrix = disassembleGeneralMatrix(generalMatrix); - Matrix _A = destroyedGeneralMatrix.A; - Vector _C = destroyedGeneralMatrix.C; - Vector _b = destroyedGeneralMatrix.b; - - result.state = bounded; - result.solution = new Vector(C.size()); - for (int i = 0; i < C.size(); i++) { - result.solution->operator[](i) = 0; - } - for (size_t i = 1; i < basicVars.size(); i++) { - if (basicVars[i] <= C.size()) { - result.solution->operator[](basicVars[i]) = _b[i]; - } - } - result.objective_function_value = _C[_C.size()]; - return result; - } - } - - if (maximize == false) { - pivot_column_index = max_index(generalMatrix[0]); - - if (generalMatrix[0][pivot_column_index] < 0) { - DestroyMatrix destroyedGeneralMatrix = disassembleGeneralMatrix(generalMatrix); - Matrix _A = destroyedGeneralMatrix.A; - Vector _C = destroyedGeneralMatrix.C; - Vector _b = destroyedGeneralMatrix.b; - - result.state = bounded; - result.solution = new Vector(C.size()); - for (int i = 0; i < C.size(); i++) { - result.solution->operator[](i) = 0; - } - for (size_t i = 1; i < basicVars.size(); i++) { - if (basicVars[i] <= C.size()) { - result.solution->operator[](basicVars[i]) = _b[i]; - } - } - result.objective_function_value = _C[_C.size()]; - return result; - } - } - //4 Vector ratio_vector(generalMatrix.getRows()); for (int i = 1; i < generalMatrix.getRows(); i++) { if (generalMatrix[i][pivot_column_index] != 0) { ratio_vector[i] = generalMatrix[i][generalMatrix.getColumns() - 1] / generalMatrix[i][pivot_column_index]; - } else { + if (std::abs(ratio_vector[i]) < eps) { + ratio_vector[i] = 0; + } + } else { ratio_vector[i] = 0; } } @@ -103,91 +106,20 @@ Result Simplex(Vector C, Matrix A, Vector b, double eps = 0.01, bool maximize=tr int pivot_row_index = min_index_positive(ratio_vector); + // No leaving variable exists + if (pivot_row_index == -1) { + _stopIterating(generalMatrix, C, basicVars, unbounded, result); + return result; + } + basicVars[pivot_row_index] = pivot_column_index; //5 elimination(generalMatrix, pivot_row_index, pivot_column_index); - std::cout << "After:" << std::endl; - showMatrix(generalMatrix); - - if (maximize) { - bool thereIsNegative = false; - for (int j = 0; j < generalMatrix.getColumns()-1; ++j) { - if (generalMatrix[0][j] < 0) { - thereIsNegative = true; - } - } - if (thereIsNegative) { - for (int j = 0; j < generalMatrix.getColumns()-1; ++j) { - if (generalMatrix[0][j] > 0) { - if (generalMatrix[0][j] < (eps * (-1))) { - showMatrix(generalMatrix); - std::cout << generalMatrix[0][j] << std::endl; - return result; - } - } - } - } - } - if (maximize == false) { - bool thereIsPositive = false; - for (int j = 0; j < generalMatrix.getColumns()-1; ++j) { - if (generalMatrix[0][j] > 0) { - thereIsPositive = true; - } - } - if (thereIsPositive) { - for (int j = 0; j < generalMatrix.getColumns()-1; ++j) { - if (generalMatrix[0][j] > 0) { - if (generalMatrix[0][j] < eps) { - showMatrix(generalMatrix); - std::cout << generalMatrix[0][j] << std::endl; - return result; - } - } - } - } - } - - - + std::cout << "After:" << std::endl << generalMatrix; } return result; - - /* - Result result; - std::vector basicVars(A.getColumns() - A.getRows()); - basicVars[0] = -1; - - for (int i = 1; i < basicVars.size(); i++) { - basicVars[i] = static_cast(basicVars.size()) + i; - } - - int kc = 0; - double temp = A[0][0]; - for (int j = 0; j< A.getColumns(); j++) { - if (A[0][j] < temp) { - temp = A[0][j]; - kc = j; - } - } - - if (A[0][kc] >= 0) { - result.state = unbounded; - result.solution = new Vector(C.getRows()); - for (int i = 0; i < C.getRows(); i++) { - result.solution->operator[](i) = 0; - } - for (int i = 1; i < basicVars.size(); i++) { - if (basicVars[i] <= C.getRows()) { - (*result.solution)[basicVars[i]] = b.getRows() - 1; - } - } - result.objective_fucntion_value = b[0]; - - } - */ } /* diff --git a/simplex.h b/simplex.h index bb35dc4..283eddd 100644 --- a/simplex.h +++ b/simplex.h @@ -6,15 +6,18 @@ enum solver_state { unbounded, - bounded + bounded, + unsolvable }; struct Result { solver_state state; - Vector *solution; - double objective_fucntion_value; + Vector solution; + double objective_function_value; }; -Result Simplex(Vector C, Matrix A, Vector b, double eps = 0.01, bool maximize = true); +void _printInitialInputs(Vector& C, Matrix& A, Vector& b); +void _stopIterating(Matrix& generalMatrix, std::vector& basicVars, Result& result); +Result simplex(Vector& C, Matrix& A, Vector& b, double eps = 0.01, bool maximize = true); #endif // SIMPLEX_H \ No newline at end of file diff --git a/tools/elimination.cpp b/tools/elimination.cpp index 9b4ee4c..4bd322f 100644 --- a/tools/elimination.cpp +++ b/tools/elimination.cpp @@ -18,14 +18,14 @@ DestroyMatrix disassembleGeneralMatrix(Matrix& generalMatrix) { Matrix A(rows - 1, cols - 1); Vector C(cols - 1); - Vector b(rows - 1); + Vector b(rows); for (int j = 0; j < cols - 1; ++j) { C[j] = generalMatrix[0][j]; } - for (int i = 1; i < rows; ++i) { - b[i - 1] = generalMatrix[i - 1][cols - 1]; + for (int i = 0; i < rows; ++i) { + b[i] = generalMatrix[i][cols - 1]; } for (int i = 1; i < rows; ++i) { @@ -38,19 +38,62 @@ DestroyMatrix disassembleGeneralMatrix(Matrix& generalMatrix) { } Matrix createGeneralMatrix(Matrix& A, Vector& C, Vector& b) { - Matrix generalMatrix(A.getRows() + 1, A.getColumns() + 1); - for (int i = 0; i < A.getColumns(); i++) { - generalMatrix[0][i] = C[i]; + + int m = A.getRows() + 1; + int n = A.getColumns() + 1; + + // States if equation has a slack variable + std::vector has_slack(A.getColumns(), false); + int number_of_slack = 0; + + for (int j = 0; j < A.getColumns(); ++j) { + int basic_var_index = 0; + int ones = 0; + int zeros = 0; + + for (int i = 0; i < A.getRows(); ++i) { + if (A[i][j] == 1) { + basic_var_index = i; + ++ones; + } else if (A[i][j] == 0) { + ++zeros; + } + } + + if (ones + zeros == A.getRows()) { + has_slack[basic_var_index] = true; + number_of_slack += 1; + } } - // For objective function (j=0) the value is set to zero automatically - for (int j = 1; j < A.getRows() + 1; j++) { - generalMatrix[j][A.getColumns()] = b[j-1]; + n = n + A.getRows() - number_of_slack; + Matrix generalMatrix(m, n); + for (int i = 0; i < n; i++) { + if (i < C.size()) { + generalMatrix[0][i] = C[i]; + } else { + generalMatrix[0][i] = 0; + } } - for (int i = 0; i < A.getRows(); i++) { - for (int j = 0; j < A.getColumns(); j++) { - generalMatrix[i + 1][j] = A[i][j]; + // For objective function (j=0) the value is set to zero one step before + for (int j = 1; j < m; j++) { + generalMatrix[j][n-1] = b[j-1]; + } + + int k = 0; + for (int i = 0; i < m - 1; i++) { + for (int j = 0; j < n - 1; j++) { + if (j < A.getColumns()) { + generalMatrix[i + 1][j] = A[i][j]; + } else { + generalMatrix[i + 1][j] = 0; + } + } + + if (!has_slack[i]) { + generalMatrix[i + 1][A.getColumns() + k] = 1; + ++k; } } diff --git a/tools/math.cpp b/tools/math.cpp index db43841..384fd8f 100644 --- a/tools/math.cpp +++ b/tools/math.cpp @@ -26,7 +26,8 @@ int min_index_positive(Vector vector) { ++i; } if (i >= vector.size()) { - throw std::runtime_error("No positive min found"); + // No positive value found -> iterations stop + return -1; } int temp_index = i; double temp = vector[i]; diff --git a/tools/matrix.cpp b/tools/matrix.cpp index cac1d78..0766414 100644 --- a/tools/matrix.cpp +++ b/tools/matrix.cpp @@ -1,47 +1,5 @@ #include "matrix.h" -void showMatrix(Matrix matrix) { - int maxNumberLength = 1; - for (size_t y = 0; y < matrix.getRows(); y++) { - for (size_t x = 0; x < matrix.getColumns(); x++) { - if (std::to_string(matrix[y][x]).length() > maxNumberLength) { - maxNumberLength = std::to_string(matrix[y][x]).length(); - } - } - } - //std::cout << maxNumberLength << std::endl; - std::cout << std::endl; - for (size_t y = 0; y < matrix.getRows(); y++) { - std::string row = ""; - for (size_t x = 0; x < matrix.getColumns(); x++) { - std::string strNumber = std::to_string(matrix[y][x]); - bool spaceRight = true; - while (strNumber.length() < maxNumberLength) { - if (spaceRight) { - strNumber += " "; - } - else { - strNumber = " " + strNumber; - } - spaceRight = !spaceRight; - strNumber = "" + strNumber; - - } - if (matrix[y][x] == 0) { - row += "[\033[0m" + strNumber + "\033[0m] "; - } - else if(matrix[y][x] > 0) { - row += "[\033[32m" + strNumber + "\033[0m] "; - }else { - row += "[\033[31m" + strNumber + "\033[0m] "; - } - - } - std::cout << row << std::endl; - } - std::cout << std::endl; -} - Vector::Vector(int n) { rows = n; columns = 1; @@ -263,15 +221,45 @@ std::istream& operator>>(std::istream& cin, Matrix& matrixObj) { } std::ostream& operator<<(std::ostream& cout, Matrix& matrixObj) { - for (int i = 0; i < matrixObj.rows; ++i) { - for (int j = 0; j < matrixObj.columns; ++j) { - if (j == matrixObj.columns - 1) { - cout << matrixObj[i][j] << std::endl; - } - else { - cout << matrixObj[i][j] << ' '; + size_t maxNumberLength = 1; + for (int y = 0; y < matrixObj.getRows(); y++) { + for (int x = 0; x < matrixObj.getColumns(); x++) { + if (std::to_string(matrixObj[y][x]).length() > maxNumberLength) { + maxNumberLength = std::to_string(matrixObj[y][x]).length(); } } } + //std::cout << maxNumberLength << std::endl; + std::cout << std::endl; + for (int y = 0; y < matrixObj.getRows(); y++) { + std::string row = ""; + for (int x = 0; x < matrixObj.getColumns(); x++) { + std::string strNumber = std::to_string(matrixObj[y][x]); + bool spaceRight = true; + while (strNumber.length() < maxNumberLength) { + if (spaceRight) { + strNumber += " "; + } + else { + strNumber = " " + strNumber; + } + spaceRight = !spaceRight; + strNumber = "" + strNumber; + + } + if (matrixObj[y][x] == 0) { + row += "[\033[0m" + strNumber + "\033[0m] "; + } + else if(matrixObj[y][x] > 0) { + row += "[\033[32m" + strNumber + "\033[0m] "; + }else { + row += "[\033[31m" + strNumber + "\033[0m] "; + } + + } + std::cout << row << std::endl; + } + std::cout << std::endl; + return cout; } diff --git a/tools/matrix.h b/tools/matrix.h index af29d65..d05dcd5 100644 --- a/tools/matrix.h +++ b/tools/matrix.h @@ -97,6 +97,4 @@ public: friend std::ostream& operator<<(std::ostream& cout, Matrix& matrixObj); }; -void showMatrix(Matrix); - #endif // TOOLS_MATRIX_H