# Import required libraries import numpy as np import matplotlib.pyplot as plt import streamlit as st import sympy as sp # Pre-defined functions and their derivatives def square(x): return x**2 def derivative_square(x): return 2*x def cube(x): return x**3 def derivative_cube(x): return 3 * x**2 def sin(x): return np.sin(x) def derivative_sin(x): return np.cos(x) def inverse(x): return 1/x def derivative_inverse(x): return - (1 / x **2) def poly(x): return x + 2 * (x**2) + (0.4) * x**3 def derivative_poly(x): return 1 + 4 * x + 1.2 * x**2 # Function to calculate the derivative using SymPy def calculate_derivative(func_str): x = sp.symbols('x') try: # Parse the function string into a sympy expression func = sp.sympify(func_str) # Calculate the derivative derivative = sp.diff(func, x) func_lambdified = sp.lambdify(x, func, "numpy") derivative_lambdified = sp.lambdify(x, derivative, "numpy") return func_lambdified, derivative_lambdified except sp.SympifyError: st.error("Invalid function input. Please enter a valid mathematical expression.") return None, None # Title st.title('Gradient Descent Visualizer') st.sidebar.title("It's your turn..") # User input function = st.sidebar.selectbox('Pre Defined Functions', ['Square', 'Cube', 'Polynomial', 'sin', '1/x', 'None']) starting_point = st.sidebar.number_input('Starting Point', value=5, step=1) learning_rate = st.sidebar.number_input('Learning Rate', value=0.1, step=0.01) # Define the selected function and its derivative if function == 'Square': func = square derivative_func = derivative_square elif function == 'Cube': func = cube derivative_func = derivative_cube elif function == 'Polynomial': func = poly derivative_func = derivative_poly elif function == 'sin': func = sin derivative_func = derivative_sin elif function == '1/x': func = inverse derivative_func = derivative_inverse elif function == 'None': user_func = st.sidebar.text_input("Enter a function (in terms of x): ") func, derivative_func = calculate_derivative(user_func) if func is None: st.stop() # Check if the starting point has changed if 'last_starting_point' not in st.session_state or st.session_state.last_starting_point != starting_point: st.session_state.path = [starting_point] st.session_state.iteration = 0 st.session_state.last_starting_point = starting_point # Perform one iteration of gradient descent if st.sidebar.button('Next Iteration'): current_point = st.session_state.path[-1] new_point = current_point - learning_rate * derivative_func(current_point) st.session_state.path.append(new_point) st.session_state.iteration += 1 # Create an array of values for plotting x_values = np.linspace(-10, 10, 500) y_values = func(x_values) # Dynamic scaling based on the function's range y_min, y_max = np.min(y_values), np.max(y_values) y_padding = (y_max - y_min) * 0.1 # Plot the function and the path of points plt.figure(figsize=(8, 6)) plt.plot(x_values, y_values, label=function, color='blue') plt.scatter(st.session_state.path, [func(x) for x in st.session_state.path], color='red', zorder=5) # Calculate and plot the tangent line current_point = st.session_state.path[-1] slope = derivative_func(current_point) y_tangent = slope * (x_values - current_point) + func(current_point) plt.plot(x_values, y_tangent, '--', color='red') # Set plot limits dynamically plt.xlim([-10, 10]) plt.ylim([y_min - y_padding, y_max + y_padding]) # Display the iteration number plt.title(f'Iteration: {st.session_state.iteration}') # Labels and legend plt.xlabel('x') plt.ylabel('f(x)') plt.legend() plt.grid(True) # Display the plot st.pyplot(plt)