def is_invalid( self, matrix): #tests the last row to see if it has a contradiction # should I use more conditions to test? Test row 1 and row 2? return MyDecimal(matrix[2][1]).is_near_zero() and MyDecimal( matrix[2][2]).is_near_zero() and not MyDecimal( matrix[2][3]).is_near_zero()
def invalid_plane(self): if not MyDecimal(self.constant_term).is_near_zero(): for i in range(self.dimension): if not MyDecimal(self[i]).is_near_zero(): return False return True return False
def __eq__(self, p): if self.dimension != p.dimension: return False for i in range(self.dimension): if not MyDecimal( float(self.normal_vector[i]) - float(p.normal_vector[i])).is_near_zero(): return False if not MyDecimal(self.constant_term - p.constant_term).is_near_zero(): return False return True
def __eq__(self, v): if self.dimension != v.dimension: return False for i in range(self.dimension): if not MyDecimal(self.coordinates[i] - v.coordinates[i]).is_near_zero(): return False return True
def swap_with_highest_nonzero_row(self, row, column): #swap row with the highest row that's nonzero in the same column changed = False for i in range(row+1, len(self.planes)): if not MyDecimal(self.planes[i][column]).is_near_zero(): self.swap_rows(row, i) changed = True break return changed
def reduce_matrix(self, matrix): # get [0][0] to 1, [1][0] to 0, [2][0] to 0, [1][1] to 1, [2][1] to 0, and [2][1] to 0 in that order. # if stuck, swap. If [0][0] is 0, swap with the highest row with a nonzero in that column # If [1][1] is 0, swap with row 3 #get [0][0] to 1 if MyDecimal(matrix[0][0]).is_near_zero(): if not MyDecimal(matrix[1][0]).is_near_zero(): matrix = self.swap_rows(matrix, 0, 1) elif not MyDecimal(matrix[2][0]).is_near_zero(): matrix = self.swap_rows(matrix, 0, 2) leadCoefficient = matrix[0][0] if not MyDecimal(leadCoefficient).is_near_zero(): for i in range(4): matrix[0][i] /= leadCoefficient #get [1][0] to 0 leadCoefficient = matrix[1][0] for i in range(4): matrix[1][i] -= leadCoefficient*matrix[0][i] #get [2][0] to 0 leadCoefficient = matrix[2][0] for i in range(4): matrix[2][i] -= leadCoefficient*matrix[0][i] #get [1][1] to 1 if MyDecimal(matrix[1][1]).is_near_zero(): if not MyDecimal(matrix[2][1]).is_near_zero(): matrix = self.swap_rows(matrix, 1, 2) leadCoefficient = matrix[1][1] if not MyDecimal(leadCoefficient).is_near_zero(): for i in range(3): matrix[1][i+1] /= leadCoefficient #get [2][1] to 0 leadCoefficient = matrix[2][1] if not MyDecimal(leadCoefficient).is_near_zero(): for i in range(4): matrix[2][i] -= leadCoefficient*matrix[1][i] #get [2][2] to 1 leadCoefficient = matrix[2][2] if not MyDecimal(leadCoefficient).is_near_zero(): for i in range(2): matrix[2][i+2] /= leadCoefficient return matrix
def solve_line(self, system): #should I update for x = 0 in all columns? # remember pivot vs free variables # identify whether y or z can be solved for if MyDecimal(system[1][1]).is_near_zero(): # z can be solved for #solve for z: z = c/x3 z = Decimal(round(system[1][3]/system[1][2], 3)) # modify c: c += (y * x2) c = Decimal(round(system[0][3] - (z*system[0][2]), 3)) return str(system[0][0]) + "x + " + str(system[0][1]) + "y = " + str(c) + " and z = " + str(z) elif MyDecimal(system[1][2]).is_near_zero(): # y can be solved for # solve for y: y = c y = Decimal(round(system[1][3], 3)) # modify c: c += (y * x2) c = Decimal(round(system[0][3] - (y*system[0][1]), 3)) return str(system[0][0]) + "x + " + str(system[0][2]) + "z = " + str(c) + " and y = " + str(y) else: return "Error. Could not solve."
def angle(self, v, radians): dotProduct = self.dot_product(v) magnitudeProduct = self.magnitude() * v.magnitude() if MyDecimal(Decimal(magnitudeProduct)).is_near_zero(): return 0 elif radians: return math.acos(dotProduct/magnitudeProduct) else: if round(dotProduct/magnitudeProduct, 6) == 1.0000: return 0 else: return math.degrees(math.acos(dotProduct/magnitudeProduct))
def compute_rref(self): system = self.compute_triangular_form() system.extract_basepoint() system.extract_direction_vectors() for current_row in range(len(system.planes)): pivot_found = False for current_col in range(system.planes[0].dimension): if not MyDecimal(system.planes[current_row][current_col]).is_near_zero(): if not pivot_found: pivot_found = True system.clear_above(current_row, current_col) system.set_pivot_to_one(current_row, current_col) return system
def solve_system(self): # this assumes no free variables ''' algorithm: if all are parallel to each other, return infinite solutions if any plane is invalid, indicate no solution for each row, starting from len(system.planes) and going to 0 for each col from len(system.planes[0) to 0 if a saved value exists for that column's variable, multiply it by the column variable and add it to the constant if the saved value doesn't exist, is the furthest left 1, and all values right except for constant are 0, save the value as the constant find answers if some variables are unassigned at the end, parameterize ''' system = self.compute_rref() saved_solutions = ["None"] * system.planes[0].dimension are_parallel = True first_plane = system.planes[0] for i in range(1, len(system.planes)): if system.planes[i].invalid_plane(): return "The system is invalid; there is no intersection." if not first_plane.is_parallel(system.planes[i]) or system.planes[i].is_blank(): #blank planes shouldn't count as parallel. They can be paramterized are_parallel = False break if are_parallel: return "The lines are parallel; the system has an infinite number of solutions." for i in range(len(system.planes)-1, -1, -1): leftmost_one = system.leftmost_one(i) for j in range(system.planes[0].dimension-1, -1, -1): if saved_solutions[j] != "None" and not MyDecimal(system[i][j]).is_near_zero(): system[i][system.planes[0].dimension] += saved_solutions[j] * system[i][j] system[i][j] = 0 else: if j == leftmost_one: saved_solutions[j] = system[i][system.planes[0].dimension] for i in range(len(saved_solutions)): if saved_solutions[i] == "None": copy_system = self.compute_rref() direction_vectors = copy_system.extract_direction_vectors() basepoint = copy_system.extract_basepoint() return Parametrization(basepoint, direction_vectors) #return "Infinitely many solutions." return saved_solutions
def compute_triangular_form(self): system = deepcopy(self) num_equations = len(self.planes) num_variables = self.planes[0].dimension current_column = 0 for current_row in range(num_equations): current_column = current_row first_nonzero = True while current_column < num_variables: current_coefficient = system[current_row][current_column] if MyDecimal(current_coefficient).is_near_zero(): # move the first nonzero term up high if first_nonzero: # if all preceding values are 0 if system.swap_with_highest_nonzero_row(current_row, current_column): #if a swappable row exists # set column below pivot to zeroes system.clear_below(current_row, current_column) if current_column == num_variables - 1 and self.planes[current_row][current_column] == 0: current_column += 1 else: current_column += 1 else: current_column += 1 else: if first_nonzero: # if all the prior columns in this row are zeroes # clear the first valued column's values in other rows system.clear_below(current_row, current_column) first_nonzero = False current_column+=1 continue # convert the redundant equations to all zeroes if num_equations > num_variables: for i in range(num_variables, num_equations): system[i] = Plane() return system
def first_nonzero_index(iterable): for k, item in enumerate(iterable): if not MyDecimal(item).is_near_zero(): return k raise Exception(Plane.NO_NONZERO_ELTS_FOUND_MSG)
def redundant3(self, matrix): #tests the last row to see if it's redundant return MyDecimal(matrix[2][1]).is_near_zero() and MyDecimal(matrix[2][2]).is_near_zero() and MyDecimal(matrix[2][3]).is_near_zero()
def clear_below(self, row, column): # set to 0 below a pivot for i in range(row+1, len(self.planes)): if not MyDecimal(self.planes[i][column]).is_near_zero(): cancel_ratio = -(self.planes[i][column] / self.planes[row][column]) self.add_multiple_times_row_to_row(cancel_ratio, row, i)
def clear_above(self, row, column): # set to 0 above a pivot for i in range(row): if not MyDecimal(self.planes[i][column]).is_near_zero(): coefficient = -(self.planes[i][column] / self.planes[row][column]) self.add_multiple_times_row_to_row(coefficient, row, i) #cancel the value directly above pivot
def orthogonal_to(self, v): #dot product is close enough to zero, adjusting for rounding error dotProduct = self.dot_product(v) return MyDecimal(Decimal(dotProduct)).is_near_zero()
def parallel_to(self, v): #angle is close enough to zero, adjusting for rounding error angle = self.angle(v, False) print angle return MyDecimal(Decimal(angle)).is_near_zero() or MyDecimal(Decimal(angle - 180)).is_near_zero()
def leftmost_one(self, row): for i in range(0, self.planes[0].dimension): if MyDecimal(Decimal(self.planes[row][i]) - Decimal(1.0)).is_near_zero(): return i return -1
def first_nonzero_index(iterable): # iterates through vector to return index of first nonzero term OR throws an exception for k, item in enumerate(iterable): if not MyDecimal(item).is_near_zero(): return k raise Exception(Line.NO_NONZERO_ELTS_FOUND_MSG)
def is_blank(self): is_blank = True for i in range(self.dimension + 1): if not MyDecimal(self[i]).is_near_zero(): is_blank = False return is_blank