Beispiel #1
0
    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()
Beispiel #3
0
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