ラズパイでpulpが実行できず詰まった話

投稿者: | 2023年9月10日

やりたかったこと

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 実行ファイルが用意されていないのだろう。

Windows と ラズパイ4B での、venv 仮想環境内にインストールされた 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))

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です