M1 Mac にnumpy, matplotlibなどが入らない問題の解消法

#はじめに
M1 Macbook Proが登場して意気揚々として購入したものの，普段多用しているnumpyやmatplotlibをpipでインストールしようとしたら入らなかったので，その解決法(暫定)を記しておきます．(02/19/2020)
時間が経てばライブラリ側からこの問題は解消してくれるはずなので，その時に，今まで自分が何をいじったのかが思い出せるように，ここに記しておきたいと思います．

**[numpy build fail in M1 Big sur 11.1](https://stackoverflow.com/questions/65336789/numpy-build-fail-in-m1-big-sur-11-1)**
<img width="500" alt="Screen Shot 2020-05-21 at 5.31.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505262/35fddd04-9936-1a70-9791-365e3e8b03a3.png">



#解消法

## A. condaをインストールしてその上に入れる
今回こちらの方法は使いませんが一応紹介します．
かくいう僕も以前まではAnacondaをよく使用していましたが，ライブラリがcondaで入らずに泣く泣くpipを使うたびに下の記事が頭に浮かんで心を痛めますし，何よりGitHubでソースコードを公開する場合はrequirements.txtをつけて pip install を促す場合が多いわけですから，Pythonのパッケージ管理はPython公式が推奨するpipに限定する方がいいと思います(この辺りのベストプラクティスは人それぞれだと思いますが)

- [condaとpip：混ぜるな危険](http://onoz000.hatenablog.com/entry/2018/02/11/142347)

ただ，condaの使用を許すのであれば以下の方法が一番簡単であるかと思います．[Miniforge](https://github.com/conda-forge/miniforge)というArm版に対応したcondaのインストーラーを使ってcondaをインストールし，その上にnumpyやmatplotlibをインストールします．詳しくは以下のサイトに譲ります．

- [M1 macOS で python + numpy/scipy/pandas/matplotlib/jupyterlab 環境構築のメモ(2020/12/24 時点)](https://qiita.com/syoyo/items/6ad890c8a902d02d8c5a)
- [Miniforge - Install](https://github.com/conda-forge/miniforge#install)



## B. tensorflow-macosを入れる
tensorflow-macosというMacに最適化されたTensorFlowをインストールすれば，numpyは自動的にインストールすることもできるようです．ただ，これは本来の目的とは異なりますし，如何せんmatplotlibがインストールできない問題は解決しません．

- [Mac-optimized TensorFlow and TensorFlow Addons](https://github.com/apple/tensorflow_macos) (GitHub)
- [How to install TensorFlow and NumPy on Apple Silicon (M1)](https://youtu.be/6W8pjnW65Q8) (YouTube)


## C. Python3.9をインストールする ← これが本命

冒頭で説明した[スレッド](https://stackoverflow.com/questions/65336789/numpy-build-fail-in-m1-big-sur-11-1)の回答に以下のようにあります．
> First of all numpy support is not there for m1 arm based Macs using python 3.8 version. The method to workaround with this is to update your python version to 3.9. This is the latest version of python and can be a bit unstable. If you want to work with tensorflow then do not update to python 3.9 version as they don't support it and support will come up with version 2.5 (estimated). So, if you don't want to use tensorflow and want to use numpy by using pip install then go ahead install python3.9 and make it your default python interpreter.

>I will recommend you to use Homebrew for installation of python 3.9 as it as easy as running a simple command.

現在のところ，Python3.9用のnumpyやmatplotlibはarmアーキテクチャをサポートしてそうなので，Python3.9をインストールしてその上に載せる形で実現しようと思います．

```zsh:デフォルトで入っているPython
$ python --version # /usr/bin/python
Python 2.7.16
$ python3 --version # /usr/bin/python3
Python 3.8.2
```
更地の環境は汚したくないので，pyenvを利用してバージョン管理をします(Pythonのバージョン管理とパッケージ管理のベストプラクティスは何なんでしょうね．[この記事](https://messefor.hatenablog.com/entry/2020/08/22/181519)に説得力があるので個人的には pyenv+venv を採用しています．)

### Homebrewのインストール
pyenvはHomebrew経由で入れますが，買ったばかりのMacにはHomebrewが入っていないのでインストールします．
去年の年末までは，Homebrewがまだ対応しきっておらず，arm64とx86_64でバイナリが分かれてしまうことから，下の記事にあるようにシェルのアーキテクチャを切り替えて使用する必要がありました．

- [Apple SiliconにおけるHomebrewのベストプラクティス](https://qiita.com/yujiod/items/56002a7cef5b5a3be3fb)
- [みんなの M1 Mac における Homebrew のベストプラクティス は間違っている](https://zenn.dev/ress/articles/069baf1c305523dfca3d)

しかし，2021年2月現在では[公式ページ](https://docs.brew.sh/Installation)にあるように既にHomebrewのインストーラーはM1 Macをサポートしてくれたので，下記のコマンド一発で入ります(ちなみに今やMacのデフォルトシェルはzshですが，このコマンドの'bin/bash'は気にしなくていいです)．

```zsh
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
ちなみに，コマンドラインの最後にHomebrewのパスが通っていないと忠告が出ますが，ご丁寧に`~/.zprofile`に追記すべき文まで書いてくれているので，この指示に従ってパスを通します("YOUR HOME DIRECTORY PATH"はそれぞれ異なります)

```zsh:command_output
Warning: /opt/homebrew/bin is not in your PATH.
- Add Homebrew to your PATH in (YOUR HOME DIRECTORY PATH)/.zprofiles:
    echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> (YOUR HOME DIRECTORY PATH)/.zprofile
    eval $(/opt/homebrew/bin/brew shellenv)
```

```zsh
$ echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> ~/.zprofile
$ source ~/.zprofile
$ brew --version
```
これでHomebrewが使えるようになりました．

### pyenvのインストール
pyenvの仕組みやインストール方法は[公式ページ](https://github.com/pyenv/pyenv#how-it-works)に載っているので，この通りにインストールします．
3行目はpyenvの自動起動のコマンドです．もし今後何かのソフトウェアと競合する場合は ~/.zshrc からこの文をコメントアウトすればいいでしょう．

```zsh
$ brew update
$ brew install pyenv
$ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.zshrc
$ exec "$SHELL" # シェルの再起動
$ pyenv --version
```

### Python3.9のインストール
さあ，いよいよPython3.9をインストールします．
その前に以下のパッケージのインストールが[推奨されている](https://github.com/pyenv/pyenv/wiki#suggested-build-environment)ので，従っておきます．

```zsh
# optional, but recommended:
$ brew install openssl readline sqlite3 xz zlib
```


`pyenv install --list`でインストールできるPythonのバージョンを確認できるので，インストールできるPython3.9系を確認しておきます(minicondaもインストールできるんですね，面白い)

```zsh
$ pyenv install --list | grep 3.9
 3.9.0
 3.9-dev
 3.9.1
 miniconda-3.9.1
 miniconda3-3.9.1
$ pyenv install 3.9.1 # 3.9.1をインストール
$ pyenv rehash # 新たなバージョンをインストールしたあとはこれをしないとshimsコマンドが再構成されない 
```

適当なフォルダ内で試してみます．

```zsh
$ pyenv local 3.9.1
$ python version
3.9.1 (set by (CURRENT DIR)/.python-version
$ python -m venv .venv
$ source .venv/bin/activate
$ pip instal numpy matplotlib pandas scikit-learn
```

が，matplotlibだけが入らない... 結局matplotlibはPython3.9でもサポートされていないじゃないか...

# 根本の問題は
## numpy
そうこうしている内に，numpyはM1 Macでもサポートされたようです．よって，Python3.8でもPython3.9でもpipでインストールできます．

## matplotlib
- [Apple M1 — Matplotlib, Python, and Jupyter Lab](https://alexslobodnik.medium.com/apple-m1-matplotlib-python-and-jupyter-lab-551646b9a8cd)
- [FYI : silicon M1 : installation of matplotlib requires Pillow which requires libjpeg which requires brew](https://github.com/apple/tensorflow_macos/issues/120)

とあるように，Homebrewでlibjpegというパッケージをインストールする必要があったようです．こうすると，Python3.9系でもPython3.8系でもmatplotlibが入るようになります．

```zsh
$ brew install libjpeg
```
pyenv導入しなくてもよかったですね．ただいずれにせよHomebrewは必要ですし，pyenvの+venvはベストプラクティスな気がするので，上記のステップも無駄ではないでしょう．

## pandas, scikit-learn

- [Apple M1, Python, Pandas, and Homebrew](https://alexslobodnik.medium.com/apple-m1-python-pandas-and-homebrew-20f14828ccc7)

pandasやscikit-learnが入らないのは，インストーラがarm64というアーキテクチャ上では動かないからだそうです．よって上の記事にあるように[iTerm](https://iterm2.com/)を開く前に"Open Using Rosetta"にチェックを入れてから起動するようにし(もちろん一度iTermはquitしてから再起動する)，pipでインストールすると入るようになります．(M1 Macbook Pro 2020では，arm64とx86_86を切り替えて使えるようになっています．)

もしくはzsh上では以下のコマンドで変更できるようです(@hiro-551010 さん，コメントありがとうございました)

```bash
$ arch -arm64 zsh # arm64に変更
$ arch -x86_64 zsh # x86_64に変更
```

ちなみに現在のシェルがどのアーキテクチャを使っているかは`uname -m`というコマンドで確認できます．

```bash
$ uname -m # Open Using Rosetta にチェックを入れずに起動した場合
arm64
$ uname -m # Open Using Rosetta にチェックを入れて起動した場合
x86_64
```

しかし，x86_64上でインストールしたpandasやscikit-learnは，もちろんx86_64上で実行しなくてはなりません．アーキテクチャをx86_64にした状態でインストールした後，arm64に戻してシェルを起動すると，その状態では使えないということです．

(参考) [ARMとx86/x64(Intel, AMD)のCPU、アーキテクチャの違い、シェア、性能比較、アーキテクチャ、エンディアン](https://urashita.com/archives/12325)

###追記(4/14/2021)
[この回答](https://stackoverflow.com/a/66048187)によると，pandasは以下のようにソースコードからインストールすると入るらしいです．(若干のWarningが出ましたが基本的な機能は入りました．)

```zsh
$ pip install numpy cython
$ git clone https://github.com/pandas-dev/pandas.git
$ cd pandas
$ python3 setup.py install
```

## numpy, matplotlib, pandas, scikit-learnを全て同時に使いたいときは
pandas, scikit-learnはx86_64上でしかインストールできませんし，実行できません．対してnumpy, matplotlibはarm64上でもx86_64上でも動きます．よってこれらを同時に使いたい場合は，インストール時も実行時もアーキテクチャをx86_64に統一しましょう．
現在のところはプロジェクトやディレクトリ単位で，どちらのアーキテクチャ上で行うかを逐一自分で覚えておきながら実行するということになりそうです．

#まとめ
とりあえずは上記の解決方法で，それぞれのライブラリをimportすることはできそうです．numpyのインストーラは仕様変更してくれたみたいですし，そのうちにあらゆるライブラリがM1チップの上でシームレスに動くようになってくれるでしょう．その時を早く待ちたいものです．


####余談: 問題3.8系がpyenvで入らない
pyenvで色々なバージョンのpythonを入れようとしたとき，3.9.1は入ったのだが，3.8系が入らないという問題があったが，[この方法](https://github.com/pyenv/pyenv/issues/1767#issuecomment-753401908)を用いたら入った

```zsh
$ CFLAGS="-I$(brew --prefix openssl)/include -I$(brew --prefix bzip2)/include -I$(brew --prefix readline)/include -I$(xcrun --show-sdk-path)/usr/include" LDFLAGS="-L$(brew --prefix openssl)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix zlib)/lib -L$(brew --prefix bzip2)/lib" pyenv install --patch 3.8.6 <<(curl -sSL https://raw.githubusercontent.com/Homebrew/formula-patches/113aa84/python/3.8.3.patch\?full_index\=1)
```