linalg / app.py
buoyrina
consolidate
02dd72e
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()