From 09d373d961cfdb1724978a36178bdb12e61f5f4d Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:04:35 +0000 Subject: [PATCH 01/60] Update deps --- mix.exs | 2 +- mix.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mix.exs b/mix.exs index 9a12ab9..5503d74 100644 --- a/mix.exs +++ b/mix.exs @@ -49,7 +49,7 @@ defmodule EXGBoost.MixProject do {:nimble_options, "~> 1.0"}, {:nx, "~> 0.7"}, {:jason, "~> 1.3"}, - {:ex_doc, "~> 0.31.0", only: :docs}, + {:ex_doc, "~> 0.40", only: :docs}, {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, {:ex_json_schema, "~> 0.11.0"}, diff --git a/mix.lock b/mix.lock index 452af39..a2b814c 100644 --- a/mix.lock +++ b/mix.lock @@ -6,27 +6,27 @@ "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, - "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, - "ex_json_schema": {:hex, :ex_json_schema, "0.11.1", "b593f92937a095f66054bb318681397dfe7304e7d2b6b1a7534ea3aa40024f8c", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "32d651a575a6ce2fd613f140b0fef8dd0acc7cf8e8bcd29a3a1be5c945700dd5"}, + "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, - "httpoison": {:hex, :httpoison, "2.2.3", "a599d4b34004cc60678999445da53b5e653630651d4da3d14675fedc9dd34bd6", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "fa0f2e3646d3762fdc73edb532104c8619c7636a6997d20af4003da6cfc53e53"}, + "httpoison": {:hex, :httpoison, "2.3.0", "10eef046405bc44ba77dc5b48957944df8952cc4966364b3cf6aa71dce6de587", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d388ee70be56d31a901e333dbcdab3682d356f651f93cf492ba9f06056436a2c"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "kino": {:hex, :kino, "0.17.0", "72f1a2bf691db7b8352bae86b3951fdf9b23619b5d8586cb7cd1e9c2edc8ff9b", [:mix], [{:fss, "~> 0.1.0", [hex: :fss, repo: "hexpm", optional: false]}, {:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "e1ec49a2ebbf622c1675f96b427c565ce02df6725e8f2e8d4a743c8e791bd090"}, + "kino": {:hex, :kino, "0.18.0", "a1ae60dcbd79413befea7902906be5c6a8f7380af86f86dbdc7994687ad3a13f", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "11868c6d653523550ec208c7dc3717fffb68c0380e20fca395251d9db447d1a7"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, "nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "nx": {:hex, :nx, "0.10.0", "128e4a094cb790f663e20e1334b127c1f2a4df54edfb8b13c22757ec33133b4f", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3db8892c124aeee091df0e6fbf8e5bf1b81f502eb0d4f5ba63e6378ebcae7da4"}, + "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.3", "4e741024b0b097fe783add06e53ae9a6f23ddc78df1010f215df0c02915ef5a8", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "c23f5f33cb6608542de4d04faf0f0291458c352a4648e4d28d17ee1098cddcc4"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, From a8c176c2f74fd41106959f7fe82daa247809e44f Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:09:10 +0000 Subject: [PATCH 02/60] Prefer more recent versions of elixir --- mix.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.exs b/mix.exs index 5503d74..1dbdf0d 100644 --- a/mix.exs +++ b/mix.exs @@ -15,7 +15,7 @@ defmodule EXGBoost.MixProject do make_precompiler_nif_versions: [ versions: ["2.15", "2.16", "2.17"] ], - elixir: "~> 1.14", + elixir: "~> 1.17", start_permanent: Mix.env() == :prod, compilers: [:elixir_make] ++ Mix.compilers(), deps: deps(), @@ -24,10 +24,6 @@ defmodule EXGBoost.MixProject do homepage_url: "https://github.com/acalejos/exgboost", docs: docs(), package: package(), - preferred_cli_env: [ - docs: :docs, - "hex.publish": :docs - ], before_closing_body_tag: &before_closing_body_tag/1, name: "EXGBoost", description: @@ -36,6 +32,10 @@ defmodule EXGBoost.MixProject do ] end + def cli do + [preferred_envs: [docs: :docs, "hex.publish": :docs]] + end + def application do [ extra_applications: [:logger], From 33003d04dfb4a8d97b669c874badb9d3449946a5 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:09:17 +0000 Subject: [PATCH 03/60] Update docs --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e11c5f1..610e33d 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ EXGBoost.predict(model, x) EXGBoost is designed to feel familiar to the users of the Python XGBoost library. `EXGBoost.train/2` is the primary entry point for training a model. It accepts a Nx tensor for the features and a Nx tensor for the labels. -`EXGBoost.train/2` returns a trained`Booster` struct that can be used for prediction. `EXGBoost.train/2` also +`EXGBoost.train/2` returns a trained `Booster` struct that can be used for prediction. `EXGBoost.train/2` also accepts a keyword list of options that can be used to configure the training process. See the [XGBoost documentation](https://xgboost.readthedocs.io/en/latest/parameter.html) for the full list of options. @@ -175,7 +175,7 @@ preds = EXGBoost.train(X, y) |> EXGBoost.predict(X) ### Precompiled Distribution -We currenly offer the following precompiled packages for EXGBoost: +We currently offer the following precompiled packages for EXGBoost: ```elixir %{ @@ -220,3 +220,13 @@ You also need to set `CC_PRECOMPILER_PRECOMPILE_ONLY_LOCAL=true` before the firs ## License Licensed under an [Apache-2](https://github.com/acalejos/exgboost/blob/main/LICENSE) license. + +## Maintaining this fork + +Recommended fork maintenance cadence: + +- Keep dependencies current with Dependabot PRs (configured in `.github/dependabot.yml`). +- Ensure all PRs pass CI (`.github/workflows/ci.yml`) before merge. +- Keep precompiled artefacts and checksums aligned with each release tag (`.github/workflows/precompile.yml`). +- When upgrading `nx`, verify supported Elixir/OTP versions in CI matrix and `mix.exs`. + From 80b5f97e27142c8f6995155f4423627ee27af3fb Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:09:44 +0000 Subject: [PATCH 04/60] Add CI and dependabot GH actions --- .github/dependabot.yml | 12 +++++++++++ .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++ .github/workflows/precompile.yml | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ae3052 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "mix" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..708ffc4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: ci + +on: + pull_request: + push: + branches: + - main + +jobs: + test: + name: Test OTP ${{ matrix.otp }} / Elixir ${{ matrix.elixir }} + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + otp: ["27.3", "28.3"] + elixir: ["1.17.3", "1.18.4", "1.19.5"] + + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ matrix.otp }} + elixir-version: ${{ matrix.elixir }} + + - name: Install dependencies + run: mix deps.get + + - name: Check formatting + run: mix format --check-formatted + + - name: Run tests + run: MIX_ENV=test mix test \ No newline at end of file diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index dfbae0c..15a70fc 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -12,7 +12,7 @@ jobs: MIX_ENV: "prod" strategy: matrix: - otp: ["27.0", "28.0"] + otp: ["27.3", "28.3"] elixir: ["1.18.4"] steps: - uses: actions/checkout@v3 From 3d8ec89f8ee954b33e34411f507af26b6c4fec4f Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:13:00 +0000 Subject: [PATCH 05/60] Install Hex and rebar in CI --- .github/workflows/ci.yml | 5 +++++ .github/workflows/precompile.yml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 708ffc4..cc2c912 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,11 @@ jobs: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} + - name: Install Hex and Rebar + run: | + mix local.hex --force + mix local.rebar --force + - name: Install dependencies run: mix deps.get diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 15a70fc..0b88203 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -20,6 +20,10 @@ jobs: with: otp-version: ${{matrix.otp}} elixir-version: ${{matrix.elixir}} + - name: Install Hex and Rebar + run: | + mix local.hex --force + mix local.rebar --force - name: Install system dependecies run: | sudo apt-get update From 70b9f655b4273126273a8082b88e1aa70a0e6bdf Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 18:32:01 +0000 Subject: [PATCH 06/60] Bump xgboost to 3.1.3 (from 3.0.5) Remove deprecated function --- Makefile | 4 +-- c/exgboost/src/dmatrix.c | 65 ---------------------------------------- 2 files changed, 2 insertions(+), 67 deletions(-) diff --git a/Makefile b/Makefile index 1768938..1922ef1 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,8 @@ TEMP ?= $(HOME)/.cache XGBOOST_CACHE ?= $(TEMP)/exgboost XGBOOST_GIT_REPO ?= https://github.com/dmlc/xgboost.git -# v3.0.5 tagged release -XGBOOST_GIT_REV ?= v3.0.5 +# v3.1.3 tagged release +XGBOOST_GIT_REV ?= v3.1.3 XGBOOST_NS = xgboost-$(XGBOOST_GIT_REV) XGBOOST_DIR = $(XGBOOST_CACHE)/$(XGBOOST_NS) XGBOOST_LIB_DIR = $(XGBOOST_DIR)/build/xgboost diff --git a/c/exgboost/src/dmatrix.c b/c/exgboost/src/dmatrix.c index 75dc621..f6a2839 100644 --- a/c/exgboost/src/dmatrix.c +++ b/c/exgboost/src/dmatrix.c @@ -212,71 +212,6 @@ ERL_NIF_TERM EXGDMatrixCreateFromSparse(ErlNifEnv *env, int argc, return ret; } -ERL_NIF_TERM EXGDMatrixCreateFromCSREx(ErlNifEnv *env, int argc, - const ERL_NIF_TERM argv[]) { - ErlNifBinary indptr_bin; - ErlNifBinary indices_bin; - ErlNifBinary data_bin; - int result = -1; - ErlNifUInt64 *indptr = NULL; - uint32_t *indices = NULL; - float *data = NULL; - uint32_t nindptr = 0; - ErlNifUInt64 nelem = 0; - ErlNifUInt64 ncol = 0; - DMatrixHandle handle; - ERL_NIF_TERM ret = 0; - if (argc != 6) { - ret = exg_error(env, "Wrong number of arguments"); - goto END; - } - if (!enif_inspect_binary(env, argv[0], &indptr_bin)) { - ret = exg_error(env, "Indptr must be a binary of uint64_t"); - goto END; - } - if (!enif_inspect_binary(env, argv[1], &indices_bin)) { - ret = exg_error(env, "Indices must be a binary of uint64_t"); - goto END; - } - if (!enif_inspect_binary(env, argv[2], &data_bin)) { - ret = exg_error(env, "Data must be a binary of uint64_t"); - goto END; - } - if (!enif_get_uint(env, argv[3], &nindptr)) { - ret = exg_error(env, "Nindptr must be a uint64_t"); - goto END; - } - if (!enif_get_uint64(env, argv[4], &nelem)) { - ret = exg_error(env, "Nelem must be a uint64_t"); - goto END; - } - if (!enif_get_uint64(env, argv[5], &ncol)) { - ret = exg_error(env, "Ncol must be a uint64_t"); - goto END; - } - indptr = (ErlNifUInt64 *)indptr_bin.data; - indices = (uint32_t *)indices_bin.data; - data = (float *)data_bin.data; - if (indptr_bin.size != nindptr * sizeof(ErlNifUInt64)) { - ret = exg_error(env, "Indptr size does not match nindptr"); - goto END; - } - if (data_bin.size != nelem * sizeof(float)) { - ret = exg_error(env, "Data size does not match nelem"); - goto END; - } - result = XGDMatrixCreateFromCSREx(indptr, indices, data, nindptr, nelem, ncol, - &handle); - if (result == 0) { - ret = exg_ok(env, enif_make_resource(env, handle)); - enif_release_resource(handle); - } else { - ret = exg_error(env, XGBGetLastError()); - } -END: - return ret; -} - ERL_NIF_TERM EXGDMatrixCreateFromDense(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int result = -1; From 8056642feb18e178fe31d5e7ec3e8957eee5d2f1 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 19:10:48 +0000 Subject: [PATCH 07/60] Fix ArrayInterface interface --- lib/exgboost/array_interface.ex | 59 ++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/lib/exgboost/array_interface.ex b/lib/exgboost/array_interface.ex index ad2fc8b..e6b2318 100644 --- a/lib/exgboost/array_interface.ex +++ b/lib/exgboost/array_interface.ex @@ -4,7 +4,7 @@ defmodule EXGBoost.ArrayInterface do @typedoc """ The XGBoost C API uses and is moving towards mainly supporting the use of - JSON-Encoded NumPy ArrayyInterface format to pass data to and from the C API. This struct + JSON-Encoded NumPy ArrayInterface format to pass data to and from the C API. This struct is used to represent the ArrayInterface format. If you wish to use the EXGBoost.NIF library directly, this will be the desired format @@ -17,7 +17,7 @@ defmodule EXGBoost.ArrayInterface do @type t :: %__MODULE__{ typestr: String.t(), shape: tuple(), - address: pos_integer(), + address: non_neg_integer(), readonly: boolean(), tensor: Nx.Tensor.t(), binary: binary() @@ -94,11 +94,41 @@ defmodule EXGBoost.ArrayInterface do {"data", [address, readonly]}, acc -> [{:address, address} | [{:readonly, readonly} | acc]] + {:data, [address, readonly]}, acc -> + [{:address, address} | [{:readonly, readonly} | acc]] + {"shape", shape}, acc -> [{:shape, List.to_tuple(shape)} | acc] - {key, value}, acc -> - [{String.to_existing_atom(key), value} | acc] + {:shape, shape}, acc -> + [{:shape, List.to_tuple(shape)} | acc] + + {"typestr", typestr}, acc -> + [{:typestr, typestr} | acc] + + {:typestr, typestr}, acc -> + [{:typestr, typestr} | acc] + + {"version", version}, acc -> + [{:version, version} | acc] + + {:version, version}, acc -> + [{:version, version} | acc] + + {"tensor", tensor}, acc -> + [{:tensor, tensor} | acc] + + {:tensor, tensor}, acc -> + [{:tensor, tensor} | acc] + + {"binary", binary}, acc -> + [{:binary, binary} | acc] + + {:binary, binary}, acc -> + [{:binary, binary} | acc] + + {_key, _value}, acc -> + acc end) |> then(&struct(__MODULE__, &1)) end @@ -107,7 +137,7 @@ defmodule EXGBoost.ArrayInterface do This function is used to convert Nx.Tensors to the ArrayInterface format. Example: - iex> EXGBoost.from_tensor(Nx.tensor([[1,2,3],[4,5,6]])) + iex> EXGBoost.ArrayInterface.from_tensor(Nx.tensor([[1,2,3],[4,5,6]])) #ArrayInterface< %{data: [4418559984, true], shape: [2, 3], typestr: " Tuple.to_list() |> Enum.product() - <<_endianess::utf8, char_code::binary-size(1), bytes::binary>> = arr_int.typestr + <> = arr_int.typestr + + if endianess not in [?<, ?|] do + raise ArgumentError, + "Unsupported endianness in typestr #{inspect(arr_int.typestr)}. " <> + "Expected little-endian ('<') or non-endian ('|')." + end + + bit_width = String.to_integer(bytes) * 8 nx_type = case char_code do - "i" -> {:s, String.to_integer(bytes) * 8} - other -> {String.to_existing_atom(other), String.to_integer(bytes) * 8} + "i" -> {:s, bit_width} + "u" -> {:u, bit_width} + "f" -> {:f, bit_width} + "c" -> {:c, bit_width} + other -> + raise ArgumentError, + "Unsupported typestr code #{inspect(other)} in #{inspect(arr_int.typestr)}" end tensor_bin = From 1cdcde24a38cedb1c07f1b3c49e95dbc85244935 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 23 Feb 2026 19:11:15 +0000 Subject: [PATCH 08/60] Extend tests --- test/exgboost_test.exs | 84 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/exgboost_test.exs b/test/exgboost_test.exs index fcdfec0..f62b55e 100644 --- a/test/exgboost_test.exs +++ b/test/exgboost_test.exs @@ -95,6 +95,59 @@ defmodule EXGBoostTest do assert inplace_preds_no_proxy.shape == y.shape end + test "predict is stable across repeated calls", context do + nrows = 12 + ncols = 5 + {x, new_key} = Nx.Random.normal(context.key, 0, 1, shape: {nrows, ncols}) + {y, _new_key} = Nx.Random.normal(new_key, 0, 1, shape: {nrows}) + + booster = + EXGBoost.train(x, y, + num_boost_rounds: 25, + tree_method: :hist, + eval_metric: :rmse + ) + + first_preds = EXGBoost.predict(booster, x) + second_preds = EXGBoost.predict(booster, x) + third_preds = EXGBoost.predict(booster, x) + + assert Nx.all_close(first_preds, second_preds) + assert Nx.all_close(first_preds, third_preds) + assert Nx.all_close(second_preds, third_preds) + end + + test "predict first call matches subsequent confidence values", context do + nrows = 30 + ncols = 5 + {x, new_key} = Nx.Random.normal(context.key, 0, 1, shape: {nrows, ncols}) + {y, _new_key} = Nx.Random.normal(new_key, 0, 1, shape: {nrows}) + + booster = + EXGBoost.train(x, y, + num_boost_rounds: 30, + tree_method: :hist, + eval_metric: :rmse + ) + + sample = Nx.slice_along_axis(x, 0, 1, axis: 0) + + [baseline_confidence] = + EXGBoost.predict(booster, sample) + |> Nx.to_flat_list() + + for i <- 1..10 do + [confidence] = + EXGBoost.predict(booster, sample) + |> Nx.to_flat_list() + + assert_in_delta confidence, + baseline_confidence, + 1.0e-9, + "Expected confidence to match baseline #{baseline_confidence}, got #{confidence} for iteration #{i}" + end + end + test "predict with container", context do nrows = :rand.uniform(10) ncols = :rand.uniform(10) @@ -329,6 +382,37 @@ defmodule EXGBoostTest do assert EXGBoost.ArrayInterface.get_tensor(array_interface) == tensor end + test "array interface from_map ignores optional keys" do + arr_int = + EXGBoost.ArrayInterface.from_map(%{ + "typestr" => " [2, 2], + "data" => [123, true], + "version" => 3, + "strides" => nil, + "descr" => [["", " + EXGBoost.ArrayInterface.get_tensor(%EXGBoost.ArrayInterface{ + typestr: ">f4", + shape: {1}, + address: 1, + readonly: true, + tensor: nil, + binary: <<>> + }) + end + end + describe "errors" do setup %{key: key0} do {nrows, ncols} = {10, 10} From cbe7fde9748b05d0bbdb47f9a0269a6b285cc9d6 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 08:49:39 +0000 Subject: [PATCH 09/60] Add ability to check one version against another for breaking changes --- Makefile | 22 ++++ README.md | 9 ++ scripts/check_xgboost_c_api.sh | 186 +++++++++++++++++++++++++++++++++ test/nif_contract_test.exs | 23 ++++ 4 files changed, 240 insertions(+) create mode 100755 scripts/check_xgboost_c_api.sh create mode 100644 test/nif_contract_test.exs diff --git a/Makefile b/Makefile index 1922ef1..9d6caa2 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ XGBOOST_CACHE ?= $(TEMP)/exgboost XGBOOST_GIT_REPO ?= https://github.com/dmlc/xgboost.git # v3.1.3 tagged release XGBOOST_GIT_REV ?= v3.1.3 +OLD_XGBOOST_GIT_REV ?= v3.0.5 +NEW_XGBOOST_GIT_REV ?= $(XGBOOST_GIT_REV) XGBOOST_NS = xgboost-$(XGBOOST_GIT_REV) XGBOOST_DIR = $(XGBOOST_CACHE)/$(XGBOOST_NS) XGBOOST_LIB_DIR = $(XGBOOST_DIR)/build/xgboost @@ -73,6 +75,26 @@ $(XGBOOST_LIB_DIR_FLAG): $(XGBOOST_DIR)/.git ninja -C build install touch $(XGBOOST_LIB_DIR_FLAG) +check-xgboost-c-api: $(XGBOOST_LIB_DIR_FLAG) + ./scripts/check_xgboost_c_api.sh "$(XGBOOST_LIB_DIR)/include" + +compare-xgboost-c-api: + @set -eu; \ + for rev in "$(OLD_XGBOOST_GIT_REV)" "$(NEW_XGBOOST_GIT_REV)"; do \ + dir="$(XGBOOST_CACHE)/xgboost-$$rev"; \ + mkdir -p "$$dir"; \ + if [ ! -d "$$dir/.git" ]; then \ + git -C "$$dir" init; \ + git -C "$$dir" remote add origin "$(XGBOOST_GIT_REPO)"; \ + fi; \ + git -C "$$dir" fetch --depth 1 --recurse-submodules origin "$$rev"; \ + git -C "$$dir" checkout -f FETCH_HEAD; \ + git -C "$$dir" submodule update --init --recursive; \ + done; \ + ./scripts/check_xgboost_c_api.sh --compare \ + "$(XGBOOST_CACHE)/xgboost-$(OLD_XGBOOST_GIT_REV)/include" \ + "$(XGBOOST_CACHE)/xgboost-$(NEW_XGBOOST_GIT_REV)/include" + clean: rm -rf $(EXGBOOST_CACHE_SO) rm -rf $(EXGBOOST_CACHE_LIB_DIR) diff --git a/README.md b/README.md index 610e33d..e7e4236 100644 --- a/README.md +++ b/README.md @@ -230,3 +230,12 @@ Recommended fork maintenance cadence: - Keep precompiled artefacts and checksums aligned with each release tag (`.github/workflows/precompile.yml`). - When upgrading `nx`, verify supported Elixir/OTP versions in CI matrix and `mix.exs`. +Native compatibility checks: + +- Run `make check-xgboost-c-api` after changing `XGBOOST_GIT_REV` or native C files. +- Run `mix test test/nif_test.exs` to validate runtime behavior after the API check passes. +- To compare API declarations between two XGBoost versions, run: + `scripts/check_xgboost_c_api.sh --compare ` +- Or use make to fetch and compare tags directly: + `make compare-xgboost-c-api OLD_XGBOOST_GIT_REV=v3.0.5 NEW_XGBOOST_GIT_REV=v3.1.3` + diff --git a/scripts/check_xgboost_c_api.sh b/scripts/check_xgboost_c_api.sh new file mode 100755 index 0000000..6ed3fa5 --- /dev/null +++ b/scripts/check_xgboost_c_api.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +set -euo pipefail + +extract_signatures() { + local header="$1" + + awk ' + /XG[A-Za-z0-9_]+[[:space:]]*\(/ { + in_decl=1 + decl=$0 + if ($0 ~ /;/) { + print decl + in_decl=0 + decl="" + } + next + } + in_decl { + decl=decl " " $0 + if ($0 ~ /;/) { + print decl + in_decl=0 + decl="" + } + } + ' "$header" \ + | sed -E 's/[[:space:]]+/ /g; s/^[[:space:]]+//; s/[[:space:]]+$//' \ + | awk ' + { + if (match($0, /XG[A-Za-z0-9_]+[[:space:]]*\(/)) { + name = substr($0, RSTART, RLENGTH) + gsub(/[[:space:]]*\($/, "", name) + print name "\t" $0 + } + } + ' +} + +compare_mode() { + if [[ $# -ne 2 ]]; then + echo "Usage: $0 --compare " + exit 2 + fi + + local old_header="$1/xgboost/c_api.h" + local new_header="$2/xgboost/c_api.h" + + if [[ ! -f "$old_header" ]]; then + echo "XGBoost C API header not found: $old_header" + exit 2 + fi + + if [[ ! -f "$new_header" ]]; then + echo "XGBoost C API header not found: $new_header" + exit 2 + fi + + old_tmp=$(mktemp) + new_tmp=$(mktemp) + trap 'rm -f "${old_tmp:-}" "${new_tmp:-}"' EXIT + + extract_signatures "$old_header" | sort -u > "$old_tmp" + extract_signatures "$new_header" | sort -u > "$new_tmp" + + declare -A old_map=() + declare -A new_map=() + + while IFS=$'\t' read -r name sig; do + [[ -n "${name:-}" ]] && old_map["$name"]="$sig" + done < "$old_tmp" + + while IFS=$'\t' read -r name sig; do + [[ -n "${name:-}" ]] && new_map["$name"]="$sig" + done < "$new_tmp" + + removed=() + added=() + changed=() + + for name in "${!old_map[@]}"; do + if [[ -z "${new_map[$name]+x}" ]]; then + removed+=("$name") + elif [[ "${old_map[$name]}" != "${new_map[$name]}" ]]; then + changed+=("$name") + fi + done + + for name in "${!new_map[@]}"; do + if [[ -z "${old_map[$name]+x}" ]]; then + added+=("$name") + fi + done + + if (( ${#removed[@]} == 0 && ${#changed[@]} == 0 && ${#added[@]} == 0 )); then + echo "No C API declaration differences found between versions." + return 0 + fi + + if (( ${#removed[@]} > 0 )); then + printf 'Removed symbols (%d):\n' "${#removed[@]}" + printf ' - %s\n' "${removed[@]}" + fi + + if (( ${#changed[@]} > 0 )); then + printf 'Changed signatures (%d):\n' "${#changed[@]}" + for name in "${changed[@]}"; do + printf ' - %s\n' "$name" + printf ' old: %s\n' "${old_map[$name]}" + printf ' new: %s\n' "${new_map[$name]}" + done + fi + + if (( ${#added[@]} > 0 )); then + printf 'Added symbols (%d):\n' "${#added[@]}" + printf ' - %s\n' "${added[@]}" + fi + + if (( ${#removed[@]} > 0 || ${#changed[@]} > 0 )); then + return 1 + fi + + return 0 +} + +if [[ "${1:-}" == "--compare" ]]; then + shift + compare_mode "$@" + exit $? +fi + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 [xgboost-shared-lib-path]" + echo " $0 --compare " + exit 2 +fi + +include_dir="$1" +shared_lib="${2:-}" +header="${include_dir}/xgboost/c_api.h" + +if [[ ! -f "$header" ]]; then + echo "XGBoost C API header not found: $header" + exit 2 +fi + +mapfile -t used_symbols < <( + grep -RhoE '\bXG[A-Za-z0-9_]+[[:space:]]*\(' c/exgboost/src/*.c \ + | sed -E 's/[[:space:]]*\($//' \ + | sort -u +) + +missing_in_header=() +for symbol in "${used_symbols[@]}"; do + if ! grep -qE "\\b${symbol}[[:space:]]*\\(" "$header"; then + missing_in_header+=("$symbol") + fi +done + +if (( ${#missing_in_header[@]} > 0 )); then + echo "Symbols used by exgboost NIF but missing from XGBoost C API header:" + printf ' - %s\n' "${missing_in_header[@]}" + exit 1 +fi + +if [[ -n "$shared_lib" && -f "$shared_lib" && "$(command -v nm || true)" != "" ]]; then + if [[ "$(uname -s)" == "Darwin" ]]; then + mapfile -t exported_symbols < <(nm -gU "$shared_lib" | awk '{print $3}' | sed 's/^_//' | sort -u) + else + mapfile -t exported_symbols < <(nm -D --defined-only "$shared_lib" | awk '{print $3}' | sort -u) + fi + + missing_in_lib=() + for symbol in "${used_symbols[@]}"; do + if ! printf '%s\n' "${exported_symbols[@]}" | grep -qx "$symbol"; then + missing_in_lib+=("$symbol") + fi + done + + if (( ${#missing_in_lib[@]} > 0 )); then + echo "Symbols declared in header but not exported by shared library:" + printf ' - %s\n' "${missing_in_lib[@]}" + exit 1 + fi +fi + +echo "XGBoost C API compatibility check passed (${#used_symbols[@]} symbols)." diff --git a/test/nif_contract_test.exs b/test/nif_contract_test.exs new file mode 100644 index 0000000..dfc0279 --- /dev/null +++ b/test/nif_contract_test.exs @@ -0,0 +1,23 @@ +defmodule NifContractTest do + use ExUnit.Case, async: true + + @nif_c_file Path.expand("../c/exgboost/src/exgboost.c", __DIR__) + + test "Elixir NIF stubs match C registered NIF functions" do + c_source = File.read!(@nif_c_file) + + c_exports = + Regex.scan(~r/\{"([a-z0-9_]+)",\s*(\d+),/i, c_source) + |> Enum.map(fn [_, name, arity] -> {String.to_atom(name), String.to_integer(arity)} end) + |> MapSet.new() + + elixir_exports = + EXGBoost.NIF.__info__(:functions) + |> Enum.reject(fn {name, _arity} -> name in [:module_info, :__info__, :on_load] end) + |> MapSet.new() + + assert c_exports == elixir_exports, + "NIF API mismatch. Missing in C: #{inspect(MapSet.difference(elixir_exports, c_exports))}. " <> + "Missing in Elixir: #{inspect(MapSet.difference(c_exports, elixir_exports))}" + end +end From 4372e1cc34a41f0423815e08b8930b435cb2bb53 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:17:53 +0000 Subject: [PATCH 10/60] Add 2 new exg_free functions; we'll use later to reduce duplication --- c/exgboost/include/utils.h | 4 ++++ c/exgboost/src/utils.c | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/c/exgboost/include/utils.h b/c/exgboost/include/utils.h index a995657..8a88bbb 100644 --- a/c/exgboost/include/utils.h +++ b/c/exgboost/include/utils.h @@ -42,4 +42,8 @@ int exg_get_string_list(ErlNifEnv *env, ERL_NIF_TERM term, char ***out, int exg_get_dmatrix_list(ErlNifEnv *env, ERL_NIF_TERM term, DMatrixHandle **dmats, unsigned *len); +void exg_free_string_list(char **items, unsigned len); + +void exg_free_dmatrix_list(DMatrixHandle *dmats); + #endif \ No newline at end of file diff --git a/c/exgboost/src/utils.c b/c/exgboost/src/utils.c index 401def7..a384f8a 100644 --- a/c/exgboost/src/utils.c +++ b/c/exgboost/src/utils.c @@ -121,6 +121,24 @@ int exg_get_dmatrix_list(ErlNifEnv *env, ERL_NIF_TERM term, return 1; } +void exg_free_string_list(char **items, unsigned len) { + if (items == NULL) { + return; + } + for (unsigned i = 0; i < len; ++i) { + if (items[i] != NULL) { + enif_free(items[i]); + } + } + enif_free(items); +} + +void exg_free_dmatrix_list(DMatrixHandle *dmats) { + if (dmats != NULL) { + enif_free(dmats); + } +} + ERL_NIF_TERM exg_get_binary_address(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { ErlNifBinary bin; From ecbde3a7f5ca07a6ff772ef14990a7459de9534a Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:22:21 +0000 Subject: [PATCH 11/60] Ensure allocated memory is freed appropriately --- c/exgboost/src/booster.c | 27 +++++++++++++++++++++++++-- c/exgboost/src/dmatrix.c | 15 +++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index 7f578f1..c947a95 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -49,6 +49,7 @@ ERL_NIF_TERM EXGBoosterCreate(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + exg_free_dmatrix_list(dmats); return ret; } @@ -155,6 +156,12 @@ ERL_NIF_TERM EXGBoosterSetParam(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + if (name != NULL) { + enif_free(name); + } + if (value != NULL) { + enif_free(value); + } return ret; } @@ -327,6 +334,8 @@ ERL_NIF_TERM EXGBoosterEvalOneIter(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + exg_free_dmatrix_list(dmats); + exg_free_string_list(evnames, num_evnames); return ret; } @@ -364,6 +373,9 @@ ERL_NIF_TERM EXGBoosterGetAttr(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + if (key != NULL) { + enif_free(key); + } return ret; } @@ -415,6 +427,12 @@ ERL_NIF_TERM EXGBoosterSetAttr(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + if (key != NULL) { + enif_free(key); + } + if (value != NULL) { + enif_free(value); + } return ret; } @@ -493,8 +511,10 @@ ERL_NIF_TERM EXGBoosterSetStrFeatureInfo(ErlNifEnv *env, int argc, } END: if (features != NULL) { - enif_free(features); - features = NULL; + exg_free_string_list(features, num_features); + } + if (field != NULL) { + enif_free(field); } return ret; } @@ -596,6 +616,9 @@ ERL_NIF_TERM EXGBoosterFeatureScore(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + if (config != NULL) { + enif_free(config); + } return ret; } diff --git a/c/exgboost/src/dmatrix.c b/c/exgboost/src/dmatrix.c index f6a2839..b8718c7 100644 --- a/c/exgboost/src/dmatrix.c +++ b/c/exgboost/src/dmatrix.c @@ -286,8 +286,10 @@ ERL_NIF_TERM EXGDMatrixSetStrFeatureInfo(ErlNifEnv *env, int argc, } END: if (features != NULL) { - enif_free(features); - features = NULL; + exg_free_string_list(features, num_features); + } + if (field != NULL) { + enif_free(field); } return ret; } @@ -767,6 +769,15 @@ ERL_NIF_TERM EXGDMatrixGetDataAsCSR(ErlNifEnv *env, int argc, free(out_data); out_data = NULL; } + if (indptr != NULL) { + enif_free(indptr); + } + if (indices != NULL) { + enif_free(indices); + } + if (data != NULL) { + enif_free(data); + } return ret; }; From db5b1e92bb4cdded124195798065ec7f00d1cbbf Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:34:12 +0000 Subject: [PATCH 12/60] Avoid leaking local, xgboost owns the array, beam owns the created term --- c/exgboost/src/booster.c | 15 +++------------ c/exgboost/src/dmatrix.c | 5 +---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index c947a95..78901f6 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -458,10 +458,7 @@ ERL_NIF_TERM EXGBoosterGetAttrNames(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_len]; for (bst_ulong i = 0; i < out_len; ++i) { - char *local = enif_alloc(strlen(out[i]) + 1); - strcpy(local, out[i]); - arr[i] = enif_make_string(env, local, ERL_NIF_LATIN1); - // TODO: Do we free here or is it handled by the XGBoost library / BEAM? + arr[i] = enif_make_string(env, out[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_len)); } else { @@ -551,10 +548,7 @@ ERL_NIF_TERM EXGBoosterGetStrFeatureInfo(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_size]; for (bst_ulong i = 0; i < out_size; ++i) { - char *local = enif_alloc(strlen(c_out_features[i]) + 1); - strcpy(local, c_out_features[i]); - arr[i] = enif_make_string(env, local, ERL_NIF_LATIN1); - // TODO: Do we free here or is it handled by the XGBoost library / BEAM? + arr[i] = enif_make_string(env, c_out_features[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_size)); } else { @@ -1139,10 +1133,7 @@ ERL_NIF_TERM EXGBoosterDumpModelEx(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_len]; for (bst_ulong i = 0; i < out_len; ++i) { - char *local = enif_alloc(strlen(out_dump_array[i]) + 1); - strcpy(local, out_dump_array[i]); - arr[i] = enif_make_string(env, local, ERL_NIF_LATIN1); - // TODO: Do we free here or is it handled by the XGBoost library / BEAM ? + arr[i] = enif_make_string(env, out_dump_array[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_len)); } else { diff --git a/c/exgboost/src/dmatrix.c b/c/exgboost/src/dmatrix.c index b8718c7..ee70a2d 100644 --- a/c/exgboost/src/dmatrix.c +++ b/c/exgboost/src/dmatrix.c @@ -327,10 +327,7 @@ ERL_NIF_TERM EXGDMatrixGetStrFeatureInfo(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_size]; for (bst_ulong i = 0; i < out_size; ++i) { - char *local = enif_alloc(strlen(c_out_features[i]) + 1); - strcpy(local, c_out_features[i]); - arr[i] = enif_make_string(env, local, ERL_NIF_LATIN1); - // TODO: Do we free here or is it handled by the XGBoost library / BEAM? + arr[i] = enif_make_string(env, c_out_features[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_size)); } else { From bdc5f234e2605d4aab5b9948eeeb57e88f80c6d1 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:36:36 +0000 Subject: [PATCH 13/60] Use helper to free boosters that were created but left dangling --- c/exgboost/src/booster.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index 78901f6..614a204 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -835,6 +835,7 @@ ERL_NIF_TERM EXGBoosterLoadModel(ErlNifEnv *env, int argc, if (result == 0) { ret = make_Booster_resource(env, booster); } else { + XGBoosterFree(booster); ret = exg_error(env, XGBGetLastError()); } END: @@ -937,6 +938,7 @@ ERL_NIF_TERM EXGBoosterDeserializeFromBuffer(ErlNifEnv *env, int argc, if (result == 0) { ret = make_Booster_resource(env, booster); } else { + XGBoosterFree(booster); ret = exg_error(env, XGBGetLastError()); } END: @@ -972,6 +974,7 @@ ERL_NIF_TERM EXGBoosterLoadModelFromBuffer(ErlNifEnv *env, int argc, if (result == 0) { ret = make_Booster_resource(env, booster); } else { + XGBoosterFree(booster); ret = exg_error(env, XGBGetLastError()); } END: From 5072471cb663e4b26f30a249cf20a68f38bf4776 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:38:11 +0000 Subject: [PATCH 14/60] Free things that we enif_alloc'd --- c/exgboost/src/utils.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/c/exgboost/src/utils.c b/c/exgboost/src/utils.c index a384f8a..b98aa14 100644 --- a/c/exgboost/src/utils.c +++ b/c/exgboost/src/utils.c @@ -61,12 +61,14 @@ int exg_get_list(ErlNifEnv *env, ERL_NIF_TERM term, double **out) { return 0; } *out = (double *)enif_alloc(len * sizeof(double)); - if (out == NULL) { + if (*out == NULL) { return 0; } while (enif_get_list_cell(env, term, &head, &tail)) { int ret = enif_get_double(env, head, &((*out)[i])); if (!ret) { + enif_free(*out); + *out = NULL; return 0; } term = tail; @@ -89,6 +91,8 @@ int exg_get_string_list(ErlNifEnv *env, ERL_NIF_TERM term, char ***out, while (enif_get_list_cell(env, term, &head, &tail)) { int ret = exg_get_string(env, head, &((*out)[i])); if (!ret) { + exg_free_string_list(*out, i); + *out = NULL; return 0; } term = tail; @@ -105,13 +109,15 @@ int exg_get_dmatrix_list(ErlNifEnv *env, ERL_NIF_TERM term, return 0; } *dmats = (DMatrixHandle *)enif_alloc(*len * sizeof(DMatrixHandle)); - if (NULL == dmats) { + if (NULL == *dmats) { return 0; } while (enif_get_list_cell(env, term, &head, &tail)) { DMatrixHandle **resource = NULL; if (!enif_get_resource(env, head, DMatrix_RESOURCE_TYPE, (void *)&(resource))) { + exg_free_dmatrix_list(*dmats); + *dmats = NULL; return 0; } memcpy(&((*dmats)[i]), resource, sizeof(DMatrixHandle)); From cdea402085e247219d0cbda185f28a29a1ee3f29 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:38:22 +0000 Subject: [PATCH 15/60] Fix config pointer --- c/exgboost/src/booster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index 614a204..f45b222 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -565,7 +565,7 @@ ERL_NIF_TERM EXGBoosterFeatureScore(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { BoosterHandle booster; BoosterHandle **booster_resource = NULL; - char **config = NULL; + char *config = NULL; bst_ulong out_n_features = 0; char **out_features = NULL; bst_ulong out_dim = 0; From 5c586f110b59a05c0d21855efcabeb019774e489 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:43:58 +0000 Subject: [PATCH 16/60] Fix compile warnings, use correct pointer types --- c/exgboost/src/dmatrix.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/c/exgboost/src/dmatrix.c b/c/exgboost/src/dmatrix.c index ee70a2d..3388208 100644 --- a/c/exgboost/src/dmatrix.c +++ b/c/exgboost/src/dmatrix.c @@ -278,7 +278,8 @@ ERL_NIF_TERM EXGDMatrixSetStrFeatureInfo(ErlNifEnv *env, int argc, goto END; } handle = *resource; - result = XGDMatrixSetStrFeatureInfo(handle, field, features, num_features); + result = XGDMatrixSetStrFeatureInfo(handle, field, + (const char **)features, num_features); if (result == 0) { ret = ok_atom(env); } else { @@ -580,7 +581,7 @@ ERL_NIF_TERM EXGDMatrixGetFloatInfo(ErlNifEnv *env, int argc, DMatrixHandle handle; DMatrixHandle **resource = NULL; char *field = NULL; - float *out = NULL; + const float *out = NULL; bst_ulong len = 0; int result = -1; ERL_NIF_TERM ret = 0; @@ -634,7 +635,7 @@ ERL_NIF_TERM EXGDMatrixGetUIntInfo(ErlNifEnv *env, int argc, DMatrixHandle handle; DMatrixHandle **resource = NULL; char *field = NULL; - unsigned *out = NULL; + const unsigned *out = NULL; bst_ulong len = 0; int result = -1; ERL_NIF_TERM ret = 0; From aaff90f23cdc7e99416f0caef57c6070129cfcc2 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:47:31 +0000 Subject: [PATCH 17/60] Fix elixir formatting --- lib/exgboost/array_interface.ex | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/exgboost/array_interface.ex b/lib/exgboost/array_interface.ex index e6b2318..abad5e2 100644 --- a/lib/exgboost/array_interface.ex +++ b/lib/exgboost/array_interface.ex @@ -17,7 +17,7 @@ defmodule EXGBoost.ArrayInterface do @type t :: %__MODULE__{ typestr: String.t(), shape: tuple(), - address: non_neg_integer(), + address: non_neg_integer(), readonly: boolean(), tensor: Nx.Tensor.t(), binary: binary() @@ -158,6 +158,7 @@ defmodule EXGBoost.ArrayInterface do end binary = Nx.to_binary(tensor) + tensor_addr = EXGBoost.NIF.get_binary_address(binary) |> EXGBoost.Internal.unwrap!() @@ -186,10 +187,18 @@ defmodule EXGBoost.ArrayInterface do nx_type = case char_code do - "i" -> {:s, bit_width} - "u" -> {:u, bit_width} - "f" -> {:f, bit_width} - "c" -> {:c, bit_width} + "i" -> + {:s, bit_width} + + "u" -> + {:u, bit_width} + + "f" -> + {:f, bit_width} + + "c" -> + {:c, bit_width} + other -> raise ArgumentError, "Unsupported typestr code #{inspect(other)} in #{inspect(arr_int.typestr)}" From cb807c69d2eebb7c0087395d83a5e899f6430ac9 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 10:53:35 +0000 Subject: [PATCH 18/60] Remove elixir 1.17 from CI --- .github/workflows/ci.yml | 4 ++-- .github/workflows/precompile.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc2c912..3353236 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ jobs: strategy: fail-fast: false matrix: - otp: ["27.3", "28.3"] - elixir: ["1.17.3", "1.18.4", "1.19.5"] + otp: ["28.3"] + elixir: ["1.18.4", "1.19.5"] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 0b88203..cee049a 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -12,8 +12,8 @@ jobs: MIX_ENV: "prod" strategy: matrix: - otp: ["27.3", "28.3"] - elixir: ["1.18.4"] + otp: ["28.3"] + elixir: ["1.18.4", "1.19.5"] steps: - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 From 4e21cd4c2463fa3ab0bc3a5365d06ea937e3df0c Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 11:23:25 +0000 Subject: [PATCH 19/60] Add commentary on memory usage --- c/exgboost/src/booster.c | 6 ++++++ c/exgboost/src/dmatrix.c | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index f45b222..6833115 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -7,6 +7,7 @@ static ERL_NIF_TERM make_Booster_resource(ErlNifEnv *env, enif_alloc_resource(Booster_RESOURCE_TYPE, sizeof(BoosterHandle *)); if (resource != NULL) { *resource = handle; + // BEAM resource now owns the handle and releases it in resource cleanup. ret = exg_ok(env, enif_make_resource(env, resource)); enif_release_resource(resource); } else { @@ -49,6 +50,7 @@ ERL_NIF_TERM EXGBoosterCreate(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + // exg_get_dmatrix_list allocates this temporary array. exg_free_dmatrix_list(dmats); return ret; } @@ -149,6 +151,7 @@ ERL_NIF_TERM EXGBoosterSetParam(ErlNifEnv *env, int argc, ret = exg_error(env, "Booster parameter value must be a string"); goto END; } + // XGBoost consumes name/value during this call; no ownership transfer. result = XGBoosterSetParam(booster, name, value); if (result == 0) { ret = enif_make_atom(env, "ok"); @@ -334,6 +337,7 @@ ERL_NIF_TERM EXGBoosterEvalOneIter(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + // Helper-allocated arrays must be reclaimed on all paths. exg_free_dmatrix_list(dmats); exg_free_string_list(evnames, num_evnames); return ret; @@ -548,6 +552,7 @@ ERL_NIF_TERM EXGBoosterGetStrFeatureInfo(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_size]; for (bst_ulong i = 0; i < out_size; ++i) { + // enif_make_string materializes a BEAM term; no temporary C copy needed. arr[i] = enif_make_string(env, c_out_features[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_size)); @@ -629,6 +634,7 @@ static ERL_NIF_TERM collect_prediction_results(ErlNifEnv *env, ERL_NIF_TERM shape = enif_make_tuple_from_array(env, shape_arr, out_dim); ERL_NIF_TERM result_arr[out_len]; for (bst_ulong i = 0; i < out_len; ++i) { + // Values are copied into BEAM-managed terms here. result_arr[i] = enif_make_double(env, out_result[i]); } return exg_ok(env, enif_make_tuple2( diff --git a/c/exgboost/src/dmatrix.c b/c/exgboost/src/dmatrix.c index 3388208..28fcb74 100644 --- a/c/exgboost/src/dmatrix.c +++ b/c/exgboost/src/dmatrix.c @@ -7,6 +7,7 @@ static ERL_NIF_TERM make_DMatrix_resource(ErlNifEnv *env, enif_alloc_resource(DMatrix_RESOURCE_TYPE, sizeof(DMatrixHandle *)); if (resource != NULL) { *resource = handle; + // BEAM resource now owns the handle and releases it in resource cleanup. ret = exg_ok(env, enif_make_resource(env, resource)); enif_release_resource(resource); } else { @@ -278,6 +279,7 @@ ERL_NIF_TERM EXGDMatrixSetStrFeatureInfo(ErlNifEnv *env, int argc, goto END; } handle = *resource; + // XGBoost reads features during the call; caller keeps ownership. result = XGDMatrixSetStrFeatureInfo(handle, field, (const char **)features, num_features); if (result == 0) { @@ -286,6 +288,7 @@ ERL_NIF_TERM EXGDMatrixSetStrFeatureInfo(ErlNifEnv *env, int argc, ret = exg_error(env, XGBGetLastError()); } END: + // Helper-allocated buffers must be reclaimed on all paths. if (features != NULL) { exg_free_string_list(features, num_features); } @@ -328,6 +331,7 @@ ERL_NIF_TERM EXGDMatrixGetStrFeatureInfo(ErlNifEnv *env, int argc, if (result == 0) { ERL_NIF_TERM arr[out_size]; for (bst_ulong i = 0; i < out_size; ++i) { + // enif_make_string materializes a BEAM term; no temporary C copy needed. arr[i] = enif_make_string(env, c_out_features[i], ERL_NIF_LATIN1); } ret = exg_ok(env, enif_make_list_from_array(env, arr, out_size)); @@ -614,6 +618,7 @@ ERL_NIF_TERM EXGDMatrixGetFloatInfo(ErlNifEnv *env, int argc, if (result == 0) { arr = enif_alloc(sizeof(ERL_NIF_TERM) * len); for (int i = 0; i < len; i++) { + // Values are copied into BEAM-managed terms here. arr[i] = enif_make_double(env, out[i]); } ret = exg_ok(env, enif_make_list_from_array(env, arr, len)); @@ -663,6 +668,7 @@ ERL_NIF_TERM EXGDMatrixGetUIntInfo(ErlNifEnv *env, int argc, if (result == 0) { arr = enif_alloc(sizeof(ERL_NIF_TERM) * len); for (int i = 0; i < len; i++) { + // Values are copied into BEAM-managed terms here. arr[i] = enif_make_uint(env, out[i]); } ret = exg_ok(env, enif_make_list_from_array(env, arr, len)); @@ -751,6 +757,7 @@ ERL_NIF_TERM EXGDMatrixGetDataAsCSR(ErlNifEnv *env, int argc, enif_make_list_from_array(env, indices, num_non_missing), enif_make_list_from_array(env, data, num_non_missing))); END: + // Mixed allocators: enif_free for enif_alloc buffers, free for malloc buffers. if (config != NULL) { enif_free(config); config = NULL; From 17126513f85bce2b39a5a0b424e7f4d5fd017901 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 11:25:56 +0000 Subject: [PATCH 20/60] Bump to 0.6.0 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 1dbdf0d..2635d87 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,6 @@ defmodule EXGBoost.MixProject do use Mix.Project - @version "0.5.2" + @version "0.6.0" def project do [ From d96ceb83c4096f3759adef7cd8c5c4d9d83e1631 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 11:38:07 +0000 Subject: [PATCH 21/60] Handle charlists in unwrap! --- lib/exgboost/internal.ex | 13 ++++++++++++- test/nif_test.exs | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/exgboost/internal.ex b/lib/exgboost/internal.ex index dd4a3a1..0d24ba0 100644 --- a/lib/exgboost/internal.ex +++ b/lib/exgboost/internal.ex @@ -139,6 +139,17 @@ defmodule EXGBoost.Internal do end def unwrap!({:ok, val}), do: val - def unwrap!({:error, reason}), do: raise(reason) + + def unwrap!({:error, reason}) when is_exception(reason) do + raise(reason) + end + + def unwrap!({:error, reason}) when is_list(reason) do + message = if List.ascii_printable?(reason), do: List.to_string(reason), else: inspect(reason) + raise ArgumentError, message + end + + def unwrap!({:error, reason}) when is_binary(reason), do: raise(ArgumentError, reason) + def unwrap!({:error, reason}), do: raise(ArgumentError, inspect(reason)) def unwrap!(:ok), do: :ok end diff --git a/test/nif_test.exs b/test/nif_test.exs index 383418d..39d75c3 100644 --- a/test/nif_test.exs +++ b/test/nif_test.exs @@ -7,6 +7,12 @@ defmodule NifTest do assert EXGBoost.NIF.xgboost_version() |> unwrap!() != :error end + test "unwrap! raises string error for charlist reasons" do + assert_raise ArgumentError, "boom", fn -> + unwrap!({:error, ~c"boom"}) + end + end + test "build_info" do assert EXGBoost.NIF.xgboost_build_info() |> unwrap!() != :error end From 5914c13432c665a2c74811b8bc4a810c53eaad58 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 11:49:34 +0000 Subject: [PATCH 22/60] Fallback when loading models --- lib/exgboost/booster.ex | 12 +++++++++++- test/exgboost_test.exs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/exgboost/booster.ex b/lib/exgboost/booster.ex index 1724ee2..adbdac4 100644 --- a/lib/exgboost/booster.ex +++ b/lib/exgboost/booster.ex @@ -294,7 +294,7 @@ defmodule EXGBoost.Booster do EXGBoost.NIF.booster_load_json_config(booster.ref, config) |> Internal.unwrap!() :model -> - EXGBoost.NIF.booster_deserialize_from_buffer(source) |> Internal.unwrap!() + load_model_with_fallback(source) :weights -> EXGBoost.NIF.booster_load_model_from_buffer(source) |> Internal.unwrap!() @@ -303,6 +303,16 @@ defmodule EXGBoost.Booster do struct(booster, ref: booster_ref) end + defp load_model_with_fallback(source) do + case EXGBoost.NIF.booster_load_model_from_buffer(source) do + {:error, _reason} -> + EXGBoost.NIF.booster_deserialize_from_buffer(source) |> Internal.unwrap!() + + result -> + Internal.unwrap!(result) + end + end + @doc """ Get a formatted representation of the Booster's model. diff --git a/test/exgboost_test.exs b/test/exgboost_test.exs index f62b55e..af91fe8 100644 --- a/test/exgboost_test.exs +++ b/test/exgboost_test.exs @@ -333,6 +333,24 @@ defmodule EXGBoostTest do assert is_struct(bst, EXGBoost.Booster) end + test "load_model accepts model artifacts produced by dump_weights", context do + nrows = :rand.uniform(10) + ncols = :rand.uniform(10) + {x, _new_key} = Nx.Random.normal(context[:key], 0, 1, shape: {nrows, ncols}) + {y, _new_key} = Nx.Random.normal(context[:key], 0, 1, shape: {nrows}) + + booster = + EXGBoost.train(x, y, + num_boost_rounds: 10, + tree_method: :hist, + eval_metric: :rmse + ) + + buffer = EXGBoost.dump_weights(booster) + bst = EXGBoost.load_model(buffer) + assert is_struct(bst, EXGBoost.Booster) + end + test "serialize and deserialize weights to and from buffer", context do nrows = :rand.uniform(10) ncols = :rand.uniform(10) From 912065cdc93efb44e834b3db7bad846d1e9d65c7 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 12:57:25 +0000 Subject: [PATCH 23/60] Fix makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9d6caa2..7b4779e 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ $(EXGBOOST_SO): $(EXGBOOST_CACHE_SO) $(EXGBOOST_CACHE_SO): $(XGBOOST_LIB_DIR_FLAG) $(C_SRCS) @mkdir -p cache - cp -a $(XGBOOST_LIB_DIR) $(EXGBOOST_CACHE_LIB_DIR) - mv $(XGBOOST_LIB_DIR)/lib/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR) + cp -a $(XGBOOST_LIB_DIR)/lib $(EXGBOOST_CACHE_LIB_DIR) + mv $(EXGBOOST_CACHE_LIB_DIR)/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR)/ $(CC) $(CFLAGS) $(wildcard $(EXGBOOST_DIR)/src/*.c) $(LDFLAGS) -o $(EXGBOOST_CACHE_SO) $(POST_INSTALL) From fc1ad14d7ad448deb3a42293b400161ab7b355b9 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 12:59:30 +0000 Subject: [PATCH 24/60] Revert "Fix makefile" This reverts commit 912065cdc93efb44e834b3db7bad846d1e9d65c7. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7b4779e..9d6caa2 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ $(EXGBOOST_SO): $(EXGBOOST_CACHE_SO) $(EXGBOOST_CACHE_SO): $(XGBOOST_LIB_DIR_FLAG) $(C_SRCS) @mkdir -p cache - cp -a $(XGBOOST_LIB_DIR)/lib $(EXGBOOST_CACHE_LIB_DIR) - mv $(EXGBOOST_CACHE_LIB_DIR)/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR)/ + cp -a $(XGBOOST_LIB_DIR) $(EXGBOOST_CACHE_LIB_DIR) + mv $(XGBOOST_LIB_DIR)/lib/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR) $(CC) $(CFLAGS) $(wildcard $(EXGBOOST_DIR)/src/*.c) $(LDFLAGS) -o $(EXGBOOST_CACHE_SO) $(POST_INSTALL) From 39c845613d88db920a7d7fe3939473944399c508 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 13:08:13 +0000 Subject: [PATCH 25/60] Try to fix makefile again --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9d6caa2..2a95223 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ $(EXGBOOST_SO): $(EXGBOOST_CACHE_SO) $(EXGBOOST_CACHE_SO): $(XGBOOST_LIB_DIR_FLAG) $(C_SRCS) @mkdir -p cache cp -a $(XGBOOST_LIB_DIR) $(EXGBOOST_CACHE_LIB_DIR) - mv $(XGBOOST_LIB_DIR)/lib/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR) + cp $(XGBOOST_DIR)/lib/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR) $(CC) $(CFLAGS) $(wildcard $(EXGBOOST_DIR)/src/*.c) $(LDFLAGS) -o $(EXGBOOST_CACHE_SO) $(POST_INSTALL) From edefe909b8c3fa3c3226d99667a6131f189d2ee7 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 13:21:52 +0000 Subject: [PATCH 26/60] Release build when right MIX_ENV passed --- Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2a95223..d2d3658 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ # Environment variables passed via elixir_make # ERTS_INCLUDE_DIR # MIX_APP_PATH +# MIX_ENV TEMP ?= $(HOME)/.cache +MIX_ENV ?= dev XGBOOST_CACHE ?= $(TEMP)/exgboost XGBOOST_GIT_REPO ?= https://github.com/dmlc/xgboost.git # v3.1.3 tagged release @@ -14,6 +16,14 @@ XGBOOST_DIR = $(XGBOOST_CACHE)/$(XGBOOST_NS) XGBOOST_LIB_DIR = $(XGBOOST_DIR)/build/xgboost XGBOOST_LIB_DIR_FLAG = $(XGBOOST_LIB_DIR)/exgboost.ok +# Set build type based on MIX_ENV +MIX_ENV ?= dev +ifeq ($(MIX_ENV), prod) + CMAKE_BUILD_TYPE = Release +else + CMAKE_BUILD_TYPE = RelWithDebInfo +endif + # Private configuration PRIV_DIR = $(MIX_APP_PATH)/priv EXGBOOST_DIR = $(realpath c/exgboost) @@ -71,7 +81,7 @@ $(XGBOOST_DIR)/.git: # It only contains the build commands. $(XGBOOST_LIB_DIR_FLAG): $(XGBOOST_DIR)/.git cd $(XGBOOST_DIR) && \ - cmake -B build -S . -DCMAKE_INSTALL_PREFIX=$(XGBOOST_LIB_DIR) -DCMAKE_BUILD_TYPE=RelWithDebInfo -GNinja $(CMAKE_FLAGS) && \ + cmake -B build -S . -DCMAKE_INSTALL_PREFIX=$(XGBOOST_LIB_DIR) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) -GNinja $(CMAKE_FLAGS) && \ ninja -C build install touch $(XGBOOST_LIB_DIR_FLAG) From d831c2d1e6fa295f31b178c40fc6c2ef3e479366 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:01:33 +0000 Subject: [PATCH 27/60] Nif elixir needs to match interface --- lib/exgboost/nif.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/exgboost/nif.ex b/lib/exgboost/nif.ex index bbed9e0..59cb031 100644 --- a/lib/exgboost/nif.ex +++ b/lib/exgboost/nif.ex @@ -355,13 +355,13 @@ defmodule EXGBoost.NIF do :ok | {:error, String.t()} def booster_save_model(_handle, _path), do: :erlang.nif_error(:not_implemented) - @spec booster_serialize_to_buffer(booster_reference()) :: binary() + @spec booster_serialize_to_buffer(booster_reference()) :: {:ok, binary()} | {:error, String.t()} def booster_serialize_to_buffer(_handle), do: :erlang.nif_error(:not_implemented) @spec booster_deserialize_from_buffer(binary()) :: exgboost_return_type(booster_reference()) def booster_deserialize_from_buffer(_buffer), do: :erlang.nif_error(:not_implemented) - @spec booster_save_model_to_buffer(booster_reference(), String.t()) :: binary() + @spec booster_save_model_to_buffer(booster_reference(), String.t()) :: {:ok, binary()} | {:error, String.t()} def booster_save_model_to_buffer(_handle, _config), do: :erlang.nif_error(:not_implemented) @spec booster_load_model_from_buffer(binary()) :: exgboost_return_type(booster_reference()) @@ -370,7 +370,7 @@ defmodule EXGBoost.NIF do @spec booster_load_json_config(booster_reference(), String.t()) :: :ok | {:error, String.t()} def booster_load_json_config(_handle, _config), do: :erlang.nif_error(:not_implemented) - @spec booster_save_json_config(booster_reference()) :: binary() + @spec booster_save_json_config(booster_reference()) :: {:ok, binary()} | {:error, String.t()} def booster_save_json_config(_handle), do: :erlang.nif_error(:not_implemented) def booster_dump_model(_handle, _fmap, _with_stats, _format), From 77580d24f42e7aa1749ec0f0a4892929cde580d0 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:02:06 +0000 Subject: [PATCH 28/60] Add missing typespecs --- lib/exgboost/training/callback.ex | 10 +++++++++- lib/exgboost/training/state.ex | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/exgboost/training/callback.ex b/lib/exgboost/training/callback.ex index b3e6287..ee1d28a 100644 --- a/lib/exgboost/training/callback.ex +++ b/lib/exgboost/training/callback.ex @@ -43,6 +43,14 @@ defmodule EXGBoost.Training.Callback do """ alias EXGBoost.Training.State + + @type t :: %__MODULE__{ + event: event(), + fun: fun(), + name: atom(), + init_state: any() + } + @enforce_keys [:event, :fun] defstruct [:event, :fun, :name, :init_state] @@ -54,7 +62,7 @@ defmodule EXGBoost.Training.Callback do @doc """ Factory for a new callback with an initial state. """ - @spec new(event :: event(), fun :: fun(), name :: atom(), init_state :: any()) :: Callback.t() + @spec new(event :: event(), fun :: fun(), name :: atom(), init_state :: any()) :: t() def new(event, fun, name, init_state \\ %{}) when event in @valid_events and is_function(fun, 1) and is_atom(name) and not is_nil(name) do %__MODULE__{event: event, fun: fun, name: name, init_state: init_state} diff --git a/lib/exgboost/training/state.ex b/lib/exgboost/training/state.ex index de7cd90..4741409 100644 --- a/lib/exgboost/training/state.ex +++ b/lib/exgboost/training/state.ex @@ -1,5 +1,15 @@ defmodule EXGBoost.Training.State do @moduledoc false + + @type t :: %__MODULE__{ + booster: EXGBoost.Booster.t(), + iteration: non_neg_integer(), + max_iteration: -1 | non_neg_integer(), + meta_vars: map(), + metrics: map(), + status: :cont | :halt + } + @enforce_keys [:booster] defstruct [ :booster, From 178deec8684ec2a00b0ee59f210480dbacd227c4 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:02:19 +0000 Subject: [PATCH 29/60] Fix spec --- lib/exgboost/plotting.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/exgboost/plotting.ex b/lib/exgboost/plotting.ex index 50719fb..16d048f 100644 --- a/lib/exgboost/plotting.ex +++ b/lib/exgboost/plotting.ex @@ -834,7 +834,7 @@ defmodule EXGBoost.Plotting do @spec get_defaults() :: Keyword.t() def get_defaults(), do: @defaults - @spec get_styles() :: [{atom(), [style(), ...]}, ...] + @spec get_styles() :: [{atom(), style()}, ...] def get_styles(), do: @styles defp validate_spec(spec) do From 30a6b8ec4a750a06731e3c34bb4fb594e1f66dac Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:04:26 +0000 Subject: [PATCH 30/60] Add dialyzer --- mix.exs | 3 ++- mix.lock | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 2635d87..40c1a2b 100644 --- a/mix.exs +++ b/mix.exs @@ -58,7 +58,8 @@ defmodule EXGBoost.MixProject do {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, {:scidata, "~> 0.1", only: :dev}, - {:kino_vega_lite, "~> 0.1.9", only: :dev} + {:kino_vega_lite, "~> 0.1.9", only: :dev}, + {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, ] end diff --git a/mix.lock b/mix.lock index a2b814c..6953df7 100644 --- a/mix.lock +++ b/mix.lock @@ -4,8 +4,10 @@ "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, + "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, From ecc3e397b447ce485fa3bba3ab16584b79eff373 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:13:38 +0000 Subject: [PATCH 31/60] Formatting pass --- lib/exgboost/nif.ex | 3 ++- mix.exs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/exgboost/nif.ex b/lib/exgboost/nif.ex index 59cb031..c5086ea 100644 --- a/lib/exgboost/nif.ex +++ b/lib/exgboost/nif.ex @@ -361,7 +361,8 @@ defmodule EXGBoost.NIF do @spec booster_deserialize_from_buffer(binary()) :: exgboost_return_type(booster_reference()) def booster_deserialize_from_buffer(_buffer), do: :erlang.nif_error(:not_implemented) - @spec booster_save_model_to_buffer(booster_reference(), String.t()) :: {:ok, binary()} | {:error, String.t()} + @spec booster_save_model_to_buffer(booster_reference(), String.t()) :: + {:ok, binary()} | {:error, String.t()} def booster_save_model_to_buffer(_handle, _config), do: :erlang.nif_error(:not_implemented) @spec booster_load_model_from_buffer(binary()) :: exgboost_return_type(booster_reference()) diff --git a/mix.exs b/mix.exs index 40c1a2b..f1e297a 100644 --- a/mix.exs +++ b/mix.exs @@ -59,7 +59,7 @@ defmodule EXGBoost.MixProject do {:kino, "~> 0.11"}, {:scidata, "~> 0.1", only: :dev}, {:kino_vega_lite, "~> 0.1.9", only: :dev}, - {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, + {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false} ] end From 7e79f798b31aaf63131ee35ca309780f155a504d Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:16:47 +0000 Subject: [PATCH 32/60] Remove double set of MIX_ENV in Makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index d2d3658..217cc67 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,6 @@ XGBOOST_LIB_DIR = $(XGBOOST_DIR)/build/xgboost XGBOOST_LIB_DIR_FLAG = $(XGBOOST_LIB_DIR)/exgboost.ok # Set build type based on MIX_ENV -MIX_ENV ?= dev ifeq ($(MIX_ENV), prod) CMAKE_BUILD_TYPE = Release else From 756b15f529c917e36ccf32905cbdc3f534d1c9ce Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:19:25 +0000 Subject: [PATCH 33/60] Update lib/exgboost/array_interface.ex Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/exgboost/array_interface.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/exgboost/array_interface.ex b/lib/exgboost/array_interface.ex index abad5e2..7e35ba3 100644 --- a/lib/exgboost/array_interface.ex +++ b/lib/exgboost/array_interface.ex @@ -19,8 +19,8 @@ defmodule EXGBoost.ArrayInterface do shape: tuple(), address: non_neg_integer(), readonly: boolean(), - tensor: Nx.Tensor.t(), - binary: binary() + tensor: Nx.Tensor.t() | nil, + binary: binary() | nil } @enforce_keys [:typestr, :shape, :address, :readonly] From 0250ad4fd8ed6c6d9a13b459a5b5f9416c461924 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Tue, 24 Feb 2026 19:24:09 +0000 Subject: [PATCH 34/60] Fix typespec --- lib/exgboost/plotting.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/exgboost/plotting.ex b/lib/exgboost/plotting.ex index 16d048f..a45b744 100644 --- a/lib/exgboost/plotting.ex +++ b/lib/exgboost/plotting.ex @@ -834,7 +834,7 @@ defmodule EXGBoost.Plotting do @spec get_defaults() :: Keyword.t() def get_defaults(), do: @defaults - @spec get_styles() :: [{atom(), style()}, ...] + @spec get_styles() :: EXGBoost.Plotting.Style.t() def get_styles(), do: @styles defp validate_spec(spec) do From 80440b8e6399f982aa999799b98b27c42daf35b1 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 25 Feb 2026 08:53:54 +0000 Subject: [PATCH 35/60] Bump to 0.6.1 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index f1e297a..2af10ac 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,6 @@ defmodule EXGBoost.MixProject do use Mix.Project - @version "0.6.0" + @version "0.6.1" def project do [ From ccdd4e6af742bfbcd2928ba02b67e8592f8598a2 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 25 Feb 2026 10:07:07 +0000 Subject: [PATCH 36/60] Add README note about thread safety --- README.md | 12 ++++++++++++ test/exgboost_test.exs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7e4236..be91165 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,18 @@ It accepts a `Booster` struct (which is the output of `EXGBoost.train/2`). preds = EXGBoost.train(X, y) |> EXGBoost.predict(X) ``` +## Concurrency and Thread Safety + +**Important**: Booster objects are **not thread-safe** for concurrent predictions. The underlying XGBoost C API does not provide synchronization mechanisms, and sharing a single booster reference across multiple Elixir processes for concurrent predictions can lead to race conditions, memory corruption, or incorrect results. For this reason it is not recommended that you cache boosters to be used +by multiple tasks in calling applications. + +### Why This Matters + +- `EXGBoost.predict/2` and `EXGBoost.inplace_predict/2` both use dirty CPU-bound NIF schedulers +- This prevents blocking the BEAM scheduler but **does not** provide thread safety +- Concurrent access to the same booster from multiple processes can cause undefined behavior + + ## Serialization A Booster can be serialized to a file using `EXGBoost.write_*` and loaded from a file diff --git a/test/exgboost_test.exs b/test/exgboost_test.exs index af91fe8..d6224df 100644 --- a/test/exgboost_test.exs +++ b/test/exgboost_test.exs @@ -80,7 +80,7 @@ defmodule EXGBoostTest do assert Booster.get_boosted_rounds(booster) == num_boost_round end - test "predict", context do + test "predict matches inplace_predict", context do nrows = :rand.uniform(10) ncols = :rand.uniform(10) {x, new_key} = Nx.Random.normal(context.key, 0, 1, shape: {nrows, ncols}) From 6ad3e310d8d1814f403ab854d8f4df08d4c33934 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:17:12 +0100 Subject: [PATCH 37/60] Remove duplicate assertion --- test/exgboost_test.exs | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/exgboost_test.exs b/test/exgboost_test.exs index d6224df..9c3357a 100644 --- a/test/exgboost_test.exs +++ b/test/exgboost_test.exs @@ -219,9 +219,6 @@ defmodule EXGBoostTest do ) end) end - - refute is_nil(booster.best_iteration) - refute is_nil(booster.best_score) end test "eval with multiple metrics", context do From 87c989e0eb20623728267a0d36fdaad264855e4a Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:23:44 +0100 Subject: [PATCH 38/60] Deps upgrade --- mix.exs | 2 +- mix.lock | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 2af10ac..b10780d 100644 --- a/mix.exs +++ b/mix.exs @@ -53,7 +53,7 @@ defmodule EXGBoost.MixProject do {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, {:ex_json_schema, "~> 0.11.0"}, - {:httpoison, "~> 2.0", runtime: false}, + {:httpoison, "~> 3.0", runtime: false}, {:vega_lite, "~> 0.1"}, {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, diff --git a/mix.lock b/mix.lock index 6953df7..0933dd9 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, + "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, @@ -12,9 +12,10 @@ "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, - "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, - "httpoison": {:hex, :httpoison, "2.3.0", "10eef046405bc44ba77dc5b48957944df8952cc4966364b3cf6aa71dce6de587", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d388ee70be56d31a901e333dbcdab3682d356f651f93cf492ba9f06056436a2c"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, + "hackney": {:hex, :hackney, "4.4.5", "a908f620525bb886a16613532324762e5166287f8c00c9888a762edee11a30c0", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.1", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "6d72bef4e135e94c522c271e11fbb6933efb0006ef235a3933807d0be73b71ec"}, + "httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"}, + "idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "kino": {:hex, :kino, "0.18.0", "a1ae60dcbd79413befea7902906be5c6a8f7380af86f86dbdc7994687ad3a13f", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "11868c6d653523550ec208c7dc3717fffb68c0380e20fca395251d9db447d1a7"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, @@ -22,12 +23,13 @@ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, + "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, "nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, - "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"}, + "quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, @@ -36,4 +38,5 @@ "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "vega_lite": {:hex, :vega_lite, "0.1.11", "2b261d21618f6fa9f63bb4542f0262982d2e40aea3f83e935788fe172902b3c2", [:mix], [{:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "d18c3f11369c14bdf36ab53010c06bf5505c221cbcb32faac7420cf6926b3c50"}, "vega_lite_convert": {:hex, :vega_lite_convert, "1.0.1", "1cc0309998c10bce9d944ae631938a433e9cad4ccf7344f9a192d9ddcab1bd93", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.4", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}, {:vega_lite, ">= 0.0.0", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "1e12b4ef3943510d3597e4876a10c6ab333b8ee71204b5e571605d80aa3cbd76"}, + "webtransport": {:hex, :webtransport, "0.4.1", "60ef6cd99282b5964c8172d674519239e31e357d934bde028bec70da34636fe5", [:rebar3], [{:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "006e4e52a8f03b69201d4637c85b424a4ddccc1909f32c5742fed8d495a75174"}, } From e92e57a17316457eba80a8a069dc10190e9f4a36 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:24:22 +0100 Subject: [PATCH 39/60] Implement devcontainer for more isolated builds --- .devcontainer/Dockerfile | 46 ++++++++++++++++++++++++++++ .devcontainer/devcontainer-lock.json | 24 +++++++++++++++ .devcontainer/devcontainer.json | 33 ++++++++++++++++++++ .gitignore | 1 - 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer-lock.json create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..ed08691 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,46 @@ +FROM hexpm/elixir:1.20.2-erlang-29.0.2-debian-trixie-20260623-slim + +ENV DEBIAN_FRONTEND=noninteractive +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 +ENV CC=gcc-14 +ENV CXX=g++-14 + +RUN apt-get update && \ + apt-get dist-upgrade --yes && \ + apt-get install --yes --no-install-recommends \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + g++-14 \ + gcc-14 \ + git \ + inotify-tools \ + ninja-build \ + openssh-client \ + pkg-config \ + python3 \ + python3-venv \ + sudo \ + zsh && \ + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 140 && \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 140 && \ + update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-14 140 && \ + update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-14 140 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/zsh -u 1000 -G sudo dev && \ + echo 'dev ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/dev && \ + chmod 0440 /etc/sudoers.d/dev && \ + mkdir -p /workspace && \ + chown dev:dev /workspace + +USER dev + +RUN git config --global --add safe.directory /workspace && \ + mix local.hex --force && \ + mix local.rebar --force + +WORKDIR /workspace diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 0000000..523de18 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,24 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "version": "1.10.0", + "resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e", + "integrity": "sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.7", + "resolved": "ghcr.io/devcontainers/features/git@sha256:a3e43ff91b9f5f6bd2c14bd510d43e5e698f1266dc41027ba4e04e7e45be607a", + "integrity": "sha256:a3e43ff91b9f5f6bd2c14bd510d43e5e698f1266dc41027ba4e04e7e45be607a" + }, + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "1.1.0", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671", + "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c452d66 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "EXGBoost Erlang 29 GCC 14", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", + "workspaceFolder": "/workspace", + "containerEnv": { + "CC": "gcc-14", + "CXX": "g++-14", + "MIX_ENV": "dev" + }, + "customizations": { + "vscode": { + "extensions": [ + "jakebecker.elixir-ls" + ] + } + }, + "postCreateCommand": "mix deps.get", + "remoteUser": "dev", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "configureZshAsDefaultShell": true + }, + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "moby": false + } + } +} diff --git a/.gitignore b/.gitignore index 15fd0cd..839a055 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ erl_crash.dump *.beam /config/*.secret.exs .elixir_ls/ -.tool-versions .vscode/ checksum.exs .DS_Store \ No newline at end of file From 41aca8c44423e106fc962a5fad14682c39013938 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:24:50 +0100 Subject: [PATCH 40/60] Update GH workflows --- .github/workflows/ci.yml | 20 +++++++++++++++++--- .github/workflows/precompile.yml | 18 +++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3353236..cea239a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,15 +10,29 @@ jobs: test: name: Test OTP ${{ matrix.otp }} / Elixir ${{ matrix.elixir }} runs-on: ubuntu-24.04 + env: + CC: gcc-14 + CXX: g++-14 + MIX_ENV: test strategy: fail-fast: false matrix: - otp: ["28.3"] - elixir: ["1.18.4", "1.19.5"] + include: + - otp: "28.3" + elixir: "1.18.4" + - otp: "28.3" + elixir: "1.19.5" + - otp: "29.0.2" + elixir: "1.20.2" steps: - uses: actions/checkout@v4 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake ninja-build gcc-14 g++-14 + - uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} @@ -36,4 +50,4 @@ jobs: run: mix format --check-formatted - name: Run tests - run: MIX_ENV=test mix test \ No newline at end of file + run: MIX_ENV=test mix test diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index cee049a..8107003 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -10,12 +10,19 @@ jobs: runs-on: ubuntu-24.04 env: MIX_ENV: "prod" + CC: gcc-14 + CXX: g++-14 strategy: matrix: - otp: ["28.3"] - elixir: ["1.18.4", "1.19.5"] + include: + - otp: "28.3" + elixir: "1.18.4" + - otp: "28.3" + elixir: "1.19.5" + - otp: "29.0.2" + elixir: "1.20.2" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: erlef/setup-beam@v1 with: otp-version: ${{matrix.otp}} @@ -24,10 +31,11 @@ jobs: run: | mix local.hex --force mix local.rebar --force - - name: Install system dependecies + - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y build-essential automake autoconf pkg-config bc m4 unzip zip \ + cmake ninja-build gcc-14 g++-14 \ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ gcc-riscv64-linux-gnu g++-riscv64-linux-gnu - name: Mix Test @@ -59,7 +67,7 @@ jobs: otp: ["27.0", "28.0"] elixir: ["1.18.4"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: asdf-vm/actions/install@v2 with: tool_versions: | From e1e086b4676b41c352a744c92c9e37091540cb6f Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:25:06 +0100 Subject: [PATCH 41/60] Point to my github repo --- mix.exs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index b10780d..8623000 100644 --- a/mix.exs +++ b/mix.exs @@ -8,27 +8,27 @@ defmodule EXGBoost.MixProject do version: @version, make_precompiler: {:nif, CCPrecompiler}, make_precompiler_url: - "https://github.com/acalejos/exgboost/releases/download/v#{@version}/@{artefact_filename}", + "https://github.com/iperks/exgboost/releases/download/v#{@version}/@{artefact_filename}", make_precompiler_priv_paths: ["libexgboost.*", "lib"], # NIF Versions correspond to OTP Releases # https://github.com/erlang/otp/blob/d3aa6c044c3927f011fb76ac087d5ce0e814954c/erts/emulator/beam/erl_nif.h#L57 make_precompiler_nif_versions: [ - versions: ["2.15", "2.16", "2.17"] + versions: ["2.15", "2.16", "2.17", "2.18"] ], elixir: "~> 1.17", start_permanent: Mix.env() == :prod, compilers: [:elixir_make] ++ Mix.compilers(), deps: deps(), name: "EXGBoost", - source_url: "https://github.com/acalejos/exgboost", - homepage_url: "https://github.com/acalejos/exgboost", + source_url: "https://github.com/iperks/exgboost", + homepage_url: "https://github.com/iperks/exgboost", docs: docs(), package: package(), before_closing_body_tag: &before_closing_body_tag/1, name: "EXGBoost", description: "Elixir bindings for the XGBoost library. `EXGBoost` provides an implementation of XGBoost that works with - [Nx](https://hexdocs.pm/nx/Nx.html) tensors." + [Nx](https://hexdocs.pm/nx/Nx.html) tensors. Maintained fork of acalejos/exgboost." ] end @@ -65,9 +65,9 @@ defmodule EXGBoost.MixProject do defp package do [ - maintainers: ["Andres Alejos"], + maintainers: ["Ian Perks"], licenses: ["Apache-2.0"], - links: %{"GitHub" => "https://github.com/acalejos/exgboost"}, + links: %{"GitHub" => "https://github.com/iperks/exgboost"}, files: [ "lib", "mix.exs", From d2d37791dea6e819b2e36a04862dbe45fe795394 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:25:23 +0100 Subject: [PATCH 42/60] Fix minor issues in makefile --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 217cc67..2fd89e0 100644 --- a/Makefile +++ b/Makefile @@ -54,13 +54,13 @@ else endif $(EXGBOOST_SO): $(EXGBOOST_CACHE_SO) - @ mkdir -p $(PRIV_DIR) + @mkdir -p $(PRIV_DIR) cp -a $(abspath $(EXGBOOST_CACHE_LIB_DIR)) $(EXGBOOST_LIB_DIR) ; \ cp -a $(abspath $(EXGBOOST_CACHE_SO)) $(EXGBOOST_SO) ; $(EXGBOOST_CACHE_SO): $(XGBOOST_LIB_DIR_FLAG) $(C_SRCS) @mkdir -p cache - cp -a $(XGBOOST_LIB_DIR) $(EXGBOOST_CACHE_LIB_DIR) + cp -R $(XGBOOST_LIB_DIR) $(EXGBOOST_CACHE_LIB_DIR) cp $(XGBOOST_DIR)/lib/$(LIBXGBOOST) $(EXGBOOST_CACHE_LIB_DIR) $(CC) $(CFLAGS) $(wildcard $(EXGBOOST_DIR)/src/*.c) $(LDFLAGS) -o $(EXGBOOST_CACHE_SO) $(POST_INSTALL) @@ -68,7 +68,7 @@ $(EXGBOOST_CACHE_SO): $(XGBOOST_LIB_DIR_FLAG) $(C_SRCS) # This new target handles fetching the source code. # It only runs if the .git directory inside the source folder is missing. $(XGBOOST_DIR)/.git: - mkdir -p $(XGBOOST_DIR) && \ + @mkdir -p $(XGBOOST_DIR) && \ cd $(XGBOOST_DIR) && \ git init && \ git remote add origin $(XGBOOST_GIT_REPO) && \ From 5b36911223ead6194c55d839d186862cfbb215e3 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:25:39 +0100 Subject: [PATCH 43/60] Fix for stricter C++ compile warnings --- c/exgboost/src/booster.c | 52 ++++++++++++++++++++++------------------ c/exgboost/src/config.c | 6 ++--- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/c/exgboost/src/booster.c b/c/exgboost/src/booster.c index 6833115..b0a23a2 100644 --- a/c/exgboost/src/booster.c +++ b/c/exgboost/src/booster.c @@ -300,7 +300,7 @@ ERL_NIF_TERM EXGBoosterEvalOneIter(ErlNifEnv *env, int argc, int iter = -1; unsigned num_dmats = 0; unsigned num_evnames = 0; - char *out = NULL; + const char *out = NULL; ERL_NIF_TERM ret = -1; int result = -1; if (4 != argc) { @@ -329,7 +329,7 @@ ERL_NIF_TERM EXGBoosterEvalOneIter(ErlNifEnv *env, int argc, ret = exg_error(env, "dmats and evnames must have the same length"); goto END; } - result = XGBoosterEvalOneIter(booster, iter, dmats, evnames, + result = XGBoosterEvalOneIter(booster, iter, dmats, (const char **)evnames, (bst_ulong)num_dmats, &out); if (result == 0) { ret = exg_ok(env, enif_make_string(env, out, ERL_NIF_LATIN1)); @@ -348,7 +348,7 @@ ERL_NIF_TERM EXGBoosterGetAttr(ErlNifEnv *env, int argc, BoosterHandle booster; BoosterHandle **booster_resource = NULL; char *key = NULL; - char *out = NULL; + const char *out = NULL; ERL_NIF_TERM ret = -1; int result = -1; int success = -1; @@ -444,7 +444,7 @@ ERL_NIF_TERM EXGBoosterGetAttrNames(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { BoosterHandle booster; BoosterHandle **booster_resource = NULL; - char **out = NULL; + const char **out = NULL; bst_ulong out_len = 0; ERL_NIF_TERM ret = -1; int result = -1; @@ -504,7 +504,8 @@ ERL_NIF_TERM EXGBoosterSetStrFeatureInfo(ErlNifEnv *env, int argc, goto END; } handle = *resource; - result = XGBoosterSetStrFeatureInfo(handle, field, features, num_features); + result = XGBoosterSetStrFeatureInfo(handle, field, (const char **)features, + num_features); if (result == 0) { ret = ok_atom(env); } else { @@ -572,10 +573,10 @@ ERL_NIF_TERM EXGBoosterFeatureScore(ErlNifEnv *env, int argc, BoosterHandle **booster_resource = NULL; char *config = NULL; bst_ulong out_n_features = 0; - char **out_features = NULL; + const char **out_features = NULL; bst_ulong out_dim = 0; - bst_ulong *out_shape = NULL; - float *out_scores = NULL; + const bst_ulong *out_shape = NULL; + const float *out_scores = NULL; ERL_NIF_TERM ret = -1; int result = -1; if (2 != argc) { @@ -622,9 +623,9 @@ ERL_NIF_TERM EXGBoosterFeatureScore(ErlNifEnv *env, int argc, } static ERL_NIF_TERM collect_prediction_results(ErlNifEnv *env, - bst_ulong *out_shape, + const bst_ulong *out_shape, bst_ulong out_dim, - float *out_result) { + const float *out_result) { bst_ulong out_len = 1; ERL_NIF_TERM shape_arr[out_dim]; for (bst_ulong j = 0; j < out_dim; ++j) { @@ -649,9 +650,9 @@ ERL_NIF_TERM EXGBoosterPredictFromDMatrix(ErlNifEnv *env, int argc, DMatrixHandle dmatrix; DMatrixHandle **dmatrix_resource = NULL; char *config = NULL; - bst_ulong *out_shape = NULL; + const bst_ulong *out_shape = NULL; bst_ulong out_dim = 0; - float *out_result = NULL; + const float *out_result = NULL; ERL_NIF_TERM ret = -1; int result = -1; @@ -697,9 +698,9 @@ ERL_NIF_TERM EXGBoosterPredictFromDense(ErlNifEnv *env, int argc, DMatrixHandle **proxy_resource = NULL; char *values = NULL; char *config = NULL; - bst_ulong *out_shape = NULL; + const bst_ulong *out_shape = NULL; bst_ulong out_dim = 0; - float *out_result = NULL; + const float *out_result = NULL; int result = -1; ERL_NIF_TERM ret = -1; if (4 != argc) { @@ -752,10 +753,10 @@ ERL_NIF_TERM EXGBoosterPredictFromCSR(ErlNifEnv *env, int argc, char *indices = NULL; char *data = NULL; char *config = NULL; - bst_ulong ncols = 0; - bst_ulong *out_shape = NULL; + int ncols = 0; + const bst_ulong *out_shape = NULL; bst_ulong out_dim = 0; - float *out_result = NULL; + const float *out_result = NULL; int result = -1; ERL_NIF_TERM ret = -1; if (7 != argc) { @@ -783,6 +784,10 @@ ERL_NIF_TERM EXGBoosterPredictFromCSR(ErlNifEnv *env, int argc, ret = exg_error(env, "Ncols must be an integer"); goto END; } + if (ncols < 0) { + ret = exg_error(env, "Ncols must be non-negative"); + goto END; + } if (!exg_get_string(env, argv[5], &config)) { ret = exg_error(env, "Config must be a JSON-encoded string"); goto END; @@ -795,8 +800,9 @@ ERL_NIF_TERM EXGBoosterPredictFromCSR(ErlNifEnv *env, int argc, } booster = *booster_resource; result = - XGBoosterPredictFromCSR(booster, indptr, indices, data, ncols, config, - proxy, &out_shape, &out_dim, &out_result); + XGBoosterPredictFromCSR(booster, indptr, indices, data, + (bst_ulong)ncols, config, proxy, &out_shape, + &out_dim, &out_result); if (result == 0) { ret = collect_prediction_results(env, out_shape, out_dim, out_result); } else { @@ -890,7 +896,7 @@ ERL_NIF_TERM EXGBoosterSerializeToBuffer(ErlNifEnv *env, int argc, BoosterHandle booster; BoosterHandle **booster_resource = NULL; bst_ulong out_len = 0; - char *out_buf = NULL; + const char *out_buf = NULL; int result = -1; ERL_NIF_TERM ret = -1; ErlNifBinary out_bin; @@ -995,7 +1001,7 @@ ERL_NIF_TERM EXGBoosterSaveModelToBuffer(ErlNifEnv *env, int argc, BoosterHandle booster; BoosterHandle **booster_resource = NULL; bst_ulong out_len = 0; - char *out_buf = NULL; + const char *out_buf = NULL; char *config = NULL; int result = -1; ERL_NIF_TERM ret = -1; @@ -1038,7 +1044,7 @@ ERL_NIF_TERM EXGBoosterSaveJsonConfig(ErlNifEnv *env, int argc, BoosterHandle booster; BoosterHandle **booster_resource = NULL; bst_ulong out_len = 0; - char *out_buf = NULL; + const char *out_buf = NULL; int result = -1; ERL_NIF_TERM ret = -1; ErlNifBinary out_bin; @@ -1109,7 +1115,7 @@ ERL_NIF_TERM EXGBoosterDumpModelEx(ErlNifEnv *env, int argc, BoosterHandle booster; BoosterHandle **booster_resource = NULL; bst_ulong out_len = 0; - char **out_dump_array = NULL; + const char **out_dump_array = NULL; char *fmap = NULL; int with_stats = 0; char *format = NULL; diff --git a/c/exgboost/src/config.c b/c/exgboost/src/config.c index 89f432e..9590d66 100644 --- a/c/exgboost/src/config.c +++ b/c/exgboost/src/config.c @@ -59,7 +59,7 @@ ERL_NIF_TERM EXGBSetGlobalConfig(ErlNifEnv *env, int argc, ERL_NIF_TERM EXGBGetGlobalConfig(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - char *out = NULL; + const char *out = NULL; int result = -1; ERL_NIF_TERM ret = 0; if (argc != 0) { @@ -68,7 +68,7 @@ ERL_NIF_TERM EXGBGetGlobalConfig(ErlNifEnv *env, int argc, } // No need to free out, it's a pointer to a static string defined in the // xgboost config struct - result = XGBGetGlobalConfig((char const **)&out); + result = XGBGetGlobalConfig(&out); if (result == 0) { ret = exg_ok(env, enif_make_string(env, out, ERL_NIF_LATIN1)); } else { @@ -76,4 +76,4 @@ ERL_NIF_TERM EXGBGetGlobalConfig(ErlNifEnv *env, int argc, } END: return ret; -} \ No newline at end of file +} From 874c66310bae0c84ef9e5f6454a07617ef0cbe9c Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:25:48 +0100 Subject: [PATCH 44/60] Bump to 0.7.0 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 8623000..1dbd2b4 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,6 @@ defmodule EXGBoost.MixProject do use Mix.Project - @version "0.6.1" + @version "0.7.0" def project do [ From 79184212d86fdd0f6334c33b9be87b69aceb4f72 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:28:47 +0100 Subject: [PATCH 45/60] Setup and install asdf runner in osx --- .github/workflows/precompile.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 8107003..837a91b 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -68,7 +68,8 @@ jobs: elixir: ["1.18.4"] steps: - uses: actions/checkout@v4 - - uses: asdf-vm/actions/install@v2 + - uses: asdf-vm/actions/setup@v3 + - uses: asdf-vm/actions/install@v3 with: tool_versions: | erlang ${{matrix.otp}} From d95880e9205e8fd3d8e67ee715164535d7363853 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:48:32 +0100 Subject: [PATCH 46/60] Prevent nodejs warnings --- .github/workflows/ci.yml | 2 +- .github/workflows/precompile.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cea239a..1c0bf8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: elixir: "1.20.2" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install system dependencies run: | diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 837a91b..0dd75d5 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -22,7 +22,7 @@ jobs: - otp: "29.0.2" elixir: "1.20.2" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: erlef/setup-beam@v1 with: otp-version: ${{matrix.otp}} @@ -67,7 +67,7 @@ jobs: otp: ["27.0", "28.0"] elixir: ["1.18.4"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: asdf-vm/actions/setup@v3 - uses: asdf-vm/actions/install@v3 with: From ff098eb36390f1c9d4fc1d9fc3e78215b2a054a2 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:50:59 +0100 Subject: [PATCH 47/60] Use beam setup rather than asdf --- .github/workflows/precompile.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 0dd75d5..8c8ae45 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -55,10 +55,7 @@ jobs: macos: runs-on: ${{matrix.runner}} - # Homebrew supports versioned Erlang/OTP but not Elixir - # It's a deliberate design decision from Homebrew to - # only support versioned distributions for certain packages - name: Mac (ARM) Erlang/OTP ${{matrix.otp}} / Elixir + name: Mac (ARM) Erlang/OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} env: MIX_ENV: "prod" strategy: @@ -68,12 +65,10 @@ jobs: elixir: ["1.18.4"] steps: - uses: actions/checkout@v5 - - uses: asdf-vm/actions/setup@v3 - - uses: asdf-vm/actions/install@v3 + - uses: erlef/setup-beam@v1 with: - tool_versions: | - erlang ${{matrix.otp}} - elixir ${{matrix.elixir}} + otp-version: ${{matrix.otp}} + elixir-version: ${{matrix.elixir}} - name: Install libomp run: | brew install libomp From daa5b1b4ff89ac1a2309acda7f8a79ece31cfcba Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 13:59:43 +0100 Subject: [PATCH 48/60] Update precompile matrix for macos --- .github/workflows/precompile.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 8c8ae45..1494c92 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -54,21 +54,16 @@ jobs: cache/*.tar.gz macos: - runs-on: ${{matrix.runner}} - name: Mac (ARM) Erlang/OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} + runs-on: macos-15 + name: Mac (ARM) Erlang/OTP 29.0.2 / Elixir 1.20.2 env: MIX_ENV: "prod" - strategy: - matrix: - runner: ["macos-15"] - otp: ["27.0", "28.0"] - elixir: ["1.18.4"] steps: - uses: actions/checkout@v5 - uses: erlef/setup-beam@v1 with: - otp-version: ${{matrix.otp}} - elixir-version: ${{matrix.elixir}} + otp-version: "29.0.2" + elixir-version: "1.20.2" - name: Install libomp run: | brew install libomp From c6d74f577aa1b8d15f73120cb92dc41079a145ea Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 14:10:44 +0100 Subject: [PATCH 49/60] Bump to 0.8.0 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 1dbd2b4..855644c 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,6 @@ defmodule EXGBoost.MixProject do use Mix.Project - @version "0.7.0" + @version "0.8.0" def project do [ From b1b71547b2468fb8cd8751c79ad3cdcebad1878b Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 14:49:47 +0100 Subject: [PATCH 50/60] Revert upgrade to httpoison --- mix.exs | 2 +- mix.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 855644c..513a1be 100644 --- a/mix.exs +++ b/mix.exs @@ -53,7 +53,7 @@ defmodule EXGBoost.MixProject do {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, {:ex_json_schema, "~> 0.11.0"}, - {:httpoison, "~> 3.0", runtime: false}, + {:httpoison, "~> 2.0", runtime: false}, {:vega_lite, "~> 0.1"}, {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, diff --git a/mix.lock b/mix.lock index 0933dd9..659c82c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "certifi": {:hex, :certifi, "2.17.0", "835748414307e15e05b17d0e518190228ce648b08d569a5cc93a85a40f3e5c9b", [:rebar3], [], "hexpm", "8122798a17f0293c80daada25d0f81c7f4d708c73fef782c7c9b1950e26e4d21"}, + "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, @@ -13,9 +13,9 @@ "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, - "hackney": {:hex, :hackney, "4.4.5", "a908f620525bb886a16613532324762e5166287f8c00c9888a762edee11a30c0", [:rebar3], [{:certifi, "~> 2.17.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:idna, "~> 7.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.2", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:webtransport, "~> 0.4.1", [hex: :webtransport, repo: "hexpm", optional: false]}], "hexpm", "6d72bef4e135e94c522c271e11fbb6933efb0006ef235a3933807d0be73b71ec"}, - "httpoison": {:hex, :httpoison, "3.0.0", "8566a933bb9175236d1ec335978445b67cd1f5b5d3ead6ca4b80be469d41f5d9", [:mix], [{:hackney, "~> 4.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "9130197b7658901c493d6fcfb842fb9676300fa8a6c8ed058c8889cf1a77f3c2"}, - "idna": {:hex, :idna, "7.1.0", "1067a13043538129602d2f2ce6899d8713125c7d19734aa557ce2e3ea55bd4f1", [:rebar3], [], "hexpm", "6ae959a025bf36df61a8cab8508d9654891b5426a84c44d82deaffd6ddf8c71f"}, + "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, + "httpoison": {:hex, :httpoison, "2.3.0", "10eef046405bc44ba77dc5b48957944df8952cc4966364b3cf6aa71dce6de587", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d388ee70be56d31a901e333dbcdab3682d356f651f93cf492ba9f06056436a2c"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "kino": {:hex, :kino, "0.18.0", "a1ae60dcbd79413befea7902906be5c6a8f7380af86f86dbdc7994687ad3a13f", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "11868c6d653523550ec208c7dc3717fffb68c0380e20fca395251d9db447d1a7"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, @@ -28,7 +28,7 @@ "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, - "parse_trans": {:hex, :parse_trans, "3.4.2", "c352ddc1a0d5e54f9b1654d45f9c432eef76f9cea371c55ddff769ef688fdb74", [:rebar3], [], "hexpm", "4c25347de3b7c35732d32e69ab43d1ceee0beae3f3b3ade1b59cbd3dd224d9ca"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, From bf9a5ed170e50d88aecd382a3f7c9d7038dc510b Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Wed, 1 Jul 2026 14:51:17 +0100 Subject: [PATCH 51/60] Bump 0.8.1 --- mix.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 513a1be..ac6175b 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,7 @@ defmodule EXGBoost.MixProject do use Mix.Project - @version "0.8.0" + + @version "0.8.1" def project do [ From dea93602a0ba0cafa6e27db8af9d9e7132ae45ee Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 07:59:08 +0100 Subject: [PATCH 52/60] Update elixir_make dependency --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 659c82c..6964bc3 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "elixir_make": {:hex, :elixir_make, "0.10.0", "16577e2583a79bb79237bbff349619ef5d80afffc07eac6e4faf0d00e2ddaf7d", [:mix], [], "hexpm", "dc1f09fb7fa68866b886abd5f0f3c83553b1a19a52359a899e92af1bb3b31982"}, "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, From d135d08ef6287758472b42a5a55fb9dd561d38a0 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:02:14 +0100 Subject: [PATCH 53/60] Update ex_doc dependency --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 6964bc3..819e627 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.10.0", "16577e2583a79bb79237bbff349619ef5d80afffc07eac6e4faf0d00e2ddaf7d", [:mix], [], "hexpm", "dc1f09fb7fa68866b886abd5f0f3c83553b1a19a52359a899e92af1bb3b31982"}, "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, - "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, + "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, From 8f648be29e24eba3f3161b5f4ec1ae0ed62bb77e Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:02:48 +0100 Subject: [PATCH 54/60] Update ex_json_schema dependency --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 819e627..a533566 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "elixir_make": {:hex, :elixir_make, "0.10.0", "16577e2583a79bb79237bbff349619ef5d80afffc07eac6e4faf0d00e2ddaf7d", [:mix], [], "hexpm", "dc1f09fb7fa68866b886abd5f0f3c83553b1a19a52359a899e92af1bb3b31982"}, "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, - "ex_json_schema": {:hex, :ex_json_schema, "0.11.2", "8f8200e6afa5473f37dbebd1e72bf97d8d5dd0128125123ed1532611267f0138", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "395f4aaf32ea0a14d861b16695e7bc8a1b5d841e0fd374d25aef9701bf8da825"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.11.3", "0f9128fde2a46976c21d1ed9506bf8462776ac9e333e7b42ececcdbf7433a1da", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "0e8e16866bc14339b1bf1e441f1d9231031c8fe2cf45fa5a988a8b685de8c002"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, From cca04dafa9b6c01f26cc619860e056a0ffe00213 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:08:07 +0100 Subject: [PATCH 55/60] Replace httpoison with req --- lib/exgboost/plotting.ex | 4 +--- mix.exs | 2 +- mix.lock | 19 ++++++------------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/lib/exgboost/plotting.ex b/lib/exgboost/plotting.ex index a45b744..2235872 100644 --- a/lib/exgboost/plotting.ex +++ b/lib/exgboost/plotting.ex @@ -392,9 +392,7 @@ defmodule EXGBoost.Plotting do ] end - HTTPoison.start() - - @schema HTTPoison.get!("https://vega.github.io/schema/vega/v5.json").body + @schema Req.get!("https://vega.github.io/schema/vega/v5.json").body |> Jason.decode!() |> ExJsonSchema.Schema.resolve() diff --git a/mix.exs b/mix.exs index ac6175b..b83ace3 100644 --- a/mix.exs +++ b/mix.exs @@ -54,7 +54,7 @@ defmodule EXGBoost.MixProject do {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, {:ex_json_schema, "~> 0.11.0"}, - {:httpoison, "~> 2.0", runtime: false}, + {:req, "~> 0.5", runtime: false}, {:vega_lite, "~> 0.1"}, {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, diff --git a/mix.lock b/mix.lock index a533566..5ce3af5 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,6 @@ %{ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, @@ -11,32 +10,26 @@ "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, "ex_json_schema": {:hex, :ex_json_schema, "0.11.3", "0f9128fde2a46976c21d1ed9506bf8462776ac9e333e7b42ececcdbf7433a1da", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "0e8e16866bc14339b1bf1e441f1d9231031c8fe2cf45fa5a988a8b685de8c002"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, - "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, - "h2": {:hex, :h2, "0.10.2", "ea0146b9c8b5f3b5de16045765f5684db38ef1e66f1c60444890948cb1003e47", [:rebar3], [], "hexpm", "497a899f338b42e6a0b292524e635b0ce6f9379fa39395c8e38d06351cd9b9cf"}, - "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, - "httpoison": {:hex, :httpoison, "2.3.0", "10eef046405bc44ba77dc5b48957944df8952cc4966364b3cf6aa71dce6de587", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d388ee70be56d31a901e333dbcdab3682d356f651f93cf492ba9f06056436a2c"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, + "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "kino": {:hex, :kino, "0.18.0", "a1ae60dcbd79413befea7902906be5c6a8f7380af86f86dbdc7994687ad3a13f", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "11868c6d653523550ec208c7dc3717fffb68c0380e20fca395251d9db447d1a7"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mint": {:hex, :mint, "1.9.0", "d6f534c2a3e98b2a8cc749b4796eb77e9e3af79a76f96e4c74035a827de0d318", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "007154c7d8c43916aed3c93afd1f11aebbaa9c5ff4b7ba55ebe0d17ee0296042"}, "nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, - "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "quic": {:hex, :quic, "1.6.5", "28b7d49c1732b2de3861701bb03ae7798537240552370ac49f0d139d2ea8f621", [:rebar3], [], "hexpm", "de1a88972c33201a50d1a17c8c4a14528bd1d8f25ef705897d680ae312d0aa78"}, + "req": {:hex, :req, "0.6.2", "b9b2024f35bcf60a92cc8cad2eaaf9d4e7aace463ff74be1afe5986830184413", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cc9cd30a2ddd04989929b887178e1610c940456d962c6c3a52df6146d2eef9bf"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "vega_lite": {:hex, :vega_lite, "0.1.11", "2b261d21618f6fa9f63bb4542f0262982d2e40aea3f83e935788fe172902b3c2", [:mix], [{:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "d18c3f11369c14bdf36ab53010c06bf5505c221cbcb32faac7420cf6926b3c50"}, "vega_lite_convert": {:hex, :vega_lite_convert, "1.0.1", "1cc0309998c10bce9d944ae631938a433e9cad4ccf7344f9a192d9ddcab1bd93", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.4", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}, {:vega_lite, ">= 0.0.0", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "1e12b4ef3943510d3597e4876a10c6ab333b8ee71204b5e571605d80aa3cbd76"}, - "webtransport": {:hex, :webtransport, "0.4.1", "60ef6cd99282b5964c8172d674519239e31e357d934bde028bec70da34636fe5", [:rebar3], [{:h2, "~> 0.10.1", [hex: :h2, repo: "hexpm", optional: false]}, {:quic, "~> 1.6.5", [hex: :quic, repo: "hexpm", optional: false]}], "hexpm", "006e4e52a8f03b69201d4637c85b424a4ddccc1909f32c5742fed8d495a75174"}, } From cda4b83c1eec0e380fcc4961044cb34d93669be4 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:10:04 +0100 Subject: [PATCH 56/60] Update kino --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 5ce3af5..60fb63b 100644 --- a/mix.lock +++ b/mix.lock @@ -13,7 +13,7 @@ "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "kino": {:hex, :kino, "0.18.0", "a1ae60dcbd79413befea7902906be5c6a8f7380af86f86dbdc7994687ad3a13f", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "11868c6d653523550ec208c7dc3717fffb68c0380e20fca395251d9db447d1a7"}, + "kino": {:hex, :kino, "0.19.0", "fc8e46fefeb2d083e757633ddd810c499754b7a1f87ba1e92844791c1eca87c2", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "195956058730acc397375a398835c7818f92aa01a1d32c03ecb5dffff74c0b8c"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, From d19726af482b0e5ae747f1e6587a9a25c8d7f2c1 Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:10:16 +0100 Subject: [PATCH 57/60] Update jason dependency --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 60fb63b..990fcc0 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, - "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "kino": {:hex, :kino, "0.19.0", "fc8e46fefeb2d083e757633ddd810c499754b7a1f87ba1e92844791c1eca87c2", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "195956058730acc397375a398835c7818f92aa01a1d32c03ecb5dffff74c0b8c"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, From ee059acb0e56ada144fe36ffb20b91a57dd260fd Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:15:45 +0100 Subject: [PATCH 58/60] Use underlying erlang httpc library --- lib/exgboost/plotting.ex | 13 ++++++++++--- mix.exs | 1 - mix.lock | 6 ------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/exgboost/plotting.ex b/lib/exgboost/plotting.ex index 2235872..6ca6f18 100644 --- a/lib/exgboost/plotting.ex +++ b/lib/exgboost/plotting.ex @@ -392,9 +392,16 @@ defmodule EXGBoost.Plotting do ] end - @schema Req.get!("https://vega.github.io/schema/vega/v5.json").body - |> Jason.decode!() - |> ExJsonSchema.Schema.resolve() + :inets.start() + :ssl.start() + + @schema (fn -> + {:ok, {{_, 200, _}, _, body}} = + :httpc.request(:get, {"https://vega.github.io/schema/vega/v5.json", []}, [], []) + + body |> to_string() |> Jason.decode!() |> ExJsonSchema.Schema.resolve() + end).() + @mark_text_doc "Accepts a keyword list of Vega `text` Mark properties. Reference [here](https://vega.github.io/vega/docs/marks/text/) for more details. Accepts either a string (expected to be valid Vega property names) or Elixir-styled atom. Note that keys are snake-cased instead of camel-case (e.g. Vega `fontSize` becomes `font_size`)" @mark_rect_doc "Accepts a keyword list of Vega `rect` Mark properties. Reference [here](https://vega.github.io/vega/docs/marks/rect/) for more details. Accepts either a string (expected to be valid Vega property names) or Elixir-styled atom. Note that keys are snake-cased instead of camel-case (e.g. Vega `fontSize` becomes `font_size`)" diff --git a/mix.exs b/mix.exs index b83ace3..e6a19a3 100644 --- a/mix.exs +++ b/mix.exs @@ -54,7 +54,6 @@ defmodule EXGBoost.MixProject do {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, {:ex_json_schema, "~> 0.11.0"}, - {:req, "~> 0.5", runtime: false}, {:vega_lite, "~> 0.1"}, {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, diff --git a/mix.lock b/mix.lock index 990fcc0..afaf773 100644 --- a/mix.lock +++ b/mix.lock @@ -10,22 +10,16 @@ "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, "ex_json_schema": {:hex, :ex_json_schema, "0.11.3", "0f9128fde2a46976c21d1ed9506bf8462776ac9e333e7b42ececcdbf7433a1da", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "0e8e16866bc14339b1bf1e441f1d9231031c8fe2cf45fa5a988a8b685de8c002"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, - "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, - "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "kino": {:hex, :kino, "0.19.0", "fc8e46fefeb2d083e757633ddd810c499754b7a1f87ba1e92844791c1eca87c2", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "195956058730acc397375a398835c7818f92aa01a1d32c03ecb5dffff74c0b8c"}, "kino_vega_lite": {:hex, :kino_vega_lite, "0.1.13", "03c00405987a2202e4b8014ee55eb7f5727691b3f13d76a3764f6eeccef45322", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: false]}, {:vega_lite, "~> 0.1.8", [hex: :vega_lite, repo: "hexpm", optional: false]}], "hexpm", "00c72bc270e7b9d3c339f726cdab0012fd3f2fc75e36c7548e0f250fe420fa10"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, - "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, - "mint": {:hex, :mint, "1.9.0", "d6f534c2a3e98b2a8cc749b4796eb77e9e3af79a76f96e4c74035a827de0d318", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "007154c7d8c43916aed3c93afd1f11aebbaa9c5ff4b7ba55ebe0d17ee0296042"}, "nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, - "req": {:hex, :req, "0.6.2", "b9b2024f35bcf60a92cc8cad2eaaf9d4e7aace463ff74be1afe5986830184413", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cc9cd30a2ddd04989929b887178e1610c940456d962c6c3a52df6146d2eef9bf"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, From 91edcf5da995f77865946a2d105b1f4aca99fcef Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:27:18 +0100 Subject: [PATCH 59/60] ex_json_schema upgrade --- mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index e6a19a3..b49fef3 100644 --- a/mix.exs +++ b/mix.exs @@ -53,7 +53,7 @@ defmodule EXGBoost.MixProject do {:ex_doc, "~> 0.40", only: :docs}, {:cc_precompiler, "~> 0.1.0", runtime: false}, {:exterval, "0.2.0"}, - {:ex_json_schema, "~> 0.11.0"}, + {:ex_json_schema, "~> 0.11.4"}, {:vega_lite, "~> 0.1"}, {:vega_lite_convert, "~> 1.0.1"}, {:kino, "~> 0.11"}, diff --git a/mix.lock b/mix.lock index afaf773..b921fb1 100644 --- a/mix.lock +++ b/mix.lock @@ -2,13 +2,13 @@ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, - "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, + "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.10.0", "16577e2583a79bb79237bbff349619ef5d80afffc07eac6e4faf0d00e2ddaf7d", [:mix], [], "hexpm", "dc1f09fb7fa68866b886abd5f0f3c83553b1a19a52359a899e92af1bb3b31982"}, "erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"}, "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, - "ex_json_schema": {:hex, :ex_json_schema, "0.11.3", "0f9128fde2a46976c21d1ed9506bf8462776ac9e333e7b42ececcdbf7433a1da", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "0e8e16866bc14339b1bf1e441f1d9231031c8fe2cf45fa5a988a8b685de8c002"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.11.4", "d2f7d31894d048f79ed6c5a76515c266d5bd137438c53fa39c55f6ae98a05f47", [:mix], [{:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "0bbe87044ef0154be2a91ab6927d69c5fcccdb21908a135653fc10dcbbb79c3b"}, "exterval": {:hex, :exterval, "0.2.0", "cb2ce1dcaa08f7827d5eab25e0268e786b6b05b9b6d27f79d1d77f7b9f6dbd5d", [:mix], [], "hexpm", "30b48089fcc9a648d4bfcd17d51ab799b846c34db0fbf1fd6eb8a5526587510d"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, "kino": {:hex, :kino, "0.19.0", "fc8e46fefeb2d083e757633ddd810c499754b7a1f87ba1e92844791c1eca87c2", [:mix], [{:nx, "~> 0.1", [hex: :nx, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "195956058730acc397375a398835c7818f92aa01a1d32c03ecb5dffff74c0b8c"}, From 1e56709f0bc9946e2d03837ebe48ed0578fb11ca Mon Sep 17 00:00:00 2001 From: Ian Perks Date: Mon, 6 Jul 2026 08:31:45 +0100 Subject: [PATCH 60/60] Update nx and related deps --- mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index b49fef3..e700be3 100644 --- a/mix.exs +++ b/mix.exs @@ -48,7 +48,7 @@ defmodule EXGBoost.MixProject do [ {:elixir_make, "~> 0.4", runtime: false}, {:nimble_options, "~> 1.0"}, - {:nx, "~> 0.7"}, + {:nx, "~> 0.9"}, {:jason, "~> 1.3"}, {:ex_doc, "~> 0.40", only: :docs}, {:cc_precompiler, "~> 0.1.0", runtime: false}, diff --git a/mix.lock b/mix.lock index b921fb1..4fa6628 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, - "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"}, + "complex": {:hex, :complex, "0.7.0", "695632ef9487517aa5d57edd1697801079d622414cb2e1a7cf538b1f9a50f205", [:mix], [], "hexpm", "0ee39c0803129f546e7f3f640da8f021c9e659402bf59da6f7f2c4848f068f8d"}, "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, @@ -19,7 +19,7 @@ "nimble_csv": {:hex, :nimble_csv, "1.3.0", "b7f998dc62b222bce9596e46f028c7a5af04cb5dde6df2ea197c583227c54971", [:mix], [], "hexpm", "41ccdc18f7c8f8bb06e84164fc51635321e80d5a3b450761c4997d620925d619"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, + "nx": {:hex, :nx, "0.12.1", "6e9fee43a77646d04faad2ba4e449b9e270c6b23e413f203cb4d81e71c0a617f", [:mix], [{:complex, "~> 0.7", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ee80f6ae898f68bbfe7f30216b06aab10096231ec99ded1208a827256b23d0fb"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "scidata": {:hex, :scidata, "0.1.11", "fe3358bac7d740374b4f2a7eff6a1cb02e5ee7f87f7cdb1e8648ad93c533165f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.1", [hex: :nimble_csv, repo: "hexpm", optional: false]}, {:stb_image, "~> 0.4", [hex: :stb_image, repo: "hexpm", optional: true]}], "hexpm", "90873337a9d5fe880d640517efa93d3c07e46c8ba436de44117f581800549f93"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"},