import gradio as gr import numpy as np import matplotlib.pyplot as plt from io import BytesIO from PIL import Image def matrix_vector_multiplication_visualization(matrix, vector): try: # Parse inputs matrix = np.array([[float(x) for x in row.split(",")] for row in matrix.split(";")]) vector = np.array([float(x) for x in vector.split(",")]) # Ensure the matrix is 2x2 and the vector is 2D if matrix.shape != (2, 2): return "Error: Matrix must be 2x2.", None if vector.shape != (2,): return "Error: Vector must be 2D.", None # Perform matrix-vector multiplication transformed_vector = np.dot(matrix, vector) # Create a grid for visualization x = np.linspace(-1, 1, 10) y = np.linspace(-1, 1, 10) X, Y = np.meshgrid(x, y) grid = np.vstack([X.flatten(), Y.flatten()]) transformed_grid = np.dot(matrix, grid).reshape(2, -1, 10) # Create the plot fig, ax = plt.subplots(figsize=(6, 6)) # Plot the grid before and after transformation #for i in range(grid.shape[1]): # ax.plot([grid[0, i], transformed_grid[0, i]], [grid[1, i], transformed_grid[1, i]], # color="gray", linewidth=0.5, alpha=0.7) # Plot the original vector ax.quiver(0, 0, vector[0], vector[1], angles="xy", scale_units="xy", scale=1, color="red", label="Original Vector") # Plot the transformed vector ax.quiver(0, 0, transformed_vector[0], transformed_vector[1], angles="xy", scale_units="xy", scale=1, color="blue", label="Transformed Vector") # Plot settings ax.axhline(0, color='black', linewidth=0.5) ax.axvline(0, color='black', linewidth=0.5) ax.set_xlim(-2, 2) ax.set_ylim(-2, 2) ax.set_aspect('equal') ax.grid(True) ax.legend() ax.set_title("Matrix-Vector Multiplication Visualization") # Save the plot to a BytesIO object buf = BytesIO() plt.savefig(buf, format="png") buf.seek(0) plt.close(fig) return f"Transformed Vector: {transformed_vector.tolist()}", Image.open(buf) except Exception as e: return f"Error: {str(e)}", None def visualize_points_and_vectors_with_start(points, vectors): try: # Parse points points = [list(map(float, p.split(','))) for p in points.split(';') if p.strip()] # Parse vectors with starting points vectors_with_start = [ list(map(float, v.split(','))) for v in vectors.split(';') if v.strip() ] # Create the plot fig, ax = plt.subplots(figsize=(6, 6)) # Plot points for point in points: ax.plot(point[0], point[1], 'o', label=f"Point ({point[0]}, {point[1]})") # Plot vectors with starting points for vector in vectors_with_start: if len(vector) == 4: # Check format [start_x, start_y, vector_x, vector_y] start_x, start_y, vec_x, vec_y = vector ax.quiver( start_x, start_y, vec_x, vec_y, angles='xy', scale_units='xy', scale=1, color='r', label=f"Vector from ({start_x},{start_y}) to ({start_x+vec_x},{start_y+vec_y})" ) # Plot settings ax.axhline(0, color='black', linewidth=0.5) ax.axvline(0, color='black', linewidth=0.5) ax.set_xlim(-10, 10) ax.set_ylim(-10, 10) ax.set_aspect('equal') ax.grid(True) ax.legend() ax.set_title("Points and Vectors Visualization") # Save the plot to a BytesIO object buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) plt.close(fig) return Image.open(buf) except Exception as e: return f"Error: {str(e)}" def calculate_dot_product_and_angle(vector1, vector2): try: # Convert input strings to numpy arrays vec1 = np.array([float(x) for x in vector1.split(",")]) vec2 = np.array([float(x) for x in vector2.split(",")]) # Check if vectors have the same length if len(vec1) != len(vec2): return "Error: Vectors must have the same length.", None # Compute the dot product dot_product = np.dot(vec1, vec2) # Compute magnitudes of vectors magnitude_vec1 = np.linalg.norm(vec1) magnitude_vec2 = np.linalg.norm(vec2) # Normalized dot product normalized_dot_product = dot_product / (magnitude_vec1 * magnitude_vec2) # Compute the angle in radians and convert to degrees angle_radians = np.arccos(np.clip(normalized_dot_product, -1.0, 1.0)) angle_degrees = np.degrees(angle_radians) explanation = ( f"Dot product: {dot_product}\n" f"Normalized dot product: {normalized_dot_product:.4f}\n" f"Angle (radians): {angle_radians:.4f}\n" f"Angle (degrees): {angle_degrees:.4f}" ) # Plot the vectors fig, ax = plt.subplots(figsize=(6, 6)) ax.quiver(0, 0, vec1[0], vec1[1], angles='xy', scale_units='xy', scale=1, color='r', label="Vector 1") ax.quiver(0, 0, vec2[0], vec2[1], angles='xy', scale_units='xy', scale=1, color='b', label="Vector 2") # Plot settings ax.set_xlim(-max(abs(vec1[0]), abs(vec2[0])) - 1, max(abs(vec1[0]), abs(vec2[0])) + 1) ax.set_ylim(-max(abs(vec1[1]), abs(vec2[1])) - 1, max(abs(vec1[1]), abs(vec2[1])) + 1) ax.axhline(0, color='black', linewidth=0.5) ax.axvline(0, color='black', linewidth=0.5) ax.set_aspect('equal') ax.grid(True) ax.legend() ax.set_title("Vector Visualization") # Save plot to a BytesIO object buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) plt.close(fig) return explanation, Image.open(buf) except ValueError: return "Error: Please enter valid numeric values separated by commas.", None def transformation_composition_with_vectors(matrix1, matrix2, vectors): try: # Parse input matrices matrix1 = np.array([[float(x) for x in row.split(",")] for row in matrix1.split(";")]) matrix2 = np.array([[float(x) for x in row.split(",")] for row in matrix2.split(";")]) # Ensure both matrices are 2x2 if matrix1.shape != (2, 2) or matrix2.shape != (2, 2): return "Error: Both matrices must be 2x2.", None # Parse vectors vectors = np.array([[float(x) for x in vector.split(",")] for vector in vectors.split(";")]).T if vectors.shape[0] != 2: return "Error: Vectors must be 2D (two components per vector).", None # Compute the transformations vectors_after_matrix1 = np.dot(matrix1, vectors) vectors_after_composition = np.dot(np.dot(matrix2, matrix1), vectors) # Plot the transformations fig, ax = plt.subplots(figsize=(8, 8)) # Plot original vectors for vector in vectors.T: ax.quiver(0, 0, vector[0], vector[1], angles='xy', scale_units='xy', scale=1, color="gray", alpha=0.7) # Plot vectors after Matrix 1 for vector in vectors_after_matrix1.T: ax.quiver(0, 0, vector[0], vector[1], angles='xy', scale_units='xy', scale=1, color="orange", alpha=0.7) # Plot vectors after composition for vector in vectors_after_composition.T: ax.quiver(0, 0, vector[0], vector[1], angles='xy', scale_units='xy', scale=1, color="blue", alpha=0.7) # Add legend ax.legend(["Original Vectors", "After Matrix 1", "After Matrix 2 × Matrix 1"], loc="upper left") # Axes settings ax.axhline(0, color="black", linewidth=0.5) ax.axvline(0, color="black", linewidth=0.5) ax.set_xlim(-3, 3) ax.set_ylim(-3, 3) ax.set_aspect("equal") ax.grid(True) ax.set_title("Matrix-Matrix Multiplication as Transformation Composition") # Save the plot buf = BytesIO() plt.savefig(buf, format="png") buf.seek(0) plt.close(fig) return "Success", Image.open(buf) except Exception as e: return f"Error: {str(e)}", None # Create the Gradio app with gr.Blocks() as app: with gr.Tab("Points vs. Vectors"): gr.Markdown("## Points and Vectors Visualization with Starting Points") gr.Markdown(""" - **Points**: Enter semicolon-separated 2D points, e.g., `1,2; 3,4`. - **Vectors**: Enter vectors with starting points in the format `start_x,start_y,vector_x,vector_y; ...`, e.g., `0,0,2,1; 3,4,-1,2`. """) with gr.Row(): points_input = gr.Textbox(label="Points (semicolon-separated)", placeholder="e.g., 1,2; 3,4") vectors_input = gr.Textbox(label="Vectors (with starting points)", placeholder="e.g., 0,0,2,1; 3,4,-1,2") output_image = gr.Image(label="Visualization") visualize_button = gr.Button("Visualize") visualize_button.click( fn=visualize_points_and_vectors_with_start, inputs=[points_input, vectors_input], outputs=output_image ) with gr.Tab("Vector Dot Product"): gr.Markdown("## Dot Product, Normalized Dot Product, and Angle Calculator") gr.Markdown("Enter two vectors (comma-separated) to calculate their dot product, normalized dot product, and angle.") with gr.Row(): vector1_input = gr.Textbox(label="Vector 1", placeholder="e.g., 1, 2") vector2_input = gr.Textbox(label="Vector 2", placeholder="e.g., 4, 5") output_text = gr.Textbox(label="Result", lines=6) output_image = gr.Image(label="Visualization") calculate_button = gr.Button("Calculate and Visualize") calculate_button.click( fn=calculate_dot_product_and_angle, inputs=[vector1_input, vector2_input], outputs=[output_text, output_image] ) with gr.Tab("Matrix-Vector Multiplication"): gr.Markdown("## Matrix-Vector Multiplication Visualization") gr.Markdown(""" - Enter a **2x2 matrix** as `a,b;c,d` (rows separated by semicolons). - Enter a **2D vector** as `x,y`. - See the original vector (red), transformed vector (blue), and grid transformation. """) with gr.Row(): matrix_input = gr.Textbox(label="Matrix (2x2, e.g., 1,0;0,1)", placeholder="e.g., 1,0;0,1") vector_input = gr.Textbox(label="Vector (2D, e.g., 1,1)", placeholder="e.g., 1,1") output_text = gr.Textbox(label="Result") output_image = gr.Image(label="Visualization") calculate_button = gr.Button("Visualize") calculate_button.click( fn=matrix_vector_multiplication_visualization, inputs=[matrix_input, vector_input], outputs=[output_text, output_image] ) with gr.Tab('Matrix-Matrix Multiplication'): gr.Markdown("## Matrix Multiplication as Transformation Composition (Using Vectors)") gr.Markdown(""" - Enter two **2x2 matrices** in the format `a,b;c,d` (rows separated by semicolons). - Enter vectors in the format `x1,y1;x2,y2;...` (semicolon-separated pairs). - The app will show: - The original vectors. - The vectors after applying **Matrix 1**. - The vectors after applying the composition (**Matrix 2 × Matrix 1**). """) with gr.Row(): matrix1_input = gr.Textbox(label="Matrix 1 (2x2, e.g., 1,0;0,1)", placeholder="e.g., 1,0;0,1") matrix2_input = gr.Textbox(label="Matrix 2 (2x2, e.g., 2,0;0,1)", placeholder="e.g., 2,0;0,1") vectors_input = gr.Textbox(label="Vectors (e.g., 1,0;0,1)", placeholder="e.g., 1,0;0,1") output_text = gr.Textbox(label="Output") output_image = gr.Image(label="Visualization") calculate_button = gr.Button("Visualize") calculate_button.click( fn=transformation_composition_with_vectors, inputs=[matrix1_input, matrix2_input, vectors_input], outputs=[output_text, output_image] ) app.launch()