やりたかったこと
Raspberry Pi 4B ( OS は Buster ) で python の線形最適化ライブラリ PuLP を使おうとした。Windows で動くことを確かめたのち、ラズパイにそのコードを移植して動かそうとした。
環境としては venv を使っています。
結果
pulp を含むスクリプトを実行すると、以下のようなエラーが出て、実行できなかった。
(env) <user>@raspberrypi:~/Program $ python optimization.py Traceback (most recent call last): File "/home/<user>/Program/optimization.py", line 111, in lpSolve status = problem.solve(pulp.PULP_CBC_CMD(msg=False)) File "/home/<user>/Program/env/lib/python3.7/site-packages/pulp/pulp.py", line 1913, in solve status = solver.actualSolve(self, **kwargs) File "/home/<user>/Program/env/lib/python3.7/site-packages/pulp/apis/coin_api.py", line 366, in actualSolve % self.pulp_cbc_path pulp.apis.core.PulpSolverError: PULP_CBC_CMD: Not Available (check permissions on /home/<user>/Program/env/lib/python3.7/site-packages/pulp/solverdir/cbc/linux/32/cbc)
試したこと
pulp.pulpTestAll()
を実行し、状況を確認した。すると、こんな感じで大量のテストが「スキップ」されていた。
(env) <user>@raspberrypi:~/Program/ $ python Python 3.7.3 (default, Jun 29 2023, 18:03:57) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import pulp >>> pulp.pulpTestAll() sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss Test that logic put in place for deprecation handling of indexs works . Testing 'indexs' param continues to work for LpVariable.dicts Testing 'indexs' param continues to work for LpVariable.matrix . Testing 'indices' argument works in LpVariable.dicts Testing 'indices' param continues to work for LpVariable.matrix . Testing invalid status . Testing continuous LP solution - export dict . Testing export dict for LP . Testing export dict MIP . Testing maximize continuous LP solution . Testing continuous LP solution - export JSON . Testing continuous LP solution - export solver dict . Testing continuous LP solution - export solver JSON ... Testing reading MPS files - binary variable, no constraint names . Testing reading MPS files - integer variable . Testing reading MPS files - maximize . Testing reading MPS files - noname .. Testing invalid var names .. Testing makeDict general behavior . Testing makeDict default value behavior . Testing measuring optimization time . Testing that `readsol` can parse CPLEX mipopt solution . Testing the availability of the function pulpTestAll . Testing zero subtraction . Testing inconsistent lp solution . Testing continuous LP solution . Testing maximize continuous LP solution . Testing unbounded continuous LP solution . Testing Long Names . Testing repeated Names . Testing zero constraint . Testing zero objective . Testing LpVariable (not LpAffineExpression) objective .. Testing LpAffineExpression divide . Testing MIP solution . Testing MIP solution with floats in objective . Testing Initial value in MIP solution . Testing fixing value in MIP solution . Testing MIP relaxation . Testing feasibility problem (no objective) . Testing an infeasible problem . Testing an integer infeasible problem . Testing another integer infeasible problem . Testing column based modelling ..... Testing fractional constraints . Testing elastic constraints (no change) . Testing elastic constraints (freebound) . Testing elastic constraints (penalty unchanged) . Testing elastic constraints (penalty unbounded) . Testing timeLimit argument ...sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss ---------------------------------------------------------------------- Ran 1003 tests in 0.966s OK (skipped=944)
原因と解決策
pulp は、problem.solve()
を実行するとき、引数でどのソルバーを使うか指定する仕組みになっている。普通、pulp.PULP_CBC_CMD(msg=False)
を指定することで、pulp に付属している CBC ソルバーを使うことを指示する。ところが、ラズパイにインストールした pulp には CBC が付属していなかったため、ソルバーが Not Available になっていたようだ。
実際に、env フォルダの中身を覗くと、env/lib/python3.7/site-packages/pulp/solverdir/cbc
に各 OS 向けのソルバーが入る場所がある。Windows でインストールした pulp にはちゃんと cbc の実行バイナリが入っていたが(図の上段)、ラズパイでインストールした pulp には、cbc が含まれていなかった(図の下段)。推測ではあるが、ラズパイは CPU のアーキテクチャが違うので、コンパイル済みの cbc 実行ファイルが用意されていないのだろう。
したがって解決策は、ラズパイに手動で CBC をインストールし、python 側には pulp 付属ではないソルバーを使うよう教えてあげることだ。
ラズパイに CBC をインストール
https://github.com/coin-or/Cbc の手順に従い、以下のコマンドを実行する。
$ sudo apt update $ sudo apt install coinor-cbc coinor-libcbc-dev
以上で CBC がラズパイにインストールされた。試しに cbc コマンドを打って、ソルバーが起動するか確認しよう。起動したソルバーは exit と打つことで終了できる。
$ cbc Welcome to the CBC MILP Solver Version: 2.9.9 Build Date: Aug 21 2017 CoinSolver takes input from arguments ( - switches to stdin) Enter ? for list of commands or help Coin:
Python で使うソルバーを指定
problem.solve(pulp.PULP_CBC_CMD(msg=False))
という行を、
problem.solve(pulp.COIN_CMD(msg=False))
に変更する。これで、pulp に付属する cbc ではなく、マシンにインストールした cbc を実行してくれる。Windows でもラズパイでも同じ python スクリプトで動かしたいなら、アーキテクチャを判定してラズパイだったときのみマシンの cbc を実行するように、以下のようなコードにすればよいだろう(だいぶアドホックだが……)。
import platform if platform.system() == "Linux" and platform.machine() == "armv7l": # ラズパイなら status = problem.solve(pulp.COIN_CMD(msg=False)) else: status = problem.solve(pulp.PULP_CBC_CMD(msg=False))