From 235a9d9432d7fabd259d893244702b69d13dc7d8 Mon Sep 17 00:00:00 2001 From: Pragyan <85186756+PragyanTiwari@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:57:04 +0530 Subject: [PATCH 1/4] Create __init__.py --- apps/public/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/public/__init__.py diff --git a/apps/public/__init__.py b/apps/public/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/public/__init__.py @@ -0,0 +1 @@ + From 64c3fe0c6749cce725031fccc7367e2f7e05c755 Mon Sep 17 00:00:00 2001 From: Pragyan <85186756+PragyanTiwari@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:57:22 +0530 Subject: [PATCH 2/4] img --- apps/public/gs_img.png | Bin 0 -> 82670 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/public/gs_img.png diff --git a/apps/public/gs_img.png b/apps/public/gs_img.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7852d175ccf95bef434dcbf509d9a718ae52c5 GIT binary patch literal 82670 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

i*Do7AlVz3p6zW*(- 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 Date: Mon, 29 Dec 2025 15:57:51 +0530 Subject: [PATCH 3/4] Markdown Updated --- apps/gram_schmidt_process.py | 1513 +++++++++++++++++----------------- apps/temp.py | 53 ++ 2 files changed, 818 insertions(+), 748 deletions(-) create mode 100644 apps/temp.py diff --git a/apps/gram_schmidt_process.py b/apps/gram_schmidt_process.py index fa23518..b4cd7bd 100644 --- a/apps/gram_schmidt_process.py +++ b/apps/gram_schmidt_process.py @@ -1,748 +1,765 @@ -import marimo - -__generated_with = "0.14.16" -app = marimo.App(css_file="") - - -@app.cell(hide_code=True) -def _(): - import marimo as mo - import numpy as np - import pandas as pd - import plotly.express as px - import matplotlib.pyplot as plt - from wigglystuff import Matrix - from mpl_toolkits.mplot3d import Axes3D - - # styling dicts for markdown - style_dict = { - "color": "#2d3436", - "font-family": "Roboto", - "font-size": "1.05rem", - "line-height": "1.6", - "letter-spacing": "0.5px", - "padding": "12px 18px", - "border-radius": "8px" - } - - style_dict_2 = { - "background-color": "#f9f9f9", - "padding": "12px", - "border-radius": "8px", - "line-height": "1.6" - } - - style_dict_3 = { - "border": "2px solid black", - "padding": "8px", - "border-radius": "4px", - "display": "inline-block" - } - - - # additional utility functions - def to_latex(A): - """ - rendering the matrix into LaTEX code. - """ - rows = [" & ".join(map(str, row)) for row in A] - mat = r"\begin{bmatrix}" + r" \\".join(rows) + r"\end{bmatrix}" - return r"\[" + mat + r"\]" - return Matrix, mo, np, pd, plt, px, style_dict, style_dict_2, to_latex - - -@app.cell(hide_code=True) -def _(mo): - mo.md(""" - # **Orthonormal Basis Constructions with Gram-Schmidt** - --- - """).center() - return - - -@app.cell -def _(mo, style_dict): - mo.md( - r""" - #### **Orthonormal basis are the cornerstone of Linear Algebra — a set of vectors which are not only mutually perpendicular (orthogonal) but also of unit length (normalized).** - - #### **This unique combination makes them exceptionally powerful in simplifying complex problems while dealing with large equations.** - - #### **In the context of Machine Learning, orthonormal bases serve as the backbone for techniques like Singular Value Decomposition (*SVD*), Principal Component Analysis (*PCA*), and various feature engineering methods, which are yet to be shown.** - """ - ).style(style_dict) - return - - -@app.cell -def _(mo): - mo.md(r"""
""") - return - - -@app.cell -def _(mo, style_dict_2): - mo.md( - r""" - **In Gram-Schmidt Orthogonalization, to produce Orthonormal Vectors,** - **We follow this simple approach,** - - 1. **take a set of [linearly independent vectors](https://www.wikiwand.com/en/articles/Linear_independence) (*stored in a matrix*)** — think of it like having mix fruits both apples & bananas 🍎🍌. - 2. **We then find and cut down their projection on each other** — separating apples from bananas, so nothing overlaps. - 3. **and, normalizing and arranging them so that they become Orthogonal** — now each fruit gets its own clean basket, *representing its own unique dimension* - """ - ).style(style_dict_2) - return - - -@app.cell -def _(mo): - # side quest - 1 - - statement = mo.md(""" - #### **Still not getting what Orthogonality is???** - - *So, here is the call!* - - *it simply means,* - - #### **Perpendicular Vectors are Orthogonal Vectors**, - where, the dot product of any two vectors in vector space is *0*. - - """).style({'color':'purple'}) - - mo.accordion({"side quest 🏴‍☠️":statement}).right() - return - - -@app.cell -def _(mo): - mo.md( - r""" - --- - - ## **A mathematical Intuition,** - """ - ) - return - - -@app.cell(hide_code=True) -def _(mo, style_dict_2): - mo.md( - r""" - #### For a vector space having basis \( \{ \vec{v}_1, \ldots, \vec{v}_m \} \) of a subspace \( S \subset \mathbb{R}^n \), the **Gram–Schmidt** process constructs an _**orthonormal basis**_ \( \{ \vec{w}_1, \vec{w}_2, \ldots, \vec{w}_m \} \), such that: - - \[ - \operatorname{gram\_schmidt} \left( \left\{ \vec{v}_1, \vec{v}_2, \ldots, \vec{v}_m \right\} \right) - \longrightarrow \left\{ \vec{w}_1, \vec{w}_2, \ldots, \vec{w}_m \right\} - \] - - ##### where each \( \vec{w}_i \) is orthonormal, and constructed via the following steps: - - Set: - - \[ - \vec{u}_1 = \vec{v}_1, \quad \vec{w}_1 = \frac{\vec{u}_1}{\|\vec{u}_1\|} - \] - - For each \( i = 2, 3, \ldots, m \), compute: - - \[ - \vec{u}_i = \vec{v}_i - \sum_{j=1}^{i-1} \operatorname{proj}_{\vec{w}_j}(\vec{v}_i) - = \vec{v}_i - \sum_{j=1}^{i-1} \left( \frac{\vec{w}_j^\top \vec{v}_i}{\vec{w}_j^\top \vec{w}_j} \right) \vec{w}_j - \] - - in other words, - - \[ - \begin{aligned} - \vec{u}_1 &= \vec{v}_1, & - \vec{w}_1 &= \frac{\vec{u}_1}{\|\vec{u}_1\|}, \\[8pt] - \vec{u}_2 &= \vec{v}_2 - \operatorname{proj}_{\vec{u}_1}(\vec{v}_2) - = \vec{v}_2 - \frac{\vec{u}_1^{\top}\vec{v}_2}{\vec{u}_1^{\top}\vec{u}_1} \vec{u}_1, & - \vec{w}_2 &= \frac{\vec{u}_2}{\|\vec{u}_2\|}, \\[8pt] - \vec{u}_3 &= \vec{v}_3 - \operatorname{proj}_{\vec{u}_1}(\vec{v}_3) - \operatorname{proj}_{\vec{u}_2}(\vec{v}_3), & - \vec{w}_3 &= \frac{\vec{u}_3}{\|\vec{u}_3\|}, \\[6pt] - &\;\vdots & &\;\vdots \\ - \vec{u}_k &= \vec{v}_k - \sum_{j=1}^{k-1} \operatorname{proj}_{\vec{u}_j}(\vec{v}_k), & - \vec{w}_k &= \frac{\vec{u}_k}{\|\vec{u}_k\|}. - \end{aligned} - \] - - Then normalize: - - \[ - \vec{w}_i = \frac{\vec{u}_i}{\|\vec{u}_i\|} - \] - - ##### These vectors \( \{ \vec{w}_1, \ldots, \vec{w}_m \} \) satisfy the orthonormality condition: - - \[ - \vec{w}_i^\top.\vec{w}_j = - \begin{cases} - 1 & \text{if } i = j, \\ - 0 & \text{if } i \neq j - \end{cases} - \] - - ##### and such orthonormal vectors can be assembled into the columns which build an **Orthonormal Matrix \( Q \in \mathbb{R}^{n \times m} \),** such that: - - \[ - Q^T. Q = I - \] - - ##### In practical numerical implementations (due to rounding errors), we often get: - - \[ - Q^T. Q \approx I - \] - """ - ).style(style_dict_2) - return - - -@app.cell(hide_code=True) -def _(mo): - side_note_for_norm = mo.md(r""" - ##### **An info. to be pointed out...** - - In the Gram–Schmidt process, the norm \( \| \cdot \| \) used here is the **Euclidean norm** (also known as the **\(\ell^2 \)** norm). - - \[ - \| \vec{v} \| = \sqrt{v_1^2 + v_2^2 + \cdots + v_n^2} = \left( \sum_{i=1}^n v_i^2 \right)^{1/2} - \] - - - Measuring the **Euclidean distance** of a vector \( \vec{v} \in \mathbb{R}^n \) from the origin. - """) - mo.callout(side_note_for_norm,kind="neutral") - return - - -@app.cell -def _(mo): - mo.md( - r""" - --- - ## **Python Implemenation Of Gram-Schmidt** - - looking at the python implementation of gram-schimdt process step wise, and defining a function for it, which then, we'll use it later too. - """ - ) - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md(r"""**1. firstly, defining a vector space, calling it A.**""") - return - - -@app.cell -def _(mo): - mo.md( - r""" - ```python {.marimo} - import numpy as np - ``` - - ```python {.marimo} - # a vector space A having independent linearity - - A = np.array([[1,0,0], [2,0,3], [4,5,6]]).T - ``` - - ```python {.marimo} - print(A) - ``` - """ - ) - return - - -@app.cell -def _(np): - # a vector space A having independent linearity - - A = np.array([[1,0,0], [2,0,3], [4,5,6]]).T - return (A,) - - -@app.cell(hide_code=True) -def _(A, mo, to_latex): - mo.md(to_latex(A)).left() - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md(r"""**2. Now, let's define a func. `gram_schmidt` utilizing the Gram-Schmidt Process,**""") - return - - -@app.cell -def _(np): - # defining the gram-schmidt process - - def gram_schmidt(X:np.ndarray)->np.ndarray: - - """ - original -> orthogonal -> orthonormal - args: - A set of linearly independent vectors stored in columns in the array X. - returns: - Returns matrix Q of the shape of X, having orthonormal vectors for the given vectors. - """ - Q = np.copy(X).astype("float64") - n_vecs = Q.shape[1] - - # defining a function to compute the L2-norm - length = lambda x: np.linalg.norm(x) - - # iteration with each vector in the matrix X - for nth_vec in range(n_vecs): - - # iteratively removing each preceding projection from nth vector - for k_proj in range(nth_vec): - - # the dot product would be the scaler coefficient - scaler = Q[:,nth_vec] @ Q[:,k_proj] - projection = scaler * Q[:,k_proj] - Q[:,nth_vec] -= projection # removing the Kth projection - - norm = length(Q[:,nth_vec]) - - # handling the case if the loop encounters linearly dependent vectors. - # Since, they come already under the span of vector space, hence their value will be 0. - if np.isclose(norm,0, rtol=1e-15, atol=1e-14, equal_nan=False): - Q[:,nth_vec] = 0 - else: - # making orthogonal vectors -> orthonormal - Q[:,nth_vec] = Q[:,nth_vec] / norm - - return Q - - return (gram_schmidt,) - - -@app.cell(hide_code=True) -def _(mo): - mo.md( - r""" - ```python - def gram_schmidt(X:np.ndarray)->np.ndarray: - ''' - original -> orthogonal -> orthonormal - args: - A set of linearly independent vectors stored in columns in the array X. - returns: - Returns matrix Q of the shape of X, having orthonormal vectors for the given vectors. - ''' - Q = np.copy(X).astype("float64") - n_vecs = Q.shape[1] - - # defining a function to compute the L2-norm - length = lambda x: np.linalg.norm(x) - - # iteration with each vector in the matrix X - for nth_vec in range(n_vecs): - - # iteratively removing each preceding projection from nth vector - for k_proj in range(nth_vec): - - # the dot product would be the scaler coefficient - scaler = Q[:,nth_vec] @ Q[:,k_proj] - projection = scaler * Q[:,k_proj] - Q[:,nth_vec] -= projection # removing the Kth projection - - norm = length(Q[:,nth_vec]) - - # handling the case if the loop encounters linearly dependent vectors. - # Since, they come already under the span of vector space, hence their value will be 0. - if np.isclose(norm,0, rtol=1e-15, atol=1e-14, equal_nan=False): - Q[:,nth_vec] = 0 - else: - # making orthogonal vectors -> orthonormal - Q[:,nth_vec] = Q[:,nth_vec] / norm - - return Q - ``` - """ - ) - return - - -@app.cell(hide_code=True) -def _(mo, style_dict_2): - mo.md( - r""" - **3. To check whether our function is producing Orthonormal Vectors,** - - **Defining `is_Orthonormal`to check the ortho-normality of Matrix Q,** - - \[ - Q^T. Q = I - \] - """ - ).style(style_dict_2) - return - - -@app.cell -def _(A, gram_schmidt, np): - def is_Orthonormal(Q: np.ndarray)->bool: - """ - Checks if the columns of Q are orthonormal. - For Q with shape (m, n), this checks if Q.T @ Q == I_n - """ - Q_TQ = Q.T @ Q - I = np.eye(Q.shape[1], dtype=Q.dtype) - return np.allclose(Q_TQ, I) - - - # calling the function - Q_A = gram_schmidt(A) - - return Q_A, is_Orthonormal - - -@app.cell(hide_code=True) -def _(mo): - mo.md( - r""" - ```python {.marimo} - def is_Orthonormal(Q: np.ndarray)->bool: - ''' - Checks if the columns of Q are orthonormal. - For Q with shape (m, n), this checks if Q.T @ Q == I_n - ''' - Q_TQ = Q.T @ Q - I = np.eye(Q.shape[1], dtype=Q.dtype) - return np.allclose(Q_TQ, I) - - - # calling the function - Q_A = gram_schmidt(A) - - # checking the condition - is_Orthonormal(Q_A) - ``` - """ - ) - return - - -@app.cell -def _(Q_A, is_Orthonormal): - # checking the condition - is_Orthonormal(Q_A) - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md(r"""> The Orthonormal condition satisifies and hence results in TRUE. So, the above justifying the orthogonality of the matrix `Q_A`.""") - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md( - r""" - - **4. You can find the transformation we've made so far in the matrix below,** - - """ - ) - return - - -@app.cell -def _(A, Q_A, mo, to_latex): - matrices = {"Original Vectors":[mo.md(to_latex(A)), mo.md("## hmm...").left()], - "Orthonormal Vectors":[mo.md(to_latex(Q_A.astype("int64"))), mo.md("## Perfect.").left()]} - - radio = mo.ui.radio(options=matrices, - value="Original Vectors", - label="#### **select the matrix**") - return (radio,) - - -@app.cell -def _(mo, radio, style_dict): - mo.hstack([radio.center(), radio.value[0].center(), radio.value[1].left()], - widths=[1,2,1], - align="center").style(style_dict) - return - - -@app.cell -def _(A, Q_A, mo, np, plt): - # comparison plot - - # Standard basis vectors - basis = np.eye(3) - - # Apply transformations - _transformed_A = A @ basis - _transformed_Q = Q_A @ basis - - # Create figure with adjusted layout - fig2 = plt.figure(figsize=(14, 5)) - fig2.suptitle('Matrix Transformation (A v/s Q)', y=1.05, fontsize=14) - - # Plot for Original Matrix A - _ax1 = fig2.add_subplot(121, projection='3d') - _ax1.set_title("Original Matrix Transformation (A)", fontsize=12, pad=12) - _ax1.set_xlim([0, 10]) - _ax1.set_ylim([-10, 0]) - _ax1.set_zlim([0, 10]) - arrows_A = _ax1.quiver(*np.zeros((3, 3)), *_transformed_A, - color=['r', 'g', 'b'], - arrow_length_ratio=0.12, - linewidth=2.5, - label=['A·i (1st column)', 'A·j (2nd column)', 'A·k (3rd column)']) - _ax1.legend(handles=[ - plt.Line2D([0], [0], color='r', lw=2, label='A·i (1st col)'), - plt.Line2D([0], [0], color='g', lw=2, label='A·j (2nd col)'), - plt.Line2D([0], [0], color='b', lw=2, label='A·k (3rd col)') - ], loc='upper left', fontsize=9) - _ax1.set_box_aspect([1,1,1]) - _ax1.grid(True, alpha=0.3) - _ax1.set_xlabel('X', fontsize=9) - _ax1.set_ylabel('Y', fontsize=9) - _ax1.set_zlabel('Z', fontsize=9) - - # Plot for Orthogonal Matrix Q - _ax2 = fig2.add_subplot(122, projection='3d') - _ax2.set_title("Orthogonal Component (Q)", fontsize=12, pad=12) - _ax2.set_xlim([0, 1.5]) - _ax2.set_ylim([-1.5, 0]) - _ax2.set_zlim([0, -1.5]) - arrows_Q = _ax2.quiver(*np.zeros((3, 3)), *_transformed_Q, - color=['r', 'g', 'b'], - arrow_length_ratio=0.12, - linewidth=2.5, - label=['Q·i', 'Q·j', 'Q·k']) - _ax2.legend(handles=[ - plt.Line2D([0], [0], color='r', lw=2, label='Q·i (1st col)'), - plt.Line2D([0], [0], color='g', lw=2, label='Q·j (2nd col)'), - plt.Line2D([0], [0], color='b', lw=2, label='Q·k (3rd col)') - ], loc='upper left', fontsize=9) - _ax2.set_box_aspect([1,1,1]) - _ax2.grid(True, alpha=0.3) - _ax2.set_xlabel('X', fontsize=9) - _ax2.set_ylabel('Y', fontsize=9) - _ax2.set_zlabel('Z', fontsize=9) - - plt.tight_layout() - - - mo.md( - f""" - /// details | **Want a better intuition of this through a plot, click here.** - type: info - A quiver plot to understand how original vectors differ from Orthonormal Vectors. - - {mo.as_html(fig2)} - /// - """ - ) - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md( - """ - --- - ## **Try it on your own,** - - Slide the values of Matrix A, experiment with different values and check out their Orthonormal Vectors respectively. - - The radar plot showing the orientations of **Original Matrix** `(A)` and **Orthonormal Matrix** `(Q)`. The radar plot will form triangle for Q for every linear independent vectors in `A`, otherwise, the shape will be distorted. - """ - ) - return - - -@app.cell -def _(A, Matrix): - # defining the matrix from wigglystuff widget - wiggly_matrix = Matrix(matrix=A, step=0.1, flexible_cols=True) - return (wiggly_matrix,) - - -@app.cell -def _(mo, wiggly_matrix): - # making widget accessible to marimo - w_mat = mo.ui.anywidget(wiggly_matrix) - return (w_mat,) - - -@app.cell -def _(np, w_mat): - # retrieving the matrix from the widget - mat = np.array(w_mat.value['matrix']) - return (mat,) - - -@app.cell -def _(gram_schmidt, mat): - # getting the orthonormal matrix using gram-schmidt for mat - Q_mat = gram_schmidt(mat) - return (Q_mat,) - - -@app.cell -def _(Q_mat, mat, mo, np, pd, px): - # radar plot - data = { - 'n_vec' : [f"v{i+1}" for i in range(mat.T.shape[1])], - 'A' : [np.linalg.norm(i) for i in mat.T], - 'Q' : [np.linalg.norm(i) for i in Q_mat.T] - } - - v_df = pd.melt(pd.DataFrame(data), - id_vars=['n_vec'], - value_vars=['A','Q']) - - _rd_fig = px.line_polar( - v_df, - r='value', - theta='n_vec', - color='variable', - line_close=True, - start_angle=90, - markers=True, - color_discrete_sequence=["#E74C3C", "#2980B9"], - ) - - _rd_fig.update_traces( - fill="toself", - opacity=0.7, - line=dict(width=4), - marker=dict(size=8, symbol="circle") - ) - - _rd_fig.update_layout(title='Original vs Orthonormal', - template='simple_white', - title_x=0.5, title_y=0.85, - polar_radialaxis_linecolor='black', - legend=dict( - yanchor="bottom", y=0.0, - xanchor="right", - x=1.0, orientation="v")) - - rd_fig = mo.ui.plotly(_rd_fig) - return (rd_fig,) - - -@app.cell -def _(np): - # function for checking linear dependence - - def check_linear_independence(X:np.ndarray): - """ - checks linear independence of given matrix - """ - rank = np.linalg.matrix_rank(X) - n_cols = X.shape[1] - if rank == n_cols: - return True - else: - return False - return (check_linear_independence,) - - -@app.cell -def _(Matrix, Q_mat, mo): - wiggly_Q = mo.ui.anywidget(Matrix(matrix=Q_mat, static=True)) - return (wiggly_Q,) - - -@app.cell -def _(check_linear_independence, mat, mo, w_mat, wiggly_Q): - rd_stack = mo.vstack([ - mo.md("#### A").center(), - w_mat.center(), - mo.md(""), - mo.md(f"**Linear Independence: {check_linear_independence(mat)}**").center(), - mo.md(""), - mo.md("#### Q").center(), - wiggly_Q.center() - ]) - return (rd_stack,) - - -@app.cell -def _(mo, rd_fig, rd_stack): - playground = mo.hstack([rd_stack, rd_fig], widths=[1,1.5], - align='center', justify='center') - - additional_info = mo.md(r"""The Q matrix **(denoted with blue in radar plot)** will remain fixed **(having unit length)** in radar plot, for all matrix A having linear independent vectors.""").style({'color':'blue', 'text-align':'center', 'font-size':'2rem', 'font-weight':'500'}) - return additional_info, playground - - -@app.cell -def _(additional_info, mo, playground): - mo.vstack([playground, additional_info], gap=0.005) - return - - -@app.cell -def _(mo): - mo.md( - r""" - --- - ## **Moving to the next Notebook** - - This notebook covered up the basics of gram-schimdt process and how orthonormal vectors are produced through it. - - One of its basic application is in **QR Decomposition**, which we'll be exploring in the next notebook, and seeing how the matrix A will be decomposed at fixed two matrices (One will be Orthonormal & other to be Upper-Triangular). - """ - ) - return - - -@app.cell -def _(mo): - bt = mo.ui.button("Go to QR Decomposition Notebook").style({"background-color": "#0984e3", - "color": "white", - "padding": "10px 20px", - "border": "none", - "border-radius": "5px", - "font-size": "1rem", - "cursor": "pointer"}).center() - - - bt - return - - -@app.cell -def _(): - return - - -@app.cell(hide_code=True) -def _(mo): - mo.md( - r""" - --- - ## **Acknowledgements (Resources I learnt from...)** - - This project is undertaken through many resources, the topmost resources I learnt from, - - - [Wikipedia](https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) – for providing foundational definitions and mathematical references. - - [DataCamp](https://www.datacamp.com/tutorial/orthogonal-matrix) – for providing informational article upon Orthogonality. - - [MIT OpenCourseWare](https://ocw.mit.edu/courses/18-06-linear-algebra-spring-2010/resources/lecture-17-orthogonal-matrices-and-gram-schmidt/) – for refurbishing the in-depth knowledge of Gram-Schmidt Process, taught by *Prof. Gilbert Strang*. - - [Steve Brunton (*Amazing Guy*)](https://www.google.com/search?q=steve+brunton&sca_esv=55a910f019e63594&rlz=1C1GCEA_enIN1112IN1112&sxsrf=AE3TifMoAjuMLl0MOCAV5lyl_Ga8KboiEg%3A1755118367776&ei=H_ucaP-UL_Of4-EPrsmB8QY&ved=0ahUKEwi_oOa21YiPAxXzzzgGHa5kIG4Q4dUDCBA&uact=5&oq=steve+brunton&gs_lp=Egxnd3Mtd2l6LXNlcnAiDXN0ZXZlIGJydW50b24yBBAjGCcyCxAuGIAEGJECGIoFMgsQABiABBiRAhiKBTIKEAAYgAQYQxiKBTIFEAAYgAQyBRAAGIAEMgUQABiABDIFEAAYgAQyBRAAGIAEMgUQABiABEiZC1CRBljLCHABeACQAQCYAaoBoAGvAqoBAzAuMrgBA8gBAPgBAZgCA6ACwgLCAggQABiwAxjvBcICCxAAGIAEGLADGKIEwgIKEC4YgAQYQxiKBZgDAIgGAZAGBZIHAzEuMqAHuROyBwMwLjK4B7sCwgcDMi0zyAcP&sclient=gws-wiz-serp) – for sparking the interest, this is from where I started this project. *He has a great interest in Physics Implementation of every engineering field.* - """ - ) - return - - -if __name__ == "__main__": - app.run() +import marimo + +__generated_with = "0.14.10" +app = marimo.App(css_file="") + + +@app.cell(hide_code=True) +def _(): + import asyncio + import marimo as mo + import numpy as np + import pandas as pd + import plotly.express as px + import matplotlib.pyplot as plt + from wigglystuff import Matrix + from mpl_toolkits.mplot3d import Axes3D + + # styling dicts for markdown + style_dict = { + "color": "#2d3436", + "font-family": "Roboto", + "font-size": "1.05rem", + "line-height": "1.6", + "letter-spacing": "0.5px", + "padding": "12px 18px", + "border-radius": "8px" + } + + style_dict_2 = { + "background-color": "#f9f9f9", + "padding": "12px", + "border-radius": "8px", + "line-height": "1.6" + } + + style_dict_3 = { + "border": "2px solid black", + "padding": "8px", + "border-radius": "4px", + "display": "inline-block" + } + + + # additional utility functions + def to_latex(A): + """ + rendering the matrix into LaTEX code. + """ + rows = [" & ".join(map(str, row)) for row in A] + mat = r"\begin{bmatrix}" + r" \\".join(rows) + r"\end{bmatrix}" + return r"\[" + mat + r"\]" + return ( + Matrix, + asyncio, + mo, + np, + pd, + plt, + px, + style_dict, + style_dict_2, + to_latex, + ) + + +@app.cell(hide_code=True) +def _(mo): + mo.md(""" + ## **Creating Orthonormal Vectors using Gram-Schmidt Process** + """).center() + return + + +@app.cell +def _(mo): + mo.md( + r""" +
+ **A simple Overview,** +
+ Orthonormal basis (unit vectors which are linear independent) are one of the cornerstone of Linear Algebra. These basis are mutually perpendicular, helping us in simplifying complex steps in processing large equations & problems that exist today. + + **Where it is really used and for what??** + + Some cases where you might haven't observed its presence there: + + - Being the first step towards building Principal Component Analysis (PCA) + - Used in signal processing for noise reduction. + - For solving ordinary least squares. + - and a lot more... +
+ ###### **Here the steps for it in simple terms:** + """ + ) + return + + +@app.cell +def _(): + return + + +@app.cell +def _(mo): + _src = ( + r"E:\Machine learning\Github Projects\Matrix-Decompositions-Implementation-for-SVD-PCA\apps\public\gs_img.png" + ) + mo.image(src=_src).center() + return + + +@app.cell +def _(mo): + # side quest - 1 + + statement = mo.md(""" + #### **Still not getting what Orthogonality is???** + + *So, here is the call!* + + *it simply means,* + + #### **Perpendicular Vectors are Orthogonal Vectors**, + where, the dot product of any two vectors in vector space is *0*. + + """).style({'color':'purple'}) + + mo.accordion({"side quest 🏴‍☠️":statement}).right() + return + + +@app.cell +def _(mo): + mo.md( + r""" + ## **Its mathematical Intuition,** + --- + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo, style_dict_2): + mo.md( + r""" + #### For a vector space having basis \( \{ \vec{v}_1, \ldots, \vec{v}_m \} \) of a subspace \( S \subset \mathbb{R}^n \), the **Gram–Schmidt** process constructs an _**orthonormal basis**_ \( \{ \vec{w}_1, \vec{w}_2, \ldots, \vec{w}_m \} \), such that: + + \[ + \operatorname{gram\_schmidt} \left( \left\{ \vec{v}_1, \vec{v}_2, \ldots, \vec{v}_m \right\} \right) + \longrightarrow \left\{ \vec{w}_1, \vec{w}_2, \ldots, \vec{w}_m \right\} + \] + + ##### where each \( \vec{w}_i \) is orthonormal, and constructed via the following steps: + + Set: + + \[ + \vec{u}_1 = \vec{v}_1, \quad \vec{w}_1 = \frac{\vec{u}_1}{\|\vec{u}_1\|} + \] + + For each \( i = 2, 3, \ldots, m \), compute: + + \[ + \vec{u}_i = \vec{v}_i - \sum_{j=1}^{i-1} \operatorname{proj}_{\vec{w}_j}(\vec{v}_i) + = \vec{v}_i - \sum_{j=1}^{i-1} \left( \frac{\vec{w}_j^\top \vec{v}_i}{\vec{w}_j^\top \vec{w}_j} \right) \vec{w}_j + \] + + in other words, + + \[ + \begin{aligned} + \vec{u}_1 &= \vec{v}_1, & + \vec{w}_1 &= \frac{\vec{u}_1}{\|\vec{u}_1\|}, \\[8pt] + \vec{u}_2 &= \vec{v}_2 - \operatorname{proj}_{\vec{u}_1}(\vec{v}_2) + = \vec{v}_2 - \frac{\vec{u}_1^{\top}\vec{v}_2}{\vec{u}_1^{\top}\vec{u}_1} \vec{u}_1, & + \vec{w}_2 &= \frac{\vec{u}_2}{\|\vec{u}_2\|}, \\[8pt] + \vec{u}_3 &= \vec{v}_3 - \operatorname{proj}_{\vec{u}_1}(\vec{v}_3) - \operatorname{proj}_{\vec{u}_2}(\vec{v}_3), & + \vec{w}_3 &= \frac{\vec{u}_3}{\|\vec{u}_3\|}, \\[6pt] + &\;\vdots & &\;\vdots \\ + \vec{u}_k &= \vec{v}_k - \sum_{j=1}^{k-1} \operatorname{proj}_{\vec{u}_j}(\vec{v}_k), & + \vec{w}_k &= \frac{\vec{u}_k}{\|\vec{u}_k\|}. + \end{aligned} + \] + + Then normalize: + + \[ + \vec{w}_i = \frac{\vec{u}_i}{\|\vec{u}_i\|} + \] + + ##### These vectors \( \{ \vec{w}_1, \ldots, \vec{w}_m \} \) satisfy the orthonormality condition: + + \[ + \vec{w}_i^\top.\vec{w}_j = + \begin{cases} + 1 & \text{if } i = j, \\ + 0 & \text{if } i \neq j + \end{cases} + \] + + ##### and such orthonormal vectors can be assembled into the columns which build an **Orthonormal Matrix \( Q \in \mathbb{R}^{n \times m} \),** such that: + + \[ + Q^T. Q = I + \] + + ##### In practical numerical implementations (due to rounding errors), we often get: + + \[ + Q^T. Q \approx I + \] + """ + ).style(style_dict_2) + return + + +@app.cell(hide_code=True) +def _(mo): + side_note_for_norm = mo.md(r""" + ##### **An info. to be pointed out...** + + In the Gram–Schmidt process, the norm \( \| \cdot \| \) used here is the **Euclidean norm** (also known as the **\(\ell^2 \)** norm). + + \[ + \| \vec{v} \| = \sqrt{v_1^2 + v_2^2 + \cdots + v_n^2} = \left( \sum_{i=1}^n v_i^2 \right)^{1/2} + \] + + + Measuring the **Euclidean distance** of a vector \( \vec{v} \in \mathbb{R}^n \) from the origin. + """) + mo.callout(side_note_for_norm,kind="neutral") + return + + +@app.cell +def _(mo): + mo.md( + r""" +
+ ## **Implementing Gram-Schmidt Orthogonalization in Python** + + Let's have a look at the python implemenation of gram-schmidt process defined as a function for the given `matrix A`. Follow the steps and build your own gram-schmidt function. + + _You can later modify it as per your need like extending to QR decomposition and so on..._ + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""**1. firstly, defining a vector space, calling it A.**""") + return + + +@app.cell +def _(mo): + mo.md( + r""" + ```python {.marimo} + import numpy as np + ``` + + ```python {.marimo} + # a vector space A having independent linearity + + A = np.array([[1,0,0], [2,0,3], [4,5,6]]).T + ``` + + ```python {.marimo} + print(A) + ``` + """ + ) + return + + +@app.cell +def _(np): + # a vector space A having independent linearity + + A = np.array([[1,0,0], [2,0,3], [4,5,6]]).T + return (A,) + + +@app.cell(hide_code=True) +def _(A, mo, to_latex): + mo.md(to_latex(A)).left() + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""**2. Now, let's define a func. `gram_schmidt` utilizing the Gram-Schmidt Process,**""") + return + + +@app.cell +def _(np): + # defining the gram-schmidt process + + def gram_schmidt(X:np.ndarray)->np.ndarray: + + """ + original -> orthogonal -> orthonormal + args: + A set of linearly independent vectors stored in columns in the array X. + returns: + Returns matrix Q of the shape of X, having orthonormal vectors for the given vectors. + """ + Q = np.copy(X).astype("float64") + n_vecs = Q.shape[1] + + # defining a function to compute the L2-norm + length = lambda x: np.linalg.norm(x) + + # iteration with each vector in the matrix X + for nth_vec in range(n_vecs): + + # iteratively removing each preceding projection from nth vector + for k_proj in range(nth_vec): + + # the dot product would be the scaler coefficient + scaler = Q[:,nth_vec] @ Q[:,k_proj] + projection = scaler * Q[:,k_proj] + Q[:,nth_vec] -= projection # removing the Kth projection + + norm = length(Q[:,nth_vec]) + + # handling the case if the loop encounters linearly dependent vectors. + # Since, they come already under the span of vector space, hence their value will be 0. + if np.isclose(norm,0, rtol=1e-15, atol=1e-14, equal_nan=False): + Q[:,nth_vec] = 0 + else: + # making orthogonal vectors -> orthonormal + Q[:,nth_vec] = Q[:,nth_vec] / norm + + return Q + + return (gram_schmidt,) + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ```python + def gram_schmidt(X:np.ndarray)->np.ndarray: + ''' + original -> orthogonal -> orthonormal + args: + A set of linearly independent vectors stored in columns in the array X. + returns: + Returns matrix Q of the shape of X, having orthonormal vectors for the given vectors. + ''' + Q = np.copy(X).astype("float64") + n_vecs = Q.shape[1] + + # defining a function to compute the L2-norm + length = lambda x: np.linalg.norm(x) + + # iteration with each vector in the matrix X + for nth_vec in range(n_vecs): + + # iteratively removing each preceding projection from nth vector + for k_proj in range(nth_vec): + + # the dot product would be the scaler coefficient + scaler = Q[:,nth_vec] @ Q[:,k_proj] + projection = scaler * Q[:,k_proj] + Q[:,nth_vec] -= projection # removing the Kth projection + + norm = length(Q[:,nth_vec]) + + # handling the case if the loop encounters linearly dependent vectors. + # Since, they come already under the span of vector space, hence their value will be 0. + if np.isclose(norm,0, rtol=1e-15, atol=1e-14, equal_nan=False): + Q[:,nth_vec] = 0 + else: + # making orthogonal vectors -> orthonormal + Q[:,nth_vec] = Q[:,nth_vec] / norm + + return Q + ``` + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo, style_dict_2): + mo.md( + r""" + **3. To check whether our function is producing Orthonormal Vectors,** + + **Defining `is_Orthonormal`to check the ortho-normality of Matrix Q,** + + \[ + Q^T. Q = I + \] + """ + ).style(style_dict_2) + return + + +@app.cell +def _(A, gram_schmidt, np): + def is_Orthonormal(Q: np.ndarray)->bool: + """ + Checks if the columns of Q are orthonormal. + For Q with shape (m, n), this checks if Q.T @ Q == I_n + """ + Q_TQ = Q.T @ Q + I = np.eye(Q.shape[1], dtype=Q.dtype) + return np.allclose(Q_TQ, I) + + + # calling the function + Q_A = gram_schmidt(A) + + return Q_A, is_Orthonormal + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + ```python {.marimo} + def is_Orthonormal(Q: np.ndarray)->bool: + ''' + Checks if the columns of Q are orthonormal. + For Q with shape (m, n), this checks if Q.T @ Q == I_n + ''' + Q_TQ = Q.T @ Q + I = np.eye(Q.shape[1], dtype=Q.dtype) + return np.allclose(Q_TQ, I) + + + # calling the function + Q_A = gram_schmidt(A) + + # checking the condition + is_Orthonormal(Q_A) + ``` + """ + ) + return + + +@app.cell +def _(Q_A, is_Orthonormal): + # checking the condition + is_Orthonormal(Q_A) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""> The Orthonormal condition satisifies and hence results in TRUE. So, the above justifying the orthogonality of the matrix `Q_A`.""") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + + **4. You can find the transformation we've made so far in the matrix below,** + + """ + ) + return + + +@app.cell +def _(A, Q_A, mo, to_latex): + matrices = {"Original Vectors":[mo.md(to_latex(A)), mo.md("## hmm...").left()], + "Orthonormal Vectors":[mo.md(to_latex(Q_A.astype("int64"))), mo.md("## Perfect.").left()]} + + radio = mo.ui.radio(options=matrices, + value="Original Vectors", + label="#### **select the matrix**") + return (radio,) + + +@app.cell +def _(mo, radio, style_dict): + mo.hstack([radio.center(), radio.value[0].center(), radio.value[1].left()], + widths=[1,2,1], + align="center").style(style_dict) + return + + +@app.cell +def _(A, Q_A, mo, np, plt): + # comparison plot + + # Standard basis vectors + basis = np.eye(3) + + # Apply transformations + _transformed_A = A @ basis + _transformed_Q = Q_A @ basis + + # Create figure with adjusted layout + fig2 = plt.figure(figsize=(14, 5)) + fig2.suptitle('Matrix Transformation (A v/s Q)', y=1.05, fontsize=14) + + # Plot for Original Matrix A + _ax1 = fig2.add_subplot(121, projection='3d') + _ax1.set_title("Original Matrix Transformation (A)", fontsize=12, pad=12) + _ax1.set_xlim([0, 10]) + _ax1.set_ylim([-10, 0]) + _ax1.set_zlim([0, 10]) + arrows_A = _ax1.quiver(*np.zeros((3, 3)), *_transformed_A, + color=['r', 'g', 'b'], + arrow_length_ratio=0.12, + linewidth=2.5, + label=['A·i (1st column)', 'A·j (2nd column)', 'A·k (3rd column)']) + _ax1.legend(handles=[ + plt.Line2D([0], [0], color='r', lw=2, label='A·i (1st col)'), + plt.Line2D([0], [0], color='g', lw=2, label='A·j (2nd col)'), + plt.Line2D([0], [0], color='b', lw=2, label='A·k (3rd col)') + ], loc='upper left', fontsize=9) + _ax1.set_box_aspect([1,1,1]) + _ax1.grid(True, alpha=0.3) + _ax1.set_xlabel('X', fontsize=9) + _ax1.set_ylabel('Y', fontsize=9) + _ax1.set_zlabel('Z', fontsize=9) + + # Plot for Orthogonal Matrix Q + _ax2 = fig2.add_subplot(122, projection='3d') + _ax2.set_title("Orthogonal Component (Q)", fontsize=12, pad=12) + _ax2.set_xlim([0, 1.5]) + _ax2.set_ylim([-1.5, 0]) + _ax2.set_zlim([0, -1.5]) + arrows_Q = _ax2.quiver(*np.zeros((3, 3)), *_transformed_Q, + color=['r', 'g', 'b'], + arrow_length_ratio=0.12, + linewidth=2.5, + label=['Q·i', 'Q·j', 'Q·k']) + _ax2.legend(handles=[ + plt.Line2D([0], [0], color='r', lw=2, label='Q·i (1st col)'), + plt.Line2D([0], [0], color='g', lw=2, label='Q·j (2nd col)'), + plt.Line2D([0], [0], color='b', lw=2, label='Q·k (3rd col)') + ], loc='upper left', fontsize=9) + _ax2.set_box_aspect([1,1,1]) + _ax2.grid(True, alpha=0.3) + _ax2.set_xlabel('X', fontsize=9) + _ax2.set_ylabel('Y', fontsize=9) + _ax2.set_zlabel('Z', fontsize=9) + + plt.tight_layout() + + + mo.md( + f""" + /// details | **Want a better intuition of this through a plot, click here.** + type: info + A quiver plot to understand how original vectors differ from Orthonormal Vectors. + + {mo.as_html(fig2)} + /// + """ + ) + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + """ +
+ ## **Try it on your own _(Playground)_,** + + Slide the values of `Matrix A`, experiment with different values for it and check out its Orthonormal Vectors respectively. + + The radar plot is showing the orientations of **Original Matrix** `(A)` and **Orthonormal Matrix** `(Q)`. It'll take the shape of regular triangle for Q for every linear independent vectors in `A`. + + If the vectors in A are linearly dependent, the shape becomes distorted, indicating a _loss of orthonormality_. + """ + ) + return + + +@app.cell +def _(A, Matrix): + # defining the matrix from wigglystuff widget + wiggly_matrix = Matrix(matrix=A, step=0.1, flexible_cols=True) + return (wiggly_matrix,) + + +@app.cell +def _(mo, wiggly_matrix): + # making widget accessible to marimo + w_mat = mo.ui.anywidget(wiggly_matrix) + return (w_mat,) + + +@app.cell +def _(np, w_mat): + # retrieving the matrix from the widget + mat = np.array(w_mat.value['matrix']) + return (mat,) + + +@app.cell +async def _(asyncio, gram_schmidt, mat, mo): + # getting the orthonormal matrix using gram-schmidt for mat + with mo.status.spinner("loading matrix..."): + Q_mat = await asyncio.to_thread(gram_schmidt, mat) + + return (Q_mat,) + + +@app.cell +def _(Q_mat, mat, mo, np, pd, px): + # radar plot + data = { + 'n_vec' : [f"v{i+1}" for i in range(mat.T.shape[1])], + 'A' : [np.linalg.norm(i) for i in mat.T], + 'Q' : [np.linalg.norm(i) for i in Q_mat.T] + } + + v_df = pd.melt(pd.DataFrame(data), + id_vars=['n_vec'], + value_vars=['A','Q']) + + _rd_fig = px.line_polar( + v_df, + r='value', + theta='n_vec', + color='variable', + line_close=True, + start_angle=90, + markers=True, + color_discrete_sequence=["#E74C3C", "#2980B9"], + ) + + _rd_fig.update_traces( + fill="toself", + opacity=0.7, + line=dict(width=4), + marker=dict(size=8, symbol="circle") + ) + + _rd_fig.update_layout(title='Original vs Orthonormal', + template='simple_white', + title_x=0.5, title_y=0.85, + polar_radialaxis_linecolor='black', + legend=dict( + yanchor="bottom", y=0.0, + xanchor="right", + x=1.0, orientation="v")) + + rd_fig = mo.ui.plotly(_rd_fig) + return (rd_fig,) + + +@app.cell +def _(np): + # function for checking linear dependence + + def check_linear_independence(X:np.ndarray): + """ + checks linear independence of given matrix, + by comparing no. of cols with rank of matrix. + """ + rank = np.linalg.matrix_rank(X) + n_cols = X.shape[1] + if rank == n_cols: + return True + else: + return False + return (check_linear_independence,) + + +@app.cell +def _(Matrix, Q_mat, mo): + # q matrix as wigglystuff object + wiggly_Q = mo.ui.anywidget(Matrix(matrix=Q_mat, static=True)) + return (wiggly_Q,) + + +@app.cell +def _(check_linear_independence, mat, mo, w_mat, wiggly_Q): + rd_stack = mo.vstack([ + mo.md("#### A").center(), + w_mat.center(), + mo.md(""), + mo.md(f"**Linear Independence: {check_linear_independence(mat)}**").center(), + mo.md(""), + mo.md("#### Q").center(), + wiggly_Q.center() + ]) + return (rd_stack,) + + +@app.cell +def _(mo, rd_fig, rd_stack): + playground = mo.hstack([rd_stack, rd_fig], widths=[1,1.5], + align='center', justify='center') + + _info = mo.md(r"""The Q matrix **(denoted with blue in radar plot)** will remain fixed **(having unit length)** in radar plot, for all matrix A having linear independent vectors.""").style({'color':'grey', 'text-align':'center', 'font-size':'2rem', 'font-weight':'500'}) + + additional_info = mo.callout(kind='info', value=_info) + return additional_info, playground + + +@app.cell +def _(additional_info, mo, playground): + pg = mo.vstack([playground, additional_info]) + return (pg,) + + +@app.cell +def _(pg): + pg + return + + +@app.cell +def _(mo): + mo.md( + r""" + --- + ## **Moving to the next Notebook** + + This notebook covered up the basics of gram-schimdt process and how orthonormal vectors are produced through it. + + One of its basic application is **QR Decomposition**, which we'll be exploring in the next notebook, and seeing how the matrix A will be decomposed at fixed two matrices **(One will be Orthonormal & other to be Upper-Triangular)**. + """ + ) + return + + +@app.cell +def _(mo): + bt = mo.ui.button(label="Click to go to next notebook!", + kind="info") + bt + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md( + r""" + --- + ## **Acknowledgements (Resources I learnt from...)** + + This project is undertaken through many resources, the topmost resources I learnt from, + + - [Wikipedia](https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) – for providing foundational definitions and mathematical references. + - [DataCamp](https://www.datacamp.com/tutorial/orthogonal-matrix) – for providing informational article upon Orthogonality. + - [MIT OpenCourseWare](https://ocw.mit.edu/courses/18-06-linear-algebra-spring-2010/resources/lecture-17-orthogonal-matrices-and-gram-schmidt/) – for refurbishing the in-depth knowledge of Gram-Schmidt Process, taught by *Prof. Gilbert Strang*. + - [Steve Brunton (*Amazing Guy*)](https://www.google.com/search?q=steve+brunton&sca_esv=55a910f019e63594&rlz=1C1GCEA_enIN1112IN1112&sxsrf=AE3TifMoAjuMLl0MOCAV5lyl_Ga8KboiEg%3A1755118367776&ei=H_ucaP-UL_Of4-EPrsmB8QY&ved=0ahUKEwi_oOa21YiPAxXzzzgGHa5kIG4Q4dUDCBA&uact=5&oq=steve+brunton&gs_lp=Egxnd3Mtd2l6LXNlcnAiDXN0ZXZlIGJydW50b24yBBAjGCcyCxAuGIAEGJECGIoFMgsQABiABBiRAhiKBTIKEAAYgAQYQxiKBTIFEAAYgAQyBRAAGIAEMgUQABiABDIFEAAYgAQyBRAAGIAEMgUQABiABEiZC1CRBljLCHABeACQAQCYAaoBoAGvAqoBAzAuMrgBA8gBAPgBAZgCA6ACwgLCAggQABiwAxjvBcICCxAAGIAEGLADGKIEwgIKEC4YgAQYQxiKBZgDAIgGAZAGBZIHAzEuMqAHuROyBwMwLjK4B7sCwgcDMi0zyAcP&sclient=gws-wiz-serp) – for sparking the interest, this is from where I started this project. *He has a great interest in Physics Implementation of every engineering field.* + """ + ) + return + + +if __name__ == "__main__": + app.run() diff --git a/apps/temp.py b/apps/temp.py new file mode 100644 index 0000000..0709d5d --- /dev/null +++ b/apps/temp.py @@ -0,0 +1,53 @@ +import marimo + +__generated_with = "0.14.10" +app = marimo.App(width="medium") + + +@app.cell +def _(): + import marimo as mo + return (mo,) + + +@app.cell +def _(mo): + mo.sidebar( + [ + mo.md("# marimo"), + mo.nav_menu( + { + "# Overview": f"{mo.icon('lucide:home')} Home", + "#/about": f"{mo.icon('lucide:user')} About", + "##/Steps": f"{mo.icon('lucide:phone')} Contact", + "Links": { + "https://twitter.com/marimo_io": "Twitter", + "https://github.com/marimo-team/marimo": "GitHub", + }, + }, + orientation="vertical", + ), + ] + ) + return + + +@app.cell +def _(mo): + mo.md(r"""# Overview""") + return + + +@app.cell +def _(mo): + mo.md(r"""## Steps""") + return + + +@app.cell +def _(): + return + + +if __name__ == "__main__": + app.run() From 79e9fb9562fd97ed25748e6337e6309b9c671ccc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 21:34:04 +0000 Subject: [PATCH 4/4] Add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +}