Использование пользовательского двоичного файла
Многие решатели написаны не на Julia, а на таких языках, как C или C++. JuMP взаимодействует с такими решателями через двоичные зависимости.
Для многих решателей с открытым исходным кодом соответствующий двоичный файл устанавливается автоматически при выполнении команды Pkg.add("Solver")
. Например, Pkg.add("ECOS")
также установит двоичный файл ECOS.
На этой странице описывается, как происходит эта установка и как можно использовать пользовательский двоичный файл.
Совместимость
Для выполнения этих инструкций требуется версия не ниже Julia 1.6. |
Вводная информация
Каждый решатель, поддерживаемый JuMP, структурирован как пакет Julia. Например, интерфейс для решателя ECOS предоставляется пакетом ECOS.jl.
На этой странице в качестве примера используется пакет ECOS.jl, потому что его легко компилировать. В других решателях действуют те же соглашения. Например, интерфейс для решателя Clp предоставляется пакетом Clp.jl. |
Пакет ECOS.jl предоставляет интерфейс между API ECOS на C и MathOptInterface. Однако он не занимается установкой двоичного файла решателя; за это отвечает пакет JLL.
JLL — это пакет Julia, который служит оболочкой для предварительно скомпилированного двоичного файла. Сборка двоичных файлов выполняется с помощью Yggdrasil (например, ECOS), а размещаются они в репозитории JuliaBinaryWrappers на GitHub (например, ECOS_jll.jl).
В пакетах JLL мало кода. Их единственная задача — применять dlopen
к динамической библиотеке вместе со всеми зависимостями.
Пакеты JLL управляют своими двоичными зависимостями с помощью системы артефактов Julia. В каждом пакете JLL есть файл Artifacts.toml
с описанием того, где находится каждый двоичный артефакт для каждой платформы, на которой он может быть установлен. Вот файл Artifacts.toml для ECOS_jll.jl.
Двоичных файлов, устанавливаемых пакетом JLL, большинству пользователей должно быть достаточно. Однако в редких случаях может потребоваться пользовательский двоичный файл. Вот две основные причины для использования пользовательского двоичного файла:
-
Требуется двоичный файл с пользовательскими настройками компиляции (например, для отладки).
-
Требуется двоичный файл с набором зависимостей, которые отсутствуют в Yggdrasil (например, с зависимостью от коммерческого решателя, такого как Gurobi или CPLEX).
В следующих разделах объясняется, как заменить двоичные файлы, предоставляемые пакетом JLL, на скомпилированные самостоятельно. Напомним, что для простоты мы используем в качестве примера ECOS, но для других решателей процедура та же самая.
Изучение переопределяемого пакета JLL
Сначала нужно изучить структуру и имена файлов пакета JLL, который требуется переопределить.
Местонахождение файлов можно определить с помощью .artifact_dir
:
julia> using ECOS_jll
julia> ECOS_jll.artifact_dir
"/Users/oscar/.julia/artifacts/2addb75332eff5a1657b46bb6bf30d2410bc7ecf"
На других компьютерах этот путь может быть иным. |
Вот что содержится в папке:
julia> readdir(ECOS_jll.artifact_dir)
4-element Vector{String}:
"include"
"lib"
"logs"
"share"
julia> readdir(joinpath(ECOS_jll.artifact_dir, "lib"))
1-element Vector{String}:
"libecos.dylib"
У других решателей может быть каталог bin
с исполняемыми файлами. Чтобы использовать пользовательский двоичный файл в ECOS, нужно заменить файл /lib/libecos.dylib
им.
Компиляция пользовательского двоичного файла
Следующий шаг — компиляция пользовательского двоичного файла. Так как решатель ECOS написан на языке C без каких-либо зависимостей, это легко сделать при наличии компилятора C:
oscar@Oscars-MBP jll_example % git clone https://github.com/embotech/ecos.git
[... lines omitted ...]
oscar@Oscars-MBP jll_example % cd ecos
oscar@Oscars-MBP ecos % make shared
[... many lines omitted...]
oscar@Oscars-MBP ecos % mkdir lib
oscar@Oscars-MBP ecos % cp libecos.dylib lib
Компиляция пользовательских двоичных файлов решателей — сложная операция. Из-за сложностей, связанных с компиляцией различных решателей, сообщество JuMP не сможет помочь вам в диагностике и устранении проблемы компиляции. |
После этого этапа компиляции появляется папка /tmp/jll_example/ecos
, содержащая каталоги lib
и include
с теми же файлами, что и в ECOS_jll
:
julia> readdir(joinpath("ecos", "lib"))
1-element Vector{String}:
"libecos.dylib"
Переопределение одной библиотеки
Чтобы переопределить библиотеку libecos
, нужно знать, как она называется в ECOS_jll
. (В большинстве случаев она также будет называться libecos
, но не всегда.)
Проверить это можно двумя способами.
-
Прочитайте информацию в конце файла сведений пакета JLL на GitHub. Например, для
ECOS_jll есть единственный элемент LibraryProduct
с именем libecos
.
-
Введите
ECOS_jll.
и дважды нажмите клавишу[TAB]
для автоматического заполнения
доступных вариантов:
julia> ECOS_jll.
LIBPATH PATH_list best_wrapper get_libecos_path libecos_handle
LIBPATH_list __init__ dev_jll is_available libecos_path
PATH artifact_dir find_artifact_dir libecos
Here you can see there is libecos
, and more usefully for us, libecos_path
.
Узнав имя переопределяемой переменной (заканчивающееся на _path
), используйте Preferences.jl, чтобы указать новый путь:
using Preferences
set_preferences!(
"LocalPreferences.toml",
"ECOS_jll",
"libecos_path" => "/tmp/jll_example/ecos/lib/libecos"
)
В результате в текущем каталоге будет создан файл с именем LocalPreferences.toml
и таким содержимым:
[ECOS_jll]
libecos_path = "/tmp/jll_example/ecos/lib/libecos"
Теперь, если перезапустить Julia, получится следующее.
julia> using ECOS_jll
julia> ECOS_jll.libecos
"/tmp/jll_example/ecos/lib/libecos"
Чтобы вернуться к использованию библиотеки по умолчанию, просто удалите файл LocalPreferences.toml
.
Переопределение всего артефакта
Иногда решатель может предоставлять несколько библиотек и исполняемых файлов, и указывать пути для всех них утомительно. В этом случае можно заменить весь артефакт с помощью файла Override.toml
Julia.
Для переопределения всего артефакта необходимо воспроизвести структуру и содержимое пакета JLL, рассмотренные выше.
В большинстве случаев достаточно воссоздать каталоги include
, lib
и bin
(если они есть). Каталоги logs
и share
можно спокойно пропустить. Хорошо запомните, какие файлы содержатся в каждом каталоге и как они называются.
В нашем примере с ECOS мы уже воспроизвели структуру, когда скомпилировали ECOS.
Поэтому теперь нужно сообщить Julia, что должна использоваться пользовательская установка вместо установки по умолчанию. Для этого можно создать файл переопределения по пути ~/.julia/artifacts/Overrides.toml
.
Overrides.toml
имеет следующее содержимое:
# Переопределение для ECOS_jll
2addb75332eff5a1657b46bb6bf30d2410bc7ecf = "/tmp/jll_example/ecos"
где 2addb75332eff5a1657b46bb6bf30d2410bc7ecf
— папка из исходного каталога ECOS_jll.artifact_dir
, а "/tmp/jll_example/ecos"
— расположение новой установки. Замените их соответствующим образом для вашей системы.
Если перезапустить Julia после создания файла переопределения, вы увидите следующее.
julia> using ECOS_jll
julia> ECOS_jll.artifact_dir
"/tmp/jll_example/ecos"
Теперь при использовании ECOS будет применяться пользовательский двоичный файл.
Использование Cbc с пользовательским двоичным файлом
В качестве еще одного примера продемонстрируем использование Cbc.jl с пользовательским двоичным файлом.
Изучение переопределяемого пакета JLL
Сначала узнаем, где установлен пакет Cbc_jll
:
julia> using Cbc_jll
julia> Cbc_jll.artifact_dir
"/Users/oscar/.julia/artifacts/e481bc81db5e229ba1f52b2b4bd57484204b1b06"
julia> readdir(Cbc_jll.artifact_dir)
5-element Vector{String}:
"bin"
"include"
"lib"
"logs"
"share"
julia> readdir(joinpath(Cbc_jll.artifact_dir, "bin"))
1-element Vector{String}:
"cbc"
julia> readdir(joinpath(Cbc_jll.artifact_dir, "lib"))
10-element Vector{String}:
"libCbc.3.10.5.dylib"
"libCbc.3.dylib"
"libCbc.dylib"
"libCbcSolver.3.10.5.dylib"
"libCbcSolver.3.dylib"
"libCbcSolver.dylib"
"libOsiCbc.3.10.5.dylib"
"libOsiCbc.3.dylib"
"libOsiCbc.dylib"
"pkgconfig"
Компиляция пользовательского двоичного файла
Далее необходимо скомпилировать Cbc. Компиляция пакета Cbc может быть непростой задачей (у него много зависимостей), но для пользователей macOS есть рецепт Homebrew:
(base) oscar@Oscars-MBP jll_example % brew install cbc [ ... lines omitted ... ] (base) oscar@Oscars-MBP jll_example % brew list cbc /usr/local/Cellar/cbc/2.10.5/bin/cbc /usr/local/Cellar/cbc/2.10.5/include/cbc/ (76 files) /usr/local/Cellar/cbc/2.10.5/lib/libCbc.3.10.5.dylib /usr/local/Cellar/cbc/2.10.5/lib/libCbcSolver.3.10.5.dylib /usr/local/Cellar/cbc/2.10.5/lib/libOsiCbc.3.10.5.dylib /usr/local/Cellar/cbc/2.10.5/lib/pkgconfig/ (2 files) /usr/local/Cellar/cbc/2.10.5/lib/ (6 other files) /usr/local/Cellar/cbc/2.10.5/share/cbc/ (59 files) /usr/local/Cellar/cbc/2.10.5/share/coin/ (4 files)
Переопределение отдельных библиотек
Для переопределения определенных библиотек с помощью Preferences.jl
сначала нужно проверить имя каждой библиотеки в Cbc_jll
:
julia> Cbc_jll.
LIBPATH cbc get_libcbcsolver_path libOsiCbc_path
LIBPATH_list cbc_path is_available libcbcsolver
PATH dev_jll libCbc libcbcsolver_handle
PATH_list find_artifact_dir libCbc_handle libcbcsolver_path
__init__ get_cbc_path libCbc_path
artifact_dir get_libCbc_path libOsiCbc
best_wrapper get_libOsiCbc_path libOsiCbc_handle
Затем в файл LocalPreferences.toml
нужно добавить следующее:
[Cbc_jll]
cbc_path = "/usr/local/Cellar/cbc/2.10.5/bin/cbc"
libCbc_path = "/usr/local/Cellar/cbc/2.10.5/lib/libCbc.3.10.5"
libOsiCbc_path = "/usr/local/Cellar/cbc/2.10.5/lib/libOsiCbc.3.10.5"
libcbcsolver_path = "/usr/local/Cellar/cbc/2.10.5/lib/libCbcSolver.3.10.5"
Обратите внимание, что регистр символов учитывается, поэтому |