Nxで九九 (Elixir)

# はじめに
- [Elixir](https://elixir-lang.org/)楽しんでいますか :bangbang::bangbang::bangbang:
- 話題の[Nx](https://github.com/elixir-nx/nx)を触ってみました
    - 本当に文字通りさわってみただけです
    - 公式のExamplesを写した + 九九(大筋は @kikuyuta 先生のコード例を拝借 :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:)
- [Introducing Nx - José Valim | Lambda Days 2021](https://www.youtube.com/watch?v=fPKMmJpAGWc)
    - [Elixir](https://elixir-lang.org/)作者である[José Valim](https://twitter.com/josevalim)さんの講演 :video_camera: 
- 2021/02/20(土)に開催された[kokura.ex#18](https://fukuokaex.connpass.com/event/204749/)ので成果です
    - @im_miolab さんありがとうございます！
- 私の使った環境です
    - macOS 10.15.7
    - docker desktop 3.1.0
    - Docker version 20.10.2, build 2291f61


## 参考
- すでによい記事がいくつもできあがっています
    - 私も 🌊🌊🌊 :surfer::surfer_tone1::surfer_tone2::surfer_tone3::surfer_tone4::surfer_tone5: 🌊🌊🌊に乗ります 
- [Elixirでディープラーニング①：革新的ライブラリ「Nx」の導入](https://qiita.com/piacerex/items/db33fbe80751fdd30e48) -- @piacerexさん
- [Elixirの革新的ライブラリ「Nx」をMacでも動かしてみた](https://qiita.com/mokichi/items/1716b75709559b18ef6c) -- @mokichiさん
- [Nxで始めるゼロから作るディープラーニング 準備編](https://qiita.com/the_haigo/items/1a2f0b371a3644960251) -- @the_haigoさん
- [Elixirでディープラーニング②：Nxのdefn＋XLAでGPUを動か…せなかった(T_T)](https://qiita.com/piacerex/items/cb369ff43b1504f12d86) -- @piacerexさん


# 環境構築
- 楽しむためには少しの準備が必要です
- [ALGYAN x Seeed x NervesJPハンズオン！に向けた開発環境の準備方法 → これからの開発環境についても追記](https://qiita.com/takasehideki/items/79d4ba3f95b1463105f8)
- :point_up::point_up_tone1::point_up_tone2::point_up_tone3::point_up_tone4::point_up_tone5:を参考に[Docker](https://www.docker.com/)、[Visual Studio Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)と[拡張機能 Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)のインストールを行ってください

## .devcontainer
- 適当なディレクトリに`.devcontainer`ディレクトリをつくってその中に次の2つのファイルを入れてください

```:.devcontainer/devcontainer.json
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.106.0/containers/elm
{
    "name": "Elixir 1.11",
    "build": {
        "dockerfile": "Dockerfile"
    },

    // Set *default* container specific settings.json values on container create.
    "settings": {
      "terminal.integrated.shell.linux": "/bin/bash"
    },
  
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
      "elixir-lsp.elixir-ls",
    ]
  
    // Uncomment the next line if you want start specific services in your Docker Compose config.
    // "runServices": [],
  
    // Uncomment the line below if you want to keep your containers running after VS Code shuts down.
    // "shutdownAction": "none",
  
    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "yarn install",
  
    // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
    // "remoteUser": "node"
  }
```

```docker:.devcontainer/Dockerfile
FROM elixir:1.11
```

# Remote-Containers: Open Folder in Container...
- [開発環境の展開](https://qiita.com/takasehideki/items/79d4ba3f95b1463105f8#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%AE%E5%B1%95%E9%96%8B)と同じ要領でさきほどつくった`.devcontainer`があるフォルダを選んでください
    - :coffee: でも飲んで待ちましょう (by @takasehideki 先生)
- 左下がこんなのになっていれば成功です

![スクリーンショット 2021-02-20 12.32.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/08c2f31d-6cff-6dc7-9f37-6e90f622096e.png)

- Terminal > New Terminal として、ターミナルを開きます

# プロジェクト作成

```
# mix new hello
```

# [Nx](https://github.com/elixir-nx/nx)の導入

```elixir:mix.exs
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
      {:nx, "~> 0.1.0-dev", github: "elixir-nx/nx", branch: "main", sparse: "nx"}
    ]
  end
end

```

- 2021/2/20時点はこんな感じです
    - おそらく今後は、[hex](https://hex.pm/)のほうにも登録されてもっと簡単な指定方法ですむようになるとおもいます
- `mix.exs`を書き換えたらターミナルで以下のコマンドをうって、[Nx](https://github.com/elixir-nx/nx)を導入します

```
# cd hello
# mix deps.get
```

# Run
- 私は現段階ではAI?、ML?、TensorFlow?の区別すらよくわかっていないのでとりあえず写しただけです

## [Examples](https://github.com/elixir-nx/nx/tree/main/nx#examples)を写してみます

```
# iex -S mix
```
- リンク先と同じですので割愛です

```elixir:lib/my_module.ex
defmodule MyModule do
  import Nx.Defn

  defn softmax(t) do
    Nx.exp(t) / Nx.sum(Nx.exp(t))
  end
end
```
- そのまま写して、

```elixir
iex> recompile
:ok

iex> MyModule.softmax 1
#Nx.Tensor<
  f64
  1.0
>
```
- これでいいのかな


## 九九
- 大筋は @kikuyuta 先生のコード例を拝借です :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:

```elixir
iex> IEx.configure(inspect: [limit: :infinity])
:ok

iex> s = Enum.map(1..9, &List.duplicate(&1, 9)) |> Nx.tensor()
#Nx.Tensor<
  s64[9][9]
  [
    [1, 1, 1, 1, 1, 1, 1, 1, 1],
    [2, 2, 2, 2, 2, 2, 2, 2, 2],
    [3, 3, 3, 3, 3, 3, 3, 3, 3],
    [4, 4, 4, 4, 4, 4, 4, 4, 4],
    [5, 5, 5, 5, 5, 5, 5, 5, 5],
    [6, 6, 6, 6, 6, 6, 6, 6, 6],
    [7, 7, 7, 7, 7, 7, 7, 7, 7], 
    [8, 8, 8, 8, 8, 8, 8, 8, 8],
    [9, 9, 9, 9, 9, 9, 9, 9, 9]
  ]
>

iex> t = Nx.transpose(s)
#Nx.Tensor<
  s64[9][9]
  [
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9], 
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
  ]
>

iex> Nx.multiply(s,t)
#Nx.Tensor<
  s64[9][9]
  [
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    [2, 4, 6, 8, 10, 12, 14, 16, 18],
    [3, 6, 9, 12, 15, 18, 21, 24, 27],
    [4, 8, 12, 16, 20, 24, 28, 32, 36],
    [5, 10, 15, 20, 25, 30, 35, 40, 45],
    [6, 12, 18, 24, 30, 36, 42, 48, 54],
    [7, 14, 21, 28, 35, 42, 49, 56, 63],
    [8, 16, 24, 32, 40, 48, 56, 64, 72],
    [9, 18, 27, 36, 45, 54, 63, 72, 81]
  ]
>
```

## [Nx](https://github.com/elixir-nx/nx)を使わない例

### [for/1](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1)をつかって

```elixir
iex> for(x <- 1..9, y <- 1..9, do: x * y) |> Enum.chunk_every(9)
[
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [2, 4, 6, 8, 10, 12, 14, 16, 18],
  [3, 6, 9, 12, 15, 18, 21, 24, 27],
  [4, 8, 12, 16, 20, 24, 28, 32, 36],
  [5, 10, 15, 20, 25, 30, 35, 40, 45],
  [6, 12, 18, 24, 30, 36, 42, 48, 54],
  [7, 14, 21, 28, 35, 42, 49, 56, 63],
  [8, 16, 24, 32, 40, 48, 56, 64, 72],
  [9, 18, 27, 36, 45, 54, 63, 72, 81]
]
```

### [Enum.reduce/3](https://hexdocs.pm/elixir/Enum.html#reduce/3)をつかって

```elixir
iex> (
...> Enum.reduce(1..9, [], fn i, acc ->
...>   [ Enum.map(1..9, & &1 * i) ] |> Kernel.++(acc)
...> end)
...> |> Enum.reverse()
...> )
[
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [2, 4, 6, 8, 10, 12, 14, 16, 18],
  [3, 6, 9, 12, 15, 18, 21, 24, 27],
  [4, 8, 12, 16, 20, 24, 28, 32, 36],
  [5, 10, 15, 20, 25, 30, 35, 40, 45],
  [6, 12, 18, 24, 30, 36, 42, 48, 54],
  [7, 14, 21, 28, 35, 42, 49, 56, 63],
  [8, 16, 24, 32, 40, 48, 56, 64, 72],
  [9, 18, 27, 36, 45, 54, 63, 72, 81]
]
```

### [Nx](https://github.com/elixir-nx/nx)をつかわずに

```elixir
iex> s = Enum.map(1..9, &List.duplicate(&1, 9))                                                           
[                                        
  [1, 1, 1, 1, 1, 1, 1, 1, 1],
  [2, 2, 2, 2, 2, 2, 2, 2, 2],
  [3, 3, 3, 3, 3, 3, 3, 3, 3],
  [4, 4, 4, 4, 4, 4, 4, 4, 4],
  [5, 5, 5, 5, 5, 5, 5, 5, 5],
  [6, 6, 6, 6, 6, 6, 6, 6, 6],
  '\a\a\a\a\a\a\a\a\a',
  '\b\b\b\b\b\b\b\b\b',
  '\t\t\t\t\t\t\t\t\t'
]

# Nx.transpose/1 相当
iex> t = List.zip(s) |> Enum.map(&Tuple.to_list/1)                                                        
[                                        
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 5, 6, 7, 8, 9]
]

# Nx.multiply/2相当
iex> (
...> Enum.zip(s, t)
...> |> Enum.map(fn {list1, list2} -> Enum.zip(list1, list2) end)
...> |> Enum.map(&Enum.map(&1, fn {a, b} -> a * b end))
...> )
[
  [1, 2, 3, 4, 5, 6, 7, 8, 9],
  [2, 4, 6, 8, 10, 12, 14, 16, 18],
  [3, 6, 9, 12, 15, 18, 21, 24, 27],
  [4, 8, 12, 16, 20, 24, 28, 32, 36],
  [5, 10, 15, 20, 25, 30, 35, 40, 45],
  [6, 12, 18, 24, 30, 36, 42, 48, 54],
  [7, 14, 21, 28, 35, 42, 49, 56, 63],
  [8, 16, 24, 32, 40, 48, 56, 64, 72],
  [9, 18, 27, 36, 45, 54, 63, 72, 81]
]
```

# Wrapping Up :lgtm::lgtm::lgtm::lgtm::lgtm:
- とりあえず触ってみました
    - 触ってみただけです
    - 雰囲気すごそうな感じはしています
    - まだみなさんと語り合うレベルにはないので周辺知識とかをつけていきたいと強く感じました
    - 九九という簡単な例ですが、素の[Elixir](https://elixir-lang.org/)だとなんだかこねくり回した感じでしか書けそうにないものを`Nx.transpose`とか`Nx.multiply`とかで
    - <font color="purple">$\huge{必殺技}$</font>
    - っぽく書けそうで理解を深めていきたいです
- Enjoy [Elixir](https://elixir-lang.org/) :rocket::rocket::rocket::rocket::rocket: