def fixed_point(self, expr_g, x0, tolerance, n): ''' It calculates an approximation to a root through finding an intersection between y = x and x = g(x) where g(x) is derivated from the function f. parameters: - x0 : initial point - expr_g: function expresion derivated from f - tolerance: maximum allowed error - n: number of iterations until failure ''' if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate number of iterations = {n}") else: fx = self.__function.eval(x0) count = 0 error = tolerance + 1 data = { "x": np.array([x0], dtype=np.float64), "f(x)": np.array([fx], dtype=np.float64), "E": np.array([np.NaN], dtype=np.float64) } g = Function(expr_g) while fx != 0 and error > tolerance and count < n: xn = g.eval(x0) fx = self.__function.eval(xn) error = self.__error(x0, xn) x0 = xn count += 1 data["x"] = np.append(data["x"], [xn]) data["f(x)"] = np.append(data["f(x)"], [fx]) data["E"] = np.append(data["E"], [error]) if fx == 0: print(f"{x0} is a root") elif error < tolerance: print( f"{x0} is an approximation to a root with tolerance = {tolerance}" ) else: print(f"failure in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame)
def run(step, point_type, lambda_p, method): f = Function(lambda_p) n = f.get_size() # x size # Initial point if point_type == "const": x0 = np.ones(n) else: x0 = np.random.uniform(-2, 2, n) mxitr = 50000 # Max number of iterations tol_g = 1e-8 # Tolerance for gradient tol_x = 1e-8 # Tolerance for x tol_f = 1e-8 # Tolerance for function # Method for step update if step == "fijo": msg = "StepFijo" elif step == "hess": msg = "StepHess" else: msg = "Backtracking" step_size = 1 # Gradient step size for "StepFijo" method # Estimate minimum point through optimization method chosen if method == "gd": alg = GD() elif method == "newton": alg = Newton() else: print("\n Error: Invalid optimization method: %s\n" % method) return xs = alg.iterate(x0, mxitr, tol_g, tol_x, tol_f, f, msg, step_size) # Print point x found and function value f(x) # print("\nPoint x found: ", xs[-1]) print("\nf(x) = ", f.eval(xs[-1])) plt.plot(np.array(range(n)), xs[-1]) plt.plot(np.array(range(n)), f.y) plt.legend(['x*', 'y'], loc = 'best') plt.show()
class OpenMethods: ''' This class contains the followings methods to caltulate some root in an one variable function: - Fixed point - Newton's method - Secant - Multiple roots Args: - function f: function to work with - error: function to calculate the type of error (absolute or relative). relative is set by default. ''' def __init__(self, expression, error=relative): self.__function = Function(expression) self.__error = error def fixed_point(self, expr_g, x0, tolerance, n): ''' It calculates an approximation to a root through finding an intersection between y = x and x = g(x) where g(x) is derivated from the function f. parameters: - x0 : initial point - expr_g: function expresion derivated from f - tolerance: maximum allowed error - n: number of iterations until failure ''' if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate number of iterations = {n}") else: fx = self.__function.eval(x0) count = 0 error = tolerance + 1 data = { "x": np.array([x0], dtype=np.float64), "f(x)": np.array([fx], dtype=np.float64), "E": np.array([np.NaN], dtype=np.float64) } g = Function(expr_g) while fx != 0 and error > tolerance and count < n: xn = g.eval(x0) fx = self.__function.eval(xn) error = self.__error(x0, xn) x0 = xn count += 1 data["x"] = np.append(data["x"], [xn]) data["f(x)"] = np.append(data["f(x)"], [fx]) data["E"] = np.append(data["E"], [error]) if fx == 0: print(f"{x0} is a root") elif error < tolerance: print( f"{x0} is an approximation to a root with tolerance = {tolerance}" ) else: print(f"failure in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def newtons_method(self, x0, tolerance, n): ''' It calculates an approximation to a root through finding an intersection between dy_dx and axis x where dy_dx is the first diff of the function f. - x0 : initial point - tolerance: maximum allowed error - n: number of iterations until failure parameters: ''' if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate number of iterations = {n}") else: fx = self.__function.eval(x0) df = self.__function.dfn(x0, 1) count = 0 error = tolerance + 1 data = { "x": np.array([x0], dtype=np.float64), "f(x)": np.array([fx], dtype=np.float64), "f'(x)": np.array([df], dtype=np.float64), "E": np.array([np.NaN], dtype=np.float64) } while fx != 0 and df != 0 and error > tolerance and count < n: xn = x0 - (fx / df) fx = self.__function.eval(xn) df = self.__function.dfn(xn, 1) error = self.__error(x0, xn) x0 = xn count += 1 data["x"] = np.append(data["x"], [xn]) data["f(x)"] = np.append(data["f(x)"], [fx]) data["f'(x)"] = np.append(data["f'(x)"], [df]) data["E"] = np.append(data["E"], [error]) if fx == 0: print(f"{x0} is a root") elif error < tolerance: print( f"{x0} is an approximation to a root with tolerance = {tolerance}" ) elif df == 0: print(f"{x0} is a possible multiple root") else: print(f"failure in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)", "f'(x)", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def secant(self, x0, x1, tolerance, n): ''' It calculates an approximation to a root by finding an intersection between the intersection of the secant line (with x0,x1) and axis x where x0, x1 are known initial points. parameters: - x0 : initial point 1 - x1 : initial point 2 - tolerance: maximum allowed error - n: number of iterations until failure ''' fx0 = self.__function.eval(x0) if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate number of iterations = {n}") elif fx0 == 0: print(f"{x0} is a root") else: fx1 = self.__function.eval(x1) den = fx1 - fx0 count = 0 error = tolerance + 1 data = { "x": np.array([x0, x1], dtype=np.float64), "f(x)": np.array([fx0, fx1], dtype=np.float64), "den": np.array([np.NaN, den], dtype=np.float64), "E": np.array([np.NaN, np.NaN], dtype=np.float64) } while fx1 != 0 and error > tolerance and den != 0 and count < n: x2 = x1 - (fx1 * (x1 - x0)) / den error = self.__error(x1, x2) x0 = x1 fx0 = fx1 x1 = x2 fx1 = self.__function.eval(x1) den = fx1 - fx0 count += 1 data["x"] = np.append(data["x"], [x1]) data["f(x)"] = np.append(data["f(x)"], [fx1]) data["den"] = np.append(data["den"], [den]) data["E"] = np.append(data["E"], [error]) if fx1 == 0: print(f"{x1} is a root") elif error < tolerance: print( f"{x1} is an approximation to a root with tolerance = {tolerance}" ) elif den == 0: print(f"{x1} is a possible multiple root") else: print(f"failure in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)", "den", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def multiple_roots(self, x0, tolerance, n): ''' It calculates an approximation to a multiple root by becoming it a simple root and applying Newton's method. parameters: - x0 : initial point - tolerance: maximum allowed error - n: number of iterations until failure ''' if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate number of iterations = {n}") else: fx = self.__function.eval(x0) df1 = self.__function.dfn(x0, 1) df2 = self.__function.dfn(x0, 2) count = 0 error = tolerance + 1 data = { "x": np.array([x0], dtype=np.float64), "f(x)": np.array([fx], dtype=np.float64), "f'(x)": np.array([df1], dtype=np.float64), "f''(x)": np.array([df2], dtype=np.float64), "E": np.array([np.NaN], dtype=np.float64) } while fx != 0 and error > tolerance and count < n: xn = x0 - (fx * df1) / ((df1**2) - fx * df2) fx = self.__function.eval(xn) df1 = self.__function.dfn(xn, 1) df2 = self.__function.dfn(xn, 2) error = self.__error(x0, xn) x0 = xn count += 1 data["x"] = np.append(data["x"], [xn]) data["f(x)"] = np.append(data["f(x)"], [fx]) data["f'(x)"] = np.append(data["f'(x)"], [df1]) data["f''(x)"] = np.append(data["f''(x)"], [df2]) data["E"] = np.append(data["E"], [error]) if fx == 0: print(f"{x0} is a root") elif error < tolerance: print( f"{x0} is an approximation to a root with tolerance = {tolerance}" ) else: print(f"failure in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)", "f'(x)", "f''(x)", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def get_function(self): return self.__function
class ClosedMethods: ''' This class contains the followings methods to caltulate some root in an one variable function: - Incremental search - Bisection - False rule parameters: - expr: math expression to work with - error: function to calculate the type of error (absolute or relative). relative is set by default. ''' def __init__(self, expr, error=relative): self.__function = Function(expr) self.error = error def incremental_search(self, x0, delta, n): ''' Tries to find out initial values beginning by a random x and going forward incrementally until a sign change happens or n iterations are reached. parameters: - x0: random x value to begin - delta: increment for x in each iteration. - n: number of iterations. ''' if n < 1: print(f"innapropiate num of iterations = {n}") else: delta = np.abs(delta) # handling negative values fx0 = self.__function.eval(x0) data = { "x": np.array([x0], dtype=np.float64), "f(x)": np.array([fx0], dtype=np.float64) } if fx0 == 0: print(f"{x0} is a root") else: x1 = x0 + delta count = 1 fx1 = self.__function.eval(x1) while (fx0 * fx1 > 0) and (count < n): data["x"] = np.append(data["x"], [x1]) data["f(x)"] = np.append(data["f(x)"], [fx1]) x0 = x1 fx0 = fx1 x1 += delta fx1 = self.__function.eval(x1) count += 1 data["x"] = np.append(data["x"], [x1]) data["f(x)"] = np.append(data["f(x)"], [fx1]) if fx1 == 0: print(f"{x1} is a root") elif fx0 * fx1 < 0: print(f" There is a root between {x0} and {x1}") else: print(f"Failed in {n} iterations") data_frame = pd.DataFrame(data, columns=("x", "f(x)"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def bisection(self, xi, xs, tolerance, n, false_rule=False): ''' Given a continuos function within [xi, xs] interval and a sign change between it, it tries to find out the closest value to a root by calculating a mean of x. parameters: - xi: lower end of the interval. - xs: higher end of the interval. - tolerance: maximun allowed error. - n: number of iterations. - false_rule: set True if you want to choose false rule method ''' if tolerance < 0: print(f"innapropiate tolerance = {tolerance}") elif n < 1: print(f"innapropiate num of iterations = {n}") else: data = { "xi": np.array([], dtype=np.float64), "xs": np.array([], dtype=np.float64), "xm": np.array([], dtype=np.float64), "f(xm)": np.array([], dtype=np.float64), "E": np.array([], dtype=np.float64) } fxi = self.__function.eval(xi) fxs = self.__function.eval(xs) if fxi == 0: print(f"{xi} is a root") elif fxs == 0: print(f"{xs} is a root") elif fxi * fxs < 0: xm = np.float64(xi - (fxi * (xs - xi)) / (fxs - fxi)) if false_rule else np.mean( np.array((xi, xs), dtype=np.float64)) fxm = self.__function.eval(xm) count = 1 error = tolerance + 1 data["xi"] = np.append(data["xi"], [xi]) data["xs"] = np.append(data["xs"], [xs]) data["xm"] = np.append(data["xm"], [xm]) data["f(xm)"] = np.append(data["f(xm)"], [fxm]) data["E"] = np.append(data["E"], [np.NaN]) while error > tolerance and fxm != 0 and count < n: if fxi * fxm < 0: xs = xm fxs = fxm else: xi = xm fxi = fxm x_aux = xm xm = np.float64(xi - (fxi * (xs - xi)) / (fxs - fxi)) if false_rule else np.mean( np.array((xi, xs), dtype=np.float64)) fxm = self.__function.eval(xm) error = self.error(xm, x_aux) count += 1 data["xi"] = np.append(data["xi"], [xi]) data["xs"] = np.append(data["xs"], [xs]) data["xm"] = np.append(data["xm"], [xm]) data["f(xm)"] = np.append(data["f(xm)"], [fxm]) data["E"] = np.append(data["E"], [error]) if fxm == 0: print(f"{xm} is a root") elif error < tolerance: print( f"{xm} is an approximation to a root with tolerance = {tolerance}" ) else: print(f"Failure in {n} iterations") else: print("The interval is inappropriate") data_frame = pd.DataFrame(data, columns=("xi", "xs", "xm", "f(xm)", "E"), dtype=np.float64) data_frame.index.name = "n" print(data_frame) def get_function(self): return self.__function