import numpy as np from ast_parser.parser import EquationKind # Assuming EquationKind is imported from another module class Solver: """Solver takes in the equations which have been parsed from the input, it converts them to matrix arrays and applies the matrix operations on them. Args: objective_functions (list of Equation objects): List of objective functions. constraints (list of Equation objects): List of constraint equations. """ def __init__(self, objective_functions, constraints): # Extract objective function variables and negate their coefficients self.objective_functions = objective_functions[0].variables self.objective_functions.pop('Z') self.constraints = [] for i, _ in enumerate(constraints): self.constraints.append(constraints[i]) # Negate coefficients of objective function for maximization for key in self.objective_functions: self.objective_functions[key] *= -1 # Add slack variables to constraints if necessary for temp_dict in constraints: for key in self.objective_functions.keys(): if key not in temp_dict.variables: temp_dict.variables[key] = 0 slack = len(self.constraints) i = 0 for _, constraint in enumerate(self.constraints): if constraint.kind == EquationKind.LEQ: constraint.variables["s_" + str(i)] = 1.0 self.objective_functions["s_" + str(i)] = 0 i += 1 while i < slack: constraint.variables["s_" + str(i)] = 1.0 self.objective_functions["s_" + str(i)] = 0 i += 1 self.A, self.B, self.C = self.convert_to_matrices() def get_results(self): objective_values, solution, variable_names = self.advanced_simplex(self.A, self.B, self.C) variable_names = list(variable_names.values()) results_str = "The vector of decision variables is : \n" for i in range(len(objective_values)): results_str+= f"{variable_names[i]} : {round(objective_values[i, 0], 2)}\n" results_str+= "The optimal solution is {}\n".format(solution) return results_str def convert_to_matrices(self): """Converts objective functions and constraints to matrices. Returns: A (numpy.ndarray): Coefficients matrix for constraints. B (numpy.ndarray): Right-hand side matrix for constraints. C (numpy.ndarray): Coefficients matrix for objective function. """ num_constraints = len(self.constraints) num_variables = len(self.objective_functions) A = np.zeros((num_constraints, num_variables)) C = np.zeros((1, num_variables)) B = np.zeros((num_constraints, 1)) for i, constraint in enumerate(self.constraints): for variable_name, coefficient in constraint.variables.items(): j = list(self.objective_functions.keys()).index(variable_name) A[i, j] = coefficient for variable_name, coefficient in self.objective_functions.items(): j = list(self.objective_functions.keys()).index(variable_name) C[0, j] = coefficient B[i, 0] = constraint.bound return A, B, C def advanced_simplex(self, A, b, C): """Performs the advanced simplex algorithm to find the optimal solution. Args: A (numpy.ndarray): Coefficients matrix for constraints. b (numpy.ndarray): Right-hand side matrix for constraints. C (numpy.ndarray): Coefficients matrix for objective function. Returns: objective_values (numpy.ndarray): The values of the objective function variables. solution (float): The optimal solution value. """ n, m = A.shape B = np.eye(n) C_B = np.zeros((1, n)) count = 0 variable_names = list(self.objective_functions.keys())[-n:] variable_names = {key: value for key, value in zip(range(n), variable_names)} prev_solution = float('inf') while True: count += 1 B_inverse = np.around(np.linalg.inv(B), decimals=2) for row in B_inverse: for value in row: if value == -0: value = 0 X_B = np.matmul(B_inverse, b) P_table = np.round(np.matmul(B_inverse, A), 2) objective_values = np.matmul(C_B, P_table) - C solution = np.round(np.matmul(C_B, X_B), 2) if abs(prev_solution - solution) < 0.0001: return X_B, solution, variable_names entering_var_idx = np.argmin(objective_values) ratios = [] for i in range(n): if P_table[i, entering_var_idx] > 0: ratios.append(X_B[i, 0] / P_table[i, entering_var_idx]) else: ratios.append(np.inf) exiting_var_idx = np.argmin(ratios) temp_list = list(self.objective_functions.keys()) variable_names[exiting_var_idx] = temp_list[entering_var_idx] B[:, exiting_var_idx] = A[:, entering_var_idx] C_B[:, exiting_var_idx] = C[:, entering_var_idx] prev_solution = solution