Remove breaks to improve readability

This commit is contained in:
José Valim 2021-09-25 20:54:27 +02:00
parent cd2c3bd5a8
commit 026945e669

View file

@ -39,8 +39,6 @@ Systems of equations are a central theme in numerical computing.
These equations are often expressed and solved with multidimensional These equations are often expressed and solved with multidimensional
arrays. For example, this is a two dimensional array: arrays. For example, this is a two dimensional array:
<!-- livebook:{"break_markdown":true} -->
$$ $$
\begin{bmatrix} \begin{bmatrix}
1 & 2 \\ 1 & 2 \\
@ -48,8 +46,6 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
Elixir programmers typically express a similar data structure using Elixir programmers typically express a similar data structure using
a list of lists, like this: a list of lists, like this:
@ -77,8 +73,6 @@ than `Enum.map/2` and `Enum.reduce/3`.
In this section, we'll look at some of the various tools for In this section, we'll look at some of the various tools for
creating and interacting with tensors. creating and interacting with tensors.
<!-- livebook:{"break_markdown":true} -->
To get started, we'll first need to include the Nx dependency. To get started, we'll first need to include the Nx dependency.
In Livebook, we can simply use Mix install. At the time of this writing, In Livebook, we can simply use Mix install. At the time of this writing,
there's no Hex package for Nx, so we'll use a git there's no Hex package for Nx, so we'll use a git
@ -102,8 +96,6 @@ Now, everything is set up, so we're ready to create some tensors.
### Creating tensors ### Creating tensors
<!-- livebook:{"break_markdown":true} -->
Start out by getting a feel for Nx through its documentation. Start out by getting a feel for Nx through its documentation.
Do so through the IEx helpers, like this: Do so through the IEx helpers, like this:
@ -138,8 +130,6 @@ The result shows all of the major fields that make up a tensor:
* The shape of the tensor, going left to right, with the outside dimensions listed first. * The shape of the tensor, going left to right, with the outside dimensions listed first.
* The names of each dimension. * The names of each dimension.
<!-- livebook:{"break_markdown":true} -->
We can easily convert it to a binary: We can easily convert it to a binary:
```elixir ```elixir
@ -168,8 +158,6 @@ because they must often be processed. Elixir binaries make quick work
of dealing with numerical data structured for platforms other than of dealing with numerical data structured for platforms other than
Elixir. Elixir.
<!-- livebook:{"break_markdown":true} -->
We can get any cell of the tensor: We can get any cell of the tensor:
```elixir ```elixir
@ -200,8 +188,6 @@ Now,
* return a `{2, 2}` tensor containing the first two columns * return a `{2, 2}` tensor containing the first two columns
of the first two rows of the first two rows
<!-- livebook:{"break_markdown":true} -->
We can get information about this most recent term with We can get information about this most recent term with
the IEx helper `i`, like this: the IEx helper `i`, like this:
@ -223,8 +209,6 @@ to get more documentation about tensors. We could also open an Elixir
cell, type Nx.tensor, and hover the cursor over the word `tensor` cell, type Nx.tensor, and hover the cursor over the word `tensor`
to see the help about that function. to see the help about that function.
<!-- livebook:{"break_markdown":true} -->
We can get the shape of the tensor with `Nx.shape/1`: We can get the shape of the tensor with `Nx.shape/1`:
```elixir ```elixir
@ -242,8 +226,6 @@ changes the metadata, so it has no notable cost.
The new tensor has the same type, but a new shape. The new tensor has the same type, but a new shape.
<!-- livebook:{"break_markdown":true} -->
Now, reshape the tensor to contain three dimensions with Now, reshape the tensor to contain three dimensions with
one batch, one row, and four columns. one batch, one row, and four columns.
@ -263,8 +245,6 @@ Nx.tensor([[1, 2, 3]], names: [:rows, :cols], type: {:u, 8})
We created a tensor of the shape `{1, 3}`, with the type `u8`, We created a tensor of the shape `{1, 3}`, with the type `u8`,
the values `[1, 2, 3]`, and two axes named `rows` and `cols`. the values `[1, 2, 3]`, and two axes named `rows` and `cols`.
<!-- livebook:{"break_markdown":true} -->
Now we know how to create tensors, so it's time to do something with them. Now we know how to create tensors, so it's time to do something with them.
## Tensor aware functions ## Tensor aware functions
@ -324,8 +304,6 @@ Nx.sum(tensor, axes: [:x])
Nx sums the values across the `x` dimension: `1 + 2` in the first row Nx sums the values across the `x` dimension: `1 + 2` in the first row
and `3 + 4` in the second row. and `3 + 4` in the second row.
<!-- livebook:{"break_markdown":true} -->
Now, Now,
* create a `{2, 2, 2}` tensor * create a `{2, 2, 2}` tensor
@ -341,8 +319,6 @@ Sometimes, we need to combine two tensors together with an
operator. Let's say we wanted to subtract one tensor from operator. Let's say we wanted to subtract one tensor from
another. Mathematically, the expression looks like this: another. Mathematically, the expression looks like this:
<!-- livebook:{"break_markdown":true} -->
$$ $$
\begin{bmatrix} \begin{bmatrix}
5 & 6 \\ 5 & 6 \\
@ -358,8 +334,6 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
To solve this problem, add each integer on the left with the To solve this problem, add each integer on the left with the
corresponding integer on the right. Unfortunately, we cannot corresponding integer on the right. Unfortunately, we cannot
use Elixir's built-in subtraction operator as it is not tensor-aware. use Elixir's built-in subtraction operator as it is not tensor-aware.
@ -383,8 +357,6 @@ Often, the dimensions of tensors in an operator don't match.
For example, you might want to subtract a `1` from every For example, you might want to subtract a `1` from every
element of a `{2, 2}` tensor, like this: element of a `{2, 2}` tensor, like this:
<!-- livebook:{"break_markdown":true} -->
$$ $$
\begin{bmatrix} \begin{bmatrix}
1 & 2 \\ 1 & 2 \\
@ -396,8 +368,6 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
Mathematically, it's the same as this: Mathematically, it's the same as this:
$$ $$
@ -415,8 +385,6 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
That means we need a way to convert `1` to a `{2, 2}` tensor. That means we need a way to convert `1` to a `{2, 2}` tensor.
`Nx.broadcast/2` solves that problem. This function takes `Nx.broadcast/2` solves that problem. This function takes
a tensor or a scalar and a shape. a tensor or a scalar and a shape.
@ -451,8 +419,6 @@ Nx.subtract(10, tensor)
Or subtract a row or column. Mathematically, it would look like this: Or subtract a row or column. Mathematically, it would look like this:
<!-- livebook:{"break_markdown":true} -->
$$ $$
\begin{bmatrix} \begin{bmatrix}
1 & 2 \\ 1 & 2 \\
@ -467,12 +433,8 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
which is the same as this: which is the same as this:
<!-- livebook:{"break_markdown":true} -->
$$ $$
\begin{bmatrix} \begin{bmatrix}
1 & 2 \\ 1 & 2 \\
@ -488,8 +450,6 @@ $$
\end{bmatrix} \end{bmatrix}
$$ $$
<!-- livebook:{"break_markdown":true} -->
This rewrite happens in Nx too, also through a broadcast. We want to This rewrite happens in Nx too, also through a broadcast. We want to
broadcast the tensor `[1, 2]` to match the `{2, 2}` shape, like this: broadcast the tensor `[1, 2]` to match the `{2, 2}` shape, like this:
@ -531,8 +491,6 @@ h Nx.broadcast
Much of the time, you won't have to broadcast yourself. Many of Much of the time, you won't have to broadcast yourself. Many of
the functions and operators Nx supports will do so automatically. the functions and operators Nx supports will do so automatically.
<!-- livebook:{"break_markdown":true} -->
We can use tensor-aware operators via various `Nx` functions and We can use tensor-aware operators via various `Nx` functions and
many of them implicitly broadcast tensors. many of them implicitly broadcast tensors.
@ -556,8 +514,6 @@ over classic Elixir functions.
individual operations and using a just-in-time (JIT) compiler to emit individual operations and using a just-in-time (JIT) compiler to emit
highly specialized native code for the desired computation unit. highly specialized native code for the desired computation unit.
<!-- livebook:{"break_markdown":true} -->
We don't have to do anything special to get access to We don't have to do anything special to get access to
get tensor awareness beyond importing `Nx.Defn` and writing get tensor awareness beyond importing `Nx.Defn` and writing
our code within a `defn` block. our code within a `defn` block.
@ -612,8 +568,6 @@ defn subtract(a, b) do
end end
``` ```
<!-- livebook:{"break_markdown":true} -->
Now, it's your turn. Add a `defn` to `TensorMath` Now, it's your turn. Add a `defn` to `TensorMath`
that accepts two tensors representing the lengths of sides of a that accepts two tensors representing the lengths of sides of a
right triangle and uses the pythagorean theorem to return the right triangle and uses the pythagorean theorem to return the
@ -647,20 +601,14 @@ a function returning the gradient. We have two functions: `poly/1`
is a simple numerical definitin, and `poly_slope_at/1` returns is a simple numerical definitin, and `poly_slope_at/1` returns
its gradient: its gradient:
<!-- livebook:{"break_markdown":true} -->
$$ $$
poly: f(x) = 3x^2 + 2x + 1 \\ poly: f(x) = 3x^2 + 2x + 1 \\
$$ $$
<!-- livebook:{"break_markdown":true} -->
$$ $$
polySlopeAt: g(x) = 6x + 2 polySlopeAt: g(x) = 6x + 2
$$ $$
<!-- livebook:{"break_markdown":true} -->
Here's the Elixir equivalent of those functions: Here's the Elixir equivalent of those functions:
```elixir ```elixir