Koushik Khan commited on
Commit
cd95559
ยท
unverified ยท
2 Parent(s): bca2d8f cef6f59

Merge branch 'marimo-team:main' into feat/issue#18/polars-data-wrangling

Browse files
README.md CHANGED
@@ -26,6 +26,7 @@ notebooks for educators, students, and practitioners.
26
 
27
  - ๐ŸŽฒ Probability
28
  - ๐Ÿ“ Linear algebra
 
29
  - โ„๏ธ Polars
30
  - ๐Ÿ”ฅ Pytorch
31
 
 
26
 
27
  - ๐ŸŽฒ Probability
28
  - ๐Ÿ“ Linear algebra
29
+ - โš–๏ธ Optimization
30
  - โ„๏ธ Polars
31
  - ๐Ÿ”ฅ Pytorch
32
 
optimization/01_least_squares.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = [
4
+ # "cvxpy==1.6.0",
5
+ # "marimo",
6
+ # "numpy==2.2.2",
7
+ # ]
8
+ # ///
9
+
10
+ import marimo
11
+
12
+ __generated_with = "0.11.0"
13
+ app = marimo.App()
14
+
15
+
16
+ @app.cell(hide_code=True)
17
+ def _(mo):
18
+ mo.md(
19
+ r"""
20
+ # Least squares
21
+
22
+ In a least-squares problem, we have measurements $A \in \mathcal{R}^{m \times
23
+ n}$ (i.e., $m$ rows and $n$ columns) and $b \in \mathcal{R}^m$. We seek a vector
24
+ $x \in \mathcal{R}^{n}$ such that $Ax$ is close to $b$. The matrices $A$ and $b$ are problem data or constants, and $x$ is the variable we are solving for.
25
+
26
+ Closeness is defined as the sum of the squared differences:
27
+
28
+ \[ \sum_{i=1}^m (a_i^Tx - b_i)^2, \]
29
+
30
+ also known as the $\ell_2$-norm squared, $\|Ax - b\|_2^2$.
31
+
32
+ For example, we might have a dataset of $m$ users, each represented by $n$ features. Each row $a_i^T$ of $A$ is the feature vector for user $i$, while the corresponding entry $b_i$ of $b$ is the measurement we want to predict from $a_i^T$, such as ad spending. The prediction for user $i$ is given by $a_i^Tx$.
33
+
34
+ We find the optimal value of $x$ by solving the optimization problem
35
+
36
+ \[
37
+ \begin{array}{ll}
38
+ \text{minimize} & \|Ax - b\|_2^2.
39
+ \end{array}
40
+ \]
41
+
42
+ Let $x^\star$ denote the optimal $x$. The quantity $r = Ax^\star - b$ is known as the residual. If $\|r\|_2 = 0$, we have a perfect fit.
43
+ """
44
+ )
45
+ return
46
+
47
+
48
+ @app.cell(hide_code=True)
49
+ def _(mo):
50
+ mo.md(
51
+ r"""
52
+ ## Example
53
+
54
+ In this example, we use the Python library [CVXPY](https://github.com/cvxpy/cvxpy) to construct and solve a least-squares problems.
55
+ """
56
+ )
57
+ return
58
+
59
+
60
+ @app.cell
61
+ def _():
62
+ import cvxpy as cp
63
+ import numpy as np
64
+ return cp, np
65
+
66
+
67
+ @app.cell
68
+ def _():
69
+ m = 20
70
+ n = 15
71
+ return m, n
72
+
73
+
74
+ @app.cell
75
+ def _(m, n, np):
76
+ np.random.seed(0)
77
+ A = np.random.randn(m, n)
78
+ b = np.random.randn(m)
79
+ return A, b
80
+
81
+
82
+ @app.cell
83
+ def _(A, b, cp, n):
84
+ x = cp.Variable(n)
85
+ objective = cp.sum_squares(A @ x - b)
86
+ problem = cp.Problem(cp.Minimize(objective))
87
+ optimal_value = problem.solve()
88
+ return objective, optimal_value, problem, x
89
+
90
+
91
+ @app.cell
92
+ def _(A, b, cp, mo, optimal_value, x):
93
+ mo.md(
94
+ f"""
95
+ - The optimal value is **{optimal_value:.04f}**.
96
+ - The optimal value of $x$ is {mo.as_html(list(x.value))}
97
+ - The norm of the residual is **{cp.norm(A @ x - b, p=2).value:0.4f}**
98
+ """
99
+ )
100
+ return
101
+
102
+
103
+ @app.cell(hide_code=True)
104
+ def _(mo):
105
+ mo.md(
106
+ r"""
107
+ ## Further reading
108
+
109
+ For a primer on least squares, with many real-world examples, check out the free book
110
+ [Vectors, Matrices, and Least Squares](https://web.stanford.edu/~boyd/vmls/), which is used for undergraduate linear algebra education at Stanford.
111
+ """
112
+ )
113
+ return
114
+
115
+
116
+ @app.cell
117
+ def _():
118
+ import marimo as mo
119
+ return (mo,)
120
+
121
+
122
+ if __name__ == "__main__":
123
+ app.run()
optimization/02_linear_program.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.13"
3
+ # dependencies = [
4
+ # "cvxpy==1.6.0",
5
+ # "marimo",
6
+ # "matplotlib==3.10.0",
7
+ # "numpy==2.2.2",
8
+ # "wigglystuff==0.1.9",
9
+ # ]
10
+ # ///
11
+
12
+ import marimo
13
+
14
+ __generated_with = "0.11.0"
15
+ app = marimo.App()
16
+
17
+
18
+ @app.cell(hide_code=True)
19
+ def _(mo):
20
+ mo.md(
21
+ r"""
22
+ # Linear program
23
+
24
+ A linear program is an optimization problem with a linear objective and affine
25
+ inequality constraints. A common standard form is the following:
26
+
27
+ \[
28
+ \begin{array}{ll}
29
+ \text{minimize} & c^Tx \\
30
+ \text{subject to} & Ax \leq b.
31
+ \end{array}
32
+ \]
33
+
34
+ Here $A \in \mathcal{R}^{m \times n}$, $b \in \mathcal{R}^m$, and $c \in \mathcal{R}^n$ are problem data and $x \in \mathcal{R}^{n}$ is the optimization variable. The inequality constraint $Ax \leq b$ is elementwise.
35
+
36
+ For example, we might have $n$ different products, each constructed out of $m$ components. Each entry $A_{ij}$ is the amount of component $i$ required to build one unit of product $j$. Each entry $b_i$ is the total amount of component $i$ available. We lose $c_j$ for each unit of product $j$ ($c_j < 0$ indicates profit). Our goal then is to choose how many units of each product $j$ to make, $x_j$, in order to minimize loss without exceeding our budget for any component.
37
+
38
+ In addition to a solution $x^\star$, we obtain a dual solution $\lambda^\star$. A positive entry $\lambda^\star_i$ indicates that the constraint $a_i^Tx \leq b_i$ holds with equality for $x^\star$ and suggests that changing $b_i$ would change the optimal value.
39
+
40
+ **Why linear programming?** Linear programming is a way to achieve an optimal outcome, such as maximum utility or lowest cost, subject to a linear objective function and affine constraints. Developed in the 20th century, linear programming is widely used today to solve problems in resource allocation, scheduling, transportation, and more. The discovery of polynomial-time algorithms to solve linear programs was of tremendous worldwide importance and entered the public discourse, even making the front page of the New York Times.
41
+
42
+ In the late 20th and early 21st century, researchers generalized linear programming to a much wider class of problems called convex optimization problems. Nearly all convex optimization problems can be solved efficiently and reliably, and even more difficult problems are readily solved by a sequence of convex optimization problems. Today, convex optimization is used to fit machine learning models, land rockets in real-time at SpaceX, plan trajectories for self-driving cars at Waymo, execute many billions of dollars of financial trades a day, and much more.
43
+
44
+ This marimo learn course uses CVXPY, a modeling language for convex optimization problems developed originally at Stanford, to construct and solve convex programs.
45
+ """
46
+ )
47
+ return
48
+
49
+
50
+ @app.cell(hide_code=True)
51
+ def _(mo):
52
+ mo.md(
53
+ f"""
54
+ {mo.image("https://www.debugmind.com/wp-content/uploads/2020/01/spacex-1.jpg")}
55
+ _SpaceX solves convex optimization problems onboard to land its rockets, using CVXGEN, a code generator for quadratic programming developed at Stephen Boydโ€™s Stanford lab. Photo by SpaceX, licensed CC BY-NC 2.0._
56
+ """
57
+ )
58
+ return
59
+
60
+
61
+ @app.cell(hide_code=True)
62
+ def _(mo):
63
+ mo.md(
64
+ r"""
65
+ ## Example
66
+
67
+ Here we use CVXPY to construct and solve a linear program.
68
+ """
69
+ )
70
+ return
71
+
72
+
73
+ @app.cell
74
+ def _():
75
+ import cvxpy as cp
76
+ import numpy as np
77
+ return cp, np
78
+
79
+
80
+ @app.cell(hide_code=True)
81
+ def _(np):
82
+ A = np.array(
83
+ [
84
+ [0.76103773, 0.12167502],
85
+ [0.44386323, 0.33367433],
86
+ [1.49407907, -0.20515826],
87
+ [0.3130677, -0.85409574],
88
+ [-2.55298982, 0.6536186],
89
+ [0.8644362, -0.74216502],
90
+ [2.26975462, -1.45436567],
91
+ [0.04575852, -0.18718385],
92
+ [1.53277921, 1.46935877],
93
+ [0.15494743, 0.37816252],
94
+ ]
95
+ )
96
+
97
+ b = np.array(
98
+ [
99
+ 2.05062369,
100
+ 0.94934659,
101
+ 0.89559424,
102
+ 1.04389978,
103
+ 2.45035643,
104
+ -0.95479445,
105
+ -0.83801349,
106
+ -0.26562529,
107
+ 2.35763652,
108
+ 0.98286942,
109
+ ]
110
+ )
111
+ return A, b
112
+
113
+
114
+ @app.cell(hide_code=True)
115
+ def _(mo):
116
+ mo.md(r"""We've randomly generated problem data $A$ and $B$. The vector for $c$ is shown below. Try playing with the value of $c$ by dragging the components, and see how the level curves change in the visualization below.""")
117
+ return
118
+
119
+
120
+ @app.cell
121
+ def _(mo, np):
122
+ from wigglystuff import Matrix
123
+
124
+ c_widget = mo.ui.anywidget(Matrix(matrix=np.array([[0.1, -0.2]]), step=0.01))
125
+ c_widget
126
+ return Matrix, c_widget
127
+
128
+
129
+ @app.cell
130
+ def _(c_widget, np):
131
+ c = np.array(c_widget.value["matrix"][0])
132
+ return (c,)
133
+
134
+
135
+ @app.cell
136
+ def _(A, b, c, cp):
137
+ x = cp.Variable(A.shape[1])
138
+ prob = cp.Problem(cp.Minimize(c.T @ x), [A @ x <= b])
139
+ _ = prob.solve()
140
+ x_star = x.value
141
+ return prob, x, x_star
142
+
143
+
144
+ @app.cell(hide_code=True)
145
+ def _(mo):
146
+ mo.md(r"""Below, we plot the feasible region of the problem โ€” the intersection of the inequalities โ€” and the level curves of the objective function. The optimal value $x^\star$ is the point farthest in the feasible region in the direction $-c$.""")
147
+ return
148
+
149
+
150
+ @app.cell(hide_code=True)
151
+ def _(A, b, c, make_plot, x_star):
152
+ make_plot(A, b, c, x_star)
153
+ return
154
+
155
+
156
+ @app.cell(hide_code=True)
157
+ def _(np):
158
+ import matplotlib.pyplot as plt
159
+
160
+
161
+ def make_plot(A, b, c, x_star):
162
+ # Define a grid over a region that covers the feasible set.
163
+ # You might need to adjust these limits.
164
+ x_vals = np.linspace(-1, 1, 400)
165
+ y_vals = np.linspace(1, 3, 400)
166
+ X, Y = np.meshgrid(x_vals, y_vals)
167
+
168
+ # Flatten the grid points into an (N,2) array.
169
+ points = np.vstack([X.ravel(), Y.ravel()]).T
170
+
171
+ # For each point, check if it satisfies all the constraints: A @ x <= b.
172
+ # A dot product: shape of A @ x.T will be (m, N). We add a little tolerance.
173
+ feasible = np.all(np.dot(A, points.T) <= (b[:, None] + 1e-8), axis=0)
174
+ feasible = feasible.reshape(X.shape)
175
+
176
+ # Create the figure with a white background.
177
+ fig = plt.figure(figsize=(8, 6), facecolor="white")
178
+ ax = fig.add_subplot(111)
179
+ ax.set_facecolor("white")
180
+
181
+ # Plot the feasible region.
182
+ # Since "feasible" is a boolean array (False=0, True=1), we set contour levels so that
183
+ # the region with value 1 (feasible) gets filled.
184
+ ax.contourf(
185
+ X,
186
+ Y,
187
+ feasible.astype(float),
188
+ levels=[-0.5, 0.5, 1.5],
189
+ colors=["white", "gray"],
190
+ alpha=0.5,
191
+ )
192
+
193
+ # Plot level curves of the objective function c^T x.
194
+ # Compute c^T x over the grid:
195
+ Z = c[0] * X + c[1] * Y
196
+ # Choose several levels for the iso-cost lines.
197
+ levels = np.linspace(np.min(Z), np.max(Z), 20)
198
+ contours = ax.contour(
199
+ X, Y, Z, levels=levels, colors="gray", linestyles="--", linewidths=1
200
+ )
201
+
202
+ # Draw the vector -c as an arrow starting at x_star.
203
+ norm_c = np.linalg.norm(c)
204
+ if norm_c > 0:
205
+ head_width = norm_c * 0.1
206
+ head_length = norm_c * 0.1
207
+ # The arrow starts at x_star and points in the -c direction.
208
+ ax.arrow(
209
+ x_star[0],
210
+ x_star[1],
211
+ -c[0],
212
+ -c[1],
213
+ head_width=head_width,
214
+ head_length=head_length,
215
+ fc="black",
216
+ ec="black",
217
+ length_includes_head=True,
218
+ )
219
+ # Label the arrow near its tip.
220
+ ax.text(
221
+ x_star[0] - c[0] * 1.05,
222
+ x_star[1] - c[1] * 1.05,
223
+ r"$-c$",
224
+ color="black",
225
+ fontsize=12,
226
+ )
227
+
228
+ # Optionally, mark and label the point x_star.
229
+ ax.plot(x_star[0], x_star[1], "ko", markersize=5)
230
+ ax.text(
231
+ x_star[0],
232
+ x_star[1],
233
+ r"$\mathbf{x}^\star$",
234
+ color="black",
235
+ fontsize=12,
236
+ verticalalignment="bottom",
237
+ horizontalalignment="right",
238
+ )
239
+ # Label the axes and set title.
240
+ ax.set_xlabel("$x_1$")
241
+ ax.set_ylabel("$x_2$")
242
+ ax.set_title("Feasible Region and Level Curves of $c^Tx$")
243
+ ax.set_xlim(np.min(x_vals), np.max(x_vals))
244
+ ax.set_ylim(np.min(y_vals), np.max(y_vals))
245
+ return ax
246
+ return make_plot, plt
247
+
248
+
249
+ @app.cell(hide_code=True)
250
+ def _(mo, prob, x):
251
+ mo.md(
252
+ f"""
253
+ The optimal value is {prob.value:.04f}.
254
+
255
+ A solution $x$ is {mo.as_html(list(x.value))}
256
+ A dual solution is is {mo.as_html(list(prob.constraints[0].dual_value))}
257
+ """
258
+ )
259
+ return
260
+
261
+
262
+ @app.cell
263
+ def _():
264
+ import marimo as mo
265
+ return (mo,)
266
+
267
+
268
+ if __name__ == "__main__":
269
+ app.run()
optimization/03_minimum_fuel_optimal_control.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import marimo
2
+
3
+ __generated_with = "0.11.0"
4
+ app = marimo.App()
5
+
6
+
7
+ @app.cell
8
+ def _():
9
+ import marimo as mo
10
+ return (mo,)
11
+
12
+
13
+ @app.cell(hide_code=True)
14
+ def _(mo):
15
+ mo.md(
16
+ r"""
17
+ # Minimal fuel optimal control
18
+
19
+ This notebook includes an application of linear programming to controlling a
20
+ physical system, adapted from [Convex
21
+ Optimization](https://web.stanford.edu/~boyd/cvxbook/) by Boyd and Vandenberghe.
22
+
23
+ We consider a linear dynamical system with state $x(t) \in \mathbf{R}^n$, for $t = 0, \ldots, T$. At each time step $t = 0, \ldots, T - 1$, an actuator or input signal $u(t)$ is applied, affecting the state. The dynamics
24
+ of the system is given by the linear recurrence
25
+
26
+ \[
27
+ x(t + 1) = Ax(t) + bu(t), \quad t = 0, \ldots, T - 1,
28
+ \]
29
+
30
+ where $A \in \mathbf{R}^{n \times n}$ and $b \in \mathbf{R}^n$ are given and encode how the system evolves. The initial state $x(0)$ is also given.
31
+
32
+ The _minimum fuel optimal control problem_ is to choose the inputs $u(0), \ldots, u(T - 1)$ so as to achieve
33
+ a given desired state $x_\text{des} = x(T)$ while minimizing the total fuel consumed
34
+
35
+ \[
36
+ F = \sum_{t=0}^{T - 1} f(u(t)).
37
+ \]
38
+
39
+ The function $f : \mathbf{R} \to \mathbf{R}$ tells us how much fuel is consumed as a function of the input, and is given by
40
+
41
+ \[
42
+ f(a) = \begin{cases}
43
+ |a| & |a| \leq 1 \\
44
+ 2|a| - 1 & |a| > 1.
45
+ \end{cases}
46
+ \]
47
+
48
+ This means the fuel use is proportional to the magnitude of the signal between $-1$ and $1$, but for larger signals the marginal fuel efficiency is half.
49
+
50
+ **This notebook.** In this notebook we use CVXPY to formulate the minimum fuel optimal control problem as a linear program. The notebook lets you play with the initial and target states, letting you see how they affect the planned trajectory of inputs $u$.
51
+
52
+ First, we create the **problem data**.
53
+ """
54
+ )
55
+ return
56
+
57
+
58
+ @app.cell
59
+ def _():
60
+ import numpy as np
61
+ return (np,)
62
+
63
+
64
+ @app.cell
65
+ def _():
66
+ n, T = 3, 30
67
+ return T, n
68
+
69
+
70
+ @app.cell
71
+ def _(np):
72
+ A = np.array([[-1, 0.4, 0.8], [1, 0, 0], [0, 1, 0]])
73
+ b = np.array([[1, 0, 0.3]]).T
74
+ return A, b
75
+
76
+
77
+ @app.cell(hide_code=True)
78
+ def _(mo, n, np):
79
+ import wigglystuff
80
+
81
+ x0_widget = mo.ui.anywidget(wigglystuff.Matrix(np.zeros((1, n))))
82
+ xdes_widget = mo.ui.anywidget(wigglystuff.Matrix(np.array([[7, 2, -6]])))
83
+
84
+ _a = mo.md(
85
+ rf"""
86
+
87
+ Choose a value for $x_0$ ...
88
+
89
+ {x0_widget}
90
+ """
91
+ )
92
+
93
+ _b = mo.md(
94
+ rf"""
95
+ ... and for $x_\text{{des}}$
96
+
97
+ {xdes_widget}
98
+ """
99
+ )
100
+
101
+ mo.hstack([_a, _b], justify="space-around")
102
+ return wigglystuff, x0_widget, xdes_widget
103
+
104
+
105
+ @app.cell
106
+ def _(x0_widget, xdes_widget):
107
+ x0 = x0_widget.matrix
108
+ xdes = xdes_widget.matrix
109
+ return x0, xdes
110
+
111
+
112
+ @app.cell(hide_code=True)
113
+ def _(mo):
114
+ mo.md(r"""**Next, we specify the problem as a linear program using CVXPY.** This problem is linear because the objective and constraints are affine. (In fact, the objective is piecewise affine, but CVXPY rewrites it to be affine for you.)""")
115
+ return
116
+
117
+
118
+ @app.cell
119
+ def _():
120
+ import cvxpy as cp
121
+ return (cp,)
122
+
123
+
124
+ @app.cell
125
+ def _(A, T, b, cp, mo, n, x0, xdes):
126
+ X, u = cp.Variable(shape=(n, T + 1)), cp.Variable(shape=(1, T))
127
+
128
+ objective = cp.sum(cp.maximum(cp.abs(u), 2 * cp.abs(u) - 1))
129
+ constraints = [
130
+ X[:, 1:] == A @ X[:, :-1] + b @ u,
131
+ X[:, 0] == x0,
132
+ X[:, -1] == xdes,
133
+ ]
134
+
135
+ fuel_used = cp.Problem(cp.Minimize(objective), constraints).solve()
136
+ mo.md(f"Achieved a fuel usage of {fuel_used:.02f}. ๐Ÿš€")
137
+ return X, constraints, fuel_used, objective, u
138
+
139
+
140
+ @app.cell(hide_code=True)
141
+ def _(mo):
142
+ mo.md(
143
+ """
144
+ Finally, we plot the chosen inputs over time.
145
+
146
+ **๐ŸŒŠ Try it!** Change the initial and desired states; how do fuel usage and controls change? Can you explain what you see? You can also try experimenting with the value of $T$.
147
+ """
148
+ )
149
+ return
150
+
151
+
152
+ @app.cell
153
+ def _(plot_solution, u):
154
+ plot_solution(u)
155
+ return
156
+
157
+
158
+ @app.cell
159
+ def _(T, cp, np):
160
+ def plot_solution(u: cp.Variable):
161
+ import matplotlib.pyplot as plt
162
+
163
+ plt.step(np.arange(T), u.T.value)
164
+ plt.axis("tight")
165
+ plt.xlabel("$t$")
166
+ plt.ylabel("$u$")
167
+ return plt.gca()
168
+ return (plot_solution,)
169
+
170
+
171
+ if __name__ == "__main__":
172
+ app.run()
optimization/04_quadratic_program.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.13"
3
+ # dependencies = [
4
+ # "cvxpy==1.6.0",
5
+ # "marimo",
6
+ # "matplotlib==3.10.0",
7
+ # "numpy==2.2.2",
8
+ # "wigglystuff==0.1.9",
9
+ # ]
10
+ # ///
11
+
12
+ import marimo
13
+
14
+ __generated_with = "0.11.0"
15
+ app = marimo.App()
16
+
17
+
18
+ @app.cell(hide_code=True)
19
+ def _(mo):
20
+ mo.md(
21
+ r"""
22
+ # Quadratic program
23
+
24
+ A quadratic program is an optimization problem with a quadratic objective and
25
+ affine equality and inequality constraints. A common standard form is the
26
+ following:
27
+
28
+ \[
29
+ \begin{array}{ll}
30
+ \text{minimize} & (1/2)x^TPx + q^Tx\\
31
+ \text{subject to} & Gx \leq h \\
32
+ & Ax = b.
33
+ \end{array}
34
+ \]
35
+
36
+ Here $P \in \mathcal{S}^{n}_+$, $q \in \mathcal{R}^n$, $G \in \mathcal{R}^{m \times n}$, $h \in \mathcal{R}^m$, $A \in \mathcal{R}^{p \times n}$, and $b \in \mathcal{R}^p$ are problem data and $x \in \mathcal{R}^{n}$ is the optimization variable. The inequality constraint $Gx \leq h$ is elementwise.
37
+
38
+ **Why quadratic programming?** Quadratic programs are convex optimization problems that generalize both least-squares and linear programming.They can be solved efficiently and reliably, even in real-time.
39
+
40
+ **An example from finance.** A simple example of a quadratic program arises in finance. Suppose we have $n$ different stocks, an estimate $r \in \mathcal{R}^n$ of the expected return on each stock, and an estimate $\Sigma \in \mathcal{S}^{n}_+$ of the covariance of the returns. Then we solve the optimization problem
41
+
42
+ \[
43
+ \begin{array}{ll}
44
+ \text{minimize} & (1/2)x^T\Sigma x - r^Tx\\
45
+ \text{subject to} & x \geq 0 \\
46
+ & \mathbf{1}^Tx = 1,
47
+ \end{array}
48
+ \]
49
+
50
+ to find a nonnegative portfolio allocation $x \in \mathcal{R}^n_+$ that optimally balances expected return and variance of return.
51
+
52
+ When we solve a quadratic program, in addition to a solution $x^\star$, we obtain a dual solution $\lambda^\star$ corresponding to the inequality constraints. A positive entry $\lambda^\star_i$ indicates that the constraint $g_i^Tx \leq h_i$ holds with equality for $x^\star$ and suggests that changing $h_i$ would change the optimal value.
53
+ """
54
+ )
55
+ return
56
+
57
+
58
+ @app.cell(hide_code=True)
59
+ def _(mo):
60
+ mo.md(
61
+ r"""
62
+ ## Example
63
+
64
+ In this example, we use CVXPY to construct and solve a quadratic program.
65
+ """
66
+ )
67
+ return
68
+
69
+
70
+ @app.cell
71
+ def _():
72
+ import cvxpy as cp
73
+ import numpy as np
74
+ return cp, np
75
+
76
+
77
+ @app.cell(hide_code=True)
78
+ def _(mo):
79
+ mo.md("""First we generate synthetic data. In this problem, we don't include equality constraints, only inequality.""")
80
+ return
81
+
82
+
83
+ @app.cell
84
+ def _(np):
85
+ m = 4
86
+ n = 2
87
+
88
+ np.random.seed(1)
89
+ q = np.random.randn(n)
90
+ G = np.random.randn(m, n)
91
+ h = G @ np.random.randn(n)
92
+ return G, h, m, n, q
93
+
94
+
95
+ @app.cell(hide_code=True)
96
+ def _(mo, np):
97
+ import wigglystuff
98
+
99
+ P_widget = mo.ui.anywidget(
100
+ wigglystuff.Matrix(np.array([[4.0, -1.4], [-1.4, 4]]), step=0.1)
101
+ )
102
+
103
+ mo.md(
104
+ f"""
105
+ The quadratic form $P$ is equal to the symmetrized version of this
106
+ matrix:
107
+
108
+ {P_widget.center()}
109
+ """
110
+ )
111
+ return P_widget, wigglystuff
112
+
113
+
114
+ @app.cell
115
+ def _(P_widget, np):
116
+ P = 0.5 * (np.array(P_widget.matrix) + np.array(P_widget.matrix).T)
117
+ return (P,)
118
+
119
+
120
+ @app.cell(hide_code=True)
121
+ def _(mo):
122
+ mo.md(r"""Next, we specify the problem. Notice that we use the `quad_form` function from CVXPY to create the quadratic form $x^TPx$.""")
123
+ return
124
+
125
+
126
+ @app.cell
127
+ def _(G, P, cp, h, n, q):
128
+ x = cp.Variable(n)
129
+
130
+ problem = cp.Problem(
131
+ cp.Minimize((1 / 2) * cp.quad_form(x, P) + q.T @ x),
132
+ [G @ x <= h],
133
+ )
134
+ _ = problem.solve()
135
+ return problem, x
136
+
137
+
138
+ @app.cell(hide_code=True)
139
+ def _(mo, problem, x):
140
+ mo.md(
141
+ f"""
142
+ The optimal value is {problem.value:.04f}.
143
+
144
+ A solution $x$ is {mo.as_html(list(x.value))}
145
+ A dual solution is is {mo.as_html(list(problem.constraints[0].dual_value))}
146
+ """
147
+ )
148
+ return
149
+
150
+
151
+ @app.cell
152
+ def _(G, P, h, plot_contours, q, x):
153
+ plot_contours(P, G, h, q, x.value)
154
+ return
155
+
156
+
157
+ @app.cell(hide_code=True)
158
+ def _(mo):
159
+ mo.md(
160
+ r"""
161
+ In this plot, the gray shaded region is the feasible region (points satisfying the inequality), and the ellipses are level curves of the quadratic form.
162
+
163
+ **๐ŸŒŠ Try it!** Try changing the entries of $P$ above with your mouse. How do the
164
+ level curves and the optimal value of $x$ change? Can you explain what you see?
165
+ """
166
+ )
167
+ return
168
+
169
+
170
+ @app.cell(hide_code=True)
171
+ def _(P, mo):
172
+ mo.md(
173
+ rf"""
174
+ The above contour lines were generated with
175
+
176
+ \[
177
+ P= \begin{{bmatrix}}
178
+ {P[0, 0]:.01f} & {P[0, 1]:.01f} \\
179
+ {P[1, 0]:.01f} & {P[1, 1]:.01f} \\
180
+ \end{{bmatrix}}
181
+ \]
182
+ """
183
+ )
184
+ return
185
+
186
+
187
+ @app.cell(hide_code=True)
188
+ def _(np):
189
+ def plot_contours(P, G, h, q, x_star):
190
+ import matplotlib.pyplot as plt
191
+
192
+ # Create a grid of x and y values.
193
+ x = np.linspace(-5, 5, 400)
194
+ y = np.linspace(-5, 5, 400)
195
+ X, Y = np.meshgrid(x, y)
196
+
197
+ # Compute the quadratic form Q(x, y) = a*x^2 + 2*b*x*y + c*y^2.
198
+ # Here, a = P[0,0], b = P[0,1] (and P[1,0]), c = P[1,1]
199
+ Z = (
200
+ 0.5 * (P[0, 0] * X**2 + 2 * P[0, 1] * X * Y + P[1, 1] * Y**2)
201
+ + q[0] * X
202
+ + q[1] * Y
203
+ )
204
+
205
+ # --- Evaluate the constraints on the grid ---
206
+ # We stack X and Y to get a list of (x,y) points.
207
+ points = np.vstack([X.ravel(), Y.ravel()]).T
208
+
209
+ # Start with all points feasible
210
+ feasible = np.ones(points.shape[0], dtype=bool)
211
+
212
+ # Apply the inequality constraints Gx <= h.
213
+ # Each row of G and corresponding h defines a condition.
214
+ for i in range(G.shape[0]):
215
+ # For a given point x, the condition is: G[i,0]*x + G[i,1]*y <= h[i]
216
+ feasible &= points.dot(G[i]) <= h[i] + 1e-8 # small tolerance
217
+ # Reshape the boolean mask back to grid shape.
218
+ feasible_grid = feasible.reshape(X.shape)
219
+
220
+ # --- Plot the feasible region and contour lines---
221
+ plt.figure(figsize=(8, 6))
222
+
223
+ # Use contourf to fill the region where feasible_grid is True.
224
+ # We define two levels, so that points that are True (feasible) get one
225
+ # color.
226
+ plt.contourf(
227
+ X,
228
+ Y,
229
+ feasible_grid,
230
+ levels=[-0.5, 0.5, 1.5],
231
+ colors=["white", "gray"],
232
+ alpha=0.5,
233
+ )
234
+
235
+ contours = plt.contour(X, Y, Z, levels=10, cmap="viridis")
236
+ plt.clabel(contours, inline=True, fontsize=8)
237
+ plt.title("Feasible region and level curves")
238
+ plt.xlabel("$x_1$")
239
+ plt.ylabel("$y_2$")
240
+ # plt.colorbar(contours, label='Q(x, y)')
241
+
242
+ ax = plt.gca()
243
+ # Optionally, mark and label the point x_star.
244
+ ax.plot(x_star[0], x_star[1], "ko", markersize=5)
245
+ ax.text(
246
+ x_star[0],
247
+ x_star[1],
248
+ r"$\mathbf{x}^\star$",
249
+ color="black",
250
+ fontsize=12,
251
+ verticalalignment="bottom",
252
+ horizontalalignment="right",
253
+ )
254
+ return plt.gca()
255
+ return (plot_contours,)
256
+
257
+
258
+ @app.cell
259
+ def _():
260
+ import marimo as mo
261
+ return (mo,)
262
+
263
+
264
+ if __name__ == "__main__":
265
+ app.run()
optimization/README.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Learn optimization
2
+
3
+ ๐Ÿšง _This collection is a work in progress. Check back later for new notebooks!_
4
+
5
+ This collection of marimo notebooks teaches you the basics of mathematical
6
+ optimization.
7
+
8
+ After working through these notebooks, you'll understand how to create
9
+ and solve optimization problems using the Python library
10
+ [CVXPY](https://github.com/cvxpy/cvxpy), as well as how to apply what you've
11
+ learned to real-world problems such as portfolio allocation in finance,
12
+ resource allocation, and more.
13
+
14
+ ![SpaceX](https://www.debugmind.com/wp-content/uploads/2020/01/spacex-1.jpg)
15
+
16
+ _SpaceX solves convex optimization problems onboard to land its rockets, using CVXGEN, a code generator for quadratic programming developed at Stephen Boydโ€™s Stanford lab. Photo by SpaceX, licensed CC BY-NC 2.0._
17
+
18
+ **Running notebooks.** To run a notebook locally, use
19
+
20
+ ```bash
21
+ uvx marimo edit <URL>
22
+ ```
23
+
24
+ For example, run the least-squares tutorial with
25
+
26
+ ```bash
27
+ uvx marimo edit https://github.com/marimo-team/learn/blob/main/optimization/01_least_squares.py
28
+ ```
29
+
30
+ You can also open notebooks in our online playground by appending `marimo.app/`
31
+ to a notebook's URL: [marimo.app/github.com/marimo-team/learn/blob/main/optimization/01_least_squares.py](https://marimo.app/https://github.com/marimo-team/learn/blob/main/optimization/01_least_squares.py).
probability/01_sets.py ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = [
4
+ # "marimo",
5
+ # ]
6
+ # ///
7
+
8
+ import marimo
9
+
10
+ __generated_with = "0.11.0"
11
+ app = marimo.App()
12
+
13
+
14
+ @app.cell(hide_code=True)
15
+ def _(mo):
16
+ mo.md(
17
+ r"""
18
+ # Sets
19
+
20
+ Probability is the study of "events", assigning numerical values to how likely
21
+ events are to occur. For example, probability lets us quantify how likely it is for it to rain or shine on a given day.
22
+
23
+
24
+ Typically we reason about _sets_ of events. In mathematics,
25
+ a set is a collection of elements, with no element included more than once.
26
+ Elements can be any kind of object.
27
+
28
+ For example:
29
+
30
+ - โ˜€๏ธ Weather events: $\{\text{Rain}, \text{Overcast}, \text{Clear}\}$
31
+ - ๐ŸŽฒ Die rolls: $\{1, 2, 3, 4, 5, 6\}$
32
+ - ๐Ÿช™ Pairs of coin flips = $\{ \text{(Heads, Heads)}, \text{(Heads, Tails)}, \text{(Tails, Tails)} \text{(Tails, Heads)}\}$
33
+
34
+ Sets are the building blocks of probability, and will arise frequently in our study.
35
+ """
36
+ )
37
+ return
38
+
39
+
40
+ @app.cell(hide_code=True)
41
+ def _(mo):
42
+ mo.md(r"""## Set operations""")
43
+ return
44
+
45
+
46
+ @app.cell(hide_code=True)
47
+ def _(mo):
48
+ mo.md(r"""In Python, sets are made with the `set` function:""")
49
+ return
50
+
51
+
52
+ @app.cell
53
+ def _():
54
+ A = set([2, 3, 5, 7])
55
+ A
56
+ return (A,)
57
+
58
+
59
+ @app.cell
60
+ def _():
61
+ B = set([0, 1, 2, 3, 5, 8])
62
+ B
63
+ return (B,)
64
+
65
+
66
+ @app.cell(hide_code=True)
67
+ def _(mo):
68
+ mo.md(
69
+ r"""
70
+ Below we explain common operations on sets.
71
+
72
+ _**Try it!** Try modifying the definitions of `A` and `B` above, and see how the results change below._
73
+
74
+ The **union** $A \cup B$ of sets $A$ and $B$ is the set of elements in $A$, $B$, or both.
75
+ """
76
+ )
77
+ return
78
+
79
+
80
+ @app.cell
81
+ def _(A, B):
82
+ A | B
83
+ return
84
+
85
+
86
+ @app.cell(hide_code=True)
87
+ def _(mo):
88
+ mo.md(r"""The **intersection** $A \cap B$ is the set of elements in both $A$ and $B$""")
89
+ return
90
+
91
+
92
+ @app.cell
93
+ def _(A, B):
94
+ A & B
95
+ return
96
+
97
+
98
+ @app.cell(hide_code=True)
99
+ def _(mo):
100
+ mo.md(r"""The **difference** $A \setminus B$ is the set of elements in $A$ that are not in $B$.""")
101
+ return
102
+
103
+
104
+ @app.cell
105
+ def _(A, B):
106
+ A - B
107
+ return
108
+
109
+
110
+ @app.cell(hide_code=True)
111
+ def _(mo):
112
+ mo.md(
113
+ """
114
+ ### ๐ŸŽฌ An interactive example
115
+
116
+ Here's a simple example that classifies TV shows into sets by genre, and uses these sets to recommend shows to a user based on their preferences.
117
+ """
118
+ )
119
+ return
120
+
121
+
122
+ @app.cell(hide_code=True)
123
+ def _(mo):
124
+ viewer_type = mo.ui.radio(
125
+ options={
126
+ "I like action and drama!": "New Viewer",
127
+ "I only like action shows": "Action Fan",
128
+ "I only like dramas": "Drama Fan",
129
+ },
130
+ value="I like action and drama!",
131
+ label="Which genre do you prefer?",
132
+ )
133
+ return (viewer_type,)
134
+
135
+
136
+ @app.cell(hide_code=True)
137
+ def _(viewer_type):
138
+ viewer_type
139
+ return
140
+
141
+
142
+ @app.cell
143
+ def _():
144
+ action_shows = {"Stranger Things", "The Witcher", "Money Heist"}
145
+ drama_shows = {"The Crown", "Money Heist", "Bridgerton"}
146
+ return action_shows, drama_shows
147
+
148
+
149
+ @app.cell
150
+ def _(action_shows, drama_shows):
151
+ recommendations = {
152
+ "New Viewer": action_shows | drama_shows, # Union for new viewers
153
+ "Action Fan": action_shows - drama_shows, # Unique action shows
154
+ "Drama Fan": drama_shows - action_shows, # Unique drama shows
155
+ }
156
+ return (recommendations,)
157
+
158
+
159
+ @app.cell(hide_code=True)
160
+ def _(mo, recommendations, viewer_type):
161
+ result = recommendations[viewer_type.value]
162
+
163
+ explanation = {
164
+ "New Viewer": "You get everything to explore!",
165
+ "Action Fan": "Pure action, no drama!",
166
+ "Drama Fan": "Drama-focused selections!",
167
+ }
168
+
169
+ mo.md(f"""
170
+ **๐ŸŽฌ Recommended shows.** Based on your preference for **{viewer_type.value}**,
171
+ we recommend:
172
+
173
+ {", ".join(result)}
174
+
175
+ **Why these shows?**
176
+ {explanation[viewer_type.value]}
177
+ """)
178
+ return explanation, result
179
+
180
+
181
+ @app.cell(hide_code=True)
182
+ def _(mo):
183
+ mo.md("""
184
+ ### Exercise
185
+
186
+ Given these sets:
187
+
188
+ - A = {๐ŸŽฎ, ๐Ÿ“ฑ, ๐Ÿ’ป}
189
+
190
+ - B = {๐Ÿ“ฑ, ๐Ÿ’ป, ๐Ÿ–จ๏ธ}
191
+
192
+ - C = {๐Ÿ’ป, ๐Ÿ–จ๏ธ, โŒจ๏ธ}
193
+
194
+ Can you:
195
+
196
+ 1. Find all elements that are in A or B
197
+
198
+ 2. Find elements common to all three sets
199
+
200
+ 3. Find elements in A that aren't in C
201
+
202
+ <details>
203
+
204
+ <summary>Check your answers!</summary>
205
+
206
+ 1. A โˆช B = {๐ŸŽฎ, ๐Ÿ“ฑ, ๐Ÿ’ป, ๐Ÿ–จ๏ธ}<br>
207
+ 2. A โˆฉ B โˆฉ C = {๐Ÿ’ป}<br>
208
+ 3. A - C = {๐ŸŽฎ, ๐Ÿ“ฑ}
209
+
210
+ </details>
211
+ """)
212
+ return
213
+
214
+
215
+ @app.cell(hide_code=True)
216
+ def _(mo):
217
+ mo.md(
218
+ r"""
219
+ ## ๐Ÿงฎ Set properties
220
+
221
+ Here are some important properties of the set operations:
222
+
223
+ 1. **Commutative**: $A \cup B = B \cup A$
224
+ 2. **Associative**: $(A \cup B) \cup C = A \cup (B \cup C)$
225
+ 3. **Distributive**: $A \cup (B \cap C) = (A \cup B) \cap (A \cup C)$
226
+ """
227
+ )
228
+ return
229
+
230
+
231
+ @app.cell(hide_code=True)
232
+ def _(mo):
233
+ mo.md(
234
+ r"""
235
+ ## Set builder notation
236
+
237
+ To compactly describe the elements in a set, we can use **set builder notation**, which specifies conditions that must be true for elements to be in the set.
238
+
239
+ For example, here is how to specify the set of positive numbers less than 10:
240
+
241
+ \[
242
+ \{x \mid 0 < x < 10 \}
243
+ \]
244
+
245
+ The predicate to the right of the vertical bar $\mid$ specifies conditions that must be true for an element to be in the set; the expression to the left of $\mid$ specifies the value being included.
246
+
247
+ In Python, set builder notation is called a "set comprehension."
248
+ """
249
+ )
250
+ return
251
+
252
+
253
+ @app.cell
254
+ def _():
255
+ def predicate(x):
256
+ return x > 0 and x < 10
257
+ return (predicate,)
258
+
259
+
260
+ @app.cell
261
+ def _(predicate):
262
+ set(x for x in range(100) if predicate(x))
263
+ return
264
+
265
+
266
+ @app.cell(hide_code=True)
267
+ def _(mo):
268
+ mo.md("""**Try it!** Try modifying the `predicate` function above and see how the set changes.""")
269
+ return
270
+
271
+
272
+ @app.cell(hide_code=True)
273
+ def _(mo):
274
+ mo.md("""
275
+ ## Summary
276
+
277
+ You've learned:
278
+
279
+ - Basic set operations
280
+ - Set properties
281
+ - Real-world applications
282
+
283
+ In the next lesson, we'll define probability from the ground up, using sets.
284
+
285
+ Remember: In probability, every event is a set, and every set can be an event!
286
+ """)
287
+ return
288
+
289
+
290
+ @app.cell
291
+ def _():
292
+ import marimo as mo
293
+ return (mo,)
294
+
295
+
296
+ if __name__ == "__main__":
297
+ app.run()
probability/README.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Learn probability
2
+
3
+ ๐Ÿšง _This collection is a work in progress. Check back later for new noteboks._
4
+
5
+ This collection of marimo notebooks teaches the fundamentals of probability,
6
+ with an emphasis on computation with Python.
7
+
8
+
9
+ **Running notebooks.** To run a notebook locally, use
10
+
11
+ ```bash
12
+ uvx marimo edit <URL>
13
+ ```
14
+
15
+ For example, run the numbers tutorial with
16
+
17
+ ```bash
18
+ uvx marimo edit https://github.com/marimo-team/learn/blob/main/probability/01_sets.py
19
+ ```
20
+
21
+ You can also open notebooks in our online playground by appending `marimo.app/`
22
+ to a notebook's URL: [marimo.app/https://github.com/marimo-team/learn/blob/main/probability/01_sets.py](https://marimo.app/https://github.com/marimo-team/learn/blob/main/probability/01_sets.py).