From 24163085a5b35a6cadcdd3ddb02a8b36639fbfca Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 5 Apr 2024 19:39:17 +0100 Subject: [PATCH 01/52] feat(bayes3d): init bayes3d @ ba4234a4 --- flake.nix | 15 +++++++++ lib/mkScopes/default.nix | 14 +++++++-- pkgs/bayes3d/default.nix | 67 ++++++++++++++++++++++++++++++++++++++++ pkgs/open3d/default.nix | 53 +++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 pkgs/bayes3d/default.nix create mode 100644 pkgs/open3d/default.nix diff --git a/flake.nix b/flake.nix index 27b865f..b92bb0f 100644 --- a/flake.nix +++ b/flake.nix @@ -6,6 +6,9 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; + nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; + nixConfig.extra-trusted-public-keys = [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" ]; + outputs = inputs@{ self, nixpkgs, flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ @@ -33,6 +36,7 @@ scopes = (self.lib.mkScopes { inherit pkgs; + self = self'; basicTools = self.lib.basicTools; }); loom = scopes.callPy3Package ./pkgs/loom { }; @@ -44,8 +48,19 @@ ociImgBase ; + + bayes3d = scopes.callPy3Package ./pkgs/bayes3d { }; + open3d = scopes.callPy3Package ./pkgs/open3d { }; }; in { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + config = { + allowUnfree = true; + cudaSupport = true; + }; + }; + inherit packages; }; diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix index 3218d45..d8f5559 100644 --- a/lib/mkScopes/default.nix +++ b/lib/mkScopes/default.nix @@ -1,11 +1,14 @@ -{ pkgs, basicTools }: let +{ pkgs, basicTools, self }: +let callPackage = pkgs.newScope ( pkgs // { inherit callPackage; basicTools = basicTools pkgs; } + // self.packages ); + callPy3Package = pkgs.newScope ( pkgs // pkgs.python3Packages @@ -13,7 +16,12 @@ inherit callPackage; basicTools = basicTools pkgs; } + // self.packages ); -in { - inherit callPackage callPy3Package ; +in +{ + inherit + callPackage + callPy3Package + ; } diff --git a/pkgs/bayes3d/default.nix b/pkgs/bayes3d/default.nix new file mode 100644 index 0000000..0a3d74d --- /dev/null +++ b/pkgs/bayes3d/default.nix @@ -0,0 +1,67 @@ +{ pkgs +, fetchFromGitHub +, python3Packages +, cudaPackages +, which +, libglvnd +, libGLU +, open3d +}: +let + rev = "ba4234a4720512f7dc45d11b8b8fbf449a439c0a"; + + #torch-cuda = python3Packages.torch.override (final: prev: { + #}); +in +python3Packages.buildPythonPackage rec { + pname = "bayes3d"; + version = "0.1.0+${builtins.substring 0 8 rev}"; + + src = fetchFromGitHub { + repo = pname; + owner = "probcomp"; + inherit rev; + hash = "sha256-/Cdm4Syfhm8QFCgKWITvaSGKmDjR38mepQez4xOzH1A="; + }; + + pyproject = true; + + nativeBuildInputs = with python3Packages; [ + setuptools + setuptools-scm + which + ]; + + buildInputs = [ + cudaPackages.cuda_nvcc + cudaPackages.cuda_cudart + cudaPackages.libcusparse + cudaPackages.cuda_cccl + cudaPackages.libcublas + cudaPackages.libcusolver + libglvnd + libGLU + ]; + + propagatedBuildInputs = [ + python3Packages.torch + python3Packages.graphviz + #python3Packages.genjax # TODO: missing + #python3Packages.distinctipy # TODO: missing + python3Packages.imageio + python3Packages.matplotlib + python3Packages.meshcat + python3Packages.natsort + open3d + #python3Packages.opencv-python # TODO: missing + python3Packages.opencv4 + python3Packages.plyfile + python3Packages.liblzfse + #python3Packages.pyransac3d # TODO: missing + python3Packages.tensorflow-probability + python3Packages.timm + python3Packages.trimesh + ]; + + env.CUDA_HOME = "${cudaPackages.cuda_nvcc}"; +} diff --git a/pkgs/open3d/default.nix b/pkgs/open3d/default.nix new file mode 100644 index 0000000..deb1130 --- /dev/null +++ b/pkgs/open3d/default.nix @@ -0,0 +1,53 @@ +{ fetchFromGitHub +, python3Packages +, cmake +, git +, ispc +, nasm +, xorg +, vulkan-tools +, libjpeg +}: +python3Packages.buildPythonPackage rec { + pname = "open3d"; + version = "0.18.0"; + format = "setuptools"; + + src = fetchFromGitHub { + owner = "isl-org"; + repo = pname; + rev = "v${version}"; + hash = "sha256-VMykWYfWUzhG+Db1I/9D1GTKd3OzmSXvwzXwaZnu8uI="; + }; + + #doCheck = false; + + env.VERBOSE = "1"; + + nativeBuildInputs = [ + cmake + ispc + git + nasm + ]; + + buildInputs = [ + xorg.libX11 + xorg.libXrandr + xorg.libXinerama + xorg.libXcursor + vulkan-tools + libjpeg + ]; + + propagatedBuildInputs = [ + python3Packages.pybind11 + ]; + + cmakeFlags = [ + #"--debug-output" + "--loglevel=VERBOSE" + #"--trace" + "-DCMAKE_ISPC_COMPILER=${ispc}/bin/ispc" + ]; +} From 82b2700020973e9791f9aceef73e95c28305be12 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 26 Apr 2024 19:06:27 +0100 Subject: [PATCH 02/52] WIP: try using pypi wheel --- flake.nix | 25 +++++-- lib/mkScopes/default.nix | 10 +-- pkgs/loom/default.nix | 8 +-- pkgs/open3d/default.nix | 147 +++++++++++++++++++++++++++++---------- 4 files changed, 139 insertions(+), 51 deletions(-) diff --git a/flake.nix b/flake.nix index b92bb0f..5c78519 100644 --- a/flake.nix +++ b/flake.nix @@ -35,29 +35,40 @@ }; scopes = (self.lib.mkScopes { - inherit pkgs; - self = self'; + inherit pkgs internalPackages; basicTools = self.lib.basicTools; }); loom = scopes.callPy3Package ./pkgs/loom { }; - packages = loom.more_packages // { + bayes3d = scopes.callPackage ./pkgs/bayes3d { }; + open3d = scopes.callPackage ./pkgs/open3d { }; + + internalPackages = { + tinygltf = scopes.callPackage ./pkgs/tinygltf { }; + PoissonRecon = scopes.callPackage ./pkgs/PoissonRecon { }; + goftests = scopes.callPackage ./pkgs/goftests { }; + parsable = scopes.callPackage ./pkgs/parsable { }; + pymetis = scopes.callPackage ./pkgs/pymetis { }; + distributions = scopes.callPackage ./pkgs/distributions { }; + } // packages; + + packages = { inherit loom sppl ociImgBase - ; - bayes3d = scopes.callPy3Package ./pkgs/bayes3d { }; - open3d = scopes.callPy3Package ./pkgs/open3d { }; + bayes3d + open3d + ; }; in { _module.args.pkgs = import inputs.nixpkgs { inherit system; config = { allowUnfree = true; - cudaSupport = true; + #cudaSupport = true; }; }; diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix index d8f5559..60d1fb1 100644 --- a/lib/mkScopes/default.nix +++ b/lib/mkScopes/default.nix @@ -1,22 +1,22 @@ -{ pkgs, basicTools, self }: +{ pkgs, basicTools, internalPackages }: let callPackage = pkgs.newScope ( pkgs // { - inherit callPackage; + inherit callPackage callPy3Package; basicTools = basicTools pkgs; } - // self.packages + // internalPackages ); callPy3Package = pkgs.newScope ( pkgs // pkgs.python3Packages // { - inherit callPackage; + inherit callPackage callPy3Package; basicTools = basicTools pkgs; } - // self.packages + // internalPackages ); in { diff --git a/pkgs/loom/default.nix b/pkgs/loom/default.nix index 7a8292e..0fbea08 100644 --- a/pkgs/loom/default.nix +++ b/pkgs/loom/default.nix @@ -29,12 +29,12 @@ , gperftools , dockerTools , basicTools +, distributions +, goftests +, parsable +, pymetis }: let - goftests = callPackage ./../goftests { }; - parsable = callPackage ./../parsable { }; - pymetis = callPackage ./../pymetis { }; - distributions = callPackage ./../distributions {inherit goftests parsable;}; protobuf = protobuf3_20; diff --git a/pkgs/open3d/default.nix b/pkgs/open3d/default.nix index deb1130..66aaecc 100644 --- a/pkgs/open3d/default.nix +++ b/pkgs/open3d/default.nix @@ -1,53 +1,130 @@ -{ fetchFromGitHub +{ stdenv +, lib , python3Packages -, cmake -, git -, ispc -, nasm +, fetchPypi +, fetchurl + +, tree +, unzip +, zip + +, autoPatchelfHook +, breakpointHook +, libtensorflow-bin +, libusb +, cudaPackages + +, libGL +, libglvnd +, libdrm +, expat , xorg -, vulkan-tools -, libjpeg +, libllvm +, llvmPackages_10 }: -python3Packages.buildPythonPackage rec { - pname = "open3d"; +let + inherit (python3Packages) + buildPythonPackage + ipywidgets + matplotlib + numpy + pandas + plyfile + pytorchWithCuda + pyyaml + scikitlearn + scipy + tqdm + ; + + #addict = buildPythonPackage { + #pname = "addict"; + #version = "2.4.0"; + + #src = fetchPypi { + #pname = "addict"; + #version = "2.4.0"; + #sha256 = "1574sicy5ydx9pvva3lbx8qp56z9jbdwbj26aqgjhyh61q723cmk"; + #}; + #}; + version = "0.18.0"; - format = "setuptools"; + pname = "open3d"; + pythonAbi = "cp311"; + pythonPlatform = "manylinux_2_27_x86_64"; - src = fetchFromGitHub { - owner = "isl-org"; - repo = pname; - rev = "v${version}"; - hash = "sha256-VMykWYfWUzhG+Db1I/9D1GTKd3OzmSXvwzXwaZnu8uI="; - }; +in buildPythonPackage { + inherit pname version; + format = "wheel"; - #doCheck = false; + #src = fetchPypi { + #inherit pname version; + #format = "wheel"; + #python = pythonAbi; + #abi = pythonAbi; + #dist = "py3"; + #sha256 = ""; + #}; + + src = fetchurl { + url = "https://files.pythonhosted.org/packages/5c/ba/a4c5986951344f804b5cbd86f0a87d9ea5969e8d13f1e8913e2d8276e0d8/open3d-0.18.0-cp311-cp311-manylinux_2_27_x86_64.whl"; + hash = "sha256-jj0dGQCo9NlW9oGcJGx4CBclubCIj4VJ0qeknI2qEwM="; + }; - env.VERBOSE = "1"; + patchPhase = '' + ${unzip}/bin/unzip ./dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp + rm ./dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl + #sed -i 's/sklearn/scikit-learn/g' tmp/open3d-${version}.dist-info/METADATA + cd tmp + ${zip}/bin/zip -0 -r ../dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* + cd ../ + ''; nativeBuildInputs = [ - cmake - ispc - git - nasm + autoPatchelfHook + breakpointHook ]; buildInputs = [ - xorg.libX11 - xorg.libXrandr - xorg.libXinerama - xorg.libXcursor - vulkan-tools - libjpeg + # so deps + stdenv.cc.cc.lib + libusb.out + pytorchWithCuda + libtensorflow-bin + cudaPackages.cudatoolkit.lib + libGL + libglvnd + libdrm + expat + xorg.libXxf86vm + xorg.libXfixes + libllvm ]; propagatedBuildInputs = [ - python3Packages.pybind11 + # py deps + ipywidgets + tqdm + pyyaml + pandas + plyfile + scipy + scikitlearn + numpy + #addict + matplotlib ]; - cmakeFlags = [ - #"--debug-output" - "--loglevel=VERBOSE" - #"--trace" - "-DCMAKE_ISPC_COMPILER=${ispc}/bin/ispc" - ]; + #preBuild = '' + #mkdir $out + #''; + + preFixup = '' + echo "OUTPUT TO: $out" + cd $out/lib/python3.*/site-packages/open3d + rm libGL.so.1 libEGL.so.1 + ln -s ${libGL}/lib/libGL.so.1 libGL.so.1 + ln -s ${libGL}/lib/libEGL.so.1 libEGL.so.1 + #exit 1 + ''; } From eea7adf364021a234ce37375162095a2b9b2b9f9 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 26 Jun 2024 16:32:53 +0100 Subject: [PATCH 03/52] WIP: building opencv from source failing with py.typed not found --- flake.lock | 157 +++++++++++++++++- flake.nix | 24 ++- lib/mkScopes/default.nix | 18 +- pkgs/bayes3d/default.nix | 50 ++++-- pkgs/distinctipy/default.nix | 24 +++ pkgs/genjax/default.nix | 49 ++++++ pkgs/open3d/default.nix | 27 ++- pkgs/opencv-python/default.nix | 97 +++++++++++ .../relax-dependency-ranges.patch | 26 +++ pkgs/oryx/default.nix | 38 +++++ pkgs/plum-dispatch/default.nix | 76 +++++++++ pkgs/pyransac3d/default.nix | 26 +++ 12 files changed, 582 insertions(+), 30 deletions(-) create mode 100644 pkgs/distinctipy/default.nix create mode 100644 pkgs/genjax/default.nix create mode 100644 pkgs/opencv-python/default.nix create mode 100644 pkgs/opencv-python/relax-dependency-ranges.patch create mode 100644 pkgs/oryx/default.nix create mode 100644 pkgs/plum-dispatch/default.nix create mode 100644 pkgs/pyransac3d/default.nix diff --git a/flake.lock b/flake.lock index 3f4f54a..bc1a987 100644 --- a/flake.lock +++ b/flake.lock @@ -2,7 +2,9 @@ "nodes": { "flake-parts": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib" + "nixpkgs-lib": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1714641030, @@ -18,6 +20,62 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "genjax": { + "flake": false, + "locked": { + "lastModified": 1706565447, + "narHash": "sha256-dMTB2YPnmboU+ZFpNF3+ZrcK4uH2ZjQ4hQKzXu49sjc=", + "owner": "probcomp", + "repo": "genjax", + "rev": "3357b75b7ae64121b2848254e11c5b79ee7f1820", + "type": "github" + }, + "original": { + "owner": "probcomp", + "ref": "v0.1.1", + "repo": "genjax", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703863825, + "narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "5163432afc817cf8bd1f031418d1869e4c9d5547", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1714253743, @@ -34,22 +92,103 @@ "type": "github" } }, - "nixpkgs-lib": { + "nixpkgs-llvm-10": { + "locked": { + "lastModified": 1706589919, + "narHash": "sha256-pNHnDITxSI3a17GOF1RUF3jBO1OiNYTRH2yV/cJG4m4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "222c1940fafeda4dea161858ffe6ebfc853d3db5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "222c1940fafeda4dea161858ffe6ebfc853d3db5", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2", + "treefmt-nix": "treefmt-nix" + }, "locked": { - "lastModified": 1714640452, - "narHash": "sha256-QBx10+k6JWz6u7VsohfSw8g8hjdBZEf8CFzXH1/1Z94=", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/50eb7ecf4cd0a5756d7275c8ba36790e5bd53e33.tar.gz" + "lastModified": 1718726452, + "narHash": "sha256-w4hJSYvACz0i5XHtxc6XNyHwbxpisN13M2kA2Y7937o=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "53e534a08c0cd2a9fa7587ed1c3e7f6aeb804a2c", + "type": "github" }, "original": { - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/50eb7ecf4cd0a5756d7275c8ba36790e5bd53e33.tar.gz" + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" } }, "root": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs" + "genjax": "genjax", + "nixpkgs": "nixpkgs", + "nixpkgs-llvm-10": "nixpkgs-llvm-10", + "poetry2nix": "poetry2nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "id": "systems", + "type": "indirect" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1718522839, + "narHash": "sha256-ULzoKzEaBOiLRtjeY3YoGFJMwWSKRYOic6VNw2UyTls=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "68eb1dc333ce82d0ab0c0357363ea17c31ea1f81", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 5c78519..2fea5d5 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,16 @@ inputs = { flake-parts.url = "github:hercules-ci/flake-parts"; + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-llvm-10.url = "github:NixOS/nixpkgs?rev=222c1940fafeda4dea161858ffe6ebfc853d3db5"; + + genjax.url = "github:probcomp/genjax?ref=v0.1.1"; + genjax.flake = false; + + poetry2nix.url = "github:nix-community/poetry2nix"; + poetry2nix.inputs.nixpkgs.follows = "nixpkgs"; }; nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; @@ -35,7 +44,7 @@ }; scopes = (self.lib.mkScopes { - inherit pkgs internalPackages; + inherit pkgs internalPackages inputs; basicTools = self.lib.basicTools; }); loom = scopes.callPy3Package ./pkgs/loom { }; @@ -50,6 +59,12 @@ parsable = scopes.callPackage ./pkgs/parsable { }; pymetis = scopes.callPackage ./pkgs/pymetis { }; distributions = scopes.callPackage ./pkgs/distributions { }; + genjax = scopes.callPy3Package ./pkgs/genjax { }; + distinctipy = scopes.callPy3Package ./pkgs/distinctipy { }; + pyransac3d = scopes.callPy3Package ./pkgs/pyransac3d { }; + opencv-python = scopes.callPy3Package ./pkgs/opencv-python { }; + oryx = scopes.callPy3Package ./pkgs/oryx { }; + plum-dispatch = scopes.callPy3Package ./pkgs/plum-dispatch { }; } // packages; packages = { @@ -68,8 +83,13 @@ inherit system; config = { allowUnfree = true; - #cudaSupport = true; + cudaSupport = true; }; + overlays = [ + (final: prev: { + inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; + }) + ]; }; inherit packages; diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix index 60d1fb1..761b1a7 100644 --- a/lib/mkScopes/default.nix +++ b/lib/mkScopes/default.nix @@ -1,9 +1,16 @@ -{ pkgs, basicTools, internalPackages }: +{ pkgs, basicTools, internalPackages, inputs }: let + poetry2nix = inputs.poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }; + callPackage = pkgs.newScope ( pkgs // { - inherit callPackage callPy3Package; + inherit + callPackage + callPy3Package + inputs + poetry2nix + ; basicTools = basicTools pkgs; } // internalPackages @@ -13,7 +20,12 @@ let pkgs // pkgs.python3Packages // { - inherit callPackage callPy3Package; + inherit + callPackage + callPy3Package + inputs + poetry2nix + ; basicTools = basicTools pkgs; } // internalPackages diff --git a/pkgs/bayes3d/default.nix b/pkgs/bayes3d/default.nix index 0a3d74d..351749b 100644 --- a/pkgs/bayes3d/default.nix +++ b/pkgs/bayes3d/default.nix @@ -1,17 +1,42 @@ -{ pkgs +{ lib , fetchFromGitHub +, breakpointHook , python3Packages , cudaPackages , which , libglvnd , libGLU , open3d +, symlinkJoin +, genjax +, distinctipy +, pyransac3d +, opencv-python }: let - rev = "ba4234a4720512f7dc45d11b8b8fbf449a439c0a"; + rev = "21fc1ec8439b11eecc308610b23df45e7eee5ee0"; #torch-cuda = python3Packages.torch.override (final: prev: { #}); + + cuda-common-redist = with cudaPackages; [ + cuda_cccl # + libcublas # cublas_v2.h + libcurand + libcusolver # cusolverDn.h + libcusparse # cusparse.h + ]; + + cuda-native-redist = symlinkJoin { + name = "cuda-native-redist-${cudaPackages.cudaVersion}"; + paths = + with cudaPackages; + [ + cuda_cudart # cuda_runtime.h cuda_runtime_api.h + cuda_nvcc + ] + ++ cuda-common-redist; + }; in python3Packages.buildPythonPackage rec { pname = "bayes3d"; @@ -21,7 +46,7 @@ python3Packages.buildPythonPackage rec { repo = pname; owner = "probcomp"; inherit rev; - hash = "sha256-/Cdm4Syfhm8QFCgKWITvaSGKmDjR38mepQez4xOzH1A="; + hash = "sha256-8HUtf9AGgsMSSarFpRhDSzen6Mt1TJNkAhm6T3o/fO0="; }; pyproject = true; @@ -30,6 +55,7 @@ python3Packages.buildPythonPackage rec { setuptools setuptools-scm which + #breakpointHook ]; buildInputs = [ @@ -46,22 +72,26 @@ python3Packages.buildPythonPackage rec { propagatedBuildInputs = [ python3Packages.torch python3Packages.graphviz - #python3Packages.genjax # TODO: missing - #python3Packages.distinctipy # TODO: missing python3Packages.imageio python3Packages.matplotlib python3Packages.meshcat python3Packages.natsort - open3d - #python3Packages.opencv-python # TODO: missing python3Packages.opencv4 python3Packages.plyfile python3Packages.liblzfse - #python3Packages.pyransac3d # TODO: missing python3Packages.tensorflow-probability python3Packages.timm python3Packages.trimesh - ]; - env.CUDA_HOME = "${cudaPackages.cuda_nvcc}"; + distinctipy + open3d + opencv-python + pyransac3d + ] ++ genjax.poetryPackages; + + preBuild = '' + export CUDA_HOME=${cuda-native-redist} + ''; + + #preferLocalBuild = true; } diff --git a/pkgs/distinctipy/default.nix b/pkgs/distinctipy/default.nix new file mode 100644 index 0000000..39afbe8 --- /dev/null +++ b/pkgs/distinctipy/default.nix @@ -0,0 +1,24 @@ +{ fetchPypi +, python3Packages +}: +python3Packages.buildPythonPackage rec { + pname = "distinctipy"; + version = "1.3.4"; + format = "pyproject"; + + src = fetchPypi { + inherit pname version; + hash = "sha256-/tl6//Gvtz7KqHyFRhAh8LqJ+uYwZ8ASW5ZzUmUQqsQ="; + }; + + doCheck = false; + + nativeBuildInputs = [ + python3Packages.setuptools + ]; + + propagatedBuildInputs = with python3Packages; [ + numpy + ]; +} + diff --git a/pkgs/genjax/default.nix b/pkgs/genjax/default.nix new file mode 100644 index 0000000..5200012 --- /dev/null +++ b/pkgs/genjax/default.nix @@ -0,0 +1,49 @@ +{ callPy3Package +, inputs +, poetry2nix +, python311 +, pkgs +, oryx +, plum-dispatch +}: +poetry2nix.mkPoetryPackages { + projectDir = inputs.genjax; + + overrides = poetry2nix.overrides.withDefaults (final: prev: { + #dm-tree = python311.pkgs.dm-tree; + #ruff = python311.pkgs.python-lsp-ruff; + }); + + preferWheels = true; + + python = python311; +} +#python311.pkgs.buildPythonPackage { + #pname = "genjax"; + #version = "0.1.0"; + #inherit src; + #format = "pyproject"; + + #nativeBuildInputs = [ + #pkgs.poetry + #python311.pkgs.poetry-core + #python311.pkgs.poetry-dynamic-versioning + #]; + + #propagatedBuildInputs = with python311.pkgs; [ + #beartype + #deprecated + #dill + #equinox + #jax + #jaxtyping + #numpy + #optax + #oryx + #plum-dispatch + #pygments + #rich + #tensorflow-probability + #typing-extensions + #]; +#} diff --git a/pkgs/open3d/default.nix b/pkgs/open3d/default.nix index 66aaecc..1728bf2 100644 --- a/pkgs/open3d/default.nix +++ b/pkgs/open3d/default.nix @@ -9,18 +9,19 @@ , zip , autoPatchelfHook -, breakpointHook , libtensorflow-bin , libusb -, cudaPackages +, cudaPackages_11 , libGL , libglvnd , libdrm , expat , xorg -, libllvm +, mesa , llvmPackages_10 +, buildEnv +, runCommand }: let inherit (python3Packages) @@ -37,6 +38,19 @@ let tqdm ; + libllvm-wrapped = + let + libllvm = llvmPackages_10.libllvm.lib; + name = libllvm.name; + in + buildEnv { + inherit name; + paths = [ + llvmPackages_10.libllvm.lib + (runCommand "${name}.1" {} "mkdir -p $out/lib && ln -sf ${libllvm}/lib/libLLVM-10.so $out/lib/libLLVM-10.so.1") + ]; + }; + #addict = buildPythonPackage { #pname = "addict"; #version = "2.4.0"; @@ -82,7 +96,6 @@ in buildPythonPackage { nativeBuildInputs = [ autoPatchelfHook - breakpointHook ]; buildInputs = [ @@ -91,14 +104,16 @@ in buildPythonPackage { libusb.out pytorchWithCuda libtensorflow-bin - cudaPackages.cudatoolkit.lib + cudaPackages_11.cudatoolkit.lib + #cudaPackages_11.cuda_cudart.lib libGL libglvnd libdrm expat xorg.libXxf86vm xorg.libXfixes - libllvm + libllvm-wrapped + mesa ]; propagatedBuildInputs = [ diff --git a/pkgs/opencv-python/default.nix b/pkgs/opencv-python/default.nix new file mode 100644 index 0000000..97c6195 --- /dev/null +++ b/pkgs/opencv-python/default.nix @@ -0,0 +1,97 @@ +{ lib +, buildPythonPackage +, fetchPypi +, fetchFromGitHub +, writeText +, git +, cmake +, numpy +, pip +, scikit-build +, setuptools +, wheel +, breakpointHook +}: +let + version = "4.10.0.84"; + + versionPy = writeText "version.py" '' + opencv_version = "${version}" + contrib = 1 + headless = 0 + rolling = 0 + ci_build = 0 + ''; + + src = fetchFromGitHub { + owner = "opencv"; + repo = "opencv-python"; + rev = "cce7c994d46406205eb39300bb7ca9c48d80185a"; + hash = "sha256-qxpZsH1bZNBRIbaN2gvH1/GN6CM08XudSrg4uOJqwbA="; + fetchSubmodules = true; + #leaveDotGit = true; + }; +in +buildPythonPackage rec { + pname = "opencv-python"; + inherit version; + pyproject = true; + + inherit src; + + patches = [ + ./relax-dependency-ranges.patch + ]; + + env = { + ENABLE_CONTRIB = 1; + ENABLE_HEADLESS = 0; + ENABLE_ROLLING = 0; + }; + + preConfigure = '' + #python -m find_version $ENABLE_CONTRIB $ENABLE_HEADLESS $ENABLE_ROLLING 0 + cp ${versionPy} cv2/version.py + ''; + + dontUseCmakeConfigure = true; + + nativeBuildInputs = [ + git + cmake + numpy + pip + scikit-build + setuptools + wheel + breakpointHook + ]; + + propagatedBuildInputs = [ + numpy + ]; + + cmakeFlags = [ + "-D WITH_GSTREAMER=ON" + "-D WITH_QT=ON" + "-D WITH_TBB=ON" + "-D ENABLE_FAST_MATH=1" + "-D CUDA_FAST_MATH=0" + "-D WITH_CUDA=OFF" + "-DBUILD_opencv_cudacodec=OFF" + "-DBUILD_opencv_cudaoptflow=OFF" + ]; + + pythonImportsCheck = [ "opencv_python" ]; + + #preBuild = '' + #echo export PATH="$PATH" + #''; + + meta = with lib; { + description = "Wrapper package for OpenCV python bindings"; + homepage = "https://pypi.org/project/opencv-python"; + license = with licenses; [ asl20 mit ]; + maintainers = with maintainers; [ ]; + }; +} diff --git a/pkgs/opencv-python/relax-dependency-ranges.patch b/pkgs/opencv-python/relax-dependency-ranges.patch new file mode 100644 index 0000000..8349092 --- /dev/null +++ b/pkgs/opencv-python/relax-dependency-ranges.patch @@ -0,0 +1,26 @@ +diff a/pyproject.toml b/pyproject.toml +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -6,8 +6,8 @@ requires = [ + "numpy==1.17.5; python_version=='3.8' and platform_machine != 'aarch64' and platform_machine != 'arm64'", + "numpy==1.19.3; python_version<'3.9' and sys_platform == 'linux' and platform_machine == 'aarch64'", + "numpy==1.21.0; python_version<'3.9' and sys_platform == 'darwin' and platform_machine == 'arm64'", ++ "numpy>=1.21.0; python_version>'3.9'", +- "numpy>=2.0.0; python_version>='3.9'", + "pip", + "scikit-build>=0.14.0", +- "setuptools==59.2.0", ++ "setuptools>=59.2.0", + ] + diff a/setup.py b/setup.py +--- a/setup.py ++++ b/setup.py +@@ -32,7 +32,7 @@ def main(): + 'numpy>=1.21.0; python_version<="3.9" and platform_system=="Darwin" and platform_machine=="arm64"', + 'numpy>=1.21.4; python_version>="3.10" and platform_system=="Darwin"', + "numpy>=1.23.5; python_version>='3.11'", +- "numpy>=1.26.0; python_version>='3.12'" ++ "numpy>=1.26.0; python_version>='3.11'" + ] + + python_version = cmaker.CMaker.get_python_version() diff --git a/pkgs/oryx/default.nix b/pkgs/oryx/default.nix new file mode 100644 index 0000000..792ce6b --- /dev/null +++ b/pkgs/oryx/default.nix @@ -0,0 +1,38 @@ +{ lib +, buildPythonPackage +, fetchPypi +, poetry-core +, jax +, jaxlib +, tensorflow-probability +}: + +buildPythonPackage rec { + pname = "oryx"; + version = "0.2.6"; + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-8spdSJN9e9jDdc6KxSmh7Z+NoxJjNNLgz91rfxuepI8="; + }; + + nativeBuildInputs = [ + poetry-core + ]; + + propagatedBuildInputs = [ + jax + jaxlib + tensorflow-probability + ]; + + pythonImportsCheck = [ "oryx" ]; + + meta = with lib; { + description = "Probabilistic programming and deep learning in JAX"; + homepage = "https://pypi.org/project/oryx"; + license = licenses.asl20; + maintainers = with maintainers; [ ]; + }; +} diff --git a/pkgs/plum-dispatch/default.nix b/pkgs/plum-dispatch/default.nix new file mode 100644 index 0000000..01acda7 --- /dev/null +++ b/pkgs/plum-dispatch/default.nix @@ -0,0 +1,76 @@ +{ lib +, buildPythonPackage +, fetchPypi +, hatch-vcs +, hatchling +, beartype +, rich +, typing-extensions +, black +, build +, coveralls +, ghp-import +, ipython +, jupyter-book +, mypy +, numpy +, pre-commit +, pyright +, pytest +, pytest-cov +, ruff +, tox +, wheel +}: + +buildPythonPackage rec { + pname = "plum-dispatch"; + version = "2.3.5"; + pyproject = true; + + src = fetchPypi { + pname = "plum_dispatch"; + inherit version; + hash = "sha256-eticwgKdh7Djusx8x3Pxlq4ynOEV8wi2Ly0GxosYo40="; + }; + + nativeBuildInputs = [ + hatch-vcs + hatchling + ]; + + propagatedBuildInputs = [ + beartype + rich + typing-extensions + ]; + + passthru.optional-dependencies = { + dev = [ + black + build + coveralls + ghp-import + ipython + jupyter-book + mypy + numpy + pre-commit + pyright + pytest + pytest-cov + ruff + tox + wheel + ]; + }; + + pythonImportsCheck = [ "plum" ]; + + meta = with lib; { + description = "Multiple dispatch in Python"; + homepage = "https://pypi.org/project/plum-dispatch"; + license = licenses.mit; + maintainers = with maintainers; [ ]; + }; +} diff --git a/pkgs/pyransac3d/default.nix b/pkgs/pyransac3d/default.nix new file mode 100644 index 0000000..6638659 --- /dev/null +++ b/pkgs/pyransac3d/default.nix @@ -0,0 +1,26 @@ +{ fetchFromGitHub +, python3 +}: +python3.pkgs.buildPythonApplication rec { + pname = "pyransac3d"; + version = "0.6.0"; + pyproject = true; + + src = fetchFromGitHub { + owner = "leomariga"; + repo = "pyRANSAC-3D"; + rev = "v${version}"; + hash = "sha256-QplIgH+zjkZgPWMvvpV2yM/HEEBRea4D+dG7G7h2jdQ="; + }; + + nativeBuildInputs = [ + python3.pkgs.setuptools + python3.pkgs.wheel + ]; + + propagatedBuildInputs = with python3.pkgs; [ + numpy + ]; + + pythonImportsCheck = [ "pyransac3d" ]; +} From 7c0daf314197e57a0ef8c6b02ce6b16b7a7b26e6 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Thu, 27 Jun 2024 10:17:05 +0100 Subject: [PATCH 04/52] WIP: everything builds but has package conflicts due to genjax being built with poetry2nix --- flake.nix | 7 +- lib/mkScopes/default.nix | 4 +- pkgs/bayes3d/default.nix | 3 +- pkgs/genjax/default.nix | 24 ++++- pkgs/genjax/set-pyproject-version.patch | 11 ++ pkgs/opencv-python/default.nix | 128 +++++++++++++----------- 6 files changed, 111 insertions(+), 66 deletions(-) create mode 100644 pkgs/genjax/set-pyproject-version.patch diff --git a/flake.nix b/flake.nix index 2fea5d5..b0df408 100644 --- a/flake.nix +++ b/flake.nix @@ -42,9 +42,11 @@ inherit nixpkgs; basicTools = self.lib.basicTools; }; + + poetry2nix = inputs.poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }; scopes = (self.lib.mkScopes { - inherit pkgs internalPackages inputs; + inherit pkgs internalPackages inputs poetry2nix; basicTools = self.lib.basicTools; }); loom = scopes.callPy3Package ./pkgs/loom { }; @@ -78,6 +80,7 @@ open3d ; }; + in { _module.args.pkgs = import inputs.nixpkgs { inherit system; @@ -93,6 +96,8 @@ }; inherit packages; + + legacyPackages.internalPackages = internalPackages; }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix index 761b1a7..b7488e3 100644 --- a/lib/mkScopes/default.nix +++ b/lib/mkScopes/default.nix @@ -1,7 +1,5 @@ -{ pkgs, basicTools, internalPackages, inputs }: +{ pkgs, basicTools, internalPackages, poetry2nix, inputs }: let - poetry2nix = inputs.poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }; - callPackage = pkgs.newScope ( pkgs // { diff --git a/pkgs/bayes3d/default.nix b/pkgs/bayes3d/default.nix index 351749b..e2e4679 100644 --- a/pkgs/bayes3d/default.nix +++ b/pkgs/bayes3d/default.nix @@ -87,7 +87,8 @@ python3Packages.buildPythonPackage rec { open3d opencv-python pyransac3d - ] ++ genjax.poetryPackages; + genjax + ]; preBuild = '' export CUDA_HOME=${cuda-native-redist} diff --git a/pkgs/genjax/default.nix b/pkgs/genjax/default.nix index 5200012..01fb0a2 100644 --- a/pkgs/genjax/default.nix +++ b/pkgs/genjax/default.nix @@ -2,12 +2,30 @@ , inputs , poetry2nix , python311 +, stdenv , pkgs , oryx , plum-dispatch }: -poetry2nix.mkPoetryPackages { - projectDir = inputs.genjax; +let + src = stdenv.mkDerivation { + name = "genjax-source"; + version = inputs.genjax.shortRev; + src = inputs.genjax; + + patches = [ + ./set-pyproject-version.patch + ]; + + installPhase = '' + mkdir $out + ls -alsph $src + cp -rfv ./. $out + ''; + }; +in +poetry2nix.mkPoetryApplication { + projectDir = src.out; overrides = poetry2nix.overrides.withDefaults (final: prev: { #dm-tree = python311.pkgs.dm-tree; @@ -20,7 +38,7 @@ poetry2nix.mkPoetryPackages { } #python311.pkgs.buildPythonPackage { #pname = "genjax"; - #version = "0.1.0"; + #version = "0.1.1"; #inherit src; #format = "pyproject"; diff --git a/pkgs/genjax/set-pyproject-version.patch b/pkgs/genjax/set-pyproject-version.patch new file mode 100644 index 0000000..2b516d1 --- /dev/null +++ b/pkgs/genjax/set-pyproject-version.patch @@ -0,0 +1,11 @@ +diff a/pyproject.toml b/pyproject.toml +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -1,6 +1,6 @@ + [tool.poetry] + name = "genjax" +-version = "0.0.0" ++version = "0.1.1" + description = "Probabilistic programming with Gen, built on top of JAX." + authors = [ + "McCoy R. Becker ", diff --git a/pkgs/opencv-python/default.nix b/pkgs/opencv-python/default.nix index 97c6195..0f8af01 100644 --- a/pkgs/opencv-python/default.nix +++ b/pkgs/opencv-python/default.nix @@ -1,8 +1,13 @@ { lib +, stdenv , buildPythonPackage +, fetchurl , fetchPypi -, fetchFromGitHub -, writeText +, autoPatchelfHook + +, unzip +, zip + , git , cmake , numpy @@ -10,83 +15,90 @@ , scikit-build , setuptools , wheel +, gcc +, libGL +, xorg +, libz +, qt5 + , breakpointHook }: let version = "4.10.0.84"; + pname = "opencv-python"; + pythonAbi = "cp311"; + pythonPlatform = "manylinux_2_27_x86_64"; + + wheelUrls = { + "x86_64-linux" = { + url = "https://files.pythonhosted.org/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"; + hash = "sha256-ms4UD8bWR/vhxpK8sqvOdolzSRIiwGfBMdgJV8WVtx8="; + }; + + "aarch64-linux" = { + url = "https://files.pythonhosted.org/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"; + hash = ""; + }; - versionPy = writeText "version.py" '' - opencv_version = "${version}" - contrib = 1 - headless = 0 - rolling = 0 - ci_build = 0 - ''; - - src = fetchFromGitHub { - owner = "opencv"; - repo = "opencv-python"; - rev = "cce7c994d46406205eb39300bb7ca9c48d80185a"; - hash = "sha256-qxpZsH1bZNBRIbaN2gvH1/GN6CM08XudSrg4uOJqwbA="; - fetchSubmodules = true; - #leaveDotGit = true; + "x86_64-darwin" = { + url = "https://files.pythonhosted.org/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl"; + hash = ""; + }; + + "aarch64-darwin" = { + url = "https://files.pythonhosted.org/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl"; + hash = ""; + }; }; + + src = fetchurl ( + if builtins.hasAttr stdenv.system wheelUrls + then wheelUrls.${stdenv.system} + else throw "Unsupported system" + ); in buildPythonPackage rec { - pname = "opencv-python"; - inherit version; - pyproject = true; + inherit pname version; + format = "wheel"; inherit src; - patches = [ - ./relax-dependency-ranges.patch - ]; + #patchPhase = '' + #pwd - env = { - ENABLE_CONTRIB = 1; - ENABLE_HEADLESS = 0; - ENABLE_ROLLING = 0; - }; - - preConfigure = '' - #python -m find_version $ENABLE_CONTRIB $ENABLE_HEADLESS $ENABLE_ROLLING 0 - cp ${versionPy} cv2/version.py - ''; - - dontUseCmakeConfigure = true; + #${unzip}/bin/unzip $src/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp + #cd tmp + #${zip}/bin/zip -0 -r ../dist/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* + #cd ../ + #''; nativeBuildInputs = [ - git - cmake - numpy - pip - scikit-build - setuptools - wheel breakpointHook + ] + ++ lib.optionals stdenv.isLinux [ + autoPatchelfHook ]; - propagatedBuildInputs = [ - numpy - ]; + dontWrapQtApps = true; - cmakeFlags = [ - "-D WITH_GSTREAMER=ON" - "-D WITH_QT=ON" - "-D WITH_TBB=ON" - "-D ENABLE_FAST_MATH=1" - "-D CUDA_FAST_MATH=0" - "-D WITH_CUDA=OFF" - "-DBUILD_opencv_cudacodec=OFF" - "-DBUILD_opencv_cudaoptflow=OFF" + buildInputs = [ + stdenv.cc.cc.lib + libGL + libz + qt5.qtbase + ] ++ lib.optionals stdenv.isLinux [ + xorg.libxcb + xorg.libXext + xorg.libX11 + xorg.libSM + xorg.libICE ]; - pythonImportsCheck = [ "opencv_python" ]; + propagatedBuildInputs = [ + numpy + ]; - #preBuild = '' - #echo export PATH="$PATH" - #''; + pythonImportsCheck = [ "cv2" ]; meta = with lib; { description = "Wrapper package for OpenCV python bindings"; From 4c3872eb655e12f8777379dc3cb539065895849d Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 28 Jun 2024 10:07:41 +0100 Subject: [PATCH 05/52] WIP: genjax still broken, jaxlib FOD is non-reproducible --- flake.lock | 6 +- flake.nix | 4 + pkgs/genjax/default.nix | 94 +++++++++++++-------- pkgs/jax/default.nix | 166 +++++++++++++++++++++++++++++++++++++ pkgs/jaxlib/default.nix | 52 ++++++++++++ pkgs/jaxtyping/default.nix | 81 ++++++++++++++++++ pkgs/open3d/default.nix | 3 +- 7 files changed, 367 insertions(+), 39 deletions(-) create mode 100644 pkgs/jax/default.nix create mode 100644 pkgs/jaxlib/default.nix create mode 100644 pkgs/jaxtyping/default.nix diff --git a/flake.lock b/flake.lock index bc1a987..03f2be0 100644 --- a/flake.lock +++ b/flake.lock @@ -78,11 +78,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1714253743, - "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=", + "lastModified": 1719254875, + "narHash": "sha256-ECni+IkwXjusHsm9Sexdtq8weAq/yUyt1TWIemXt3Ko=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994", + "rev": "2893f56de08021cffd9b6b6dfc70fd9ccd51eb60", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b0df408..0b711a9 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,7 @@ nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; nixConfig.extra-trusted-public-keys = [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" ]; + nixConfig.sandbox = "relaxed"; outputs = inputs@{ self, nixpkgs, flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } { @@ -55,6 +56,9 @@ open3d = scopes.callPackage ./pkgs/open3d { }; internalPackages = { + #jaxlib = scopes.callPy3Package ./pkgs/jaxlib { }; + #jax = scopes.callPy3Package ./pkgs/jax { }; + jaxtyping = scopes.callPy3Package ./pkgs/jaxtyping { }; tinygltf = scopes.callPackage ./pkgs/tinygltf { }; PoissonRecon = scopes.callPackage ./pkgs/PoissonRecon { }; goftests = scopes.callPackage ./pkgs/goftests { }; diff --git a/pkgs/genjax/default.nix b/pkgs/genjax/default.nix index 01fb0a2..a06e093 100644 --- a/pkgs/genjax/default.nix +++ b/pkgs/genjax/default.nix @@ -2,10 +2,13 @@ , inputs , poetry2nix , python311 +, fetchPypi , stdenv , pkgs , oryx , plum-dispatch +, jax +, jaxtyping }: let src = stdenv.mkDerivation { @@ -23,45 +26,66 @@ let cp -rfv ./. $out ''; }; -in -poetry2nix.mkPoetryApplication { - projectDir = src.out; - overrides = poetry2nix.overrides.withDefaults (final: prev: { - #dm-tree = python311.pkgs.dm-tree; - #ruff = python311.pkgs.python-lsp-ruff; - }); + #jax = python311.pkgs.buildPythonPackage rec { + #pname = "jax"; + #version = "0.4.28"; - preferWheels = true; + #src = fetchPypi { + #inherit pname version; + #hash = "sha256-3PCkSv8uFxPworNpKBzVt52MGPwQGJBcQSWJfLBrN+k="; + #}; - python = python311; -} -#python311.pkgs.buildPythonPackage { - #pname = "genjax"; - #version = "0.1.1"; - #inherit src; - #format = "pyproject"; + #nativeBuildInputs = [ + + #]; + + #propagatedBuildInputs = with python311.pkgs; [ + #pip + #scipy + #opt-einsum + #ml-dtypes + #jaxlib + #]; + #}; + + #jaxlib = python311.pkgs.buildPythonPackage { + #pname = ""; + #version = ""; + + #src = fetchPypi { - #nativeBuildInputs = [ + #}; + #}; +in +python311.pkgs.buildPythonPackage { + __noChroot = true; + + pname = "genjax"; + version = "0.1.1"; + inherit src; + format = "pyproject"; + + nativeBuildInputs = [ #pkgs.poetry #python311.pkgs.poetry-core - #python311.pkgs.poetry-dynamic-versioning - #]; + python311.pkgs.poetry-dynamic-versioning + ]; - #propagatedBuildInputs = with python311.pkgs; [ - #beartype - #deprecated - #dill - #equinox - #jax - #jaxtyping - #numpy - #optax - #oryx - #plum-dispatch - #pygments - #rich - #tensorflow-probability - #typing-extensions - #]; -#} + propagatedBuildInputs = [ ] ++ (with python311.pkgs; [ + beartype + deprecated + dill + jax + jaxtyping + equinox + numpy + optax + oryx + plum-dispatch + pygments + rich + tensorflow-probability + typing-extensions + ]); +} diff --git a/pkgs/jax/default.nix b/pkgs/jax/default.nix new file mode 100644 index 0000000..95e85bf --- /dev/null +++ b/pkgs/jax/default.nix @@ -0,0 +1,166 @@ +{ + lib, + blas, + buildPythonPackage, + callPackage, + setuptools, + importlib-metadata, + fetchFromGitHub, + jaxlib, + jaxlib-bin, + hypothesis, + lapack, + matplotlib, + ml-dtypes, + numpy, + opt-einsum, + pytestCheckHook, + pytest-xdist, + pythonOlder, + scipy, + stdenv, +}: + +let + usingMKL = blas.implementation == "mkl" || lapack.implementation == "mkl"; + # jaxlib is broken on aarch64-* as of 2023-03-05, but the binary wheels work + # fine. jaxlib is only used in the checkPhase, so switching backends does not + # impact package behavior. Get rid of this once jaxlib is fixed on aarch64-*. + jaxlib' = if jaxlib.meta.broken then jaxlib-bin else jaxlib; +in +buildPythonPackage rec { + pname = "jax"; + version = "0.4.28"; + pyproject = true; + + disabled = pythonOlder "3.9"; + + src = fetchFromGitHub { + owner = "google"; + repo = "jax"; + # google/jax contains tags for jax and jaxlib. Only use jax tags! + rev = "refs/tags/jax-v${version}"; + hash = "sha256-qSHPwi3is6Ts7pz5s4KzQHBMbcjGp+vAOsejW3o36Ek="; + }; + + nativeBuildInputs = [ setuptools ]; + + # The version is automatically set to ".dev" if this variable is not set. + # https://github.com/google/jax/commit/e01f2617b85c5bdffc5ffb60b3d8d8ca9519a1f3 + JAX_RELEASE = "1"; + + # jaxlib is _not_ included in propagatedBuildInputs because there are + # different versions of jaxlib depending on the desired target hardware. The + # JAX project ships separate wheels for CPU, GPU, and TPU. + propagatedBuildInputs = [ + ml-dtypes + numpy + opt-einsum + scipy + ] ++ lib.optional (pythonOlder "3.10") importlib-metadata; + + nativeCheckInputs = [ + hypothesis + jaxlib' + matplotlib + pytestCheckHook + pytest-xdist + ]; + + # high parallelism will result in the tests getting stuck + dontUsePytestXdist = true; + + # NOTE: Don't run the tests in the expiremental directory as they require flax + # which creates a circular dependency. See https://discourse.nixos.org/t/how-to-nix-ify-python-packages-with-circular-dependencies/14648/2. + # Not a big deal, this is how the JAX docs suggest running the test suite + # anyhow. + pytestFlagsArray = [ + "--numprocesses=4" + "-W ignore::DeprecationWarning" + "tests/" + ]; + + # Prevents `tests/export_back_compat_test.py::CompatTest::test_*` tests from failing on darwin with + # PermissionError: [Errno 13] Permission denied: '/tmp/back_compat_testdata/test_*.py' + # See https://github.com/google/jax/blob/jaxlib-v0.4.27/jax/_src/internal_test_util/export_back_compat_test_util.py#L240-L241 + # NOTE: this doesn't seem to be an issue on linux + preCheck = lib.optionalString stdenv.isDarwin '' + export TEST_UNDECLARED_OUTPUTS_DIR=$(mktemp -d) + ''; + + disabledTests = + [ + # Exceeds tolerance when the machine is busy + "test_custom_linear_solve_aux" + # UserWarning: Explicitly requested dtype + # requested in astype is not available, and will be truncated to + # dtype float32. (With numpy 1.24) + "testKde3" + "testKde5" + "testKde6" + # Invokes python manually in a subprocess, which does not have the correct dependencies + # ImportError: This version of jax requires jaxlib version >= 0.4.19. + "test_no_log_spam" + ] + ++ lib.optionals usingMKL [ + # See + # * https://github.com/google/jax/issues/9705 + # * https://discourse.nixos.org/t/getting-different-results-for-the-same-build-on-two-equally-configured-machines/17921 + # * https://github.com/NixOS/nixpkgs/issues/161960 + "test_custom_linear_solve_cholesky" + "test_custom_root_with_aux" + "testEigvalsGrad_shape" + ] + ++ lib.optionals stdenv.isAarch64 [ + # See https://github.com/google/jax/issues/14793. + "test_for_loop_fixpoint_correctly_identifies_loop_varying_residuals_unrolled_for_loop" + "testQdwhWithRandomMatrix3" + "testScanGrad_jit_scan" + + # See https://github.com/google/jax/issues/17867. + "test_array" + "test_async" + "test_copy0" + "test_device_put" + "test_make_array_from_callback" + "test_make_array_from_single_device_arrays" + + # Fails on some hardware due to some numerical error + # See https://github.com/google/jax/issues/18535 + "testQdwhWithOnRankDeficientInput5" + ]; + + disabledTestPaths = lib.optionals (stdenv.isDarwin && stdenv.isAarch64) [ + # RuntimeWarning: invalid value encountered in cast + "tests/lax_test.py" + ]; + + pythonImportsCheck = [ "jax" ]; + + # Test CUDA-enabled jax and jaxlib. Running CUDA-enabled tests is not + # currently feasible within the nix build environment so we have to maintain + # this script separately. See https://github.com/NixOS/nixpkgs/pull/256230 + # for a possible remedy to this situation. + # + # Run these tests with eg + # + # NIXPKGS_ALLOW_UNFREE=1 nixglhost -- nix run --impure .#python3Packages.jax.passthru.tests.test_cuda_jaxlibBin + passthru.tests = { + test_cuda_jaxlibSource = callPackage ./test-cuda.nix { + jaxlib = jaxlib.override { cudaSupport = true; }; + }; + test_cuda_jaxlibBin = callPackage ./test-cuda.nix { + jaxlib = jaxlib-bin.override { cudaSupport = true; }; + }; + }; + + # updater fails to pick the correct branch + passthru.skipBulkUpdate = true; + + meta = with lib; { + description = "Differentiate, compile, and transform Numpy code"; + homepage = "https://github.com/google/jax"; + license = licenses.asl20; + maintainers = with maintainers; [ samuela ]; + }; +} diff --git a/pkgs/jaxlib/default.nix b/pkgs/jaxlib/default.nix new file mode 100644 index 0000000..0cc9657 --- /dev/null +++ b/pkgs/jaxlib/default.nix @@ -0,0 +1,52 @@ +{ fetchurl +, callPy3Package +, inputs +, poetry2nix +, python311 +, fetchPypi +, stdenv +, pkgs +, oryx +, plum-dispatch +}: +let + wheelUrls = { + "x86_64-linux" = { + url = "https://files.pythonhosted.org/packages/a6/a3/951da3d1487b2f8995a2a14cc7e9496c9a7c93aa1f1d0b33e833e24dee92/jaxlib-0.4.30-cp311-cp311-manylinux2014_x86_64.whl"; + hash = "sha256-FrKrGOqQ0uFZQbz0XeN6/C8omgKRKciMjXq6BATdAEM="; + }; + + "aarch64-linux" = { + url = "https://files.pythonhosted.org/packages/a4/f8/b85a46cb0cc4bc228cea4366b0d15caf42656c6d43cf8c91d90f7399aa4d/jaxlib-0.4.30-cp311-cp311-manylinux2014_aarch64.whl"; + hash = ""; + }; + + "x86_64-darwin" = { + url = "https://files.pythonhosted.org/packages/12/95/399da9204c3b13696baefb93468402f3389416b0caecfd9126aa94742bf2/jaxlib-0.4.30-cp311-cp311-macosx_11_0_arm64.whl"; + hash = ""; + }; + + "aarch64-darwin" = { + url = "https://files.pythonhosted.org/packages/33/2d/b6078f5d173d3087d32b1b49e5f65d406985fb3894ff1d21905972b9c89d/jaxlib-0.4.30-cp311-cp311-macosx_10_14_x86_64.whl"; + hash = ""; + }; + }; +in +python311.pkgs.buildPythonPackage rec { + pname = "jaxlib"; + version = "0.4.30"; + + src = fetchurl ( + if builtins.hasAttr stdenv.system wheelUrls + then wheelUrls.${stdenv.system} + else throw "Unsupported system" + ); + + format = "wheel"; + + nativeBuildInputs = [ + ]; + + propagatedBuildInputs = [ ] ++ (with python311.pkgs; [ + ]); +} diff --git a/pkgs/jaxtyping/default.nix b/pkgs/jaxtyping/default.nix new file mode 100644 index 0000000..298ae13 --- /dev/null +++ b/pkgs/jaxtyping/default.nix @@ -0,0 +1,81 @@ +{ + lib, + buildPythonPackage, + pythonOlder, + fetchFromGitHub, + hatchling, + pythonRelaxDepsHook, + numpy, + typeguard, + typing-extensions, + cloudpickle, + equinox, + ipython, + jax, + jaxlib, + pytestCheckHook, + tensorflow, + torch, +}: + +let + self = buildPythonPackage rec { + pname = "jaxtyping"; + version = "0.2.28"; + pyproject = true; + + disabled = pythonOlder "3.9"; + + src = fetchFromGitHub { + owner = "google"; + repo = "jaxtyping"; + rev = "refs/tags/v${version}"; + hash = "sha256-xDFrgPecUIfCACg/xkMQ8G1+6hNiUUDg9eCZKNpNfzs="; + }; + + nativeBuildInputs = [ + hatchling + pythonRelaxDepsHook + ]; + + propagatedBuildInputs = [ + numpy + typeguard + typing-extensions + ]; + + pythonRelaxDeps = [ "typeguard" ]; + + nativeCheckInputs = [ + cloudpickle + equinox + ipython + jax + jaxlib + pytestCheckHook + tensorflow + torch + ]; + + doCheck = false; + + # Enable tests via passthru to avoid cyclic dependency with equinox. + passthru.tests = { + check = self.overridePythonAttrs { + # We disable tests because they complain about the version of typeguard being too new. + doCheck = false; + catchConflicts = false; + }; + }; + + pythonImportsCheck = [ "jaxtyping" ]; + + meta = with lib; { + description = "Type annotations and runtime checking for JAX arrays and PyTrees"; + homepage = "https://github.com/google/jaxtyping"; + license = licenses.mit; + maintainers = with maintainers; [ GaetanLepage ]; + }; + }; +in +self diff --git a/pkgs/open3d/default.nix b/pkgs/open3d/default.nix index 1728bf2..89c0a13 100644 --- a/pkgs/open3d/default.nix +++ b/pkgs/open3d/default.nix @@ -79,7 +79,8 @@ in buildPythonPackage { #dist = "py3"; #sha256 = ""; #}; - + + # TODO: make this multiplatform src = fetchurl { url = "https://files.pythonhosted.org/packages/5c/ba/a4c5986951344f804b5cbd86f0a87d9ea5969e8d13f1e8913e2d8276e0d8/open3d-0.18.0-cp311-cp311-manylinux_2_27_x86_64.whl"; hash = "sha256-jj0dGQCo9NlW9oGcJGx4CBclubCIj4VJ0qeknI2qEwM="; From d343d10e89c3ab96bcc5caec5a6b2cf5fde3fe77 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 10:12:00 +0100 Subject: [PATCH 06/52] WIP: bayes3d building with overrides --- flake.lock | 16 +- flake.nix | 41 +++- pkgs/bayes3d/default.nix | 98 -------- .../beartype_0_16_4/default.nix | 12 + .../jax_}/default.nix | 29 +-- pkgs/disabled-modules/jaxlib_/default.nix | 156 ++++++++++++ pkgs/disabled-modules/jaxlib_/default.nix.bak | 86 +++++++ .../jaxtyping_}/default.nix | 0 pkgs/disabled-modules/optax_0_1_7/default.nix | 12 + pkgs/genjax/default.nix | 91 ------- pkgs/jaxlib/default.nix | 52 ---- pkgs/open3d/default.nix | 146 ------------ pkgs/python-modules/bayes3d/default.nix | 111 +++++++++ .../distinctipy/default.nix | 9 +- .../distributions/default.nix | 5 +- .../distributions/distributions-shared.nix | 0 .../use-imread-instead-of-scipy.patch | 0 pkgs/python-modules/genjax/default.nix | 73 ++++++ .../genjax/set-pyproject-version.patch | 0 .../genjax/use-beartype-0.18.0-version.patch | 14 ++ .../{ => python-modules}/goftests/default.nix | 9 +- pkgs/{ => python-modules}/loom/default.nix | 19 +- pkgs/{ => python-modules}/loom/test.nix | 0 pkgs/python-modules/open3d/default.nix | 222 ++++++++++++++++++ .../opencv-python/default.nix | 0 .../relax-dependency-ranges.patch | 0 pkgs/{ => python-modules}/oryx/default.nix | 4 +- .../{ => python-modules}/parsable/default.nix | 0 .../plum-dispatch/default.nix | 0 pkgs/{ => python-modules}/pymetis/default.nix | 0 .../pyransac3d/default.nix | 4 +- pkgs/{ => python-modules}/sppl/default.nix | 1 + .../tensorflow-probability/default.nix | 125 ++++++++++ 33 files changed, 893 insertions(+), 442 deletions(-) delete mode 100644 pkgs/bayes3d/default.nix create mode 100644 pkgs/disabled-modules/beartype_0_16_4/default.nix rename pkgs/{jax => disabled-modules/jax_}/default.nix (79%) create mode 100644 pkgs/disabled-modules/jaxlib_/default.nix create mode 100644 pkgs/disabled-modules/jaxlib_/default.nix.bak rename pkgs/{jaxtyping => disabled-modules/jaxtyping_}/default.nix (100%) create mode 100644 pkgs/disabled-modules/optax_0_1_7/default.nix delete mode 100644 pkgs/genjax/default.nix delete mode 100644 pkgs/jaxlib/default.nix delete mode 100644 pkgs/open3d/default.nix create mode 100644 pkgs/python-modules/bayes3d/default.nix rename pkgs/{ => python-modules}/distinctipy/default.nix (70%) rename pkgs/{ => python-modules}/distributions/default.nix (94%) rename pkgs/{ => python-modules}/distributions/distributions-shared.nix (100%) rename pkgs/{ => python-modules}/distributions/use-imread-instead-of-scipy.patch (100%) create mode 100644 pkgs/python-modules/genjax/default.nix rename pkgs/{ => python-modules}/genjax/set-pyproject-version.patch (100%) create mode 100644 pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch rename pkgs/{ => python-modules}/goftests/default.nix (76%) rename pkgs/{ => python-modules}/loom/default.nix (93%) rename pkgs/{ => python-modules}/loom/test.nix (100%) create mode 100644 pkgs/python-modules/open3d/default.nix rename pkgs/{ => python-modules}/opencv-python/default.nix (100%) rename pkgs/{ => python-modules}/opencv-python/relax-dependency-ranges.patch (100%) rename pkgs/{ => python-modules}/oryx/default.nix (95%) rename pkgs/{ => python-modules}/parsable/default.nix (100%) rename pkgs/{ => python-modules}/plum-dispatch/default.nix (100%) rename pkgs/{ => python-modules}/pymetis/default.nix (100%) rename pkgs/{ => python-modules}/pyransac3d/default.nix (86%) rename pkgs/{ => python-modules}/sppl/default.nix (94%) create mode 100644 pkgs/python-modules/tensorflow-probability/default.nix diff --git a/flake.lock b/flake.lock index 03f2be0..a34b9d1 100644 --- a/flake.lock +++ b/flake.lock @@ -78,16 +78,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719254875, - "narHash": "sha256-ECni+IkwXjusHsm9Sexdtq8weAq/yUyt1TWIemXt3Ko=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2893f56de08021cffd9b6b6dfc70fd9ccd51eb60", - "type": "github" + "dirtyRev": "a128565b55085166b20cfbb0a6bcaa736d96630e-dirty", + "dirtyShortRev": "a128565b5508-dirty", + "lastModified": 1719819879, + "narHash": "sha256-z0jXs3LtmEoPT0KkqKmnteIXuE4/wBYFwdWVCiag96U=", + "type": "git", + "url": "file:///home/s/repos/nixpkgs" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", + "owner": "zimbatm", + "ref": "jax-fixes", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 0b711a9..4aa55e9 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,7 @@ flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:zimbatm/nixpkgs?ref=jax-fixes"; nixpkgs-llvm-10.url = "github:NixOS/nixpkgs?rev=222c1940fafeda4dea161858ffe6ebfc853d3db5"; genjax.url = "github:probcomp/genjax?ref=v0.1.1"; @@ -85,23 +85,58 @@ ; }; + loadPackages = callPackage: path: + let + entries = builtins.readDir path; + in + pkgs.lib.mapAttrs (name: type: + if type != "directory" then (throw "${toString path}/${name} is not a directory") + else + callPackage "${toString path}/${name}" { } + ) + entries; + + # For fixing existing packages that live in nixpkgs + # TODO: put in separate file + pythonOverrides = super: pythonSuper: { + # so we can pull from flake inputs + inherit inputs; + + # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. + + # Use the pre-built version of tensorflow + tensorflow = pythonSuper.tensorflow-bin; + + # Use the pre-built version of jaxlib + jaxlib = super.jaxlib-bin; + + # Use the pre-built version of libjax + libjax = super.libjax-bin; + }; in { _module.args.pkgs = import inputs.nixpkgs { inherit system; config = { + # FIXME: commenting these out to see if they fix the duplicate dependency issue when building bayes3d allowUnfree = true; - cudaSupport = true; + # Only enable CUDA on Linux + cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); }; overlays = [ (final: prev: { + # FIXME: say why this was added. inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; }) ]; }; inherit packages; - + legacyPackages.internalPackages = internalPackages; + legacyPackages.python3Packages = + (pkgs.python3Packages.overrideScope pythonOverrides).overrideScope (super: superPython: + loadPackages super.callPackage ./pkgs/python-modules + ); }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields diff --git a/pkgs/bayes3d/default.nix b/pkgs/bayes3d/default.nix deleted file mode 100644 index e2e4679..0000000 --- a/pkgs/bayes3d/default.nix +++ /dev/null @@ -1,98 +0,0 @@ -{ lib -, fetchFromGitHub -, breakpointHook -, python3Packages -, cudaPackages -, which -, libglvnd -, libGLU -, open3d -, symlinkJoin -, genjax -, distinctipy -, pyransac3d -, opencv-python -}: -let - rev = "21fc1ec8439b11eecc308610b23df45e7eee5ee0"; - - #torch-cuda = python3Packages.torch.override (final: prev: { - #}); - - cuda-common-redist = with cudaPackages; [ - cuda_cccl # - libcublas # cublas_v2.h - libcurand - libcusolver # cusolverDn.h - libcusparse # cusparse.h - ]; - - cuda-native-redist = symlinkJoin { - name = "cuda-native-redist-${cudaPackages.cudaVersion}"; - paths = - with cudaPackages; - [ - cuda_cudart # cuda_runtime.h cuda_runtime_api.h - cuda_nvcc - ] - ++ cuda-common-redist; - }; -in -python3Packages.buildPythonPackage rec { - pname = "bayes3d"; - version = "0.1.0+${builtins.substring 0 8 rev}"; - - src = fetchFromGitHub { - repo = pname; - owner = "probcomp"; - inherit rev; - hash = "sha256-8HUtf9AGgsMSSarFpRhDSzen6Mt1TJNkAhm6T3o/fO0="; - }; - - pyproject = true; - - nativeBuildInputs = with python3Packages; [ - setuptools - setuptools-scm - which - #breakpointHook - ]; - - buildInputs = [ - cudaPackages.cuda_nvcc - cudaPackages.cuda_cudart - cudaPackages.libcusparse - cudaPackages.cuda_cccl - cudaPackages.libcublas - cudaPackages.libcusolver - libglvnd - libGLU - ]; - - propagatedBuildInputs = [ - python3Packages.torch - python3Packages.graphviz - python3Packages.imageio - python3Packages.matplotlib - python3Packages.meshcat - python3Packages.natsort - python3Packages.opencv4 - python3Packages.plyfile - python3Packages.liblzfse - python3Packages.tensorflow-probability - python3Packages.timm - python3Packages.trimesh - - distinctipy - open3d - opencv-python - pyransac3d - genjax - ]; - - preBuild = '' - export CUDA_HOME=${cuda-native-redist} - ''; - - #preferLocalBuild = true; -} diff --git a/pkgs/disabled-modules/beartype_0_16_4/default.nix b/pkgs/disabled-modules/beartype_0_16_4/default.nix new file mode 100644 index 0000000..3765da2 --- /dev/null +++ b/pkgs/disabled-modules/beartype_0_16_4/default.nix @@ -0,0 +1,12 @@ +{ fetchPypi +, beartype +}: +(beartype.overrideAttrs (final: prev: rec { + version = "0.16.4"; + + src = fetchPypi { + inherit (prev) pname; + inherit version; + hash = "sha256-GtqJzy1usw624Vbu0utUkzV3gpN5ENdDgJGOU8Lq4L8="; + }; +})) diff --git a/pkgs/jax/default.nix b/pkgs/disabled-modules/jax_/default.nix similarity index 79% rename from pkgs/jax/default.nix rename to pkgs/disabled-modules/jax_/default.nix index 95e85bf..e84b240 100644 --- a/pkgs/jax/default.nix +++ b/pkgs/disabled-modules/jax_/default.nix @@ -6,7 +6,6 @@ setuptools, importlib-metadata, fetchFromGitHub, - jaxlib, jaxlib-bin, hypothesis, lapack, @@ -22,11 +21,7 @@ }: let - usingMKL = blas.implementation == "mkl" || lapack.implementation == "mkl"; - # jaxlib is broken on aarch64-* as of 2023-03-05, but the binary wheels work - # fine. jaxlib is only used in the checkPhase, so switching backends does not - # impact package behavior. Get rid of this once jaxlib is fixed on aarch64-*. - jaxlib' = if jaxlib.meta.broken then jaxlib-bin else jaxlib; + usingMKL = false; # blas.implementation == "mkl" || lapack.implementation == "mkl"; in buildPythonPackage rec { pname = "jax"; @@ -61,7 +56,7 @@ buildPythonPackage rec { nativeCheckInputs = [ hypothesis - jaxlib' + jaxlib-bin matplotlib pytestCheckHook pytest-xdist @@ -88,6 +83,9 @@ buildPythonPackage rec { export TEST_UNDECLARED_OUTPUTS_DIR=$(mktemp -d) ''; + # FIXME: disable the checks manually + doCheck = false; + disabledTests = [ # Exceeds tolerance when the machine is busy @@ -137,23 +135,6 @@ buildPythonPackage rec { pythonImportsCheck = [ "jax" ]; - # Test CUDA-enabled jax and jaxlib. Running CUDA-enabled tests is not - # currently feasible within the nix build environment so we have to maintain - # this script separately. See https://github.com/NixOS/nixpkgs/pull/256230 - # for a possible remedy to this situation. - # - # Run these tests with eg - # - # NIXPKGS_ALLOW_UNFREE=1 nixglhost -- nix run --impure .#python3Packages.jax.passthru.tests.test_cuda_jaxlibBin - passthru.tests = { - test_cuda_jaxlibSource = callPackage ./test-cuda.nix { - jaxlib = jaxlib.override { cudaSupport = true; }; - }; - test_cuda_jaxlibBin = callPackage ./test-cuda.nix { - jaxlib = jaxlib-bin.override { cudaSupport = true; }; - }; - }; - # updater fails to pick the correct branch passthru.skipBulkUpdate = true; diff --git a/pkgs/disabled-modules/jaxlib_/default.nix b/pkgs/disabled-modules/jaxlib_/default.nix new file mode 100644 index 0000000..e1d365c --- /dev/null +++ b/pkgs/disabled-modules/jaxlib_/default.nix @@ -0,0 +1,156 @@ +{ + lib, + stdenv, + fetchurl, + + # Build-time dependencies: + buildPythonPackage, + curl, + fetchFromGitHub, + jsoncpp, + autoPatchelfHook, + + # Python dependencies: + absl-py, + flatbuffers, + ml-dtypes, + numpy, + scipy, + six, + + # Runtime dependencies: + double-conversion, + giflib, + libjpeg_turbo, + python, + snappy, + +}: + +let + pname = "jaxlib"; + version = "0.4.28"; + + # REMOVEME + effectiveStdenv = stdenv; + + meta = with lib; { + description = "JAX is Autograd and XLA, brought together for high-performance machine learning research"; + homepage = "https://github.com/google/jax"; + license = licenses.asl20; + maintainers = with maintainers; [ ndl ]; + platforms = platforms.unix; + # aarch64-darwin is broken because of https://github.com/bazelbuild/rules_cc/pull/136 + # however even with that fix applied, it doesn't work for everyone: + # https://github.com/NixOS/nixpkgs/pull/184395#issuecomment-1207287129 + # NOTE: We always build with NCCL; if it is unsupported, then our build is broken. + # broken = effectiveStdenv.isDarwin || nccl.meta.unsupported; + }; + + + arch = + # KeyError: ('Linux', 'arm64') + if effectiveStdenv.hostPlatform.isLinux && effectiveStdenv.hostPlatform.linuxArch == "arm64" then + "aarch64" + else + effectiveStdenv.hostPlatform.linuxArch; + + xla = effectiveStdenv.mkDerivation { + pname = "xla-src"; + version = "unstable"; + + src = fetchFromGitHub { + owner = "openxla"; + repo = "xla"; + # Update this according to https://github.com/google/jax/blob/jaxlib-v${version}/third_party/xla/workspace.bzl. + rev = "e8247c3ea1d4d7f31cf27def4c7ac6f2ce64ecd4"; + hash = "sha256-ZhgMIVs3Z4dTrkRWDqaPC/i7yJz2dsYXrZbjzqvPX3E="; + }; + + dontBuild = true; + + # This is necessary for patchShebangs to know the right path to use. + nativeBuildInputs = [ python ]; + + # Main culprits we're targeting are third_party/tsl/third_party/gpus/crosstool/clang/bin/*.tpl + postPatch = '' + patchShebangs . + ''; + + installPhase = '' + cp -r . $out + ''; + }; + + platformTag = + if effectiveStdenv.hostPlatform.isLinux then + "manylinux2014_${arch}" + else if effectiveStdenv.system == "x86_64-darwin" then + "macosx_10_9_${arch}" + else if effectiveStdenv.system == "aarch64-darwin" then + "macosx_11_0_${arch}" + else + throw "Unsupported target platform: ${effectiveStdenv.hostPlatform}"; + + wheelUrls = { + "x86_64-linux" = { + url = "https://files.pythonhosted.org/packages/8e/d7/65b1f5cf05d9159abd5882a51695d4d1b386bc8e26140eff7159854777f2/jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl"; + hash = "sha256-Rc4PPIQM/4I2z/JsN/Jsn/B4aV+T4MFiwyDCgfUEEnU="; + }; + + "aarch64-linux" = { + url = "https://files.pythonhosted.org/packages/f2/87/0c07ec3ba047ca58c940d1c3050cd08c4390bca992cdfeeb2d9d356cd2c6/jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl"; + hash = ""; + }; + + "x86_64-darwin" = { + url = "https://files.pythonhosted.org/packages/e0/b2/896d8d1f35e16e9f88ae6a753012e6d5a6882507ea58e7f0dd5af68ee1e8/jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl"; + hash = ""; + }; + + "aarch64-darwin" = { + url = "https://files.pythonhosted.org/packages/75/f3/1ce8b092ca68dfcfa6a0ee0a8a410f6d877e1628c05799c5d03757682c66/jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl"; + hash = ""; + }; + }; +in +buildPythonPackage { + inherit meta pname version; + format = "wheel"; + + src = fetchurl ( + if builtins.hasAttr stdenv.system wheelUrls + then wheelUrls.${stdenv.system} + else throw "Unsupported system '${stdenv.system}'" + ); + + nativeBuildInputs = [ + autoPatchelfHook + ]; + + buildInputs = [ + stdenv.cc.cc.lib + ]; + + dependencies = [ + absl-py + curl + double-conversion + flatbuffers + giflib + jsoncpp + libjpeg_turbo + ml-dtypes + numpy + scipy + six + snappy + ]; + + pythonImportsCheck = [ + "jaxlib" + # `import jaxlib` loads surprisingly little. These imports are actually bugs that appeared in the 0.4.11 upgrade. + "jaxlib.cpu_feature_guard" + "jaxlib.xla_client" + ]; +} diff --git a/pkgs/disabled-modules/jaxlib_/default.nix.bak b/pkgs/disabled-modules/jaxlib_/default.nix.bak new file mode 100644 index 0000000..325aadc --- /dev/null +++ b/pkgs/disabled-modules/jaxlib_/default.nix.bak @@ -0,0 +1,86 @@ +{ buildPythonPackage +, fetchFromGitHub +, inputs +, fetchPypi +, stdenv +, scipy +, numpy +, ml-dtypes +, pip +, opt-einsum +, absl-py +, breakpointHook +, matplotlib +} +: +let + wheelUrls = { + "x86_64-linux" = { + url = "https://files.pythonhosted.org/packages/8e/d7/65b1f5cf05d9159abd5882a51695d4d1b386bc8e26140eff7159854777f2/jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl"; + hash = "sha256-Rc4PPIQM/4I2z/JsN/Jsn/B4aV+T4MFiwyDCgfUEEnU="; + }; + + "aarch64-linux" = { + url = "https://files.pythonhosted.org/packages/f2/87/0c07ec3ba047ca58c940d1c3050cd08c4390bca992cdfeeb2d9d356cd2c6/jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl"; + hash = ""; + }; + + "x86_64-darwin" = { + url = "https://files.pythonhosted.org/packages/e0/b2/896d8d1f35e16e9f88ae6a753012e6d5a6882507ea58e7f0dd5af68ee1e8/jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl"; + hash = ""; + }; + + "aarch64-darwin" = { + url = "https://files.pythonhosted.org/packages/75/f3/1ce8b092ca68dfcfa6a0ee0a8a410f6d877e1628c05799c5d03757682c66/jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl"; + hash = ""; + }; + }; +in +buildPythonPackage rec { + pname = "jaxlib"; + version = "0.4.28"; + + src = fetchFromGitHub { + owner = "google"; + repo = "jax"; + rev = "jaxlib-v${version}"; + hash = "sha256-qSHPwi3is6Ts7pz5s4KzQHBMbcjGp+vAOsejW3o36Ek="; + }; + + #unpackPhase = '' + #mkdir source + #cp -rv $src/jaxlib source + #chmod -R +w source + #ls -alsph source + + #ln -rsv $src/jax/version.py source/jaxlib + #cd source/jaxlib + #''; + + preConfigure = '' + cd jaxlib + mkdir jaxlib + ln -rsv $src/jax/version.py jaxlib/version.py + ln -rsv $src/third_party/xla jaxlib/xla_extension + ls -alsph . + ''; + + nativeBuildInputs = [ + breakpointHook + ]; + + nativeCheckInputs = [ + pip + ]; + + propagatedBuildInputs = [ + scipy + numpy + ml-dtypes + opt-einsum + absl-py + matplotlib + ]; + + preferLocalBuild = true; +} diff --git a/pkgs/jaxtyping/default.nix b/pkgs/disabled-modules/jaxtyping_/default.nix similarity index 100% rename from pkgs/jaxtyping/default.nix rename to pkgs/disabled-modules/jaxtyping_/default.nix diff --git a/pkgs/disabled-modules/optax_0_1_7/default.nix b/pkgs/disabled-modules/optax_0_1_7/default.nix new file mode 100644 index 0000000..e84a8ac --- /dev/null +++ b/pkgs/disabled-modules/optax_0_1_7/default.nix @@ -0,0 +1,12 @@ +{ fetchFromGitHub +, optax +}: +(optax.overrideAttrs rec { + version = "0.1.7"; + src = fetchFromGitHub { + owner = "deepmind"; + repo = "optax"; + rev = "refs/tags/v${version}"; + hash = "sha256-zSMJxagPe2rkhrawJ+TWXUzk6V58IY6MhWmEqLVtOoA="; + }; +}) diff --git a/pkgs/genjax/default.nix b/pkgs/genjax/default.nix deleted file mode 100644 index a06e093..0000000 --- a/pkgs/genjax/default.nix +++ /dev/null @@ -1,91 +0,0 @@ -{ callPy3Package -, inputs -, poetry2nix -, python311 -, fetchPypi -, stdenv -, pkgs -, oryx -, plum-dispatch -, jax -, jaxtyping -}: -let - src = stdenv.mkDerivation { - name = "genjax-source"; - version = inputs.genjax.shortRev; - src = inputs.genjax; - - patches = [ - ./set-pyproject-version.patch - ]; - - installPhase = '' - mkdir $out - ls -alsph $src - cp -rfv ./. $out - ''; - }; - - #jax = python311.pkgs.buildPythonPackage rec { - #pname = "jax"; - #version = "0.4.28"; - - #src = fetchPypi { - #inherit pname version; - #hash = "sha256-3PCkSv8uFxPworNpKBzVt52MGPwQGJBcQSWJfLBrN+k="; - #}; - - #nativeBuildInputs = [ - - #]; - - #propagatedBuildInputs = with python311.pkgs; [ - #pip - #scipy - #opt-einsum - #ml-dtypes - #jaxlib - #]; - #}; - - #jaxlib = python311.pkgs.buildPythonPackage { - #pname = ""; - #version = ""; - - #src = fetchPypi { - - #}; - #}; -in -python311.pkgs.buildPythonPackage { - __noChroot = true; - - pname = "genjax"; - version = "0.1.1"; - inherit src; - format = "pyproject"; - - nativeBuildInputs = [ - #pkgs.poetry - #python311.pkgs.poetry-core - python311.pkgs.poetry-dynamic-versioning - ]; - - propagatedBuildInputs = [ ] ++ (with python311.pkgs; [ - beartype - deprecated - dill - jax - jaxtyping - equinox - numpy - optax - oryx - plum-dispatch - pygments - rich - tensorflow-probability - typing-extensions - ]); -} diff --git a/pkgs/jaxlib/default.nix b/pkgs/jaxlib/default.nix deleted file mode 100644 index 0cc9657..0000000 --- a/pkgs/jaxlib/default.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ fetchurl -, callPy3Package -, inputs -, poetry2nix -, python311 -, fetchPypi -, stdenv -, pkgs -, oryx -, plum-dispatch -}: -let - wheelUrls = { - "x86_64-linux" = { - url = "https://files.pythonhosted.org/packages/a6/a3/951da3d1487b2f8995a2a14cc7e9496c9a7c93aa1f1d0b33e833e24dee92/jaxlib-0.4.30-cp311-cp311-manylinux2014_x86_64.whl"; - hash = "sha256-FrKrGOqQ0uFZQbz0XeN6/C8omgKRKciMjXq6BATdAEM="; - }; - - "aarch64-linux" = { - url = "https://files.pythonhosted.org/packages/a4/f8/b85a46cb0cc4bc228cea4366b0d15caf42656c6d43cf8c91d90f7399aa4d/jaxlib-0.4.30-cp311-cp311-manylinux2014_aarch64.whl"; - hash = ""; - }; - - "x86_64-darwin" = { - url = "https://files.pythonhosted.org/packages/12/95/399da9204c3b13696baefb93468402f3389416b0caecfd9126aa94742bf2/jaxlib-0.4.30-cp311-cp311-macosx_11_0_arm64.whl"; - hash = ""; - }; - - "aarch64-darwin" = { - url = "https://files.pythonhosted.org/packages/33/2d/b6078f5d173d3087d32b1b49e5f65d406985fb3894ff1d21905972b9c89d/jaxlib-0.4.30-cp311-cp311-macosx_10_14_x86_64.whl"; - hash = ""; - }; - }; -in -python311.pkgs.buildPythonPackage rec { - pname = "jaxlib"; - version = "0.4.30"; - - src = fetchurl ( - if builtins.hasAttr stdenv.system wheelUrls - then wheelUrls.${stdenv.system} - else throw "Unsupported system" - ); - - format = "wheel"; - - nativeBuildInputs = [ - ]; - - propagatedBuildInputs = [ ] ++ (with python311.pkgs; [ - ]); -} diff --git a/pkgs/open3d/default.nix b/pkgs/open3d/default.nix deleted file mode 100644 index 89c0a13..0000000 --- a/pkgs/open3d/default.nix +++ /dev/null @@ -1,146 +0,0 @@ -{ stdenv -, lib -, python3Packages -, fetchPypi -, fetchurl - -, tree -, unzip -, zip - -, autoPatchelfHook -, libtensorflow-bin -, libusb -, cudaPackages_11 - -, libGL -, libglvnd -, libdrm -, expat -, xorg -, mesa -, llvmPackages_10 -, buildEnv -, runCommand -}: -let - inherit (python3Packages) - buildPythonPackage - ipywidgets - matplotlib - numpy - pandas - plyfile - pytorchWithCuda - pyyaml - scikitlearn - scipy - tqdm - ; - - libllvm-wrapped = - let - libllvm = llvmPackages_10.libllvm.lib; - name = libllvm.name; - in - buildEnv { - inherit name; - paths = [ - llvmPackages_10.libllvm.lib - (runCommand "${name}.1" {} "mkdir -p $out/lib && ln -sf ${libllvm}/lib/libLLVM-10.so $out/lib/libLLVM-10.so.1") - ]; - }; - - #addict = buildPythonPackage { - #pname = "addict"; - #version = "2.4.0"; - - #src = fetchPypi { - #pname = "addict"; - #version = "2.4.0"; - #sha256 = "1574sicy5ydx9pvva3lbx8qp56z9jbdwbj26aqgjhyh61q723cmk"; - #}; - #}; - - version = "0.18.0"; - pname = "open3d"; - pythonAbi = "cp311"; - pythonPlatform = "manylinux_2_27_x86_64"; - -in buildPythonPackage { - inherit pname version; - format = "wheel"; - - #src = fetchPypi { - #inherit pname version; - #format = "wheel"; - #python = pythonAbi; - #abi = pythonAbi; - #dist = "py3"; - #sha256 = ""; - #}; - - # TODO: make this multiplatform - src = fetchurl { - url = "https://files.pythonhosted.org/packages/5c/ba/a4c5986951344f804b5cbd86f0a87d9ea5969e8d13f1e8913e2d8276e0d8/open3d-0.18.0-cp311-cp311-manylinux_2_27_x86_64.whl"; - hash = "sha256-jj0dGQCo9NlW9oGcJGx4CBclubCIj4VJ0qeknI2qEwM="; - }; - - patchPhase = '' - ${unzip}/bin/unzip ./dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp - rm ./dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl - #sed -i 's/sklearn/scikit-learn/g' tmp/open3d-${version}.dist-info/METADATA - cd tmp - ${zip}/bin/zip -0 -r ../dist/open3d-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* - cd ../ - ''; - - nativeBuildInputs = [ - autoPatchelfHook - ]; - - buildInputs = [ - # so deps - stdenv.cc.cc.lib - libusb.out - pytorchWithCuda - libtensorflow-bin - cudaPackages_11.cudatoolkit.lib - #cudaPackages_11.cuda_cudart.lib - libGL - libglvnd - libdrm - expat - xorg.libXxf86vm - xorg.libXfixes - libllvm-wrapped - mesa - ]; - - propagatedBuildInputs = [ - # py deps - ipywidgets - tqdm - pyyaml - pandas - plyfile - scipy - scikitlearn - numpy - #addict - matplotlib - ]; - - #preBuild = '' - #mkdir $out - #''; - - preFixup = '' - echo "OUTPUT TO: $out" - cd $out/lib/python3.*/site-packages/open3d - rm libGL.so.1 libEGL.so.1 - ln -s ${libGL}/lib/libGL.so.1 libGL.so.1 - ln -s ${libGL}/lib/libEGL.so.1 libEGL.so.1 - #exit 1 - ''; -} diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix new file mode 100644 index 0000000..2f89c8a --- /dev/null +++ b/pkgs/python-modules/bayes3d/default.nix @@ -0,0 +1,111 @@ +{ lib +, fetchFromGitHub +, breakpointHook +, buildPythonPackage +, cudaPackages_11 +, which +, libglvnd +, libGLU +, open3d +, symlinkJoin +, genjax +, distinctipy +, pyransac3d +, opencv-python +, setuptools +, setuptools-scm +, torch +, pytorchWithCuda +, graphviz +, imageio +, matplotlib +, meshcat +, natsort +, opencv4 +, plyfile +, liblzfse +, tensorflow-probability +, timm +, trimesh +}: +let + rev = "eb942aeca9441957c5cc62da26dde3e2c70c23c2"; + + cuda-common-redist = with cudaPackages_11; [ + cuda_cccl # + libcublas # cublas_v2.h + libcurand + libcusolver # cusolverDn.h + libcusparse # cusparse.h + ]; + + cuda-native-redist = symlinkJoin { + name = "cuda-native-redist-${cudaPackages_11.cudaVersion}"; + paths = + with cudaPackages_11; + [ + cuda_cudart # cuda_runtime.h cuda_runtime_api.h + cuda_nvcc + ] + ++ cuda-common-redist; + }; +in +buildPythonPackage rec { + pname = "bayes3d"; + version = "0.1.0+${builtins.substring 0 8 rev}"; + + src = fetchFromGitHub { + repo = pname; + owner = "probcomp"; + inherit rev; + hash = "sha256-jtiLyBu3G+B4Qx1iBq7gVlzb4sdaaB25Gyk+fxcgKxU="; + }; + + pyproject = true; + + nativeBuildInputs = [ + setuptools + setuptools-scm + which + #breakpointHook + ]; + + buildInputs = [ + # cudaPackages.cuda_nvcc + # cudaPackages.cuda_cudart + # cudaPackages.libcusparse + # cudaPackages.cuda_cccl + # cudaPackages.libcublas + # cudaPackages.libcusolver + cudaPackages_11.cudatoolkit.lib + pytorchWithCuda + libglvnd + libGLU + ]; + + propagatedBuildInputs = [ + distinctipy + genjax + graphviz + imageio + liblzfse + matplotlib + meshcat + natsort + open3d + opencv-python + opencv4 + plyfile + pyransac3d + tensorflow-probability + timm + #torch + trimesh + ]; + + preBuild = '' + export CUDA_HOME=${cuda-native-redist} + ''; + + #preferLocalBuild = true; +} diff --git a/pkgs/distinctipy/default.nix b/pkgs/python-modules/distinctipy/default.nix similarity index 70% rename from pkgs/distinctipy/default.nix rename to pkgs/python-modules/distinctipy/default.nix index 39afbe8..7a8a3f7 100644 --- a/pkgs/distinctipy/default.nix +++ b/pkgs/python-modules/distinctipy/default.nix @@ -1,7 +1,10 @@ { fetchPypi , python3Packages +, buildPythonPackage +, setuptools +, numpy }: -python3Packages.buildPythonPackage rec { +buildPythonPackage rec { pname = "distinctipy"; version = "1.3.4"; format = "pyproject"; @@ -14,10 +17,10 @@ python3Packages.buildPythonPackage rec { doCheck = false; nativeBuildInputs = [ - python3Packages.setuptools + setuptools ]; - propagatedBuildInputs = with python3Packages; [ + propagatedBuildInputs = [ numpy ]; } diff --git a/pkgs/distributions/default.nix b/pkgs/python-modules/distributions/default.nix similarity index 94% rename from pkgs/distributions/default.nix rename to pkgs/python-modules/distributions/default.nix index 7d4da3f..23db5b1 100644 --- a/pkgs/distributions/default.nix +++ b/pkgs/python-modules/distributions/default.nix @@ -20,7 +20,8 @@ let distributions-shared = callPackage ./distributions-shared.nix { inherit version src; }; - imageio = python3Packages.buildPythonPackage rec { + # TODO: move into own package + imageio_2_6_1 = python3Packages.buildPythonPackage rec { pname = "imageio"; version = "2.6.1"; @@ -75,7 +76,7 @@ python3Packages.buildPythonPackage { # TODO: be more precise. Some tests seem to be still in Python 2. doCheck = false; nativeCheckInputs = with python3Packages; [ - imageio + imageio_2_6_1 nose goftests pytest diff --git a/pkgs/distributions/distributions-shared.nix b/pkgs/python-modules/distributions/distributions-shared.nix similarity index 100% rename from pkgs/distributions/distributions-shared.nix rename to pkgs/python-modules/distributions/distributions-shared.nix diff --git a/pkgs/distributions/use-imread-instead-of-scipy.patch b/pkgs/python-modules/distributions/use-imread-instead-of-scipy.patch similarity index 100% rename from pkgs/distributions/use-imread-instead-of-scipy.patch rename to pkgs/python-modules/distributions/use-imread-instead-of-scipy.patch diff --git a/pkgs/python-modules/genjax/default.nix b/pkgs/python-modules/genjax/default.nix new file mode 100644 index 0000000..37cc7af --- /dev/null +++ b/pkgs/python-modules/genjax/default.nix @@ -0,0 +1,73 @@ +{ buildPythonPackage +, fetchFromGitHub +, inputs +, poetry-core +, poetry-dynamic-versioning +, fetchPypi +, stdenv +, beartype +, deprecated +, dill +, jax +, jaxtyping +, equinox +, numpy +, optax +, oryx +, plum-dispatch +, pygments +, rich +, tensorflow-probability +, typing-extensions +}: +let + src = stdenv.mkDerivation { + name = "genjax-source"; + version = inputs.genjax.shortRev; + src = inputs.genjax; + + patches = [ + ./set-pyproject-version.patch + ./use-beartype-0.18.0-version.patch + ]; + + installPhase = '' + mkdir $out + ls -alsph $src + cp -rfv ./. $out + ''; + }; +in +buildPythonPackage { + __noChroot = true; + + pname = "genjax"; + version = "0.1.1"; + inherit src; + format = "pyproject"; + + nativeBuildInputs = [ + #poetry + poetry-core + poetry-dynamic-versioning + ]; + + propagatedBuildInputs = [ + beartype + deprecated + dill + jax + jaxtyping + equinox + numpy + optax + oryx + plum-dispatch + pygments + rich + tensorflow-probability + typing-extensions + ]; + + pythonImportsCheck = [ "genjax" ]; +} diff --git a/pkgs/genjax/set-pyproject-version.patch b/pkgs/python-modules/genjax/set-pyproject-version.patch similarity index 100% rename from pkgs/genjax/set-pyproject-version.patch rename to pkgs/python-modules/genjax/set-pyproject-version.patch diff --git a/pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch b/pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch new file mode 100644 index 0000000..ec4c01e --- /dev/null +++ b/pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch @@ -0,0 +1,14 @@ +diff +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -41,7 +41,7 @@ tensorflow-probability = "^0.23.0" + rich = "^13.7.0" + jaxtyping = "^0.2.24" +-optax = "^0.1.7" ++optax = "^0.2.2" +-beartype = "^0.16.4" ++beartype = "^0.18.0" + dill = "^0.3.7" + pygments = "^2.17.2" + plum-dispatch = "^2.2.2" + diff --git a/pkgs/goftests/default.nix b/pkgs/python-modules/goftests/default.nix similarity index 76% rename from pkgs/goftests/default.nix rename to pkgs/python-modules/goftests/default.nix index a8d70cd..b640623 100644 --- a/pkgs/goftests/default.nix +++ b/pkgs/python-modules/goftests/default.nix @@ -1,8 +1,11 @@ { fetchPypi, - python3Packages, + buildPythonPackage, + numpy, + scipy, }: -python3Packages.buildPythonPackage rec { +# TODO: upstream +buildPythonPackage rec { pname = "goftests"; version = "0.2.7"; format = "setuptools"; @@ -12,7 +15,7 @@ python3Packages.buildPythonPackage rec { hash = "sha256-5s0NugSus2TuZIInesCNJNAtxEHnZLQIjn0pxGgwL/o="; }; - buildInputs = with python3Packages; [ numpy scipy ]; + buildInputs = [ numpy scipy ]; doCheck = false; diff --git a/pkgs/loom/default.nix b/pkgs/python-modules/loom/default.nix similarity index 93% rename from pkgs/loom/default.nix rename to pkgs/python-modules/loom/default.nix index 0fbea08..09cead6 100644 --- a/pkgs/loom/default.nix +++ b/pkgs/python-modules/loom/default.nix @@ -27,8 +27,8 @@ , zlib , eigen , gperftools -, dockerTools -, basicTools +# , dockerTools +# , basicTools , distributions , goftests , parsable @@ -191,13 +191,14 @@ let ; }; - passthru.ociImg = dockerTools.buildLayeredImage { - name = "probcomp/loom"; - contents = - with pkgs; [ loom bashInteractive ] ++ - basicTools - ; - }; + # TODO: move it to a different package + # passthru.ociImg = dockerTools.buildLayeredImage { + # name = "probcomp/loom"; + # contents = + # with pkgs; [ loom bashInteractive ] ++ + # basicTools + # ; + # }; passthru.tests.run = callPackage ./test.nix { inherit src; }; diff --git a/pkgs/loom/test.nix b/pkgs/python-modules/loom/test.nix similarity index 100% rename from pkgs/loom/test.nix rename to pkgs/python-modules/loom/test.nix diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix new file mode 100644 index 0000000..8f15d76 --- /dev/null +++ b/pkgs/python-modules/open3d/default.nix @@ -0,0 +1,222 @@ +{ stdenv +, lib +, pkgs +, python3Packages +, fetchPypi +, fetchurl + +, tree +, unzip +, zip + +, autoPatchelfHook +, tensorflow-bin +, libusb +, cudaPackages_11 + +, libGL +, libglvnd +, libdrm +, expat +, xorg +, llvmPackages_10 +, buildEnv +, runCommand +}: +let + inherit (python3Packages) + buildPythonPackage + ipywidgets + matplotlib + numpy + pandas + plyfile + pytorchWithCuda + pyyaml + scikitlearn + scipy + tqdm + ; + + libllvm-wrapped = + let + libllvm = llvmPackages_10.libllvm.lib; + name = libllvm.name; + in + buildEnv { + inherit name; + paths = [ + llvmPackages_10.libllvm.lib + (runCommand "${name}.1" {} "mkdir -p $out/lib && ln -sf ${libllvm}/lib/libLLVM-10.so $out/lib/libLLVM-10.so.1") + ]; + }; + + version = "0.18.0"; + pname = "open3d"; + pythonAbi = "cp311"; + + prebuiltSrcs = { + "3.8-x86_64-linux" = { + platform = "manylinux_2_27_x86_64"; + dist = "cp38"; + hash = ""; + }; + "3.8-aarch64-linux" = { + platform = "manylinux_2_27_aarch64"; + dist = "cp38"; + hash = ""; + }; + "3.8-x86_64-darwin" = { + platform = "macosx_11_0_x86_64"; + dist = "cp38"; + hash = ""; + }; + "3.8-aarch64-darwin" = { + platform = "macosx_13_0_aarch64"; + dist = "cp38"; + hash = ""; + }; + + "3.9-x86_64-linux" = { + platform = "manylinux_2_27_x86_64"; + dist = "cp39"; + hash = ""; + }; + "3.9-aarch64-linux" = { + platform = "manylinux_2_27_aarch64"; + dist = "cp39"; + hash = ""; + }; + "3.9-x86_64-darwin" = { + platform = "macosx_11_0_x86_64"; + dist = "cp39"; + hash = ""; + }; + "3.9-aarch64-darwin" = { + platform = "macosx_13_0_aarch64"; + dist = "cp39"; + hash = ""; + }; + + "3.10-x86_64-linux" = { + platform = "manylinux_2_27_x86_64"; + dist = "cp310"; + hash = ""; + }; + "3.10-aarch64-linux" = { + platform = "manylinux_2_27_aarch64"; + dist = "cp310"; + hash = ""; + }; + "3.10-x86_64-darwin" = { + platform = "macosx_11_0_x86_64"; + dist = "cp310"; + hash = ""; + }; + "3.10-aarch64-darwin" = { + platform = "macosx_13_0_aarch64"; + dist = "cp310"; + hash = ""; + }; + + "3.11-x86_64-linux" = { + platform = "manylinux_2_27_x86_64"; + dist = "cp311"; + hash = "sha256-jj0dGQCo9NlW9oGcJGx4CBclubCIj4VJ0qeknI2qEwM="; + }; + "3.11-aarch64-linux" = { + platform = "manylinux_2_27_aarch64"; + dist = "cp311"; + hash = ""; + }; + "3.11-x86_64-darwin" = { + platform = "macosx_11_0_x86_64"; + dist = "cp311"; + hash = ""; + }; + "3.11-aarch64-darwin" = { + platform = "macosx_13_0_aarch64"; + dist = "cp311"; + hash = ""; + }; + }; + + pyVersion = lib.versions.majorMinor python3Packages.python.version; + srcInputs = prebuiltSrcs."${pyVersion}-${stdenv.system}" or (throw "open3d-bin for Python version '${pyVersion}' is not supported on '${stdenv.system}'"); + + src = fetchPypi rec { + inherit pname version; + inherit (srcInputs) platform dist hash; + + python = dist; + abi = dist; + + format = "wheel"; + }; +in +buildPythonPackage { + inherit pname version; + format = "wheel"; + + # TODO: make this multiplatform + inherit src; + + patchPhase = '' + ${unzip}/bin/unzip ./dist/open3d-${version}-${srcInputs.dist}-${srcInputs.dist}-${srcInputs.platform}.whl -d tmp + rm ./dist/open3d-${version}-${srcInputs.dist}-${srcInputs.dist}-${srcInputs.platform}.whl + #sed -i 's/sklearn/scikit-learn/g' tmp/open3d-${version}.dist-info/METADATA + cd tmp + ${zip}/bin/zip -0 -r ../dist/open3d-${version}-${srcInputs.dist}-${srcInputs.dist}-${srcInputs.platform}.whl ./* + cd ../ + ''; + + nativeBuildInputs = [ + autoPatchelfHook + ]; + + buildInputs = [ + # so deps + stdenv.cc.cc.lib + libusb.out + pytorchWithCuda + tensorflow-bin + cudaPackages_11.cudatoolkit.lib + #cudaPackages_11.cuda_cudart.lib + libGL + libglvnd + libdrm + expat + xorg.libXxf86vm + xorg.libXfixes + libllvm-wrapped + pkgs.mesa + pkgs.zstd + ]; + + propagatedBuildInputs = [ + # py deps + ipywidgets + tqdm + pyyaml + pandas + plyfile + scipy + scikitlearn + numpy + #addict + matplotlib + ]; + + #preBuild = '' + #mkdir $out + #''; + + preFixup = '' + echo "OUTPUT TO: $out" + cd $out/lib/python3.*/site-packages/open3d + rm libGL.so.1 libEGL.so.1 + ln -s ${libGL}/lib/libGL.so.1 libGL.so.1 + ln -s ${libGL}/lib/libEGL.so.1 libEGL.so.1 + #exit 1 + ''; +} diff --git a/pkgs/opencv-python/default.nix b/pkgs/python-modules/opencv-python/default.nix similarity index 100% rename from pkgs/opencv-python/default.nix rename to pkgs/python-modules/opencv-python/default.nix diff --git a/pkgs/opencv-python/relax-dependency-ranges.patch b/pkgs/python-modules/opencv-python/relax-dependency-ranges.patch similarity index 100% rename from pkgs/opencv-python/relax-dependency-ranges.patch rename to pkgs/python-modules/opencv-python/relax-dependency-ranges.patch diff --git a/pkgs/oryx/default.nix b/pkgs/python-modules/oryx/default.nix similarity index 95% rename from pkgs/oryx/default.nix rename to pkgs/python-modules/oryx/default.nix index 792ce6b..82589ed 100644 --- a/pkgs/oryx/default.nix +++ b/pkgs/python-modules/oryx/default.nix @@ -3,7 +3,7 @@ , fetchPypi , poetry-core , jax -, jaxlib +, jaxlib-bin , tensorflow-probability }: @@ -23,7 +23,7 @@ buildPythonPackage rec { propagatedBuildInputs = [ jax - jaxlib + jaxlib-bin tensorflow-probability ]; diff --git a/pkgs/parsable/default.nix b/pkgs/python-modules/parsable/default.nix similarity index 100% rename from pkgs/parsable/default.nix rename to pkgs/python-modules/parsable/default.nix diff --git a/pkgs/plum-dispatch/default.nix b/pkgs/python-modules/plum-dispatch/default.nix similarity index 100% rename from pkgs/plum-dispatch/default.nix rename to pkgs/python-modules/plum-dispatch/default.nix diff --git a/pkgs/pymetis/default.nix b/pkgs/python-modules/pymetis/default.nix similarity index 100% rename from pkgs/pymetis/default.nix rename to pkgs/python-modules/pymetis/default.nix diff --git a/pkgs/pyransac3d/default.nix b/pkgs/python-modules/pyransac3d/default.nix similarity index 86% rename from pkgs/pyransac3d/default.nix rename to pkgs/python-modules/pyransac3d/default.nix index 6638659..ba3d620 100644 --- a/pkgs/pyransac3d/default.nix +++ b/pkgs/python-modules/pyransac3d/default.nix @@ -1,7 +1,9 @@ { fetchFromGitHub , python3 +, buildPythonApplication }: -python3.pkgs.buildPythonApplication rec { +# TODO: upstream me +buildPythonApplication rec { pname = "pyransac3d"; version = "0.6.0"; pyproject = true; diff --git a/pkgs/sppl/default.nix b/pkgs/python-modules/sppl/default.nix similarity index 94% rename from pkgs/sppl/default.nix rename to pkgs/python-modules/sppl/default.nix index eb9c4ec..edb97bd 100644 --- a/pkgs/sppl/default.nix +++ b/pkgs/python-modules/sppl/default.nix @@ -2,6 +2,7 @@ system, ... }: let + # FIXME: check if we still need python 3.9. Can it be switched to 3.11? # relies on specific versions of deps that are no longer present in # nixpkgs stable; we must checkout a specific SHA diff --git a/pkgs/python-modules/tensorflow-probability/default.nix b/pkgs/python-modules/tensorflow-probability/default.nix new file mode 100644 index 0000000..01ba07f --- /dev/null +++ b/pkgs/python-modules/tensorflow-probability/default.nix @@ -0,0 +1,125 @@ +{ + lib, + stdenv, + fetchFromGitHub, + bazel_6, + buildBazelPackage, + buildPythonPackage, + python, + setuptools, + wheel, + absl-py, + tensorflow, + six, + numpy, + dm-tree, + keras, + decorator, + cloudpickle, + gast, + hypothesis, + scipy, + pandas, + mpmath, + matplotlib, + mock, + pytest, + darwin, +}: + +let + version = "0.23.0"; + pname = "tensorflow-probability"; + + inherit (darwin) cctools; + + # first build all binaries and generate setup.py using bazel + bazel-wheel = buildBazelPackage { + name = "tensorflow_probability-${version}-py2.py3-none-any.whl"; + src = fetchFromGitHub { + owner = "tensorflow"; + repo = "probability"; + rev = "refs/tags/v${version}"; + hash = "sha256-cZTlWfg3pIFnJz5xrQhcS1uvRMfIOOxcUxN747txd28="; + }; + nativeBuildInputs = [ + # needed to create the output wheel in installPhase + python + setuptools + wheel + absl-py + tensorflow + ]; + + bazel = bazel_6; + + bazelTargets = [ ":pip_pkg" ]; + LIBTOOL = lib.optionalString stdenv.isDarwin "${cctools}/bin/libtool"; + + fetchAttrs = { + sha256 = "sha256-TbWcWYidyXuAMgBnO2/k0NKCzc4wThf2uUeC3QxdBJY="; + }; + + buildAttrs = { + preBuild = '' + patchShebangs . + ''; + + installPhase = '' + # work around timestamp issues + # https://github.com/NixOS/nixpkgs/issues/270#issuecomment-467583872 + export SOURCE_DATE_EPOCH=315532800 + + # First build, then move. Otherwise pip_pkg would create the dir $out + # and then put the wheel in that directory. However we want $out to + # point directly to the wheel file. + ./bazel-bin/pip_pkg . --release + mv *.whl "$out" + ''; + }; + }; +in +buildPythonPackage { + inherit version pname; + format = "wheel"; + + src = bazel-wheel; + + propagatedBuildInputs = [ + tensorflow + six + numpy + decorator + cloudpickle + gast + dm-tree + keras + ]; + + # Listed here: + # https://github.com/tensorflow/probability/blob/f3777158691787d3658b5e80883fe1a933d48989/testing/dependency_install_lib.sh#L83 + nativeCheckInputs = [ + hypothesis + pytest + scipy + pandas + mpmath + matplotlib + mock + ]; + + # Ideally, we run unit tests with pytest, but in checkPhase, only the Bazel-build wheel is available. + # But it seems not guaranteed that running the tests with pytest will even work, see + # https://github.com/tensorflow/probability/blob/c2a10877feb2c4c06a4dc58281e69c37a11315b9/CONTRIBUTING.md?plain=1#L69 + # Ideally, tests would be run using Bazel. For now, lets's do a... + + # sanity check + pythonImportsCheck = [ "tensorflow_probability" ]; + + meta = with lib; { + description = "Library for probabilistic reasoning and statistical analysis"; + homepage = "https://www.tensorflow.org/probability/"; + license = licenses.asl20; + maintainers = with maintainers; [ GaetanLepage ]; + }; +} From 84416b910617c1271d3aae3a54b8cee197efc502 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 10:14:50 +0100 Subject: [PATCH 07/52] WIP: add bayes3d pythonImportsCheck --- pkgs/python-modules/bayes3d/default.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index 2f89c8a..ea4e950 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -108,4 +108,8 @@ buildPythonPackage rec { ''; #preferLocalBuild = true; + + pythonImportsCheck = [ + "bayes3d" + ]; } From 66ff1855a26c7bc959cb5ed40430d22d09e40c54 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 13:43:40 +0100 Subject: [PATCH 08/52] WIP: Fettling to get bayes3d demo ipynb working --- .gitignore | 1 + flake.nix | 29 +++- notebooks/demo.ipynb | 164 +++++++++++++++++++++ pkgs/python-modules/bayes3d/default.nix | 6 +- pkgs/python-modules/pyransac3d/default.nix | 4 +- 5 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 notebooks/demo.ipynb diff --git a/.gitignore b/.gitignore index 750baeb..7b3ce21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ result result-* +.ipynb_checkpoints/ diff --git a/flake.nix b/flake.nix index 4aa55e9..133ed11 100644 --- a/flake.nix +++ b/flake.nix @@ -52,8 +52,9 @@ }); loom = scopes.callPy3Package ./pkgs/loom { }; - bayes3d = scopes.callPackage ./pkgs/bayes3d { }; - open3d = scopes.callPackage ./pkgs/open3d { }; + # TODO: make this cleaner + bayes3d = self'.legacyPackages.python3Packages.bayes3d; + open3d = scopes.callPy3Package ./pkgs/python-modules/open3d { }; internalPackages = { #jaxlib = scopes.callPy3Package ./pkgs/jaxlib { }; @@ -132,11 +133,33 @@ inherit packages; - legacyPackages.internalPackages = internalPackages; legacyPackages.python3Packages = (pkgs.python3Packages.overrideScope pythonOverrides).overrideScope (super: superPython: loadPackages super.callPackage ./pkgs/python-modules ); + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + self'.legacyPackages.python3Packages.python-lsp-server + (python3.withPackages (ps: with self'.legacyPackages.python3Packages; [ + jupyter + bayes3d + jax + scipy + pyransac3d + ])) + ]; + + shellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export EXTRA_CCFLAGS="-I/usr/include" + export CUDA_PATH=${pkgs.cudatoolkit_11} + export B3D_ASSETS_PATH="${bayes3d.src}/assets" + + jupyter notebook + exit + ''; + }; }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb new file mode 100644 index 0000000..827d812 --- /dev/null +++ b/notebooks/demo.ipynb @@ -0,0 +1,164 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "fc70511d-4bf5-47cb-9935-c17b4da6cf11", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[E rasterize_gl.cpp:121] OpenGL version reported as 4.6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing frame buffer size to (width, height, depth) = (128, 128, 1024)\n" + ] + }, + { + "ename": "RuntimeError", + "evalue": "Cuda error: 801[cudaGraphicsGLRegisterImage(&s.cudaColorBuffer[i], s.glColorBuffer[i], GL_TEXTURE_3D, cudaGraphicsRegisterFlagsReadOnly);]\nException raised from _setup at bayes3d/rendering/nvdiffrast/common/rasterize_gl.cpp:398 (most recent call first):\nframe #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string, std::allocator >) + 0xba (0x7fff210b9a9a in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, std::__cxx11::basic_string, std::allocator > const&) + 0xd2 (0x7fff210611dc in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #2: _setup(CUstream_st*, RasterizeGLStateWrapper&, int, int, int) + 0xb1e (0x7ffdf6395dde in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/rendering/nvdiffrast/nvdiffrast_plugin_gl.cpython-311-x86_64-linux-gnu.so)\nframe #3: + 0x43c0a0f (0x7fff6b5c0a0f in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #4: + 0x43c1215 (0x7fff6b5c1215 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #5: + 0x505b843 (0x7fff6c25b843 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #6: + 0x5058f39 (0x7fff6c258f39 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #7: + 0x505763d (0x7fff6c25763d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #8: + 0x5056faf (0x7fff6c256faf in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #9: + 0x7414ab6 (0x7fff6e614ab6 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #10: + 0x11147e5 (0x7fff683147e5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #11: + 0x1115138 (0x7fff68315138 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #12: + 0x107d238 (0x7fff6827d238 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #13: + 0x107f9a7 (0x7fff6827f9a7 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #14: + 0x1081eb0 (0x7fff68281eb0 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #15: + 0x1343c0d (0x7fff68543c0d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #16: + 0x1019218 (0x7fff68219218 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #17: + 0x101a78c (0x7fff6821a78c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #18: + 0x64c1c5 (0x7fff6784c1c5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #19: + 0x64bffd (0x7fff6784bffd in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #20: + 0x138660c (0x7fff6858660c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n\nframe #35: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #36: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #51: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #52: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 20\u001b[0m\n\u001b[1;32m 14\u001b[0m assets_dir \u001b[38;5;241m=\u001b[39m os\u001b[38;5;241m.\u001b[39mgetenv(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mB3D_ASSETS_PATH\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m intrinsics \u001b[38;5;241m=\u001b[39m b\u001b[38;5;241m.\u001b[39mIntrinsics(\n\u001b[1;32m 17\u001b[0m height\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, width\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, fx\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, fy\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, cx\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, cy\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, near\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.001\u001b[39m, far\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m6.0\u001b[39m\n\u001b[1;32m 18\u001b[0m )\n\u001b[0;32m---> 20\u001b[0m \u001b[43mb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msetup_renderer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mintrinsics\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 21\u001b[0m b\u001b[38;5;241m.\u001b[39mRENDERER\u001b[38;5;241m.\u001b[39madd_mesh_from_file(\n\u001b[1;32m 22\u001b[0m os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(assets_dir, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msample_objs/bunny.obj\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 23\u001b[0m )\n\u001b[1;32m 25\u001b[0m num_frames \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m60\u001b[39m\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/renderer.py:38\u001b[0m, in \u001b[0;36msetup_renderer\u001b[0;34m(intrinsics, num_layers)\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msetup_renderer\u001b[39m(intrinsics, num_layers\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1024\u001b[39m):\n\u001b[1;32m 34\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Setup the renderer.\u001b[39;00m\n\u001b[1;32m 35\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[1;32m 36\u001b[0m \u001b[38;5;124;03m intrinsics (bayes3d.camera.Intrinsics): The camera intrinsics.\u001b[39;00m\n\u001b[1;32m 37\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 38\u001b[0m b\u001b[38;5;241m.\u001b[39mRENDERER \u001b[38;5;241m=\u001b[39m \u001b[43mRenderer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mintrinsics\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_layers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnum_layers\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/renderer.py:67\u001b[0m, in \u001b[0;36mRenderer.__init__\u001b[0;34m(self, intrinsics, num_layers)\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mproj_matrix \u001b[38;5;241m=\u001b[39m b\u001b[38;5;241m.\u001b[39mcamera\u001b[38;5;241m.\u001b[39m_open_gl_projection_matrix(\n\u001b[1;32m 54\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mheight,\n\u001b[1;32m 55\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mwidth,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 61\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mfar,\n\u001b[1;32m 62\u001b[0m )\n\u001b[1;32m 64\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrenderer_env \u001b[38;5;241m=\u001b[39m dr\u001b[38;5;241m.\u001b[39mRasterizeGLContext(\n\u001b[1;32m 65\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mheight, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mwidth, output_db\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 66\u001b[0m )\n\u001b[0;32m---> 67\u001b[0m \u001b[43mbuild_setup_primitive\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwidth\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_layers\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbind\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmeshes \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 70\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmesh_names \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:387\u001b[0m, in \u001b[0;36mPrimitive.bind\u001b[0;34m(self, *args, **params)\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbind\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams):\n\u001b[1;32m 385\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m config\u001b[38;5;241m.\u001b[39menable_checks\u001b[38;5;241m.\u001b[39mvalue \u001b[38;5;129;01mor\u001b[39;00m\n\u001b[1;32m 386\u001b[0m \u001b[38;5;28mall\u001b[39m(\u001b[38;5;28misinstance\u001b[39m(arg, Tracer) \u001b[38;5;129;01mor\u001b[39;00m valid_jaxtype(arg) \u001b[38;5;28;01mfor\u001b[39;00m arg \u001b[38;5;129;01min\u001b[39;00m args)), args\n\u001b[0;32m--> 387\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbind_with_trace\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfind_top_trace\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:391\u001b[0m, in \u001b[0;36mPrimitive.bind_with_trace\u001b[0;34m(self, trace, args, params)\u001b[0m\n\u001b[1;32m 389\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbind_with_trace\u001b[39m(\u001b[38;5;28mself\u001b[39m, trace, args, params):\n\u001b[1;32m 390\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m pop_level(trace\u001b[38;5;241m.\u001b[39mlevel):\n\u001b[0;32m--> 391\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mtrace\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprocess_primitive\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtrace\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfull_raise\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 392\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mmap\u001b[39m(full_lower, out) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmultiple_results \u001b[38;5;28;01melse\u001b[39;00m full_lower(out)\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:879\u001b[0m, in \u001b[0;36mEvalTrace.process_primitive\u001b[0;34m(self, primitive, tracers, params)\u001b[0m\n\u001b[1;32m 877\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m call_impl_with_key_reuse_checks(primitive, primitive\u001b[38;5;241m.\u001b[39mimpl, \u001b[38;5;241m*\u001b[39mtracers, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams)\n\u001b[1;32m 878\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 879\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mprimitive\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimpl\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mtracers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/dispatch.py:86\u001b[0m, in \u001b[0;36mapply_primitive\u001b[0;34m(prim, *args, **params)\u001b[0m\n\u001b[1;32m 84\u001b[0m prev \u001b[38;5;241m=\u001b[39m lib\u001b[38;5;241m.\u001b[39mjax_jit\u001b[38;5;241m.\u001b[39mswap_thread_local_state_disable_jit(\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m 85\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 86\u001b[0m outs \u001b[38;5;241m=\u001b[39m \u001b[43mfun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 88\u001b[0m lib\u001b[38;5;241m.\u001b[39mjax_jit\u001b[38;5;241m.\u001b[39mswap_thread_local_state_disable_jit(prev)\n", + " \u001b[0;31m[... skipping hidden 10 frame]\u001b[0m\n", + "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/interpreters/pxla.py:1185\u001b[0m, in \u001b[0;36mExecuteReplicated.__call__\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 1183\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_handle_token_bufs(result_token_bufs, sharded_runtime_token)\n\u001b[1;32m 1184\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 1185\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mxla_executable\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute_sharded\u001b[49m\u001b[43m(\u001b[49m\u001b[43minput_bufs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1186\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m dispatch\u001b[38;5;241m.\u001b[39mneeds_check_special():\n\u001b[1;32m 1187\u001b[0m out_arrays \u001b[38;5;241m=\u001b[39m results\u001b[38;5;241m.\u001b[39mdisassemble_into_single_device_arrays()\n", + "\u001b[0;31mRuntimeError\u001b[0m: Cuda error: 801[cudaGraphicsGLRegisterImage(&s.cudaColorBuffer[i], s.glColorBuffer[i], GL_TEXTURE_3D, cudaGraphicsRegisterFlagsReadOnly);]\nException raised from _setup at bayes3d/rendering/nvdiffrast/common/rasterize_gl.cpp:398 (most recent call first):\nframe #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string, std::allocator >) + 0xba (0x7fff210b9a9a in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, std::__cxx11::basic_string, std::allocator > const&) + 0xd2 (0x7fff210611dc in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #2: _setup(CUstream_st*, RasterizeGLStateWrapper&, int, int, int) + 0xb1e (0x7ffdf6395dde in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/rendering/nvdiffrast/nvdiffrast_plugin_gl.cpython-311-x86_64-linux-gnu.so)\nframe #3: + 0x43c0a0f (0x7fff6b5c0a0f in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #4: + 0x43c1215 (0x7fff6b5c1215 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #5: + 0x505b843 (0x7fff6c25b843 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #6: + 0x5058f39 (0x7fff6c258f39 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #7: + 0x505763d (0x7fff6c25763d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #8: + 0x5056faf (0x7fff6c256faf in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #9: + 0x7414ab6 (0x7fff6e614ab6 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #10: + 0x11147e5 (0x7fff683147e5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #11: + 0x1115138 (0x7fff68315138 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #12: + 0x107d238 (0x7fff6827d238 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #13: + 0x107f9a7 (0x7fff6827f9a7 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #14: + 0x1081eb0 (0x7fff68281eb0 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #15: + 0x1343c0d (0x7fff68543c0d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #16: + 0x1019218 (0x7fff68219218 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #17: + 0x101a78c (0x7fff6821a78c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #18: + 0x64c1c5 (0x7fff6784c1c5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #19: + 0x64bffd (0x7fff6784bffd in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #20: + 0x138660c (0x7fff6858660c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n\nframe #35: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #36: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #51: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #52: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n" + ] + } + ], + "source": [ + "import os\n", + "import time\n", + "\n", + "import jax\n", + "import jax.numpy as jnp\n", + "from IPython import embed\n", + "from scipy.spatial.transform import Rotation as R\n", + "\n", + "import bayes3d as b\n", + "\n", + "# Can be helpful for debugging:\n", + "# jax.config.update('jax_enable_checks', True)\n", + "\n", + "assets_dir = os.getenv(\"B3D_ASSETS_PATH\")\n", + "\n", + "intrinsics = b.Intrinsics(\n", + " height=100, width=100, fx=50.0, fy=50.0, cx=50.0, cy=50.0, near=0.001, far=6.0\n", + ")\n", + "\n", + "b.setup_renderer(intrinsics)\n", + "b.RENDERER.add_mesh_from_file(\n", + " os.path.join(assets_dir, \"sample_objs/bunny.obj\")\n", + ")\n", + "\n", + "num_frames = 60\n", + "\n", + "poses = [b.t3d.transform_from_pos(jnp.array([-3.0, 0.0, 3.5]))]\n", + "delta_pose = b.t3d.transform_from_rot_and_pos(\n", + " R.from_euler(\"zyx\", [-1.0, 0.1, 2.0], degrees=True).as_matrix(),\n", + " jnp.array([0.09, 0.05, 0.02]),\n", + ")\n", + "for t in range(num_frames - 1):\n", + " poses.append(poses[-1].dot(delta_pose))\n", + "poses = jnp.stack(poses)\n", + "print(\"Number of frames: \", poses.shape[0])\n", + "\n", + "observed_images = b.RENDERER.render_many(poses[:, None, ...], jnp.array([0]))\n", + "print(\"observed_images.shape\", observed_images.shape)\n", + "\n", + "translation_deltas = b.utils.make_translation_grid_enumeration(\n", + " -0.2, -0.2, -0.2, 0.2, 0.2, 0.2, 5, 5, 5\n", + ")\n", + "rotation_deltas = jax.vmap(\n", + " lambda key: b.distributions.gaussian_vmf_zero_mean(key, 0.00001, 800.0)\n", + ")(jax.random.split(jax.random.PRNGKey(30), 100))\n", + "\n", + "likelihood = jax.vmap(\n", + " b.threedp3_likelihood_old, in_axes=(None, 0, None, None, None, None, None)\n", + ")\n", + "\n", + "\n", + "def update_pose_estimate(pose_estimate, gt_image):\n", + " proposals = jnp.einsum(\"ij,ajk->aik\", pose_estimate, translation_deltas)\n", + " rendered_images = jax.vmap(b.RENDERER.render, in_axes=(0, None))(\n", + " proposals[:, None, ...], jnp.array([0])\n", + " )\n", + " weights_new = likelihood(gt_image, rendered_images, 0.05, 0.1, 10**3, 0.1, 3)\n", + " pose_estimate = proposals[jnp.argmax(weights_new)]\n", + "\n", + " proposals = jnp.einsum(\"ij,ajk->aik\", pose_estimate, rotation_deltas)\n", + " rendered_images = jax.vmap(b.RENDERER.render, in_axes=(0, None))(\n", + " proposals[:, None, ...], jnp.array([0])\n", + " )\n", + " weights_new = likelihood(gt_image, rendered_images, 0.05, 0.1, 10**3, 0.1, 3)\n", + " pose_estimate = proposals[jnp.argmax(weights_new)]\n", + " return pose_estimate, pose_estimate\n", + "\n", + "\n", + "inference_program = jax.jit(lambda p, x: jax.lax.scan(update_pose_estimate, p, x)[1])\n", + "inferred_poses = inference_program(poses[0], observed_images)\n", + "\n", + "start = time.time()\n", + "pose_estimates_over_time = inference_program(poses[0], observed_images)\n", + "end = time.time()\n", + "print(\"Time elapsed:\", end - start)\n", + "print(\"FPS:\", poses.shape[0] / (end - start))\n", + "\n", + "rerendered_images = b.RENDERER.render_many(\n", + " pose_estimates_over_time[:, None, ...], jnp.array([0])\n", + ")\n", + "\n", + "viz_images = [\n", + " b.viz.multi_panel(\n", + " [\n", + " b.viz.scale_image(b.viz.get_depth_image(d[:, :, 2]), 3),\n", + " b.viz.scale_image(b.viz.get_depth_image(r[:, :, 2]), 3),\n", + " ],\n", + " labels=[\"Observed\", \"Rerendered\"],\n", + " label_fontsize=20,\n", + " )\n", + " for (r, d) in zip(rerendered_images, observed_images)\n", + "]\n", + "b.make_gif_from_pil_images(viz_images, \"assets/demo.gif\")\n", + "\n", + "\n", + "embed()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index ea4e950..71ade64 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -29,7 +29,7 @@ , trimesh }: let - rev = "eb942aeca9441957c5cc62da26dde3e2c70c23c2"; + rev = "8113f643a7ba084e0ca2288cf06f95a23e39d1c7"; cuda-common-redist = with cudaPackages_11; [ cuda_cccl # @@ -56,9 +56,9 @@ buildPythonPackage rec { src = fetchFromGitHub { repo = pname; - owner = "probcomp"; + owner = "srounce"; inherit rev; - hash = "sha256-jtiLyBu3G+B4Qx1iBq7gVlzb4sdaaB25Gyk+fxcgKxU="; + hash = "sha256-6AtxR8ZsByliDTQE/hEJs5+LKwdfS/sRGYXf+mgFHxw="; }; pyproject = true; diff --git a/pkgs/python-modules/pyransac3d/default.nix b/pkgs/python-modules/pyransac3d/default.nix index ba3d620..1891768 100644 --- a/pkgs/python-modules/pyransac3d/default.nix +++ b/pkgs/python-modules/pyransac3d/default.nix @@ -1,9 +1,9 @@ { fetchFromGitHub , python3 -, buildPythonApplication +, buildPythonPackage }: # TODO: upstream me -buildPythonApplication rec { +buildPythonPackage rec { pname = "pyransac3d"; version = "0.6.0"; pyproject = true; From 9c31e2810b163a66675a45569cc416b3016f5b60 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 19:41:59 +0100 Subject: [PATCH 09/52] feat: First working version of Bayes3D in Ipynb --- flake.lock | 16 ++--- flake.nix | 20 +++--- notebooks/assets/.gitkeep | 0 notebooks/demo.ipynb | 74 +++++++++++++-------- pkgs/python-modules/distinctipy/default.nix | 1 - pkgs/python-modules/open3d/default.nix | 29 ++++---- pkgs/python-modules/parsable/default.nix | 4 +- pkgs/python-modules/pymetis/default.nix | 7 +- pkgs/python-modules/pyransac3d/default.nix | 9 ++- 9 files changed, 90 insertions(+), 70 deletions(-) create mode 100644 notebooks/assets/.gitkeep diff --git a/flake.lock b/flake.lock index a34b9d1..c327708 100644 --- a/flake.lock +++ b/flake.lock @@ -78,16 +78,16 @@ }, "nixpkgs": { "locked": { - "dirtyRev": "a128565b55085166b20cfbb0a6bcaa736d96630e-dirty", - "dirtyShortRev": "a128565b5508-dirty", - "lastModified": 1719819879, - "narHash": "sha256-z0jXs3LtmEoPT0KkqKmnteIXuE4/wBYFwdWVCiag96U=", - "type": "git", - "url": "file:///home/s/repos/nixpkgs" + "lastModified": 1720028458, + "narHash": "sha256-DuQi7Eaa7hfpl5WufMiWDq/a4p5qngRtwhXNGHmdv4Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d8724afca4565614164dd81345f6137c4c6eab21", + "type": "github" }, "original": { - "owner": "zimbatm", - "ref": "jax-fixes", + "owner": "NixOS", + "ref": "d8724afca4565614164dd81345f6137c4c6eab21", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 133ed11..7cc2073 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,7 @@ flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - nixpkgs.url = "github:zimbatm/nixpkgs?ref=jax-fixes"; + nixpkgs.url = "github:NixOS/nixpkgs?ref=d8724afca4565614164dd81345f6137c4c6eab21"; nixpkgs-llvm-10.url = "github:NixOS/nixpkgs?rev=222c1940fafeda4dea161858ffe6ebfc853d3db5"; genjax.url = "github:probcomp/genjax?ref=v0.1.1"; @@ -134,19 +134,18 @@ inherit packages; legacyPackages.python3Packages = - (pkgs.python3Packages.overrideScope pythonOverrides).overrideScope (super: superPython: + (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (super: superPython: loadPackages super.callPackage ./pkgs/python-modules ); devShells.default = pkgs.mkShell { - packages = with pkgs; [ + packages = [ self'.legacyPackages.python3Packages.python-lsp-server - (python3.withPackages (ps: with self'.legacyPackages.python3Packages; [ - jupyter - bayes3d - jax - scipy - pyransac3d + (self'.legacyPackages.python3Packages.python.withPackages (p: [ + self'.legacyPackages.python3Packages.bayes3d + self'.legacyPackages.python3Packages.jax + p.jupyter + p.scipy ])) ]; @@ -154,10 +153,9 @@ export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" export EXTRA_CCFLAGS="-I/usr/include" export CUDA_PATH=${pkgs.cudatoolkit_11} - export B3D_ASSETS_PATH="${bayes3d.src}/assets" + export B3D_ASSET_PATH="${bayes3d.src}/assets" jupyter notebook - exit ''; }; }; diff --git a/notebooks/assets/.gitkeep b/notebooks/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb index 827d812..43d207a 100644 --- a/notebooks/demo.ipynb +++ b/notebooks/demo.ipynb @@ -2,14 +2,18 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, "id": "fc70511d-4bf5-47cb-9935-c17b4da6cf11", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ + "/tmp/nix-shell.EBRV2Z/ipykernel_252673/268569837.py:7: DeprecationWarning: Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython display\n", + " from IPython.core.display import Image, display\n", "[E rasterize_gl.cpp:121] OpenGL version reported as 4.6\n" ] }, @@ -17,26 +21,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Increasing frame buffer size to (width, height, depth) = (128, 128, 1024)\n" - ] - }, - { - "ename": "RuntimeError", - "evalue": "Cuda error: 801[cudaGraphicsGLRegisterImage(&s.cudaColorBuffer[i], s.glColorBuffer[i], GL_TEXTURE_3D, cudaGraphicsRegisterFlagsReadOnly);]\nException raised from _setup at bayes3d/rendering/nvdiffrast/common/rasterize_gl.cpp:398 (most recent call first):\nframe #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string, std::allocator >) + 0xba (0x7fff210b9a9a in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, std::__cxx11::basic_string, std::allocator > const&) + 0xd2 (0x7fff210611dc in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #2: _setup(CUstream_st*, RasterizeGLStateWrapper&, int, int, int) + 0xb1e (0x7ffdf6395dde in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/rendering/nvdiffrast/nvdiffrast_plugin_gl.cpython-311-x86_64-linux-gnu.so)\nframe #3: + 0x43c0a0f (0x7fff6b5c0a0f in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #4: + 0x43c1215 (0x7fff6b5c1215 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #5: + 0x505b843 (0x7fff6c25b843 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #6: + 0x5058f39 (0x7fff6c258f39 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #7: + 0x505763d (0x7fff6c25763d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #8: + 0x5056faf (0x7fff6c256faf in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #9: + 0x7414ab6 (0x7fff6e614ab6 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #10: + 0x11147e5 (0x7fff683147e5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #11: + 0x1115138 (0x7fff68315138 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #12: + 0x107d238 (0x7fff6827d238 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #13: + 0x107f9a7 (0x7fff6827f9a7 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #14: + 0x1081eb0 (0x7fff68281eb0 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #15: + 0x1343c0d (0x7fff68543c0d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #16: + 0x1019218 (0x7fff68219218 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #17: + 0x101a78c (0x7fff6821a78c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #18: + 0x64c1c5 (0x7fff6784c1c5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #19: + 0x64bffd (0x7fff6784bffd in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #20: + 0x138660c (0x7fff6858660c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n\nframe #35: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #36: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #51: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #52: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[1], line 20\u001b[0m\n\u001b[1;32m 14\u001b[0m assets_dir \u001b[38;5;241m=\u001b[39m os\u001b[38;5;241m.\u001b[39mgetenv(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mB3D_ASSETS_PATH\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m intrinsics \u001b[38;5;241m=\u001b[39m b\u001b[38;5;241m.\u001b[39mIntrinsics(\n\u001b[1;32m 17\u001b[0m height\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, width\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, fx\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, fy\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, cx\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, cy\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m50.0\u001b[39m, near\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0.001\u001b[39m, far\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m6.0\u001b[39m\n\u001b[1;32m 18\u001b[0m )\n\u001b[0;32m---> 20\u001b[0m \u001b[43mb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msetup_renderer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mintrinsics\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 21\u001b[0m b\u001b[38;5;241m.\u001b[39mRENDERER\u001b[38;5;241m.\u001b[39madd_mesh_from_file(\n\u001b[1;32m 22\u001b[0m os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(assets_dir, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msample_objs/bunny.obj\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 23\u001b[0m )\n\u001b[1;32m 25\u001b[0m num_frames \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m60\u001b[39m\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/renderer.py:38\u001b[0m, in \u001b[0;36msetup_renderer\u001b[0;34m(intrinsics, num_layers)\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msetup_renderer\u001b[39m(intrinsics, num_layers\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1024\u001b[39m):\n\u001b[1;32m 34\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Setup the renderer.\u001b[39;00m\n\u001b[1;32m 35\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[1;32m 36\u001b[0m \u001b[38;5;124;03m intrinsics (bayes3d.camera.Intrinsics): The camera intrinsics.\u001b[39;00m\n\u001b[1;32m 37\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 38\u001b[0m b\u001b[38;5;241m.\u001b[39mRENDERER \u001b[38;5;241m=\u001b[39m \u001b[43mRenderer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mintrinsics\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_layers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnum_layers\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/renderer.py:67\u001b[0m, in \u001b[0;36mRenderer.__init__\u001b[0;34m(self, intrinsics, num_layers)\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mproj_matrix \u001b[38;5;241m=\u001b[39m b\u001b[38;5;241m.\u001b[39mcamera\u001b[38;5;241m.\u001b[39m_open_gl_projection_matrix(\n\u001b[1;32m 54\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mheight,\n\u001b[1;32m 55\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mwidth,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 61\u001b[0m intrinsics\u001b[38;5;241m.\u001b[39mfar,\n\u001b[1;32m 62\u001b[0m )\n\u001b[1;32m 64\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrenderer_env \u001b[38;5;241m=\u001b[39m dr\u001b[38;5;241m.\u001b[39mRasterizeGLContext(\n\u001b[1;32m 65\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mheight, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mwidth, output_db\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 66\u001b[0m )\n\u001b[0;32m---> 67\u001b[0m \u001b[43mbuild_setup_primitive\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwidth\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_layers\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbind\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmeshes \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 70\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmesh_names \u001b[38;5;241m=\u001b[39m []\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:387\u001b[0m, in \u001b[0;36mPrimitive.bind\u001b[0;34m(self, *args, **params)\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbind\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams):\n\u001b[1;32m 385\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m config\u001b[38;5;241m.\u001b[39menable_checks\u001b[38;5;241m.\u001b[39mvalue \u001b[38;5;129;01mor\u001b[39;00m\n\u001b[1;32m 386\u001b[0m \u001b[38;5;28mall\u001b[39m(\u001b[38;5;28misinstance\u001b[39m(arg, Tracer) \u001b[38;5;129;01mor\u001b[39;00m valid_jaxtype(arg) \u001b[38;5;28;01mfor\u001b[39;00m arg \u001b[38;5;129;01min\u001b[39;00m args)), args\n\u001b[0;32m--> 387\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbind_with_trace\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfind_top_trace\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:391\u001b[0m, in \u001b[0;36mPrimitive.bind_with_trace\u001b[0;34m(self, trace, args, params)\u001b[0m\n\u001b[1;32m 389\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbind_with_trace\u001b[39m(\u001b[38;5;28mself\u001b[39m, trace, args, params):\n\u001b[1;32m 390\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m pop_level(trace\u001b[38;5;241m.\u001b[39mlevel):\n\u001b[0;32m--> 391\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mtrace\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprocess_primitive\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtrace\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfull_raise\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 392\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mmap\u001b[39m(full_lower, out) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmultiple_results \u001b[38;5;28;01melse\u001b[39;00m full_lower(out)\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/core.py:879\u001b[0m, in \u001b[0;36mEvalTrace.process_primitive\u001b[0;34m(self, primitive, tracers, params)\u001b[0m\n\u001b[1;32m 877\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m call_impl_with_key_reuse_checks(primitive, primitive\u001b[38;5;241m.\u001b[39mimpl, \u001b[38;5;241m*\u001b[39mtracers, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mparams)\n\u001b[1;32m 878\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 879\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mprimitive\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimpl\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mtracers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/dispatch.py:86\u001b[0m, in \u001b[0;36mapply_primitive\u001b[0;34m(prim, *args, **params)\u001b[0m\n\u001b[1;32m 84\u001b[0m prev \u001b[38;5;241m=\u001b[39m lib\u001b[38;5;241m.\u001b[39mjax_jit\u001b[38;5;241m.\u001b[39mswap_thread_local_state_disable_jit(\u001b[38;5;28;01mFalse\u001b[39;00m)\n\u001b[1;32m 85\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 86\u001b[0m outs \u001b[38;5;241m=\u001b[39m \u001b[43mfun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 88\u001b[0m lib\u001b[38;5;241m.\u001b[39mjax_jit\u001b[38;5;241m.\u001b[39mswap_thread_local_state_disable_jit(prev)\n", - " \u001b[0;31m[... skipping hidden 10 frame]\u001b[0m\n", - "File \u001b[0;32m/nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jax/_src/interpreters/pxla.py:1185\u001b[0m, in \u001b[0;36mExecuteReplicated.__call__\u001b[0;34m(self, *args)\u001b[0m\n\u001b[1;32m 1183\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_handle_token_bufs(result_token_bufs, sharded_runtime_token)\n\u001b[1;32m 1184\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 1185\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mxla_executable\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute_sharded\u001b[49m\u001b[43m(\u001b[49m\u001b[43minput_bufs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1186\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m dispatch\u001b[38;5;241m.\u001b[39mneeds_check_special():\n\u001b[1;32m 1187\u001b[0m out_arrays \u001b[38;5;241m=\u001b[39m results\u001b[38;5;241m.\u001b[39mdisassemble_into_single_device_arrays()\n", - "\u001b[0;31mRuntimeError\u001b[0m: Cuda error: 801[cudaGraphicsGLRegisterImage(&s.cudaColorBuffer[i], s.glColorBuffer[i], GL_TEXTURE_3D, cudaGraphicsRegisterFlagsReadOnly);]\nException raised from _setup at bayes3d/rendering/nvdiffrast/common/rasterize_gl.cpp:398 (most recent call first):\nframe #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string, std::allocator >) + 0xba (0x7fff210b9a9a in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, std::__cxx11::basic_string, std::allocator > const&) + 0xd2 (0x7fff210611dc in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/torch/lib/libc10.so)\nframe #2: _setup(CUstream_st*, RasterizeGLStateWrapper&, int, int, int) + 0xb1e (0x7ffdf6395dde in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/bayes3d/rendering/nvdiffrast/nvdiffrast_plugin_gl.cpython-311-x86_64-linux-gnu.so)\nframe #3: + 0x43c0a0f (0x7fff6b5c0a0f in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #4: + 0x43c1215 (0x7fff6b5c1215 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #5: + 0x505b843 (0x7fff6c25b843 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #6: + 0x5058f39 (0x7fff6c258f39 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #7: + 0x505763d (0x7fff6c25763d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #8: + 0x5056faf (0x7fff6c256faf in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #9: + 0x7414ab6 (0x7fff6e614ab6 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #10: + 0x11147e5 (0x7fff683147e5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #11: + 0x1115138 (0x7fff68315138 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #12: + 0x107d238 (0x7fff6827d238 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #13: + 0x107f9a7 (0x7fff6827f9a7 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #14: + 0x1081eb0 (0x7fff68281eb0 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #15: + 0x1343c0d (0x7fff68543c0d in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #16: + 0x1019218 (0x7fff68219218 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #17: + 0x101a78c (0x7fff6821a78c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #18: + 0x64c1c5 (0x7fff6784c1c5 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #19: + 0x64bffd (0x7fff6784bffd in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #20: + 0x138660c (0x7fff6858660c in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n\nframe #35: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #36: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #51: + 0x6a8fee (0x7fff678a8fee in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\nframe #52: + 0x6a75a3 (0x7fff678a75a3 in /nix/store/v3j9fv95hvmmj6bp7x9sq4vcx8z7fphk-python3-3.11.9-env/lib/python3.11/site-packages/jaxlib/xla_extension.so)\n" + "Increasing frame buffer size to (width, height, depth) = (128, 128, 1024)\n", + "Centering mesh with translation [0.0000000e+00 2.9802322e-08 0.0000000e+00]\n", + "Number of frames: 60\n", + "observed_images.shape (60, 100, 100, 4)\n", + "Time elapsed: 0.3095111846923828\n", + "FPS: 193.85406074947772\n" ] } ], @@ -47,6 +37,7 @@ "import jax\n", "import jax.numpy as jnp\n", "from IPython import embed\n", + "from IPython.core.display import Image, display\n", "from scipy.spatial.transform import Rotation as R\n", "\n", "import bayes3d as b\n", @@ -54,7 +45,7 @@ "# Can be helpful for debugging:\n", "# jax.config.update('jax_enable_checks', True)\n", "\n", - "assets_dir = os.getenv(\"B3D_ASSETS_PATH\")\n", + "assets_dir = os.getenv(\"B3D_ASSET_PATH\")\n", "\n", "intrinsics = b.Intrinsics(\n", " height=100, width=100, fx=50.0, fy=50.0, cx=50.0, cy=50.0, near=0.001, far=6.0\n", @@ -134,10 +125,41 @@ " for (r, d) in zip(rerendered_images, observed_images)\n", "]\n", "b.make_gif_from_pil_images(viz_images, \"assets/demo.gif\")\n", - "\n", - "\n", - "embed()\n" + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c39a88e9-b18d-4d91-b001-f2a3abdd6804", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "expected str, bytes or os.PathLike object, not list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m display(\u001b[43mImage\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mviz_images\u001b[49m\u001b[43m)\u001b[49m)\n", + "File \u001b[0;32m/nix/store/6514avl2wlhjgdhvvw80f3qm8ps8kv5b-python3-3.11.9-env/lib/python3.11/site-packages/IPython/core/display.py:923\u001b[0m, in \u001b[0;36mImage.__init__\u001b[0;34m(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)\u001b[0m\n\u001b[1;32m 921\u001b[0m ext \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_find_ext(filename)\n\u001b[1;32m 922\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m url \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 923\u001b[0m ext \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_find_ext\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 924\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 925\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo image data found. Expecting filename, url, or data.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "File \u001b[0;32m/nix/store/6514avl2wlhjgdhvvw80f3qm8ps8kv5b-python3-3.11.9-env/lib/python3.11/site-packages/IPython/core/display.py:1074\u001b[0m, in \u001b[0;36mImage._find_ext\u001b[0;34m(self, s)\u001b[0m\n\u001b[1;32m 1073\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_find_ext\u001b[39m(\u001b[38;5;28mself\u001b[39m, s):\n\u001b[0;32m-> 1074\u001b[0m base, ext \u001b[38;5;241m=\u001b[39m \u001b[43msplitext\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1076\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m ext:\n\u001b[1;32m 1077\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m base\n", + "File \u001b[0;32m:118\u001b[0m, in \u001b[0;36msplitext\u001b[0;34m(p)\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: expected str, bytes or os.PathLike object, not list" + ] + } + ], + "source": [ + "display(Image(url=viz_images))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a450ad59-1f75-4e1d-b150-fa62e47ba345", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/pkgs/python-modules/distinctipy/default.nix b/pkgs/python-modules/distinctipy/default.nix index 7a8a3f7..1716aa3 100644 --- a/pkgs/python-modules/distinctipy/default.nix +++ b/pkgs/python-modules/distinctipy/default.nix @@ -1,5 +1,4 @@ { fetchPypi -, python3Packages , buildPythonPackage , setuptools , numpy diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix index 8f15d76..3dac4c2 100644 --- a/pkgs/python-modules/open3d/default.nix +++ b/pkgs/python-modules/open3d/default.nix @@ -1,7 +1,6 @@ { stdenv , lib , pkgs -, python3Packages , fetchPypi , fetchurl @@ -10,9 +9,21 @@ , zip , autoPatchelfHook +, python , tensorflow-bin , libusb , cudaPackages_11 +, buildPythonPackage +, ipywidgets +, matplotlib +, numpy +, pandas +, plyfile +, pytorchWithCuda +, pyyaml +, scikitlearn +, scipy +, tqdm , libGL , libglvnd @@ -24,20 +35,6 @@ , runCommand }: let - inherit (python3Packages) - buildPythonPackage - ipywidgets - matplotlib - numpy - pandas - plyfile - pytorchWithCuda - pyyaml - scikitlearn - scipy - tqdm - ; - libllvm-wrapped = let libllvm = llvmPackages_10.libllvm.lib; @@ -141,7 +138,7 @@ let }; }; - pyVersion = lib.versions.majorMinor python3Packages.python.version; + pyVersion = lib.versions.majorMinor python.version; srcInputs = prebuiltSrcs."${pyVersion}-${stdenv.system}" or (throw "open3d-bin for Python version '${pyVersion}' is not supported on '${stdenv.system}'"); src = fetchPypi rec { diff --git a/pkgs/python-modules/parsable/default.nix b/pkgs/python-modules/parsable/default.nix index 2a5ea1b..f9aa16e 100644 --- a/pkgs/python-modules/parsable/default.nix +++ b/pkgs/python-modules/parsable/default.nix @@ -1,8 +1,8 @@ { fetchPypi, - python3Packages + buildPythonPackage }: -python3Packages.buildPythonPackage rec { +buildPythonPackage rec { pname = "parsable"; version = "0.3.1"; format = "setuptools"; diff --git a/pkgs/python-modules/pymetis/default.nix b/pkgs/python-modules/pymetis/default.nix index f6ed7f5..2f663ef 100644 --- a/pkgs/python-modules/pymetis/default.nix +++ b/pkgs/python-modules/pymetis/default.nix @@ -1,7 +1,8 @@ { fetchPypi -, python3Packages +, buildPythonPackage +, pybind11 }: -python3Packages.buildPythonPackage rec { +buildPythonPackage rec { pname = "PyMetis"; version = "2023.1.1"; format = "setuptools"; @@ -14,6 +15,6 @@ python3Packages.buildPythonPackage rec { doCheck = false; nativeBuildInputs = [ - python3Packages.pybind11 + pybind11 ]; } diff --git a/pkgs/python-modules/pyransac3d/default.nix b/pkgs/python-modules/pyransac3d/default.nix index 1891768..89b6b39 100644 --- a/pkgs/python-modules/pyransac3d/default.nix +++ b/pkgs/python-modules/pyransac3d/default.nix @@ -1,6 +1,9 @@ { fetchFromGitHub , python3 , buildPythonPackage +, setuptools +, wheel +, numpy }: # TODO: upstream me buildPythonPackage rec { @@ -16,11 +19,11 @@ buildPythonPackage rec { }; nativeBuildInputs = [ - python3.pkgs.setuptools - python3.pkgs.wheel + setuptools + wheel ]; - propagatedBuildInputs = with python3.pkgs; [ + propagatedBuildInputs = [ numpy ]; From d36d5421fd2c87af565ed24de99ae6752a06bf81 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 9 Jul 2024 21:20:52 +0200 Subject: [PATCH 10/52] chore(flake): expose packages as checks --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 7cc2073..2e9c131 100644 --- a/flake.nix +++ b/flake.nix @@ -133,6 +133,8 @@ inherit packages; + checks = packages; + legacyPackages.python3Packages = (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (super: superPython: loadPackages super.callPackage ./pkgs/python-modules From 4d45705c3052396ba24018d229608bac863c3cda Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 9 Jul 2024 21:45:03 +0200 Subject: [PATCH 11/52] chore(flake): use final/prev This is the new name for self and super --- flake.nix | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index 2e9c131..ecca3d8 100644 --- a/flake.nix +++ b/flake.nix @@ -99,20 +99,20 @@ # For fixing existing packages that live in nixpkgs # TODO: put in separate file - pythonOverrides = super: pythonSuper: { + pythonOverrides = final: prev: { # so we can pull from flake inputs inherit inputs; # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. # Use the pre-built version of tensorflow - tensorflow = pythonSuper.tensorflow-bin; + tensorflow = final.tensorflow-bin; # Use the pre-built version of jaxlib - jaxlib = super.jaxlib-bin; + jaxlib = final.jaxlib-bin; # Use the pre-built version of libjax - libjax = super.libjax-bin; + libjax = final.libjax-bin; }; in { _module.args.pkgs = import inputs.nixpkgs { @@ -136,8 +136,8 @@ checks = packages; legacyPackages.python3Packages = - (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (super: superPython: - loadPackages super.callPackage ./pkgs/python-modules + (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: prev: + loadPackages final.callPackage ./pkgs/python-modules ); devShells.default = pkgs.mkShell { From 90e17a9b7ea024ed6c74a7a9acd55add04ec093c Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 9 Jul 2024 21:45:42 +0200 Subject: [PATCH 12/52] chore(flake): add the devshell python to the checks --- flake.nix | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index ecca3d8..7f0723e 100644 --- a/flake.nix +++ b/flake.nix @@ -114,6 +114,13 @@ # Use the pre-built version of libjax libjax = final.libjax-bin; }; + + devshellPython = (self'.legacyPackages.python3Packages.python.withPackages (p: [ + self'.legacyPackages.python3Packages.bayes3d + self'.legacyPackages.python3Packages.jax + p.jupyter + p.scipy + ])); in { _module.args.pkgs = import inputs.nixpkgs { inherit system; @@ -133,7 +140,9 @@ inherit packages; - checks = packages; + checks = packages // { + inherit devshellPython; + }; legacyPackages.python3Packages = (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: prev: @@ -143,12 +152,7 @@ devShells.default = pkgs.mkShell { packages = [ self'.legacyPackages.python3Packages.python-lsp-server - (self'.legacyPackages.python3Packages.python.withPackages (p: [ - self'.legacyPackages.python3Packages.bayes3d - self'.legacyPackages.python3Packages.jax - p.jupyter - p.scipy - ])) + devshellPython ]; shellHook = '' From ff6060bc590c0ed04ebb6fe780fab9c94720a775 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 20:45:18 +0100 Subject: [PATCH 13/52] fix: Correct invalid packages attributes --- flake.nix | 45 +++------------------------- pkgs/python-modules/sppl/default.nix | 42 ++++++++++++++++---------- 2 files changed, 30 insertions(+), 57 deletions(-) diff --git a/flake.nix b/flake.nix index 7f0723e..747fa0c 100644 --- a/flake.nix +++ b/flake.nix @@ -10,9 +10,6 @@ genjax.url = "github:probcomp/genjax?ref=v0.1.1"; genjax.flake = false; - - poetry2nix.url = "github:nix-community/poetry2nix"; - poetry2nix.inputs.nixpkgs.follows = "nixpkgs"; }; nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; @@ -37,52 +34,18 @@ # apps, and devshells are per system. perSystem = { config, self', inputs', pkgs, system, ... }: let - sppl = pkgs.callPackage ./pkgs/sppl {}; - ociImgBase = pkgs.callPackage ./pkgs/ociBase { inherit nixpkgs; basicTools = self.lib.basicTools; }; - poetry2nix = inputs.poetry2nix.lib.mkPoetry2Nix { inherit pkgs; }; - - scopes = (self.lib.mkScopes { - inherit pkgs internalPackages inputs poetry2nix; - basicTools = self.lib.basicTools; - }); - loom = scopes.callPy3Package ./pkgs/loom { }; - - # TODO: make this cleaner - bayes3d = self'.legacyPackages.python3Packages.bayes3d; - open3d = scopes.callPy3Package ./pkgs/python-modules/open3d { }; - - internalPackages = { - #jaxlib = scopes.callPy3Package ./pkgs/jaxlib { }; - #jax = scopes.callPy3Package ./pkgs/jax { }; - jaxtyping = scopes.callPy3Package ./pkgs/jaxtyping { }; - tinygltf = scopes.callPackage ./pkgs/tinygltf { }; - PoissonRecon = scopes.callPackage ./pkgs/PoissonRecon { }; - goftests = scopes.callPackage ./pkgs/goftests { }; - parsable = scopes.callPackage ./pkgs/parsable { }; - pymetis = scopes.callPackage ./pkgs/pymetis { }; - distributions = scopes.callPackage ./pkgs/distributions { }; - genjax = scopes.callPy3Package ./pkgs/genjax { }; - distinctipy = scopes.callPy3Package ./pkgs/distinctipy { }; - pyransac3d = scopes.callPy3Package ./pkgs/pyransac3d { }; - opencv-python = scopes.callPy3Package ./pkgs/opencv-python { }; - oryx = scopes.callPy3Package ./pkgs/oryx { }; - plum-dispatch = scopes.callPy3Package ./pkgs/plum-dispatch { }; - } // packages; - packages = { - inherit + inherit ociImgBase; + + inherit (self'.legacyPackages.python3Packages) loom sppl - - ociImgBase - bayes3d - open3d ; }; @@ -159,7 +122,7 @@ export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" export EXTRA_CCFLAGS="-I/usr/include" export CUDA_PATH=${pkgs.cudatoolkit_11} - export B3D_ASSET_PATH="${bayes3d.src}/assets" + export B3D_ASSET_PATH="${packages.bayes3d.src}/assets" jupyter notebook ''; diff --git a/pkgs/python-modules/sppl/default.nix b/pkgs/python-modules/sppl/default.nix index edb97bd..d0e2eb0 100644 --- a/pkgs/python-modules/sppl/default.nix +++ b/pkgs/python-modules/sppl/default.nix @@ -1,24 +1,34 @@ -{ pkgs, - system, - ... +{ pkgs +, system +, buildPythonPackage + +, astunparse +, numpy +, scipy +, sympy + +, coverage +, pytest +, pytestCheckHook +, pytest-timeout }: let # FIXME: check if we still need python 3.9. Can it be switched to 3.11? # relies on specific versions of deps that are no longer present in # nixpkgs stable; we must checkout a specific SHA - nixpkgs-sppl = import (pkgs.fetchFromGitHub { - owner = "nixos"; - repo = "nixpkgs"; - rev = "994df04c3c700fe9edb1b69b82ba3c627e5e04ff"; - sha256 = "sha256-60hLkFqLRI+5XEeacXaVPHdl6m/P8rN2L4luLGxklqs="; - }) {inherit system;}; - pypkgs = nixpkgs-sppl.python39Packages; - - sppl = pypkgs.buildPythonPackage rec { # not in nixpkgs + #nixpkgs-sppl = import (pkgs.fetchFromGitHub { + #owner = "nixos"; + #repo = "nixpkgs"; + #rev = "994df04c3c700fe9edb1b69b82ba3c627e5e04ff"; + #sha256 = "sha256-60hLkFqLRI+5XEeacXaVPHdl6m/P8rN2L4luLGxklqs="; + #}) {inherit system;}; + #pypkgs = nixpkgs-sppl.python39Packages; + + sppl = buildPythonPackage rec { pname = "sppl"; version = "2.0.4"; - # Use a version of sppl that has Nixpkgs-compatible versions of some packages + # Use a version of sppl that has Nixpkgs-compatible versions of some packages src = pkgs.fetchFromGitHub { owner = "probsys"; repo = "sppl"; @@ -26,14 +36,14 @@ sha256 = "sha256-hFIR073wDRXyt8EqFkLZNDdGUjPTyWYOfDg5eGTjvz0="; }; - propagatedBuildInputs = with pypkgs; [ + propagatedBuildInputs = [ astunparse numpy scipy sympy ]; - checkInputs = with pypkgs; [ + checkInputs = [ coverage pytest pytestCheckHook @@ -44,7 +54,7 @@ pipInstallFlags = [ "--no-deps" ]; - passthru.runtimePython = nixpkgs-sppl.python39.withPackages (p: [ sppl ]); + #passthru.runtimePython = nixpkgs-sppl.python39.withPackages (p: [ sppl ]); passthru.checkInputs = checkInputs; }; From 144916425c3b54ce0e47cb6da84ffbd12efd18c7 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 21:39:06 +0100 Subject: [PATCH 14/52] chore(flake): Remove unused poetry2nix --- flake.lock | 116 +---------------------------------------------------- 1 file changed, 1 insertion(+), 115 deletions(-) diff --git a/flake.lock b/flake.lock index c327708..2ad1f39 100644 --- a/flake.lock +++ b/flake.lock @@ -20,24 +20,6 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "genjax": { "flake": false, "locked": { @@ -55,27 +37,6 @@ "type": "github" } }, - "nix-github-actions": { - "inputs": { - "nixpkgs": [ - "poetry2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1703863825, - "narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "5163432afc817cf8bd1f031418d1869e4c9d5547", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1720028458, @@ -108,87 +69,12 @@ "type": "github" } }, - "poetry2nix": { - "inputs": { - "flake-utils": "flake-utils", - "nix-github-actions": "nix-github-actions", - "nixpkgs": [ - "nixpkgs" - ], - "systems": "systems_2", - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1718726452, - "narHash": "sha256-w4hJSYvACz0i5XHtxc6XNyHwbxpisN13M2kA2Y7937o=", - "owner": "nix-community", - "repo": "poetry2nix", - "rev": "53e534a08c0cd2a9fa7587ed1c3e7f6aeb804a2c", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "poetry2nix", - "type": "github" - } - }, "root": { "inputs": { "flake-parts": "flake-parts", "genjax": "genjax", "nixpkgs": "nixpkgs", - "nixpkgs-llvm-10": "nixpkgs-llvm-10", - "poetry2nix": "poetry2nix" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "id": "systems", - "type": "indirect" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "poetry2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1718522839, - "narHash": "sha256-ULzoKzEaBOiLRtjeY3YoGFJMwWSKRYOic6VNw2UyTls=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "68eb1dc333ce82d0ab0c0357363ea17c31ea1f81", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" + "nixpkgs-llvm-10": "nixpkgs-llvm-10" } } }, From eee9ba4265cb1d691dfc366c074019901234791e Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Tue, 9 Jul 2024 21:40:38 +0100 Subject: [PATCH 15/52] fix(distributions): Patch source to use GNU Sed on Darwin --- pkgs/python-modules/distributions/default.nix | 23 +++++++++++-------- .../distributions/gnu-sed-on-darwin.patch | 12 ++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 pkgs/python-modules/distributions/gnu-sed-on-darwin.patch diff --git a/pkgs/python-modules/distributions/default.nix b/pkgs/python-modules/distributions/default.nix index 23db5b1..89674d1 100644 --- a/pkgs/python-modules/distributions/default.nix +++ b/pkgs/python-modules/distributions/default.nix @@ -1,12 +1,13 @@ -{ - fetchFromGitHub, - callPackage, - fetchPypi, - eigen, - parsable, - goftests, - protobuf3_20, - python3Packages, +{ lib +, stdenv +, fetchFromGitHub +, callPackage +, fetchPypi +, eigen +, parsable +, goftests +, protobuf3_20 +, python3Packages }: let version = "2.2.1"; @@ -88,7 +89,9 @@ python3Packages.buildPythonPackage { patches = [ ./use-imread-instead-of-scipy.patch - ]; + ] ++ (lib.optionals stdenv.isDarwin [ + ./gnu-sed-on-darwin.patch + ]); env.DISTRIBUTIONS_USE_PROTOBUF = 1; diff --git a/pkgs/python-modules/distributions/gnu-sed-on-darwin.patch b/pkgs/python-modules/distributions/gnu-sed-on-darwin.patch new file mode 100644 index 0000000..8235d84 --- /dev/null +++ b/pkgs/python-modules/distributions/gnu-sed-on-darwin.patch @@ -0,0 +1,12 @@ +--- a/Makefile ++++ b/Makefile +@@ -10,7 +10,7 @@ ifeq ($(uname),Linux) + endif + ifeq ($(uname),Darwin) + ld_library_path=DYLD_LIBRARY_PATH +- sed_no_backup_arg=-i '' ++ sed_no_backup_arg=-i + endif + + cmake_args= + From 47517c5c75d55056a8ec76c704bf0bd877f1a4e1 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 10:12:55 +0200 Subject: [PATCH 16/52] fixup! chore(flake): Remove unused poetry2nix --- lib/mkScopes/default.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix index b7488e3..ddbc2b1 100644 --- a/lib/mkScopes/default.nix +++ b/lib/mkScopes/default.nix @@ -1,4 +1,4 @@ -{ pkgs, basicTools, internalPackages, poetry2nix, inputs }: +{ pkgs, basicTools, internalPackages, inputs }: let callPackage = pkgs.newScope ( pkgs @@ -7,7 +7,6 @@ let callPackage callPy3Package inputs - poetry2nix ; basicTools = basicTools pkgs; } @@ -22,7 +21,6 @@ let callPackage callPy3Package inputs - poetry2nix ; basicTools = basicTools pkgs; } From b49dbdcc05a97af87dbf5f2f568f736f8a486964 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 13:06:49 +0200 Subject: [PATCH 17/52] python3Packages.sppl: fix build --- pkgs/python-modules/sppl/default.nix | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkgs/python-modules/sppl/default.nix b/pkgs/python-modules/sppl/default.nix index d0e2eb0..4a1e941 100644 --- a/pkgs/python-modules/sppl/default.nix +++ b/pkgs/python-modules/sppl/default.nix @@ -36,6 +36,13 @@ sha256 = "sha256-hFIR073wDRXyt8EqFkLZNDdGUjPTyWYOfDg5eGTjvz0="; }; + patches = [ + (pkgs.fetchpatch { + url = "https://patch-diff.githubusercontent.com/raw/probsys/sppl/pull/129.diff"; + hash = "sha256-UkW3131ZeJHdrPJiAIqN4j/YkbRoNzbzJPhDxawBNy4="; + }) + ]; + propagatedBuildInputs = [ astunparse numpy From a093b28a0274f979c0ac41447afce28fa1cacd83 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 13:22:42 +0200 Subject: [PATCH 18/52] chore(flake): disable aarch64-linux It's not a target for us right now --- flake.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 747fa0c..e153c50 100644 --- a/flake.nix +++ b/flake.nix @@ -26,7 +26,12 @@ ./lib inputs.flake-parts.flakeModules.easyOverlay ]; - systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; + systems = [ + "aarch64-darwin" + # "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of From 0560a069f22b12b34e5d62c918cad0c1f26a7a05 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 13:43:42 +0200 Subject: [PATCH 19/52] chore(flake): switch tensorflow and jaxlib versions based on support --- flake.nix | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index e153c50..62b71b5 100644 --- a/flake.nix +++ b/flake.nix @@ -74,13 +74,10 @@ # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. # Use the pre-built version of tensorflow - tensorflow = final.tensorflow-bin; + tensorflow = if final.tensorflow-bin.meta.broken then final.tensorflow-build else final.tensorflow-bin; # Use the pre-built version of jaxlib - jaxlib = final.jaxlib-bin; - - # Use the pre-built version of libjax - libjax = final.libjax-bin; + jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; }; devshellPython = (self'.legacyPackages.python3Packages.python.withPackages (p: [ From 3fae3813972d23aa8dc61a3298aa4a7b9a834893 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 14:35:08 +0200 Subject: [PATCH 20/52] chore(nix): remove last uses of python3Packages --- lib/default.nix | 1 - lib/mkScopes/default.nix | 35 ------------------- pkgs/python-modules/distributions/default.nix | 33 +++++++++++------ pkgs/python-modules/loom/default.nix | 5 +-- 4 files changed, 25 insertions(+), 49 deletions(-) delete mode 100644 lib/mkScopes/default.nix diff --git a/lib/default.nix b/lib/default.nix index 6590ac7..fa02230 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,6 +1,5 @@ _: { flake.lib = { basicTools = import ./devtools; - mkScopes = import ./mkScopes; }; } diff --git a/lib/mkScopes/default.nix b/lib/mkScopes/default.nix deleted file mode 100644 index ddbc2b1..0000000 --- a/lib/mkScopes/default.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ pkgs, basicTools, internalPackages, inputs }: -let - callPackage = pkgs.newScope ( - pkgs - // { - inherit - callPackage - callPy3Package - inputs - ; - basicTools = basicTools pkgs; - } - // internalPackages - ); - - callPy3Package = pkgs.newScope ( - pkgs - // pkgs.python3Packages - // { - inherit - callPackage - callPy3Package - inputs - ; - basicTools = basicTools pkgs; - } - // internalPackages - ); -in -{ - inherit - callPackage - callPy3Package - ; -} diff --git a/pkgs/python-modules/distributions/default.nix b/pkgs/python-modules/distributions/default.nix index 89674d1..12ac345 100644 --- a/pkgs/python-modules/distributions/default.nix +++ b/pkgs/python-modules/distributions/default.nix @@ -3,11 +3,22 @@ , fetchFromGitHub , callPackage , fetchPypi +, protobuf3_20 +, buildPythonPackage + , eigen -, parsable +, enum34 , goftests -, protobuf3_20 -, python3Packages +, numpy +, parsable +, pillow +, protobuf +, pyflakes +, pytest +, cython_0 +, scipy +, simplejson +, nose }: let version = "2.2.1"; @@ -22,7 +33,7 @@ let distributions-shared = callPackage ./distributions-shared.nix { inherit version src; }; # TODO: move into own package - imageio_2_6_1 = python3Packages.buildPythonPackage rec { + imageio_2_6_1 = buildPythonPackage rec { pname = "imageio"; version = "2.6.1"; @@ -33,28 +44,28 @@ let doCheck = false; - nativeBuildInputs = with python3Packages; [ + nativeBuildInputs = [ pytest ]; - propagatedBuildInputs = with python3Packages; [ + propagatedBuildInputs = [ pillow ]; - buildInputs = with python3Packages; [ + buildInputs = [ enum34 numpy ]; }; in -python3Packages.buildPythonPackage { +buildPythonPackage { pname = "distributions"; inherit version src; nativeBuildInputs = [ protobuf3_20 - python3Packages.pyflakes + pyflakes ]; buildInputs = [ @@ -64,7 +75,7 @@ python3Packages.buildPythonPackage { protobuf3_20 ]; - propagatedBuildInputs = with python3Packages; [ + propagatedBuildInputs = [ protobuf3_20 protobuf cython_0 @@ -76,7 +87,7 @@ python3Packages.buildPythonPackage { # TODO: be more precise. Some tests seem to be still in Python 2. doCheck = false; - nativeCheckInputs = with python3Packages; [ + nativeCheckInputs = [ imageio_2_6_1 nose goftests diff --git a/pkgs/python-modules/loom/default.nix b/pkgs/python-modules/loom/default.nix index 09cead6..3b6c8b2 100644 --- a/pkgs/python-modules/loom/default.nix +++ b/pkgs/python-modules/loom/default.nix @@ -33,6 +33,7 @@ , goftests , parsable , pymetis +, contextlib2 }: let @@ -81,7 +82,7 @@ let ''; }; - contextlib2 = python3Packages.contextlib2.overrideAttrs (final: prev: { + contextlib2_ = contextlib2.overrideAttrs (final: prev: { # https://github.com/jazzband/contextlib2/pull/52 # updated to support Python3 src = fetchFromGitHub { @@ -128,7 +129,7 @@ let propagatedBuildInputs = [ loom-cpp - contextlib2 + contextlib2_ cpplint cython_0 distributions From 488b76c37c608be53113e9cff913c505d6fcfdc1 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 15:07:18 +0200 Subject: [PATCH 21/52] fix(distributions): add missing pyflakes for macOS --- pkgs/python-modules/distributions/distributions-shared.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/python-modules/distributions/distributions-shared.nix b/pkgs/python-modules/distributions/distributions-shared.nix index c70ac57..2888966 100644 --- a/pkgs/python-modules/distributions/distributions-shared.nix +++ b/pkgs/python-modules/distributions/distributions-shared.nix @@ -3,6 +3,8 @@ cmake, eigen, protobuf3_20, + pyflakes, + src, version }: stdenv.mkDerivation { @@ -10,7 +12,7 @@ stdenv.mkDerivation { inherit version src; - nativeBuildInputs = [ cmake ]; + nativeBuildInputs = [ cmake pyflakes ]; buildInputs = [eigen protobuf3_20 ]; env.DISTRIBUTIONS_USE_PROTOBUF = 1; From 2c398032d990f744585e2e18a20e6709ee82df45 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 15:12:02 +0200 Subject: [PATCH 22/52] chore: remove unused packages --- .../beartype_0_16_4/default.nix | 12 -- pkgs/disabled-modules/jax_/default.nix | 147 ----------------- pkgs/disabled-modules/jaxlib_/default.nix | 156 ------------------ pkgs/disabled-modules/jaxlib_/default.nix.bak | 86 ---------- pkgs/disabled-modules/jaxtyping_/default.nix | 81 --------- pkgs/disabled-modules/optax_0_1_7/default.nix | 12 -- 6 files changed, 494 deletions(-) delete mode 100644 pkgs/disabled-modules/beartype_0_16_4/default.nix delete mode 100644 pkgs/disabled-modules/jax_/default.nix delete mode 100644 pkgs/disabled-modules/jaxlib_/default.nix delete mode 100644 pkgs/disabled-modules/jaxlib_/default.nix.bak delete mode 100644 pkgs/disabled-modules/jaxtyping_/default.nix delete mode 100644 pkgs/disabled-modules/optax_0_1_7/default.nix diff --git a/pkgs/disabled-modules/beartype_0_16_4/default.nix b/pkgs/disabled-modules/beartype_0_16_4/default.nix deleted file mode 100644 index 3765da2..0000000 --- a/pkgs/disabled-modules/beartype_0_16_4/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ fetchPypi -, beartype -}: -(beartype.overrideAttrs (final: prev: rec { - version = "0.16.4"; - - src = fetchPypi { - inherit (prev) pname; - inherit version; - hash = "sha256-GtqJzy1usw624Vbu0utUkzV3gpN5ENdDgJGOU8Lq4L8="; - }; -})) diff --git a/pkgs/disabled-modules/jax_/default.nix b/pkgs/disabled-modules/jax_/default.nix deleted file mode 100644 index e84b240..0000000 --- a/pkgs/disabled-modules/jax_/default.nix +++ /dev/null @@ -1,147 +0,0 @@ -{ - lib, - blas, - buildPythonPackage, - callPackage, - setuptools, - importlib-metadata, - fetchFromGitHub, - jaxlib-bin, - hypothesis, - lapack, - matplotlib, - ml-dtypes, - numpy, - opt-einsum, - pytestCheckHook, - pytest-xdist, - pythonOlder, - scipy, - stdenv, -}: - -let - usingMKL = false; # blas.implementation == "mkl" || lapack.implementation == "mkl"; -in -buildPythonPackage rec { - pname = "jax"; - version = "0.4.28"; - pyproject = true; - - disabled = pythonOlder "3.9"; - - src = fetchFromGitHub { - owner = "google"; - repo = "jax"; - # google/jax contains tags for jax and jaxlib. Only use jax tags! - rev = "refs/tags/jax-v${version}"; - hash = "sha256-qSHPwi3is6Ts7pz5s4KzQHBMbcjGp+vAOsejW3o36Ek="; - }; - - nativeBuildInputs = [ setuptools ]; - - # The version is automatically set to ".dev" if this variable is not set. - # https://github.com/google/jax/commit/e01f2617b85c5bdffc5ffb60b3d8d8ca9519a1f3 - JAX_RELEASE = "1"; - - # jaxlib is _not_ included in propagatedBuildInputs because there are - # different versions of jaxlib depending on the desired target hardware. The - # JAX project ships separate wheels for CPU, GPU, and TPU. - propagatedBuildInputs = [ - ml-dtypes - numpy - opt-einsum - scipy - ] ++ lib.optional (pythonOlder "3.10") importlib-metadata; - - nativeCheckInputs = [ - hypothesis - jaxlib-bin - matplotlib - pytestCheckHook - pytest-xdist - ]; - - # high parallelism will result in the tests getting stuck - dontUsePytestXdist = true; - - # NOTE: Don't run the tests in the expiremental directory as they require flax - # which creates a circular dependency. See https://discourse.nixos.org/t/how-to-nix-ify-python-packages-with-circular-dependencies/14648/2. - # Not a big deal, this is how the JAX docs suggest running the test suite - # anyhow. - pytestFlagsArray = [ - "--numprocesses=4" - "-W ignore::DeprecationWarning" - "tests/" - ]; - - # Prevents `tests/export_back_compat_test.py::CompatTest::test_*` tests from failing on darwin with - # PermissionError: [Errno 13] Permission denied: '/tmp/back_compat_testdata/test_*.py' - # See https://github.com/google/jax/blob/jaxlib-v0.4.27/jax/_src/internal_test_util/export_back_compat_test_util.py#L240-L241 - # NOTE: this doesn't seem to be an issue on linux - preCheck = lib.optionalString stdenv.isDarwin '' - export TEST_UNDECLARED_OUTPUTS_DIR=$(mktemp -d) - ''; - - # FIXME: disable the checks manually - doCheck = false; - - disabledTests = - [ - # Exceeds tolerance when the machine is busy - "test_custom_linear_solve_aux" - # UserWarning: Explicitly requested dtype - # requested in astype is not available, and will be truncated to - # dtype float32. (With numpy 1.24) - "testKde3" - "testKde5" - "testKde6" - # Invokes python manually in a subprocess, which does not have the correct dependencies - # ImportError: This version of jax requires jaxlib version >= 0.4.19. - "test_no_log_spam" - ] - ++ lib.optionals usingMKL [ - # See - # * https://github.com/google/jax/issues/9705 - # * https://discourse.nixos.org/t/getting-different-results-for-the-same-build-on-two-equally-configured-machines/17921 - # * https://github.com/NixOS/nixpkgs/issues/161960 - "test_custom_linear_solve_cholesky" - "test_custom_root_with_aux" - "testEigvalsGrad_shape" - ] - ++ lib.optionals stdenv.isAarch64 [ - # See https://github.com/google/jax/issues/14793. - "test_for_loop_fixpoint_correctly_identifies_loop_varying_residuals_unrolled_for_loop" - "testQdwhWithRandomMatrix3" - "testScanGrad_jit_scan" - - # See https://github.com/google/jax/issues/17867. - "test_array" - "test_async" - "test_copy0" - "test_device_put" - "test_make_array_from_callback" - "test_make_array_from_single_device_arrays" - - # Fails on some hardware due to some numerical error - # See https://github.com/google/jax/issues/18535 - "testQdwhWithOnRankDeficientInput5" - ]; - - disabledTestPaths = lib.optionals (stdenv.isDarwin && stdenv.isAarch64) [ - # RuntimeWarning: invalid value encountered in cast - "tests/lax_test.py" - ]; - - pythonImportsCheck = [ "jax" ]; - - # updater fails to pick the correct branch - passthru.skipBulkUpdate = true; - - meta = with lib; { - description = "Differentiate, compile, and transform Numpy code"; - homepage = "https://github.com/google/jax"; - license = licenses.asl20; - maintainers = with maintainers; [ samuela ]; - }; -} diff --git a/pkgs/disabled-modules/jaxlib_/default.nix b/pkgs/disabled-modules/jaxlib_/default.nix deleted file mode 100644 index e1d365c..0000000 --- a/pkgs/disabled-modules/jaxlib_/default.nix +++ /dev/null @@ -1,156 +0,0 @@ -{ - lib, - stdenv, - fetchurl, - - # Build-time dependencies: - buildPythonPackage, - curl, - fetchFromGitHub, - jsoncpp, - autoPatchelfHook, - - # Python dependencies: - absl-py, - flatbuffers, - ml-dtypes, - numpy, - scipy, - six, - - # Runtime dependencies: - double-conversion, - giflib, - libjpeg_turbo, - python, - snappy, - -}: - -let - pname = "jaxlib"; - version = "0.4.28"; - - # REMOVEME - effectiveStdenv = stdenv; - - meta = with lib; { - description = "JAX is Autograd and XLA, brought together for high-performance machine learning research"; - homepage = "https://github.com/google/jax"; - license = licenses.asl20; - maintainers = with maintainers; [ ndl ]; - platforms = platforms.unix; - # aarch64-darwin is broken because of https://github.com/bazelbuild/rules_cc/pull/136 - # however even with that fix applied, it doesn't work for everyone: - # https://github.com/NixOS/nixpkgs/pull/184395#issuecomment-1207287129 - # NOTE: We always build with NCCL; if it is unsupported, then our build is broken. - # broken = effectiveStdenv.isDarwin || nccl.meta.unsupported; - }; - - - arch = - # KeyError: ('Linux', 'arm64') - if effectiveStdenv.hostPlatform.isLinux && effectiveStdenv.hostPlatform.linuxArch == "arm64" then - "aarch64" - else - effectiveStdenv.hostPlatform.linuxArch; - - xla = effectiveStdenv.mkDerivation { - pname = "xla-src"; - version = "unstable"; - - src = fetchFromGitHub { - owner = "openxla"; - repo = "xla"; - # Update this according to https://github.com/google/jax/blob/jaxlib-v${version}/third_party/xla/workspace.bzl. - rev = "e8247c3ea1d4d7f31cf27def4c7ac6f2ce64ecd4"; - hash = "sha256-ZhgMIVs3Z4dTrkRWDqaPC/i7yJz2dsYXrZbjzqvPX3E="; - }; - - dontBuild = true; - - # This is necessary for patchShebangs to know the right path to use. - nativeBuildInputs = [ python ]; - - # Main culprits we're targeting are third_party/tsl/third_party/gpus/crosstool/clang/bin/*.tpl - postPatch = '' - patchShebangs . - ''; - - installPhase = '' - cp -r . $out - ''; - }; - - platformTag = - if effectiveStdenv.hostPlatform.isLinux then - "manylinux2014_${arch}" - else if effectiveStdenv.system == "x86_64-darwin" then - "macosx_10_9_${arch}" - else if effectiveStdenv.system == "aarch64-darwin" then - "macosx_11_0_${arch}" - else - throw "Unsupported target platform: ${effectiveStdenv.hostPlatform}"; - - wheelUrls = { - "x86_64-linux" = { - url = "https://files.pythonhosted.org/packages/8e/d7/65b1f5cf05d9159abd5882a51695d4d1b386bc8e26140eff7159854777f2/jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl"; - hash = "sha256-Rc4PPIQM/4I2z/JsN/Jsn/B4aV+T4MFiwyDCgfUEEnU="; - }; - - "aarch64-linux" = { - url = "https://files.pythonhosted.org/packages/f2/87/0c07ec3ba047ca58c940d1c3050cd08c4390bca992cdfeeb2d9d356cd2c6/jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl"; - hash = ""; - }; - - "x86_64-darwin" = { - url = "https://files.pythonhosted.org/packages/e0/b2/896d8d1f35e16e9f88ae6a753012e6d5a6882507ea58e7f0dd5af68ee1e8/jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl"; - hash = ""; - }; - - "aarch64-darwin" = { - url = "https://files.pythonhosted.org/packages/75/f3/1ce8b092ca68dfcfa6a0ee0a8a410f6d877e1628c05799c5d03757682c66/jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl"; - hash = ""; - }; - }; -in -buildPythonPackage { - inherit meta pname version; - format = "wheel"; - - src = fetchurl ( - if builtins.hasAttr stdenv.system wheelUrls - then wheelUrls.${stdenv.system} - else throw "Unsupported system '${stdenv.system}'" - ); - - nativeBuildInputs = [ - autoPatchelfHook - ]; - - buildInputs = [ - stdenv.cc.cc.lib - ]; - - dependencies = [ - absl-py - curl - double-conversion - flatbuffers - giflib - jsoncpp - libjpeg_turbo - ml-dtypes - numpy - scipy - six - snappy - ]; - - pythonImportsCheck = [ - "jaxlib" - # `import jaxlib` loads surprisingly little. These imports are actually bugs that appeared in the 0.4.11 upgrade. - "jaxlib.cpu_feature_guard" - "jaxlib.xla_client" - ]; -} diff --git a/pkgs/disabled-modules/jaxlib_/default.nix.bak b/pkgs/disabled-modules/jaxlib_/default.nix.bak deleted file mode 100644 index 325aadc..0000000 --- a/pkgs/disabled-modules/jaxlib_/default.nix.bak +++ /dev/null @@ -1,86 +0,0 @@ -{ buildPythonPackage -, fetchFromGitHub -, inputs -, fetchPypi -, stdenv -, scipy -, numpy -, ml-dtypes -, pip -, opt-einsum -, absl-py -, breakpointHook -, matplotlib -} -: -let - wheelUrls = { - "x86_64-linux" = { - url = "https://files.pythonhosted.org/packages/8e/d7/65b1f5cf05d9159abd5882a51695d4d1b386bc8e26140eff7159854777f2/jaxlib-0.4.28-cp311-cp311-manylinux2014_x86_64.whl"; - hash = "sha256-Rc4PPIQM/4I2z/JsN/Jsn/B4aV+T4MFiwyDCgfUEEnU="; - }; - - "aarch64-linux" = { - url = "https://files.pythonhosted.org/packages/f2/87/0c07ec3ba047ca58c940d1c3050cd08c4390bca992cdfeeb2d9d356cd2c6/jaxlib-0.4.28-cp311-cp311-manylinux2014_aarch64.whl"; - hash = ""; - }; - - "x86_64-darwin" = { - url = "https://files.pythonhosted.org/packages/e0/b2/896d8d1f35e16e9f88ae6a753012e6d5a6882507ea58e7f0dd5af68ee1e8/jaxlib-0.4.28-cp311-cp311-macosx_10_14_x86_64.whl"; - hash = ""; - }; - - "aarch64-darwin" = { - url = "https://files.pythonhosted.org/packages/75/f3/1ce8b092ca68dfcfa6a0ee0a8a410f6d877e1628c05799c5d03757682c66/jaxlib-0.4.28-cp311-cp311-macosx_11_0_arm64.whl"; - hash = ""; - }; - }; -in -buildPythonPackage rec { - pname = "jaxlib"; - version = "0.4.28"; - - src = fetchFromGitHub { - owner = "google"; - repo = "jax"; - rev = "jaxlib-v${version}"; - hash = "sha256-qSHPwi3is6Ts7pz5s4KzQHBMbcjGp+vAOsejW3o36Ek="; - }; - - #unpackPhase = '' - #mkdir source - #cp -rv $src/jaxlib source - #chmod -R +w source - #ls -alsph source - - #ln -rsv $src/jax/version.py source/jaxlib - #cd source/jaxlib - #''; - - preConfigure = '' - cd jaxlib - mkdir jaxlib - ln -rsv $src/jax/version.py jaxlib/version.py - ln -rsv $src/third_party/xla jaxlib/xla_extension - ls -alsph . - ''; - - nativeBuildInputs = [ - breakpointHook - ]; - - nativeCheckInputs = [ - pip - ]; - - propagatedBuildInputs = [ - scipy - numpy - ml-dtypes - opt-einsum - absl-py - matplotlib - ]; - - preferLocalBuild = true; -} diff --git a/pkgs/disabled-modules/jaxtyping_/default.nix b/pkgs/disabled-modules/jaxtyping_/default.nix deleted file mode 100644 index 298ae13..0000000 --- a/pkgs/disabled-modules/jaxtyping_/default.nix +++ /dev/null @@ -1,81 +0,0 @@ -{ - lib, - buildPythonPackage, - pythonOlder, - fetchFromGitHub, - hatchling, - pythonRelaxDepsHook, - numpy, - typeguard, - typing-extensions, - cloudpickle, - equinox, - ipython, - jax, - jaxlib, - pytestCheckHook, - tensorflow, - torch, -}: - -let - self = buildPythonPackage rec { - pname = "jaxtyping"; - version = "0.2.28"; - pyproject = true; - - disabled = pythonOlder "3.9"; - - src = fetchFromGitHub { - owner = "google"; - repo = "jaxtyping"; - rev = "refs/tags/v${version}"; - hash = "sha256-xDFrgPecUIfCACg/xkMQ8G1+6hNiUUDg9eCZKNpNfzs="; - }; - - nativeBuildInputs = [ - hatchling - pythonRelaxDepsHook - ]; - - propagatedBuildInputs = [ - numpy - typeguard - typing-extensions - ]; - - pythonRelaxDeps = [ "typeguard" ]; - - nativeCheckInputs = [ - cloudpickle - equinox - ipython - jax - jaxlib - pytestCheckHook - tensorflow - torch - ]; - - doCheck = false; - - # Enable tests via passthru to avoid cyclic dependency with equinox. - passthru.tests = { - check = self.overridePythonAttrs { - # We disable tests because they complain about the version of typeguard being too new. - doCheck = false; - catchConflicts = false; - }; - }; - - pythonImportsCheck = [ "jaxtyping" ]; - - meta = with lib; { - description = "Type annotations and runtime checking for JAX arrays and PyTrees"; - homepage = "https://github.com/google/jaxtyping"; - license = licenses.mit; - maintainers = with maintainers; [ GaetanLepage ]; - }; - }; -in -self diff --git a/pkgs/disabled-modules/optax_0_1_7/default.nix b/pkgs/disabled-modules/optax_0_1_7/default.nix deleted file mode 100644 index e84a8ac..0000000 --- a/pkgs/disabled-modules/optax_0_1_7/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ fetchFromGitHub -, optax -}: -(optax.overrideAttrs rec { - version = "0.1.7"; - src = fetchFromGitHub { - owner = "deepmind"; - repo = "optax"; - rev = "refs/tags/v${version}"; - hash = "sha256-zSMJxagPe2rkhrawJ+TWXUzk6V58IY6MhWmEqLVtOoA="; - }; -}) From dec66120d28877cb510ff2ef52092c388b1ee2fe Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 15:20:32 +0200 Subject: [PATCH 23/52] chore: run deadnix on all the files --- envs-flake/flake.nix | 4 ++-- envs-flake/images/gensql.query/default.nix | 1 - flake.nix | 8 ++++---- pkgs/ociBase/default.nix | 2 -- pkgs/python-modules/bayes3d/default.nix | 2 -- pkgs/python-modules/genjax/default.nix | 2 -- pkgs/python-modules/loom/default.nix | 3 +-- pkgs/python-modules/open3d/default.nix | 4 ---- pkgs/python-modules/opencv-python/default.nix | 13 ------------- pkgs/python-modules/pyransac3d/default.nix | 1 - pkgs/python-modules/sppl/default.nix | 1 - 11 files changed, 7 insertions(+), 34 deletions(-) diff --git a/envs-flake/flake.nix b/envs-flake/flake.nix index d2e4e10..441a23e 100644 --- a/envs-flake/flake.nix +++ b/envs-flake/flake.nix @@ -11,7 +11,7 @@ gensqlquery.url = "github:OpenGen/GenSQL.query"; }; - outputs = inputs@{ self, nixpkgs, flake-parts, opengen, gensqlquery, ... }: + outputs = inputs@{ nixpkgs, flake-parts, opengen, gensqlquery, ... }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ # To import a flake module @@ -26,7 +26,7 @@ # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of # the flake that are replicated for each supported system. Typically packages, # apps, and devshells are per system. - perSystem = { config, self', inputs', pkgs, system, ... }: + perSystem = { pkgs, system, ... }: let toolkit = opengen.lib.basicTools pkgs; sppl = opengen.packages.${system}.sppl; diff --git a/envs-flake/images/gensql.query/default.nix b/envs-flake/images/gensql.query/default.nix index 6b23aba..e33ef56 100644 --- a/envs-flake/images/gensql.query/default.nix +++ b/envs-flake/images/gensql.query/default.nix @@ -6,7 +6,6 @@ }: let # in OCI context, whatever our host platform we want to build same arch but linux systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; - crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; base = opengen.packages.${system}.ociImgBase; ociBin = gensqlquery.packages.${systemWithLinux}.bin; diff --git a/flake.nix b/flake.nix index 62b71b5..547caf8 100644 --- a/flake.nix +++ b/flake.nix @@ -37,7 +37,7 @@ # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of # the flake that are replicated for each supported system. Typically packages, # apps, and devshells are per system. - perSystem = { config, self', inputs', pkgs, system, ... }: + perSystem = { config, self', pkgs, system, ... }: let ociImgBase = pkgs.callPackage ./pkgs/ociBase { inherit nixpkgs; @@ -67,7 +67,7 @@ # For fixing existing packages that live in nixpkgs # TODO: put in separate file - pythonOverrides = final: prev: { + pythonOverrides = final: _prev: { # so we can pull from flake inputs inherit inputs; @@ -96,7 +96,7 @@ cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); }; overlays = [ - (final: prev: { + (_final: _prev: { # FIXME: say why this was added. inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; }) @@ -110,7 +110,7 @@ }; legacyPackages.python3Packages = - (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: prev: + (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: _prev: loadPackages final.callPackage ./pkgs/python-modules ); diff --git a/pkgs/ociBase/default.nix b/pkgs/ociBase/default.nix index b38df6b..5e39767 100644 --- a/pkgs/ociBase/default.nix +++ b/pkgs/ociBase/default.nix @@ -8,8 +8,6 @@ crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; - basicToolsFn = import ./../../../lib/basic.nix; - baseImg = pkgs.dockerTools.buildLayeredImage { name = "probcomp/nix-base"; tag = systemWithLinux; diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index 71ade64..d21a612 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -1,6 +1,5 @@ { lib , fetchFromGitHub -, breakpointHook , buildPythonPackage , cudaPackages_11 , which @@ -14,7 +13,6 @@ , opencv-python , setuptools , setuptools-scm -, torch , pytorchWithCuda , graphviz , imageio diff --git a/pkgs/python-modules/genjax/default.nix b/pkgs/python-modules/genjax/default.nix index 37cc7af..6642c50 100644 --- a/pkgs/python-modules/genjax/default.nix +++ b/pkgs/python-modules/genjax/default.nix @@ -1,9 +1,7 @@ { buildPythonPackage -, fetchFromGitHub , inputs , poetry-core , poetry-dynamic-versioning -, fetchPypi , stdenv , beartype , deprecated diff --git a/pkgs/python-modules/loom/default.nix b/pkgs/python-modules/loom/default.nix index 3b6c8b2..20ff068 100644 --- a/pkgs/python-modules/loom/default.nix +++ b/pkgs/python-modules/loom/default.nix @@ -13,7 +13,6 @@ , numpy , pandas , pep8 -, pkgs , protobuf3_20 , pyflakes , python3Packages @@ -82,7 +81,7 @@ let ''; }; - contextlib2_ = contextlib2.overrideAttrs (final: prev: { + contextlib2_ = contextlib2.overrideAttrs (_final: _prev: { # https://github.com/jazzband/contextlib2/pull/52 # updated to support Python3 src = fetchFromGitHub { diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix index 3dac4c2..901fb27 100644 --- a/pkgs/python-modules/open3d/default.nix +++ b/pkgs/python-modules/open3d/default.nix @@ -2,9 +2,6 @@ , lib , pkgs , fetchPypi -, fetchurl - -, tree , unzip , zip @@ -50,7 +47,6 @@ let version = "0.18.0"; pname = "open3d"; - pythonAbi = "cp311"; prebuiltSrcs = { "3.8-x86_64-linux" = { diff --git a/pkgs/python-modules/opencv-python/default.nix b/pkgs/python-modules/opencv-python/default.nix index 0f8af01..c4f9f02 100644 --- a/pkgs/python-modules/opencv-python/default.nix +++ b/pkgs/python-modules/opencv-python/default.nix @@ -2,20 +2,9 @@ , stdenv , buildPythonPackage , fetchurl -, fetchPypi , autoPatchelfHook -, unzip -, zip - -, git -, cmake , numpy -, pip -, scikit-build -, setuptools -, wheel -, gcc , libGL , xorg , libz @@ -26,8 +15,6 @@ let version = "4.10.0.84"; pname = "opencv-python"; - pythonAbi = "cp311"; - pythonPlatform = "manylinux_2_27_x86_64"; wheelUrls = { "x86_64-linux" = { diff --git a/pkgs/python-modules/pyransac3d/default.nix b/pkgs/python-modules/pyransac3d/default.nix index 89b6b39..ee7844a 100644 --- a/pkgs/python-modules/pyransac3d/default.nix +++ b/pkgs/python-modules/pyransac3d/default.nix @@ -1,5 +1,4 @@ { fetchFromGitHub -, python3 , buildPythonPackage , setuptools , wheel diff --git a/pkgs/python-modules/sppl/default.nix b/pkgs/python-modules/sppl/default.nix index 4a1e941..d3fba7e 100644 --- a/pkgs/python-modules/sppl/default.nix +++ b/pkgs/python-modules/sppl/default.nix @@ -1,5 +1,4 @@ { pkgs -, system , buildPythonPackage , astunparse From 6d41acbb30a82d1e5d6555d9e772758bd71359b6 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 15:21:00 +0200 Subject: [PATCH 24/52] chore: run nixfmt on all the files --- envs-flake/flake.nix | 62 +++--- envs-flake/images/gensql.loom/default.nix | 22 +- envs-flake/images/gensql.query/default.nix | 9 +- flake.nix | 193 ++++++++++-------- pkgs/ociBase/default.nix | 37 ++-- pkgs/python-modules/bayes3d/default.nix | 103 +++++----- pkgs/python-modules/distinctipy/default.nix | 18 +- pkgs/python-modules/distributions/default.nix | 55 +++-- .../distributions/distributions-shared.nix | 13 +- pkgs/python-modules/genjax/default.nix | 41 ++-- pkgs/python-modules/goftests/default.nix | 5 +- pkgs/python-modules/loom/default.nix | 143 ++++++------- pkgs/python-modules/loom/test.nix | 37 ++-- pkgs/python-modules/open3d/default.nix | 103 +++++----- pkgs/python-modules/opencv-python/default.nix | 90 ++++---- pkgs/python-modules/oryx/default.nix | 19 +- pkgs/python-modules/parsable/default.nix | 5 +- pkgs/python-modules/plum-dispatch/default.nix | 47 ++--- pkgs/python-modules/pymetis/default.nix | 11 +- pkgs/python-modules/pyransac3d/default.nix | 15 +- pkgs/python-modules/sppl/default.nix | 42 ++-- pkgs/tcmalloc/default.nix | 11 +- 22 files changed, 561 insertions(+), 520 deletions(-) diff --git a/envs-flake/flake.nix b/envs-flake/flake.nix index 441a23e..2acf9ac 100644 --- a/envs-flake/flake.nix +++ b/envs-flake/flake.nix @@ -11,7 +11,14 @@ gensqlquery.url = "github:OpenGen/GenSQL.query"; }; - outputs = inputs@{ nixpkgs, flake-parts, opengen, gensqlquery, ... }: + outputs = + inputs@{ + nixpkgs, + flake-parts, + opengen, + gensqlquery, + ... + }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ # To import a flake module @@ -20,46 +27,37 @@ # 3. Add here: foo.flakeModule inputs.flake-parts.flakeModules.easyOverlay ]; - systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; - + systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ]; # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of # the flake that are replicated for each supported system. Typically packages, # apps, and devshells are per system. - perSystem = { pkgs, system, ... }: - let - toolkit = opengen.lib.basicTools pkgs; - sppl = opengen.packages.${system}.sppl; + perSystem = + { pkgs, system, ... }: + let + toolkit = opengen.lib.basicTools pkgs; + sppl = opengen.packages.${system}.sppl; - ociImgGensqlQuery = pkgs.callPackage ./images/gensql.query { - inherit nixpkgs opengen gensqlquery; - }; + ociImgGensqlQuery = pkgs.callPackage ./images/gensql.query { inherit nixpkgs opengen gensqlquery; }; - ociImgLoom = pkgs.callPackage ./images/gensql.loom { - inherit nixpkgs opengen; - }; + ociImgLoom = pkgs.callPackage ./images/gensql.loom { inherit nixpkgs opengen; }; - packages = { - inherit - ociImgGensqlQuery - ociImgLoom - ; - }; - in { - devShells.default = pkgs.mkShell { - packages = [] ++ toolkit; - }; + packages = { + inherit ociImgGensqlQuery ociImgLoom; + }; + in + { + devShells.default = pkgs.mkShell { packages = [ ] ++ toolkit; }; - devShells.sppl = pkgs.mkShell { - packages - = [sppl] - ++ sppl.checkInputs - ++ toolkit - ; - }; + devShells.sppl = pkgs.mkShell { packages = [ sppl ] ++ sppl.checkInputs ++ toolkit; }; - inherit packages; - }; + inherit packages; + }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields # of the flake that are NOT per system, such as generic `lib` code or other diff --git a/envs-flake/images/gensql.loom/default.nix b/envs-flake/images/gensql.loom/default.nix index 5e61c02..5a4c918 100644 --- a/envs-flake/images/gensql.loom/default.nix +++ b/envs-flake/images/gensql.loom/default.nix @@ -1,8 +1,10 @@ -{ pkgs, +{ + pkgs, nixpkgs, opengen, system, -}: let +}: +let # in OCI context, whatever our host platform we want to build same arch but linux systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; @@ -11,15 +13,21 @@ base = opengen.packages.${system}.ociImgBase; loom = opengen.packages.${systemWithLinux}.loom; -in pkgs.dockerTools.buildLayeredImage { +in +pkgs.dockerTools.buildLayeredImage { name = "probcomp/gensql.loom"; tag = systemWithLinux; fromImage = base; - contents = [ loom python ]; + contents = [ + loom + python + ]; config = { - Cmd = [ "${python}/bin/python" "-m" "loom.tasks" ]; - Env = [ - "LOOM_STORE=/loom/store" + Cmd = [ + "${python}/bin/python" + "-m" + "loom.tasks" ]; + Env = [ "LOOM_STORE=/loom/store" ]; }; } diff --git a/envs-flake/images/gensql.query/default.nix b/envs-flake/images/gensql.query/default.nix index e33ef56..54d7a14 100644 --- a/envs-flake/images/gensql.query/default.nix +++ b/envs-flake/images/gensql.query/default.nix @@ -1,15 +1,18 @@ -{ pkgs, +{ + pkgs, nixpkgs, system, opengen, gensqlquery, -}: let +}: +let # in OCI context, whatever our host platform we want to build same arch but linux systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; base = opengen.packages.${system}.ociImgBase; ociBin = gensqlquery.packages.${systemWithLinux}.bin; -in pkgs.dockerTools.buildImage { +in +pkgs.dockerTools.buildImage { name = "probcomp/gensql.query"; tag = systemWithLinux; fromImage = base; diff --git a/flake.nix b/flake.nix index 547caf8..8997728 100644 --- a/flake.nix +++ b/flake.nix @@ -13,10 +13,18 @@ }; nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; - nixConfig.extra-trusted-public-keys = [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" ]; + nixConfig.extra-trusted-public-keys = [ + "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" + ]; nixConfig.sandbox = "relaxed"; - outputs = inputs@{ self, nixpkgs, flake-parts, ... }: + outputs = + inputs@{ + self, + nixpkgs, + flake-parts, + ... + }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ # To import a flake module @@ -33,103 +41,110 @@ "x86_64-linux" ]; - # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of # the flake that are replicated for each supported system. Typically packages, # apps, and devshells are per system. - perSystem = { config, self', pkgs, system, ... }: - let - ociImgBase = pkgs.callPackage ./pkgs/ociBase { - inherit nixpkgs; - basicTools = self.lib.basicTools; - }; - - packages = { - inherit ociImgBase; - - inherit (self'.legacyPackages.python3Packages) - loom - sppl - bayes3d - ; - }; + perSystem = + { + config, + self', + pkgs, + system, + ... + }: + let + ociImgBase = pkgs.callPackage ./pkgs/ociBase { + inherit nixpkgs; + basicTools = self.lib.basicTools; + }; - loadPackages = callPackage: path: - let - entries = builtins.readDir path; - in - pkgs.lib.mapAttrs (name: type: - if type != "directory" then (throw "${toString path}/${name} is not a directory") - else - callPackage "${toString path}/${name}" { } - ) - entries; - - # For fixing existing packages that live in nixpkgs - # TODO: put in separate file - pythonOverrides = final: _prev: { - # so we can pull from flake inputs - inherit inputs; - - # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. - - # Use the pre-built version of tensorflow - tensorflow = if final.tensorflow-bin.meta.broken then final.tensorflow-build else final.tensorflow-bin; - - # Use the pre-built version of jaxlib - jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; - }; + packages = { + inherit ociImgBase; - devshellPython = (self'.legacyPackages.python3Packages.python.withPackages (p: [ - self'.legacyPackages.python3Packages.bayes3d - self'.legacyPackages.python3Packages.jax - p.jupyter - p.scipy - ])); - in { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; - config = { - # FIXME: commenting these out to see if they fix the duplicate dependency issue when building bayes3d - allowUnfree = true; - # Only enable CUDA on Linux - cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); + inherit (self'.legacyPackages.python3Packages) loom sppl bayes3d; }; - overlays = [ - (_final: _prev: { - # FIXME: say why this was added. - inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; - }) - ]; - }; - inherit packages; + loadPackages = + callPackage: path: + let + entries = builtins.readDir path; + in + pkgs.lib.mapAttrs ( + name: type: + if type != "directory" then + (throw "${toString path}/${name} is not a directory") + else + callPackage "${toString path}/${name}" { } + ) entries; + + # For fixing existing packages that live in nixpkgs + # TODO: put in separate file + pythonOverrides = final: _prev: { + # so we can pull from flake inputs + inherit inputs; + + # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. + + # Use the pre-built version of tensorflow + tensorflow = + if final.tensorflow-bin.meta.broken then final.tensorflow-build else final.tensorflow-bin; + + # Use the pre-built version of jaxlib + jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; + }; - checks = packages // { - inherit devshellPython; - }; + devshellPython = ( + self'.legacyPackages.python3Packages.python.withPackages (p: [ + self'.legacyPackages.python3Packages.bayes3d + self'.legacyPackages.python3Packages.jax + p.jupyter + p.scipy + ]) + ); + in + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + config = { + # FIXME: commenting these out to see if they fix the duplicate dependency issue when building bayes3d + allowUnfree = true; + # Only enable CUDA on Linux + cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); + }; + overlays = [ + (_final: _prev: { + # FIXME: say why this was added. + inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; + }) + ]; + }; - legacyPackages.python3Packages = - (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: _prev: - loadPackages final.callPackage ./pkgs/python-modules - ); - - devShells.default = pkgs.mkShell { - packages = [ - self'.legacyPackages.python3Packages.python-lsp-server - devshellPython - ]; - - shellHook = '' - export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" - export EXTRA_CCFLAGS="-I/usr/include" - export CUDA_PATH=${pkgs.cudatoolkit_11} - export B3D_ASSET_PATH="${packages.bayes3d.src}/assets" - - jupyter notebook - ''; + inherit packages; + + checks = packages // { + inherit devshellPython; + }; + + legacyPackages.python3Packages = + (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope + (final: _prev: loadPackages final.callPackage ./pkgs/python-modules); + + devShells.default = pkgs.mkShell { + packages = [ + self'.legacyPackages.python3Packages.python-lsp-server + devshellPython + ]; + + shellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export EXTRA_CCFLAGS="-I/usr/include" + export CUDA_PATH=${pkgs.cudatoolkit_11} + export B3D_ASSET_PATH="${packages.bayes3d.src}/assets" + + jupyter notebook + ''; + }; }; - }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields # of the flake that are NOT per system, such as generic `lib` code or other diff --git a/pkgs/ociBase/default.nix b/pkgs/ociBase/default.nix index 5e39767..a75dd60 100644 --- a/pkgs/ociBase/default.nix +++ b/pkgs/ociBase/default.nix @@ -1,24 +1,27 @@ -{ pkgs, +{ + pkgs, nixpkgs, system, basicTools, -}: let - # in OCI context, whatever our host platform we want to build same arch but linux - systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; +}: +let + # in OCI context, whatever our host platform we want to build same arch but linux + systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; - crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; + crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; - baseImg = pkgs.dockerTools.buildLayeredImage { - name = "probcomp/nix-base"; - tag = systemWithLinux; - contents = - (basicTools crossPkgsLinux) ++ (with crossPkgsLinux; [ - bashInteractive - busybox # NOTE: might be unnecessary + baseImg = pkgs.dockerTools.buildLayeredImage { + name = "probcomp/nix-base"; + tag = systemWithLinux; + contents = + (basicTools crossPkgsLinux) + ++ (with crossPkgsLinux; [ + bashInteractive + busybox # NOTE: might be unnecessary ]); - config = { - Cmd = [ "${crossPkgsLinux.bashInteractive}/bin/bash" ]; - }; + config = { + Cmd = [ "${crossPkgsLinux.bashInteractive}/bin/bash" ]; }; -in baseImg - + }; +in +baseImg diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index d21a612..14e98a1 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -1,55 +1,56 @@ -{ lib -, fetchFromGitHub -, buildPythonPackage -, cudaPackages_11 -, which -, libglvnd -, libGLU -, open3d -, symlinkJoin -, genjax -, distinctipy -, pyransac3d -, opencv-python -, setuptools -, setuptools-scm -, pytorchWithCuda -, graphviz -, imageio -, matplotlib -, meshcat -, natsort -, opencv4 -, plyfile -, liblzfse -, tensorflow-probability -, timm -, trimesh +{ + lib, + fetchFromGitHub, + buildPythonPackage, + cudaPackages_11, + which, + libglvnd, + libGLU, + open3d, + symlinkJoin, + genjax, + distinctipy, + pyransac3d, + opencv-python, + setuptools, + setuptools-scm, + pytorchWithCuda, + graphviz, + imageio, + matplotlib, + meshcat, + natsort, + opencv4, + plyfile, + liblzfse, + tensorflow-probability, + timm, + trimesh, }: let rev = "8113f643a7ba084e0ca2288cf06f95a23e39d1c7"; - cuda-common-redist = with cudaPackages_11; [ - cuda_cccl # - libcublas # cublas_v2.h - libcurand - libcusolver # cusolverDn.h - libcusparse # cusparse.h - ]; - - cuda-native-redist = symlinkJoin { - name = "cuda-native-redist-${cudaPackages_11.cudaVersion}"; - paths = - with cudaPackages_11; - [ - cuda_cudart # cuda_runtime.h cuda_runtime_api.h - cuda_nvcc - ] - ++ cuda-common-redist; - }; + cuda-common-redist = with cudaPackages_11; [ + cuda_cccl # + libcublas # cublas_v2.h + libcurand + libcusolver # cusolverDn.h + libcusparse # cusparse.h + ]; + + cuda-native-redist = symlinkJoin { + name = "cuda-native-redist-${cudaPackages_11.cudaVersion}"; + paths = + with cudaPackages_11; + [ + cuda_cudart # cuda_runtime.h cuda_runtime_api.h + cuda_nvcc + ] + ++ cuda-common-redist; + }; in buildPythonPackage rec { - pname = "bayes3d"; + pname = "bayes3d"; version = "0.1.0+${builtins.substring 0 8 rev}"; src = fetchFromGitHub { @@ -101,13 +102,11 @@ buildPythonPackage rec { trimesh ]; - preBuild = '' - export CUDA_HOME=${cuda-native-redist} - ''; + preBuild = '' + export CUDA_HOME=${cuda-native-redist} + ''; #preferLocalBuild = true; - pythonImportsCheck = [ - "bayes3d" - ]; + pythonImportsCheck = [ "bayes3d" ]; } diff --git a/pkgs/python-modules/distinctipy/default.nix b/pkgs/python-modules/distinctipy/default.nix index 1716aa3..6729655 100644 --- a/pkgs/python-modules/distinctipy/default.nix +++ b/pkgs/python-modules/distinctipy/default.nix @@ -1,7 +1,8 @@ -{ fetchPypi -, buildPythonPackage -, setuptools -, numpy +{ + fetchPypi, + buildPythonPackage, + setuptools, + numpy, }: buildPythonPackage rec { pname = "distinctipy"; @@ -15,12 +16,7 @@ buildPythonPackage rec { doCheck = false; - nativeBuildInputs = [ - setuptools - ]; + nativeBuildInputs = [ setuptools ]; - propagatedBuildInputs = [ - numpy - ]; + propagatedBuildInputs = [ numpy ]; } - diff --git a/pkgs/python-modules/distributions/default.nix b/pkgs/python-modules/distributions/default.nix index 12ac345..2933630 100644 --- a/pkgs/python-modules/distributions/default.nix +++ b/pkgs/python-modules/distributions/default.nix @@ -1,24 +1,25 @@ -{ lib -, stdenv -, fetchFromGitHub -, callPackage -, fetchPypi -, protobuf3_20 -, buildPythonPackage - -, eigen -, enum34 -, goftests -, numpy -, parsable -, pillow -, protobuf -, pyflakes -, pytest -, cython_0 -, scipy -, simplejson -, nose +{ + lib, + stdenv, + fetchFromGitHub, + callPackage, + fetchPypi, + protobuf3_20, + buildPythonPackage, + + eigen, + enum34, + goftests, + numpy, + parsable, + pillow, + protobuf, + pyflakes, + pytest, + cython_0, + scipy, + simplejson, + nose, }: let version = "2.2.1"; @@ -44,13 +45,9 @@ let doCheck = false; - nativeBuildInputs = [ - pytest - ]; + nativeBuildInputs = [ pytest ]; - propagatedBuildInputs = [ - pillow - ]; + propagatedBuildInputs = [ pillow ]; buildInputs = [ enum34 @@ -100,9 +97,7 @@ buildPythonPackage { patches = [ ./use-imread-instead-of-scipy.patch - ] ++ (lib.optionals stdenv.isDarwin [ - ./gnu-sed-on-darwin.patch - ]); + ] ++ (lib.optionals stdenv.isDarwin [ ./gnu-sed-on-darwin.patch ]); env.DISTRIBUTIONS_USE_PROTOBUF = 1; diff --git a/pkgs/python-modules/distributions/distributions-shared.nix b/pkgs/python-modules/distributions/distributions-shared.nix index 2888966..7e258e1 100644 --- a/pkgs/python-modules/distributions/distributions-shared.nix +++ b/pkgs/python-modules/distributions/distributions-shared.nix @@ -5,15 +5,22 @@ protobuf3_20, pyflakes, - src, version + src, + version, }: stdenv.mkDerivation { pname = "distributions-shared"; inherit version src; - nativeBuildInputs = [ cmake pyflakes ]; - buildInputs = [eigen protobuf3_20 ]; + nativeBuildInputs = [ + cmake + pyflakes + ]; + buildInputs = [ + eigen + protobuf3_20 + ]; env.DISTRIBUTIONS_USE_PROTOBUF = 1; diff --git a/pkgs/python-modules/genjax/default.nix b/pkgs/python-modules/genjax/default.nix index 6642c50..56a65d5 100644 --- a/pkgs/python-modules/genjax/default.nix +++ b/pkgs/python-modules/genjax/default.nix @@ -1,29 +1,30 @@ -{ buildPythonPackage -, inputs -, poetry-core -, poetry-dynamic-versioning -, stdenv -, beartype -, deprecated -, dill -, jax -, jaxtyping -, equinox -, numpy -, optax -, oryx -, plum-dispatch -, pygments -, rich -, tensorflow-probability -, typing-extensions +{ + buildPythonPackage, + inputs, + poetry-core, + poetry-dynamic-versioning, + stdenv, + beartype, + deprecated, + dill, + jax, + jaxtyping, + equinox, + numpy, + optax, + oryx, + plum-dispatch, + pygments, + rich, + tensorflow-probability, + typing-extensions, }: let src = stdenv.mkDerivation { name = "genjax-source"; version = inputs.genjax.shortRev; src = inputs.genjax; - + patches = [ ./set-pyproject-version.patch ./use-beartype-0.18.0-version.patch diff --git a/pkgs/python-modules/goftests/default.nix b/pkgs/python-modules/goftests/default.nix index b640623..c19d138 100644 --- a/pkgs/python-modules/goftests/default.nix +++ b/pkgs/python-modules/goftests/default.nix @@ -15,7 +15,10 @@ buildPythonPackage rec { hash = "sha256-5s0NugSus2TuZIInesCNJNAtxEHnZLQIjn0pxGgwL/o="; }; - buildInputs = [ numpy scipy ]; + buildInputs = [ + numpy + scipy + ]; doCheck = false; diff --git a/pkgs/python-modules/loom/default.nix b/pkgs/python-modules/loom/default.nix index 20ff068..3cd4e63 100644 --- a/pkgs/python-modules/loom/default.nix +++ b/pkgs/python-modules/loom/default.nix @@ -1,38 +1,39 @@ -{ lib -, callPackage -, buildPythonPackage -, fetchFromGitHub -, setuptools -, wheel -, cpplint -, cython_0 -, imageio -, matplotlib -, mock -, nose -, numpy -, pandas -, pep8 -, protobuf3_20 -, pyflakes -, python3Packages -, pytest -, scikit-learn -, scipy -, simplejson -, cmake -, gnumake -, stdenv -, zlib -, eigen -, gperftools -# , dockerTools -# , basicTools -, distributions -, goftests -, parsable -, pymetis -, contextlib2 +{ + lib, + callPackage, + buildPythonPackage, + fetchFromGitHub, + setuptools, + wheel, + cpplint, + cython_0, + imageio, + matplotlib, + mock, + nose, + numpy, + pandas, + pep8, + protobuf3_20, + pyflakes, + python3Packages, + pytest, + scikit-learn, + scipy, + simplejson, + cmake, + gnumake, + stdenv, + zlib, + eigen, + gperftools, + # , dockerTools + # , basicTools + distributions, + goftests, + parsable, + pymetis, + contextlib2, }: let @@ -60,13 +61,14 @@ let gperftools ]; - buildInputs = [ - distributions.distributions-shared - ]; + buildInputs = [ distributions.distributions-shared ]; enableParallelBuilding = true; - outputs = [ "out" "dev" ]; + outputs = [ + "out" + "dev" + ]; prePatch = '' sed -i 's/-Werror//g' CMakeLists.txt # remove -Werror @@ -81,16 +83,18 @@ let ''; }; - contextlib2_ = contextlib2.overrideAttrs (_final: _prev: { - # https://github.com/jazzband/contextlib2/pull/52 - # updated to support Python3 - src = fetchFromGitHub { - owner = "jazzband"; - repo = "contextlib2"; - rev = "b8b7eb8ecd9e012178b5dcec4313edded751a459"; - hash = "sha256-FSx/vKctoFl4NlwzNDa9eDNUXeW1J875/nB6of+5gQk="; - }; - }); + contextlib2_ = contextlib2.overrideAttrs ( + _final: _prev: { + # https://github.com/jazzband/contextlib2/pull/52 + # updated to support Python3 + src = fetchFromGitHub { + owner = "jazzband"; + repo = "contextlib2"; + rev = "b8b7eb8ecd9e012178b5dcec4313edded751a459"; + hash = "sha256-FSx/vKctoFl4NlwzNDa9eDNUXeW1J875/nB6of+5gQk="; + }; + } + ); loom = buildPythonPackage { inherit version; @@ -117,9 +121,7 @@ let gperftools ]; - nativeCheckInputs = [ - pyflakes - ]; + nativeCheckInputs = [ pyflakes ]; # https://github.com/numba/numba/issues/8698#issuecomment-1584888063 env.NUMPY_EXPERIMENTAL_DTYPE_API = 1; @@ -184,11 +186,11 @@ let passthru.more_packages = { inherit - goftests - distributions - pymetis - parsable - ; + goftests + distributions + pymetis + parsable + ; }; # TODO: move it to a different package @@ -202,18 +204,21 @@ let passthru.tests.run = callPackage ./test.nix { inherit src; }; - passthru.test-shell = callPackage ({ - mkShell - , python3 - , loom - , which - }: mkShell { - packages = [ - python3 - loom - which - ]; - }) {}; + passthru.test-shell = callPackage ( + { + mkShell, + python3, + loom, + which, + }: + mkShell { + packages = [ + python3 + loom + which + ]; + } + ) { }; meta = with lib; { description = "A streaming cross-cat inference engine"; diff --git a/pkgs/python-modules/loom/test.nix b/pkgs/python-modules/loom/test.nix index 63a0fe2..7de47d6 100644 --- a/pkgs/python-modules/loom/test.nix +++ b/pkgs/python-modules/loom/test.nix @@ -1,19 +1,22 @@ -{ runCommand -, python3 -, loom -, src -, which +{ + runCommand, + python3, + loom, + src, + which, }: -runCommand "loom-test" { - nativeBuildInputs = [ - python3 - loom - which - ]; -} '' - cp -r ${src} source - mkdir $out +runCommand "loom-test" + { + nativeBuildInputs = [ + python3 + loom + which + ]; + } + '' + cp -r ${src} source + mkdir $out - cd source/examples/taxi - LOOM_STORE=$out python -m loom.tasks ingest quickstart schema.json example.csv -'' + cd source/examples/taxi + LOOM_STORE=$out python -m loom.tasks ingest quickstart schema.json example.csv + '' diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix index 901fb27..e1d2ec3 100644 --- a/pkgs/python-modules/open3d/default.nix +++ b/pkgs/python-modules/open3d/default.nix @@ -1,49 +1,52 @@ -{ stdenv -, lib -, pkgs -, fetchPypi -, unzip -, zip - -, autoPatchelfHook -, python -, tensorflow-bin -, libusb -, cudaPackages_11 -, buildPythonPackage -, ipywidgets -, matplotlib -, numpy -, pandas -, plyfile -, pytorchWithCuda -, pyyaml -, scikitlearn -, scipy -, tqdm - -, libGL -, libglvnd -, libdrm -, expat -, xorg -, llvmPackages_10 -, buildEnv -, runCommand +{ + stdenv, + lib, + pkgs, + fetchPypi, + unzip, + zip, + + autoPatchelfHook, + python, + tensorflow-bin, + libusb, + cudaPackages_11, + buildPythonPackage, + ipywidgets, + matplotlib, + numpy, + pandas, + plyfile, + pytorchWithCuda, + pyyaml, + scikitlearn, + scipy, + tqdm, + + libGL, + libglvnd, + libdrm, + expat, + xorg, + llvmPackages_10, + buildEnv, + runCommand, }: let - libllvm-wrapped = - let - libllvm = llvmPackages_10.libllvm.lib; - name = libllvm.name; - in - buildEnv { - inherit name; - paths = [ - llvmPackages_10.libllvm.lib - (runCommand "${name}.1" {} "mkdir -p $out/lib && ln -sf ${libllvm}/lib/libLLVM-10.so $out/lib/libLLVM-10.so.1") - ]; - }; + libllvm-wrapped = + let + libllvm = llvmPackages_10.libllvm.lib; + name = libllvm.name; + in + buildEnv { + inherit name; + paths = [ + llvmPackages_10.libllvm.lib + (runCommand "${name}.1" { } + "mkdir -p $out/lib && ln -sf ${libllvm}/lib/libLLVM-10.so $out/lib/libLLVM-10.so.1" + ) + ]; + }; version = "0.18.0"; pname = "open3d"; @@ -135,11 +138,13 @@ let }; pyVersion = lib.versions.majorMinor python.version; - srcInputs = prebuiltSrcs."${pyVersion}-${stdenv.system}" or (throw "open3d-bin for Python version '${pyVersion}' is not supported on '${stdenv.system}'"); + srcInputs = + prebuiltSrcs."${pyVersion}-${stdenv.system}" + or (throw "open3d-bin for Python version '${pyVersion}' is not supported on '${stdenv.system}'"); src = fetchPypi rec { inherit pname version; - inherit (srcInputs) platform dist hash; + inherit (srcInputs) platform dist hash; python = dist; abi = dist; @@ -163,9 +168,7 @@ buildPythonPackage { cd ../ ''; - nativeBuildInputs = [ - autoPatchelfHook - ]; + nativeBuildInputs = [ autoPatchelfHook ]; buildInputs = [ # so deps @@ -201,7 +204,7 @@ buildPythonPackage { ]; #preBuild = '' - #mkdir $out + #mkdir $out #''; preFixup = '' diff --git a/pkgs/python-modules/opencv-python/default.nix b/pkgs/python-modules/opencv-python/default.nix index c4f9f02..ec96dce 100644 --- a/pkgs/python-modules/opencv-python/default.nix +++ b/pkgs/python-modules/opencv-python/default.nix @@ -1,16 +1,17 @@ -{ lib -, stdenv -, buildPythonPackage -, fetchurl -, autoPatchelfHook - -, numpy -, libGL -, xorg -, libz -, qt5 - -, breakpointHook +{ + lib, + stdenv, + buildPythonPackage, + fetchurl, + autoPatchelfHook, + + numpy, + libGL, + xorg, + libz, + qt5, + + breakpointHook, }: let version = "4.10.0.84"; @@ -26,7 +27,7 @@ let url = "https://files.pythonhosted.org/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"; hash = ""; }; - + "x86_64-darwin" = { url = "https://files.pythonhosted.org/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl"; hash = ""; @@ -39,9 +40,10 @@ let }; src = fetchurl ( - if builtins.hasAttr stdenv.system wheelUrls - then wheelUrls.${stdenv.system} - else throw "Unsupported system" + if builtins.hasAttr stdenv.system wheelUrls then + wheelUrls.${stdenv.system} + else + throw "Unsupported system" ); in buildPythonPackage rec { @@ -51,46 +53,44 @@ buildPythonPackage rec { inherit src; #patchPhase = '' - #pwd + #pwd - #${unzip}/bin/unzip $src/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp - #cd tmp - #${zip}/bin/zip -0 -r ../dist/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* - #cd ../ + #${unzip}/bin/unzip $src/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp + #cd tmp + #${zip}/bin/zip -0 -r ../dist/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* + #cd ../ #''; - nativeBuildInputs = [ - breakpointHook - ] - ++ lib.optionals stdenv.isLinux [ - autoPatchelfHook - ]; + nativeBuildInputs = [ breakpointHook ] ++ lib.optionals stdenv.isLinux [ autoPatchelfHook ]; dontWrapQtApps = true; - buildInputs = [ - stdenv.cc.cc.lib - libGL - libz - qt5.qtbase - ] ++ lib.optionals stdenv.isLinux [ - xorg.libxcb - xorg.libXext - xorg.libX11 - xorg.libSM - xorg.libICE - ]; - - propagatedBuildInputs = [ - numpy - ]; + buildInputs = + [ + stdenv.cc.cc.lib + libGL + libz + qt5.qtbase + ] + ++ lib.optionals stdenv.isLinux [ + xorg.libxcb + xorg.libXext + xorg.libX11 + xorg.libSM + xorg.libICE + ]; + + propagatedBuildInputs = [ numpy ]; pythonImportsCheck = [ "cv2" ]; meta = with lib; { description = "Wrapper package for OpenCV python bindings"; homepage = "https://pypi.org/project/opencv-python"; - license = with licenses; [ asl20 mit ]; + license = with licenses; [ + asl20 + mit + ]; maintainers = with maintainers; [ ]; }; } diff --git a/pkgs/python-modules/oryx/default.nix b/pkgs/python-modules/oryx/default.nix index 82589ed..0df09fc 100644 --- a/pkgs/python-modules/oryx/default.nix +++ b/pkgs/python-modules/oryx/default.nix @@ -1,10 +1,11 @@ -{ lib -, buildPythonPackage -, fetchPypi -, poetry-core -, jax -, jaxlib-bin -, tensorflow-probability +{ + lib, + buildPythonPackage, + fetchPypi, + poetry-core, + jax, + jaxlib-bin, + tensorflow-probability, }: buildPythonPackage rec { @@ -17,9 +18,7 @@ buildPythonPackage rec { hash = "sha256-8spdSJN9e9jDdc6KxSmh7Z+NoxJjNNLgz91rfxuepI8="; }; - nativeBuildInputs = [ - poetry-core - ]; + nativeBuildInputs = [ poetry-core ]; propagatedBuildInputs = [ jax diff --git a/pkgs/python-modules/parsable/default.nix b/pkgs/python-modules/parsable/default.nix index f9aa16e..8c56ca6 100644 --- a/pkgs/python-modules/parsable/default.nix +++ b/pkgs/python-modules/parsable/default.nix @@ -1,7 +1,4 @@ -{ - fetchPypi, - buildPythonPackage -}: +{ fetchPypi, buildPythonPackage }: buildPythonPackage rec { pname = "parsable"; version = "0.3.1"; diff --git a/pkgs/python-modules/plum-dispatch/default.nix b/pkgs/python-modules/plum-dispatch/default.nix index 01acda7..69886ca 100644 --- a/pkgs/python-modules/plum-dispatch/default.nix +++ b/pkgs/python-modules/plum-dispatch/default.nix @@ -1,26 +1,27 @@ -{ lib -, buildPythonPackage -, fetchPypi -, hatch-vcs -, hatchling -, beartype -, rich -, typing-extensions -, black -, build -, coveralls -, ghp-import -, ipython -, jupyter-book -, mypy -, numpy -, pre-commit -, pyright -, pytest -, pytest-cov -, ruff -, tox -, wheel +{ + lib, + buildPythonPackage, + fetchPypi, + hatch-vcs, + hatchling, + beartype, + rich, + typing-extensions, + black, + build, + coveralls, + ghp-import, + ipython, + jupyter-book, + mypy, + numpy, + pre-commit, + pyright, + pytest, + pytest-cov, + ruff, + tox, + wheel, }: buildPythonPackage rec { diff --git a/pkgs/python-modules/pymetis/default.nix b/pkgs/python-modules/pymetis/default.nix index 2f663ef..8118165 100644 --- a/pkgs/python-modules/pymetis/default.nix +++ b/pkgs/python-modules/pymetis/default.nix @@ -1,6 +1,7 @@ -{ fetchPypi -, buildPythonPackage -, pybind11 +{ + fetchPypi, + buildPythonPackage, + pybind11, }: buildPythonPackage rec { pname = "PyMetis"; @@ -14,7 +15,5 @@ buildPythonPackage rec { doCheck = false; - nativeBuildInputs = [ - pybind11 - ]; + nativeBuildInputs = [ pybind11 ]; } diff --git a/pkgs/python-modules/pyransac3d/default.nix b/pkgs/python-modules/pyransac3d/default.nix index ee7844a..0f2def9 100644 --- a/pkgs/python-modules/pyransac3d/default.nix +++ b/pkgs/python-modules/pyransac3d/default.nix @@ -1,8 +1,9 @@ -{ fetchFromGitHub -, buildPythonPackage -, setuptools -, wheel -, numpy +{ + fetchFromGitHub, + buildPythonPackage, + setuptools, + wheel, + numpy, }: # TODO: upstream me buildPythonPackage rec { @@ -22,9 +23,7 @@ buildPythonPackage rec { wheel ]; - propagatedBuildInputs = [ - numpy - ]; + propagatedBuildInputs = [ numpy ]; pythonImportsCheck = [ "pyransac3d" ]; } diff --git a/pkgs/python-modules/sppl/default.nix b/pkgs/python-modules/sppl/default.nix index d3fba7e..89d926e 100644 --- a/pkgs/python-modules/sppl/default.nix +++ b/pkgs/python-modules/sppl/default.nix @@ -1,26 +1,28 @@ -{ pkgs -, buildPythonPackage +{ + pkgs, + buildPythonPackage, -, astunparse -, numpy -, scipy -, sympy + astunparse, + numpy, + scipy, + sympy, -, coverage -, pytest -, pytestCheckHook -, pytest-timeout -}: let + coverage, + pytest, + pytestCheckHook, + pytest-timeout, +}: +let # FIXME: check if we still need python 3.9. Can it be switched to 3.11? # relies on specific versions of deps that are no longer present in # nixpkgs stable; we must checkout a specific SHA #nixpkgs-sppl = import (pkgs.fetchFromGitHub { - #owner = "nixos"; - #repo = "nixpkgs"; - #rev = "994df04c3c700fe9edb1b69b82ba3c627e5e04ff"; - #sha256 = "sha256-60hLkFqLRI+5XEeacXaVPHdl6m/P8rN2L4luLGxklqs="; - #}) {inherit system;}; + #owner = "nixos"; + #repo = "nixpkgs"; + #rev = "994df04c3c700fe9edb1b69b82ba3c627e5e04ff"; + #sha256 = "sha256-60hLkFqLRI+5XEeacXaVPHdl6m/P8rN2L4luLGxklqs="; + #}) {inherit system;}; #pypkgs = nixpkgs-sppl.python39Packages; sppl = buildPythonPackage rec { @@ -56,7 +58,10 @@ pytest-timeout ]; - pytestFlagsArray = [ "--pyargs" "sppl" ]; + pytestFlagsArray = [ + "--pyargs" + "sppl" + ]; pipInstallFlags = [ "--no-deps" ]; @@ -64,4 +69,5 @@ passthru.checkInputs = checkInputs; }; -in sppl +in +sppl diff --git a/pkgs/tcmalloc/default.nix b/pkgs/tcmalloc/default.nix index 1369baa..3c43c83 100644 --- a/pkgs/tcmalloc/default.nix +++ b/pkgs/tcmalloc/default.nix @@ -1,6 +1,7 @@ -{ fetchFromGitHub -, buildBazelPackage -, bazel +{ + fetchFromGitHub, + buildBazelPackage, + bazel, }: let src = fetchFromGitHub { @@ -28,7 +29,7 @@ buildBazelPackage { buildAttrs = { dontUseCmakeConfigure = true; - + installPhase = '' echo "installing..." ls -alspH bazel-bin/* @@ -36,7 +37,7 @@ buildBazelPackage { mkdir -p $out/lib/tcmalloc install -v -Dm0755 bazel-bin/tcmalloc/*.lo $out/lib/tcmalloc/ ''; - #ls -alspH bazel-bin/tcmalloc + #ls -alspH bazel-bin/tcmalloc }; removeRulesCC = false; From 06ef7e9fb3b99c34eb7c97dc593e036da36943ff Mon Sep 17 00:00:00 2001 From: zimbatm Date: Wed, 10 Jul 2024 15:27:21 +0200 Subject: [PATCH 25/52] feat: re-export the loom docker images as .#loomOCI --- flake.nix | 10 +++++++++- pkgs/python-modules/loom/default.nix | 11 ----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/flake.nix b/flake.nix index 8997728..a793d81 100644 --- a/flake.nix +++ b/flake.nix @@ -58,10 +58,18 @@ basicTools = self.lib.basicTools; }; - packages = { + packages = rec { inherit ociImgBase; inherit (self'.legacyPackages.python3Packages) loom sppl bayes3d; + + loomOCI = pkgs.dockerTools.buildLayeredImage { + name = "probcomp/loom"; + contents = + [ loom pkgs.bashInteractive ] ++ + (self.lib.basicTools pkgs) + ; + }; }; loadPackages = diff --git a/pkgs/python-modules/loom/default.nix b/pkgs/python-modules/loom/default.nix index 3cd4e63..cdcbe17 100644 --- a/pkgs/python-modules/loom/default.nix +++ b/pkgs/python-modules/loom/default.nix @@ -27,8 +27,6 @@ zlib, eigen, gperftools, - # , dockerTools - # , basicTools distributions, goftests, parsable, @@ -193,15 +191,6 @@ let ; }; - # TODO: move it to a different package - # passthru.ociImg = dockerTools.buildLayeredImage { - # name = "probcomp/loom"; - # contents = - # with pkgs; [ loom bashInteractive ] ++ - # basicTools - # ; - # }; - passthru.tests.run = callPackage ./test.nix { inherit src; }; passthru.test-shell = callPackage ( From 17acdadd742611ef779e5bf9dd8d7ca2488c12a3 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 15:24:57 +0100 Subject: [PATCH 26/52] fix(open3d): Enable pythonImportsCheck and add missing deps --- pkgs/python-modules/open3d/default.nix | 112 ++++++++++++++----------- 1 file changed, 62 insertions(+), 50 deletions(-) diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix index e1d2ec3..d55090a 100644 --- a/pkgs/python-modules/open3d/default.nix +++ b/pkgs/python-modules/open3d/default.nix @@ -1,36 +1,39 @@ -{ - stdenv, - lib, - pkgs, - fetchPypi, - unzip, - zip, - - autoPatchelfHook, - python, - tensorflow-bin, - libusb, - cudaPackages_11, - buildPythonPackage, - ipywidgets, - matplotlib, - numpy, - pandas, - plyfile, - pytorchWithCuda, - pyyaml, - scikitlearn, - scipy, - tqdm, - - libGL, - libglvnd, - libdrm, - expat, - xorg, - llvmPackages_10, - buildEnv, - runCommand, +{ stdenv +, lib +, pkgs +, fetchPypi +, unzip +, zip + +, autoPatchelfHook +, python +, tensorflow-bin +, libusb +, cudaPackages_11 +, buildPythonPackage +, ipywidgets +, matplotlib +, numpy +, pandas +, plyfile +, torch +, pytorchWithCuda +, pyyaml +, scikitlearn +, scipy +, tqdm +, plotly +, dash +, addict + +, libGL +, libglvnd +, libdrm +, expat +, xorg +, llvmPackages_10 +, buildEnv +, runCommand }: let libllvm-wrapped = @@ -89,7 +92,7 @@ let hash = ""; }; "3.9-aarch64-darwin" = { - platform = "macosx_13_0_aarch64"; + platform = "macosx_13_0_arm64"; dist = "cp39"; hash = ""; }; @@ -110,7 +113,7 @@ let hash = ""; }; "3.10-aarch64-darwin" = { - platform = "macosx_13_0_aarch64"; + platform = "macosx_13_0_arm64"; dist = "cp310"; hash = ""; }; @@ -131,9 +134,9 @@ let hash = ""; }; "3.11-aarch64-darwin" = { - platform = "macosx_13_0_aarch64"; + platform = "macosx_13_0_arm64"; dist = "cp311"; - hash = ""; + hash = "sha256-IYK4GNzTKQ3S3bACGtBFO/2pkJjJMdWy/GNqNByzynA="; }; }; @@ -156,7 +159,6 @@ buildPythonPackage { inherit pname version; format = "wheel"; - # TODO: make this multiplatform inherit src; patchPhase = '' @@ -168,26 +170,33 @@ buildPythonPackage { cd ../ ''; - nativeBuildInputs = [ autoPatchelfHook ]; + nativeBuildInputs = [ ] + ++ (lib.optionals stdenv.isLinux [ + autoPatchelfHook + ]); buildInputs = [ # so deps stdenv.cc.cc.lib libusb.out - pytorchWithCuda tensorflow-bin - cudaPackages_11.cudatoolkit.lib - #cudaPackages_11.cuda_cudart.lib libGL libglvnd - libdrm expat xorg.libXxf86vm xorg.libXfixes libllvm-wrapped pkgs.mesa pkgs.zstd - ]; + ] + ++ (lib.optionals stdenv.isLinux [ + libdrm + pytorchWithCuda + cudaPackages_11.cudatoolkit.lib + ]) + ++ (lib.optionals stdenv.isDarwin [ + torch + ]); propagatedBuildInputs = [ # py deps @@ -199,20 +208,23 @@ buildPythonPackage { scipy scikitlearn numpy - #addict matplotlib + plotly + dash + addict ]; - #preBuild = '' - #mkdir $out - #''; + pythonImportsCheck = [ + "open3d" + ]; preFixup = '' echo "OUTPUT TO: $out" cd $out/lib/python3.*/site-packages/open3d - rm libGL.so.1 libEGL.so.1 + + ${lib.optionalString stdenv.isLinux "rm libGL.so.1 libEGL.so.1"} + ln -s ${libGL}/lib/libGL.so.1 libGL.so.1 ln -s ${libGL}/lib/libEGL.so.1 libEGL.so.1 - #exit 1 ''; } From 88b3d117d63ce04ce8cf3c6f4a3976fb40127a21 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:15:16 +0100 Subject: [PATCH 27/52] feat: Add .envrc.local template for providing GitHub token --- .envrc | 22 ++++++++++++++++++++++ .envrc.local.template | 8 ++++++++ .gitignore | 1 + 3 files changed, 31 insertions(+) create mode 100644 .envrc create mode 100644 .envrc.local.template diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..2ef9fe2 --- /dev/null +++ b/.envrc @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# ^ added for shellcheck and file-type detection + +# Reload if any of these files change +watch_file .envrc.local + +if [[ $(type -t use_flake) != function ]]; then + echo "ERROR: use_flake function missing." + echo "Please update direnv to v2.30.0 or later." + exit 1 +fi +use_flake + +if [ -f .envrc.local ]; then + source_env .envrc.local + + export NIX_CONFIG="access-tokens = github.com=$NIX_GITHUB_TOKEN" +else + echo " +WARNING: .envrc.local not found, please run \`cp .envrc.local.template .envrc.local\` +and fill in the missing values." +fi diff --git a/.envrc.local.template b/.envrc.local.template new file mode 100644 index 0000000..5e0dc36 --- /dev/null +++ b/.envrc.local.template @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# This value is a GitHub PAT, it can be generated on the following page: +# https://github.com/settings/tokens +# +# Ensure the "repo" scope is enabled +export NIX_GITHUB_TOKEN= + diff --git a/.gitignore b/.gitignore index 7b3ce21..434263a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ result result-* .ipynb_checkpoints/ +.envrc.local From 4a6b9c93c59228033ee53144641c5cb3b5942dd2 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:17:01 +0100 Subject: [PATCH 28/52] fix(tensorflow-probability): Per-system fetchAttrs hashes for Bazel build --- .../python-modules/tensorflow-probability/default.nix | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkgs/python-modules/tensorflow-probability/default.nix b/pkgs/python-modules/tensorflow-probability/default.nix index 01ba07f..1e9742f 100644 --- a/pkgs/python-modules/tensorflow-probability/default.nix +++ b/pkgs/python-modules/tensorflow-probability/default.nix @@ -31,6 +31,11 @@ let version = "0.23.0"; pname = "tensorflow-probability"; + fetchAttrsBySystem = { + "x86_64-linux" = "sha256-TbWcWYidyXuAMgBnO2/k0NKCzc4wThf2uUeC3QxdBJY="; + "aarch64-darwin" = "sha256-xgpC3aU6sGPZFNT6HgHFMKLA+Zxw/Ue1zZ5tjreIjkQ="; + }; + inherit (darwin) cctools; # first build all binaries and generate setup.py using bazel @@ -57,7 +62,11 @@ let LIBTOOL = lib.optionalString stdenv.isDarwin "${cctools}/bin/libtool"; fetchAttrs = { - sha256 = "sha256-TbWcWYidyXuAMgBnO2/k0NKCzc4wThf2uUeC3QxdBJY="; + sha256 = if builtins.hasAttr stdenv.system fetchAttrsBySystem then + fetchAttrsBySystem.${stdenv.system} + else + throw "No hash for system" + ; }; buildAttrs = { From 68bd503bd06979509c50994bc151801be37b8587 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:19:49 +0100 Subject: [PATCH 29/52] fix(bayes3d): Only include CUDA flavoured packages on Linux --- pkgs/python-modules/bayes3d/default.nix | 31 ++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index 14e98a1..e6113c2 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -1,5 +1,6 @@ { lib, + stdenv, fetchFromGitHub, buildPythonPackage, cudaPackages_11, @@ -14,6 +15,7 @@ opencv-python, setuptools, setuptools-scm, + torch, pytorchWithCuda, graphviz, imageio, @@ -66,21 +68,15 @@ buildPythonPackage rec { setuptools setuptools-scm which - #breakpointHook ]; buildInputs = [ - # cudaPackages.cuda_nvcc - # cudaPackages.cuda_cudart - # cudaPackages.libcusparse - # cudaPackages.cuda_cccl - # cudaPackages.libcublas - # cudaPackages.libcusolver - cudaPackages_11.cudatoolkit.lib - pytorchWithCuda libglvnd libGLU - ]; + ] + ++ (lib.optionals stdenv.isLinux [ + cudaPackages_11.cudatoolkit.lib + ]); propagatedBuildInputs = [ distinctipy @@ -98,15 +94,18 @@ buildPythonPackage rec { pyransac3d tensorflow-probability timm - #torch trimesh - ]; + ] + ++ (lib.optionals stdenv.isLinux [ + pytorchWithCuda + ]) + ++ (lib.optionals stdenv.isDarwin [ + torch + ]); - preBuild = '' + preBuild = "" + (lib.optionalString stdenv.isLinux '' export CUDA_HOME=${cuda-native-redist} - ''; - - #preferLocalBuild = true; + ''); pythonImportsCheck = [ "bayes3d" ]; } From 396f7b93bad451ccf3706aba58dde55206a17bb0 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:35:15 +0100 Subject: [PATCH 30/52] chore(devshell): Move devshell to its own flake-parts module --- devshell.nix | 37 +++++++++++++++++++++++++++++++++++++ flake.nix | 26 ++------------------------ 2 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 devshell.nix diff --git a/devshell.nix b/devshell.nix new file mode 100644 index 0000000..9121d9b --- /dev/null +++ b/devshell.nix @@ -0,0 +1,37 @@ +{ ... }: { + perSystem = + { + config, + self', + pkgs, + system, + ... + }: + let + devshellPython = ( + self'.legacyPackages.python3Packages.python.withPackages (p: [ + self'.legacyPackages.python3Packages.bayes3d + self'.legacyPackages.python3Packages.jax + p.jupyter + p.scipy + ]) + ); + in + { + devShells.default = pkgs.mkShell { + packages = [ + self'.legacyPackages.python3Packages.python-lsp-server + devshellPython + ]; + + shellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export EXTRA_CCFLAGS="-I/usr/include" + export CUDA_PATH=${pkgs.cudatoolkit_11} + export B3D_ASSET_PATH="${self'.packages.bayes3d.src}/assets" + + jupyter notebook + ''; + }; + }; + } diff --git a/flake.nix b/flake.nix index a793d81..d50b196 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,7 @@ # 2. Add foo as a parameter to the outputs function # 3. Add here: foo.flakeModule ./lib + ./devshell.nix inputs.flake-parts.flakeModules.easyOverlay ]; systems = [ @@ -101,14 +102,6 @@ jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; }; - devshellPython = ( - self'.legacyPackages.python3Packages.python.withPackages (p: [ - self'.legacyPackages.python3Packages.bayes3d - self'.legacyPackages.python3Packages.jax - p.jupyter - p.scipy - ]) - ); in { _module.args.pkgs = import inputs.nixpkgs { @@ -130,28 +123,13 @@ inherit packages; checks = packages // { - inherit devshellPython; + devshellPython = self'.devShells.default; }; legacyPackages.python3Packages = (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope (final: _prev: loadPackages final.callPackage ./pkgs/python-modules); - devShells.default = pkgs.mkShell { - packages = [ - self'.legacyPackages.python3Packages.python-lsp-server - devshellPython - ]; - - shellHook = '' - export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" - export EXTRA_CCFLAGS="-I/usr/include" - export CUDA_PATH=${pkgs.cudatoolkit_11} - export B3D_ASSET_PATH="${packages.bayes3d.src}/assets" - - jupyter notebook - ''; - }; }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields From 4514a8652007087a742dda2c549ebc53a0b9004e Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 23:06:23 +0100 Subject: [PATCH 31/52] chore(flake): Move legacyPackages.python3Packages definition and overrides their own files --- flake.nix | 23 +---------------------- pkgs/python-overrides.nix | 11 +++++++++++ pkgs/python-packages.nix | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 pkgs/python-overrides.nix create mode 100644 pkgs/python-packages.nix diff --git a/flake.nix b/flake.nix index d50b196..bcfdaa1 100644 --- a/flake.nix +++ b/flake.nix @@ -33,6 +33,7 @@ # 3. Add here: foo.flakeModule ./lib ./devshell.nix + ./pkgs/python-packages.nix inputs.flake-parts.flakeModules.easyOverlay ]; systems = [ @@ -85,23 +86,6 @@ else callPackage "${toString path}/${name}" { } ) entries; - - # For fixing existing packages that live in nixpkgs - # TODO: put in separate file - pythonOverrides = final: _prev: { - # so we can pull from flake inputs - inherit inputs; - - # FIXME: I don't think this is working as expected. Better to change nixpkgs wthfor now. - - # Use the pre-built version of tensorflow - tensorflow = - if final.tensorflow-bin.meta.broken then final.tensorflow-build else final.tensorflow-bin; - - # Use the pre-built version of jaxlib - jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; - }; - in { _module.args.pkgs = import inputs.nixpkgs { @@ -125,11 +109,6 @@ checks = packages // { devshellPython = self'.devShells.default; }; - - legacyPackages.python3Packages = - (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope - (final: _prev: loadPackages final.callPackage ./pkgs/python-modules); - }; # NOTE: this property is consumed by flake-parts.mkFlake to define fields diff --git a/pkgs/python-overrides.nix b/pkgs/python-overrides.nix new file mode 100644 index 0000000..24c5a0b --- /dev/null +++ b/pkgs/python-overrides.nix @@ -0,0 +1,11 @@ +{ inputs }: final: _prev: { + # so we can pull from flake inputs + inherit inputs; + + # Use the pre-built version of tensorflow + tensorflow = + if final.tensorflow-bin.meta.broken then final.tensorflow-build else final.tensorflow-bin; + + # Use the pre-built version of jaxlib + jaxlib = if final.jaxlib-bin.meta.broken then final.jaxlib-build else final.jaxlib-bin; +} diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix new file mode 100644 index 0000000..4e04595 --- /dev/null +++ b/pkgs/python-packages.nix @@ -0,0 +1,25 @@ +inputs: { + perSystem = { pkgs, ... }: + let + loadPackages = + callPackage: path: + let + entries = builtins.readDir path; + in + pkgs.lib.mapAttrs ( + name: type: + if type != "directory" then + (throw "${toString path}/${name} is not a directory") + else + callPackage "${toString path}/${name}" { } + ) entries; + + # For fixing existing packages that live in nixpkgs + pythonOverrides = import ./python-overrides.nix { inherit inputs; }; + in + { + legacyPackages.python3Packages = + (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope + (final: _prev: loadPackages final.callPackage ./python-modules); + }; +} From 2bf47689623a07cd3712fc4c24524b9798bfc78a Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 23:07:19 +0100 Subject: [PATCH 32/52] chore(flake): Remove redundant FIXMEs --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index bcfdaa1..40e0d23 100644 --- a/flake.nix +++ b/flake.nix @@ -91,14 +91,14 @@ _module.args.pkgs = import inputs.nixpkgs { inherit system; config = { - # FIXME: commenting these out to see if they fix the duplicate dependency issue when building bayes3d allowUnfree = true; # Only enable CUDA on Linux cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); }; overlays = [ (_final: _prev: { - # FIXME: say why this was added. + # This was added due to llvmPackages_10 requirement by Open3d + # and it having been removed from Nixpkgs. inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; }) ]; From 4d9ae8516abc9db18806b51e37e6a044349e2769 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:21:27 +0100 Subject: [PATCH 33/52] fix(opencv-python): Add aarch64-darwin hash for Python3.11 wheel --- pkgs/python-modules/opencv-python/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/python-modules/opencv-python/default.nix b/pkgs/python-modules/opencv-python/default.nix index ec96dce..311f3c3 100644 --- a/pkgs/python-modules/opencv-python/default.nix +++ b/pkgs/python-modules/opencv-python/default.nix @@ -35,7 +35,7 @@ let "aarch64-darwin" = { url = "https://files.pythonhosted.org/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl"; - hash = ""; + hash = "sha256-/Bgvj0zaUbRfAcZOTL7fwvAK/3md6+vDBdjQIQxD8lE="; }; }; From 0bf3ba310d1e1ca21f6a63b0841287b729cfa768 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Wed, 10 Jul 2024 21:22:14 +0100 Subject: [PATCH 34/52] fix(opencv-python): Remove breakpointHook and commented lines --- pkgs/python-modules/opencv-python/default.nix | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/pkgs/python-modules/opencv-python/default.nix b/pkgs/python-modules/opencv-python/default.nix index 311f3c3..8ea216e 100644 --- a/pkgs/python-modules/opencv-python/default.nix +++ b/pkgs/python-modules/opencv-python/default.nix @@ -10,8 +10,6 @@ xorg, libz, qt5, - - breakpointHook, }: let version = "4.10.0.84"; @@ -46,22 +44,13 @@ let throw "Unsupported system" ); in -buildPythonPackage rec { +buildPythonPackage { inherit pname version; format = "wheel"; inherit src; - #patchPhase = '' - #pwd - - #${unzip}/bin/unzip $src/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl -d tmp - #cd tmp - #${zip}/bin/zip -0 -r ../dist/${pname}-${version}-${pythonAbi}-${pythonAbi}-${pythonPlatform}.whl ./* - #cd ../ - #''; - - nativeBuildInputs = [ breakpointHook ] ++ lib.optionals stdenv.isLinux [ autoPatchelfHook ]; + nativeBuildInputs = lib.optionals stdenv.isLinux [ autoPatchelfHook ]; dontWrapQtApps = true; From af6529380bb52790c8ac7ffcd48d2e7b0c5e2906 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Thu, 11 Jul 2024 01:21:11 +0100 Subject: [PATCH 35/52] fix(flake): Python packages can access flake inputs --- pkgs/python-packages.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix index 4e04595..1f9d157 100644 --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -1,4 +1,4 @@ -inputs: { +{ inputs, ... }: { perSystem = { pkgs, ... }: let loadPackages = From e59fe69322c2f9bc939320bde87bc1aaa4c875c3 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Thu, 11 Jul 2024 01:22:20 +0100 Subject: [PATCH 36/52] chore(flake): Cleanup moved loadPackages function --- flake.nix | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/flake.nix b/flake.nix index 40e0d23..f1324d8 100644 --- a/flake.nix +++ b/flake.nix @@ -73,19 +73,6 @@ ; }; }; - - loadPackages = - callPackage: path: - let - entries = builtins.readDir path; - in - pkgs.lib.mapAttrs ( - name: type: - if type != "directory" then - (throw "${toString path}/${name} is not a directory") - else - callPackage "${toString path}/${name}" { } - ) entries; in { _module.args.pkgs = import inputs.nixpkgs { From 7a850bd273d21b3f4f387a1ed4d60fa8a4204268 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Thu, 11 Jul 2024 19:42:14 +0200 Subject: [PATCH 37/52] python3Packages.dm-tree: use wheel --- pkgs/python-modules/dm-tree/default.nix | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 pkgs/python-modules/dm-tree/default.nix diff --git a/pkgs/python-modules/dm-tree/default.nix b/pkgs/python-modules/dm-tree/default.nix new file mode 100644 index 0000000..3cbe9ae --- /dev/null +++ b/pkgs/python-modules/dm-tree/default.nix @@ -0,0 +1,82 @@ +{ + abseil-cpp, + absl-py, + attrs, + autoPatchelfHook, + buildPythonPackage, + cmake, + fetchFromGitHub, + fetchurl, + lib, + numpy, + pybind11, + six, + stdenv, + wrapt, +}: +let + srcs = { + "aarch64-linux" = fetchurl { + url = "https://files.pythonhosted.org/packages/fe/89/386332bbd7567c4ccc13aa2e58f733237503fc75fb389955d3b06b9fb967/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"; + hash = "sha256-FgfOSapC8BDR5eYW2SzomdZoNdTYvqSWeVgkNShVFd4="; + }; + + "x86_64-linux" = fetchurl { + url = "https://files.pythonhosted.org/packages/4a/27/c5e3580a952a07e5a1428ae952874796870dc8db789f3d774e886160a9f4/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"; + hash = "sha256-g7d2TeDYVTOKvvxuPun+QNMBZoMQqjuuo/d4/wUfQ5M="; + }; + + "aarch64-darwin" = fetchurl { + url = "https://files.pythonhosted.org/packages/e2/64/901b324804793743f0fdc9e47db893bf0ded9e074850fab2440af330fe83/dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl"; + hash = "sha256-rRbOupClbsR89FshhW0UlirDFHh5de94bvtebpynXsc="; + }; + + "x86_64-darwin" = fetchurl { + url = "https://files.pythonhosted.org/packages/e2/64/901b324804793743f0fdc9e47db893bf0ded9e074850fab2440af330fe83/dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl"; + hash = "sha256-rRbOupClbsR89FshhW0UlirDFHh5de94bvtebpynXsc="; + }; + }; +in + +buildPythonPackage rec { + pname = "dm-tree"; + version = "0.1.8"; + format = "wheel"; + + src = srcs.${stdenv.system} or (throw "system ${stdenv.system} not supported"); + + nativeBuildInputs = [ + autoPatchelfHook + # pybind11 + ]; + + buildInputs = [ + # abseil-cpp + # pybind11 + stdenv.cc.cc + ]; + + propagatedBuildInputs = [ six ]; + + # nativeCheckInputs = [ + # absl-py + # attrs + # numpy + # wrapt + # ]; + + pythonImportsCheck = [ "tree" ]; + + passthru.srcs = srcs; + + meta = with lib; { + broken = stdenv.isDarwin; + description = "Tree is a library for working with nested data structures"; + homepage = "https://github.com/deepmind/tree"; + license = licenses.asl20; + maintainers = with maintainers; [ + samuela + ndl + ]; + }; +} From 4fc0f17b391ec1d8b6479a204c91d95752b003d0 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 12 Jul 2024 11:23:19 +0100 Subject: [PATCH 38/52] fix(dm-tree): Use fetchPypi and enable Darwin platforms --- pkgs/python-modules/dm-tree/default.nix | 110 +++++++++++------------- 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/pkgs/python-modules/dm-tree/default.nix b/pkgs/python-modules/dm-tree/default.nix index 3cbe9ae..f818648 100644 --- a/pkgs/python-modules/dm-tree/default.nix +++ b/pkgs/python-modules/dm-tree/default.nix @@ -1,82 +1,76 @@ -{ - abseil-cpp, - absl-py, - attrs, - autoPatchelfHook, - buildPythonPackage, - cmake, - fetchFromGitHub, - fetchurl, - lib, - numpy, - pybind11, - six, - stdenv, - wrapt, + +{ autoPatchelfHook +, buildPythonPackage +, fetchPypi +, python +, isPy311 +, lib +, stdenv }: let - srcs = { - "aarch64-linux" = fetchurl { - url = "https://files.pythonhosted.org/packages/fe/89/386332bbd7567c4ccc13aa2e58f733237503fc75fb389955d3b06b9fb967/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"; - hash = "sha256-FgfOSapC8BDR5eYW2SzomdZoNdTYvqSWeVgkNShVFd4="; - }; - - "x86_64-linux" = fetchurl { - url = "https://files.pythonhosted.org/packages/4a/27/c5e3580a952a07e5a1428ae952874796870dc8db789f3d774e886160a9f4/dm_tree-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"; + prebuiltWheels = { + "3.11-x86_64-linux" = { + platform = "manylinux_2_17_x86_64.manylinux2014_x86_64"; + dist = "cp311"; hash = "sha256-g7d2TeDYVTOKvvxuPun+QNMBZoMQqjuuo/d4/wUfQ5M="; }; - - "aarch64-darwin" = fetchurl { - url = "https://files.pythonhosted.org/packages/e2/64/901b324804793743f0fdc9e47db893bf0ded9e074850fab2440af330fe83/dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl"; - hash = "sha256-rRbOupClbsR89FshhW0UlirDFHh5de94bvtebpynXsc="; + "3.11-aarch64-linux" = { + platform = "manylinux_2_17_aarch64.manylinux2014_aarch64"; + dist = "cp311"; + hash = "sha256-FgfOSapC8BDR5eYW2SzomdZoNdTYvqSWeVgkNShVFd4="; }; - - "x86_64-darwin" = fetchurl { - url = "https://files.pythonhosted.org/packages/e2/64/901b324804793743f0fdc9e47db893bf0ded9e074850fab2440af330fe83/dm_tree-0.1.8-cp311-cp311-macosx_10_9_universal2.whl"; - hash = "sha256-rRbOupClbsR89FshhW0UlirDFHh5de94bvtebpynXsc="; + "3.11-x86_64-darwin" = { + platform = "macosx_10_9_x86_64"; + dist = "cp311"; + hash = "sha256-gDv8U7Rln0R6xpTb0EI1+Upz73wf0eDffISsQeC8ljs="; + }; + "3.11-aarch64-darwin" = { + platform = "macosx_11_0_arm64"; + dist = "cp311"; + hash = "sha256-N4zIrZPF/jWQ9AWjCZgHIfAhx5DKG9+bFbsdWdrsV/U="; }; }; + + pyVersion = lib.versions.majorMinor python.version; + srcInputs = + prebuiltWheels."${pyVersion}-${stdenv.system}" + or (throw "dm-tree for Python version '${pyVersion}' is not supported on '${stdenv.system}'"); in - buildPythonPackage rec { pname = "dm-tree"; version = "0.1.8"; format = "wheel"; - src = srcs.${stdenv.system} or (throw "system ${stdenv.system} not supported"); + disabled = !isPy311; - nativeBuildInputs = [ - autoPatchelfHook - # pybind11 - ]; + src = fetchPypi { + inherit version; + inherit (srcInputs) platform dist hash; - buildInputs = [ - # abseil-cpp - # pybind11 - stdenv.cc.cc - ]; + pname = (builtins.replaceStrings [ "-" ] [ "_" ] pname); - propagatedBuildInputs = [ six ]; + python = srcInputs.dist; + abi = srcInputs.dist; - # nativeCheckInputs = [ - # absl-py - # attrs - # numpy - # wrapt - # ]; + format = "wheel"; + }; - pythonImportsCheck = [ "tree" ]; + nativeBuildInputs = (lib.optionals stdenv.isLinux [ + autoPatchelfHook + ]); + + # Dynamic link dependencies + buildInputs = [ stdenv.cc.cc ]; - passthru.srcs = srcs; + pythonImportsCheck = [ "tree" ]; meta = with lib; { - broken = stdenv.isDarwin; - description = "Tree is a library for working with nested data structures"; - homepage = "https://github.com/deepmind/tree"; - license = licenses.asl20; - maintainers = with maintainers; [ - samuela - ndl + description = "Tree is a library for working with nested data structures."; + homepage = "https://github.com/deepmind/tree"; + license = licenses.asl20; + platforms = [ + "aarch64-linux" "x86_64-linux" + "aarch64-darwin" "x86_64-darwin" ]; }; } From 849685444d8f7174f271e6e3cb7826062aff9f7d Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 12 Jul 2024 10:39:41 +0100 Subject: [PATCH 39/52] chore(.envrc): Disable entering devshell upon loading direnv environment --- .envrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.envrc b/.envrc index 2ef9fe2..934719a 100644 --- a/.envrc +++ b/.envrc @@ -9,7 +9,6 @@ if [[ $(type -t use_flake) != function ]]; then echo "Please update direnv to v2.30.0 or later." exit 1 fi -use_flake if [ -f .envrc.local ]; then source_env .envrc.local From 7343a8bbed11a83b5c6314370f231217dd48486c Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 12 Jul 2024 10:45:47 +0100 Subject: [PATCH 40/52] fix(devshell): Execute CUDA-specific shellhook code only if CUDA is supported --- devshell.nix | 65 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/devshell.nix b/devshell.nix index 9121d9b..1ca48b2 100644 --- a/devshell.nix +++ b/devshell.nix @@ -1,37 +1,36 @@ { ... }: { - perSystem = - { - config, - self', - pkgs, - system, - ... - }: - let - devshellPython = ( - self'.legacyPackages.python3Packages.python.withPackages (p: [ - self'.legacyPackages.python3Packages.bayes3d - self'.legacyPackages.python3Packages.jax - p.jupyter - p.scipy - ]) - ); - in - { - devShells.default = pkgs.mkShell { - packages = [ - self'.legacyPackages.python3Packages.python-lsp-server - devshellPython - ]; + perSystem = { self', pkgs, ... }: + let + inherit (pkgs) lib config; - shellHook = '' - export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" - export EXTRA_CCFLAGS="-I/usr/include" - export CUDA_PATH=${pkgs.cudatoolkit_11} - export B3D_ASSET_PATH="${self'.packages.bayes3d.src}/assets" + devshellPython = ( + self'.legacyPackages.python3Packages.python.withPackages (p: [ + self'.legacyPackages.python3Packages.bayes3d + self'.legacyPackages.python3Packages.jax + p.jupyter + p.scipy + ]) + ); - jupyter notebook - ''; - }; + cudaShellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export CUDA_PATH=${pkgs.cudatoolkit_11} + ''; + in + { + devShells.default = pkgs.mkShell { + packages = [ + self'.legacyPackages.python3Packages.python-lsp-server + devshellPython + ]; + + shellHook = '' + ${lib.optionalString config.cudaSupport cudaShellHook} + export EXTRA_CCFLAGS="-I/usr/include" + export B3D_ASSET_PATH="${self'.packages.bayes3d.src}/assets" + + jupyter notebook + ''; }; - } + }; +} From 8ccf6140c2c9d76074af9a68bcc00a21ffa5390a Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 12 Jul 2024 10:52:25 +0100 Subject: [PATCH 41/52] fix(bayes3d): Toggle CUDA inclusion based on nixpkgs.cudaSupport setting --- pkgs/python-modules/bayes3d/default.nix | 22 +++++----- .../bayes3d/optional-cuda.patch | 42 +++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 pkgs/python-modules/bayes3d/optional-cuda.patch diff --git a/pkgs/python-modules/bayes3d/default.nix b/pkgs/python-modules/bayes3d/default.nix index e6113c2..47b5b5c 100644 --- a/pkgs/python-modules/bayes3d/default.nix +++ b/pkgs/python-modules/bayes3d/default.nix @@ -1,8 +1,10 @@ { lib, stdenv, + config, fetchFromGitHub, buildPythonPackage, + cudaSupport ? config.cudaSupport, cudaPackages_11, which, libglvnd, @@ -16,7 +18,6 @@ setuptools, setuptools-scm, torch, - pytorchWithCuda, graphviz, imageio, matplotlib, @@ -62,6 +63,10 @@ buildPythonPackage rec { hash = "sha256-6AtxR8ZsByliDTQE/hEJs5+LKwdfS/sRGYXf+mgFHxw="; }; + patches = [ + ./optional-cuda.patch + ]; + pyproject = true; nativeBuildInputs = [ @@ -74,7 +79,7 @@ buildPythonPackage rec { libglvnd libGLU ] - ++ (lib.optionals stdenv.isLinux [ + ++ (lib.optionals cudaSupport [ cudaPackages_11.cudatoolkit.lib ]); @@ -93,19 +98,16 @@ buildPythonPackage rec { plyfile pyransac3d tensorflow-probability - timm trimesh - ] - ++ (lib.optionals stdenv.isLinux [ - pytorchWithCuda - ]) - ++ (lib.optionals stdenv.isDarwin [ torch - ]); + timm + ]; - preBuild = "" + (lib.optionalString stdenv.isLinux '' + preBuild = "" + (lib.optionalString cudaSupport '' export CUDA_HOME=${cuda-native-redist} ''); + env.WITH_CUDA = if cudaSupport then "1" else "0"; + pythonImportsCheck = [ "bayes3d" ]; } diff --git a/pkgs/python-modules/bayes3d/optional-cuda.patch b/pkgs/python-modules/bayes3d/optional-cuda.patch new file mode 100644 index 0000000..42ff460 --- /dev/null +++ b/pkgs/python-modules/bayes3d/optional-cuda.patch @@ -0,0 +1,42 @@ +diff --git a/setup.py b/setup.py +index 44c67e9..6a1d6df 100644 +--- a/setup.py ++++ b/setup.py +@@ -56,8 +56,11 @@ source_files = [ + ] + source_files = [os.path.join(CPP_SRC_DIR, fn) for fn in source_files] + +-# Some containers set this to contain old architectures that won't compile. We only need the one installed in the machine. +-os.environ["TORCH_CUDA_ARCH_LIST"] = "" ++cuda_enabled = bool(int(os.getenv("WITH_CUDA", "1"))) ++ ++if cuda_enabled: ++ # Some containers set this to contain old architectures that won't compile. We only need the one installed in the machine. ++ os.environ["TORCH_CUDA_ARCH_LIST"] = "" + + # On Linux, show a warning if GLEW is being forcibly loaded when compiling the GL plugin. + if (os.name == "posix") and ("libGLEW" in os.environ.get("LD_PRELOAD", "")): +@@ -84,14 +87,19 @@ if os.name == "nt": + pass + + +-setuptools.setup( +- ext_modules=[ ++ext_modules = [] ++ ++if cuda_enabled: ++ ext_modules.append( + cpp_extension.CUDAExtension( + name="bayes3d.rendering.nvdiffrast.nvdiffrast_plugin_gl", + sources=source_files, + extra_compile_args={"cxx": opts, "nvcc": opts + ["-lineinfo"]}, + extra_link_args=ldflags, +- ), +- ], ++ ) ++ ) ++ ++setuptools.setup( ++ ext_modules=ext_modules, + cmdclass={"build_ext": cpp_extension.BuildExtension}, + ) From a48f9bfdb69ead94cb34c1b8362ef58f71226066 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Fri, 12 Jul 2024 11:05:45 +0100 Subject: [PATCH 42/52] fix(open3d): Toggle CUDA inclusion based on nixpkgs.cudaSupport setting --- pkgs/python-modules/open3d/default.nix | 79 +++----------------------- 1 file changed, 8 insertions(+), 71 deletions(-) diff --git a/pkgs/python-modules/open3d/default.nix b/pkgs/python-modules/open3d/default.nix index d55090a..8e979b6 100644 --- a/pkgs/python-modules/open3d/default.nix +++ b/pkgs/python-modules/open3d/default.nix @@ -1,9 +1,11 @@ { stdenv , lib +, config , pkgs , fetchPypi , unzip , zip +, cudaSupport ? config.cudaSupport , autoPatchelfHook , python @@ -17,7 +19,6 @@ , pandas , plyfile , torch -, pytorchWithCuda , pyyaml , scikitlearn , scipy @@ -55,69 +56,6 @@ let pname = "open3d"; prebuiltSrcs = { - "3.8-x86_64-linux" = { - platform = "manylinux_2_27_x86_64"; - dist = "cp38"; - hash = ""; - }; - "3.8-aarch64-linux" = { - platform = "manylinux_2_27_aarch64"; - dist = "cp38"; - hash = ""; - }; - "3.8-x86_64-darwin" = { - platform = "macosx_11_0_x86_64"; - dist = "cp38"; - hash = ""; - }; - "3.8-aarch64-darwin" = { - platform = "macosx_13_0_aarch64"; - dist = "cp38"; - hash = ""; - }; - - "3.9-x86_64-linux" = { - platform = "manylinux_2_27_x86_64"; - dist = "cp39"; - hash = ""; - }; - "3.9-aarch64-linux" = { - platform = "manylinux_2_27_aarch64"; - dist = "cp39"; - hash = ""; - }; - "3.9-x86_64-darwin" = { - platform = "macosx_11_0_x86_64"; - dist = "cp39"; - hash = ""; - }; - "3.9-aarch64-darwin" = { - platform = "macosx_13_0_arm64"; - dist = "cp39"; - hash = ""; - }; - - "3.10-x86_64-linux" = { - platform = "manylinux_2_27_x86_64"; - dist = "cp310"; - hash = ""; - }; - "3.10-aarch64-linux" = { - platform = "manylinux_2_27_aarch64"; - dist = "cp310"; - hash = ""; - }; - "3.10-x86_64-darwin" = { - platform = "macosx_11_0_x86_64"; - dist = "cp310"; - hash = ""; - }; - "3.10-aarch64-darwin" = { - platform = "macosx_13_0_arm64"; - dist = "cp310"; - hash = ""; - }; - "3.11-x86_64-linux" = { platform = "manylinux_2_27_x86_64"; dist = "cp311"; @@ -126,12 +64,12 @@ let "3.11-aarch64-linux" = { platform = "manylinux_2_27_aarch64"; dist = "cp311"; - hash = ""; + hash = "sha256-iC8eUDmjwcXsBRg+tlBTf9dDEji3zLK3QspUefAvcFs="; }; "3.11-x86_64-darwin" = { - platform = "macosx_11_0_x86_64"; + platform = "macosx_10_15_universal2"; dist = "cp311"; - hash = ""; + hash = "sha256-s1pouf7z6WMmbbO7Ffv+8g4FeHvGEZL2FyX95SFfNWA="; }; "3.11-aarch64-darwin" = { platform = "macosx_13_0_arm64"; @@ -188,14 +126,13 @@ buildPythonPackage { libllvm-wrapped pkgs.mesa pkgs.zstd + torch ] ++ (lib.optionals stdenv.isLinux [ libdrm - pytorchWithCuda - cudaPackages_11.cudatoolkit.lib ]) - ++ (lib.optionals stdenv.isDarwin [ - torch + ++ (lib.optionals cudaSupport [ + cudaPackages_11.cudatoolkit.lib ]); propagatedBuildInputs = [ From 9d507f3d29566bb64a51a685c3212730fe195203 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 10:56:02 +0200 Subject: [PATCH 43/52] chore(direnv): restore nix shell --- .envrc | 2 ++ devshell.nix | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.envrc b/.envrc index 934719a..1f960fa 100644 --- a/.envrc +++ b/.envrc @@ -19,3 +19,5 @@ else WARNING: .envrc.local not found, please run \`cp .envrc.local.template .envrc.local\` and fill in the missing values." fi + +use_flake diff --git a/devshell.nix b/devshell.nix index 1ca48b2..030e305 100644 --- a/devshell.nix +++ b/devshell.nix @@ -28,8 +28,6 @@ ${lib.optionalString config.cudaSupport cudaShellHook} export EXTRA_CCFLAGS="-I/usr/include" export B3D_ASSET_PATH="${self'.packages.bayes3d.src}/assets" - - jupyter notebook ''; }; }; From 5738d57ace9d5789dbbe25657a8033f74dd351a1 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 10:28:32 +0200 Subject: [PATCH 44/52] chore(demo): fix demo notebook --- notebooks/assets/demo.gif | Bin 0 -> 243435 bytes notebooks/demo.ipynb | 49 +++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 19 deletions(-) create mode 100644 notebooks/assets/demo.gif diff --git a/notebooks/assets/demo.gif b/notebooks/assets/demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..0e6b642effaec19ad2445c8b6c63a4af47d2f729 GIT binary patch literal 243435 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 Z>gMF8?x)x%Hy`W!M;EWH*7G5+{{f}aGl2jA literal 0 HcmV?d00001 diff --git a/notebooks/demo.ipynb b/notebooks/demo.ipynb index 43d207a..0f43f78 100644 --- a/notebooks/demo.ipynb +++ b/notebooks/demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "id": "fc70511d-4bf5-47cb-9935-c17b4da6cf11", "metadata": { "scrolled": true @@ -12,7 +12,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/tmp/nix-shell.EBRV2Z/ipykernel_252673/268569837.py:7: DeprecationWarning: Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython display\n", + "/tmp/nix-shell.m77vOf/ipykernel_15044/268569837.py:7: DeprecationWarning: Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython display\n", " from IPython.core.display import Image, display\n", "[E rasterize_gl.cpp:121] OpenGL version reported as 4.6\n" ] @@ -24,9 +24,22 @@ "Increasing frame buffer size to (width, height, depth) = (128, 128, 1024)\n", "Centering mesh with translation [0.0000000e+00 2.9802322e-08 0.0000000e+00]\n", "Number of frames: 60\n", - "observed_images.shape (60, 100, 100, 4)\n", - "Time elapsed: 0.3095111846923828\n", - "FPS: 193.85406074947772\n" + "observed_images.shape (60, 100, 100, 4)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-07-29 10:27:17.298628: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time elapsed: 1.261814832687378\n", + "FPS: 47.55055848583875\n" ] } ], @@ -130,27 +143,25 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 2, "id": "c39a88e9-b18d-4d91-b001-f2a3abdd6804", "metadata": {}, "outputs": [ { - "ename": "TypeError", - "evalue": "expected str, bytes or os.PathLike object, not list", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m display(\u001b[43mImage\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mviz_images\u001b[49m\u001b[43m)\u001b[49m)\n", - "File \u001b[0;32m/nix/store/6514avl2wlhjgdhvvw80f3qm8ps8kv5b-python3-3.11.9-env/lib/python3.11/site-packages/IPython/core/display.py:923\u001b[0m, in \u001b[0;36mImage.__init__\u001b[0;34m(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)\u001b[0m\n\u001b[1;32m 921\u001b[0m ext \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_find_ext(filename)\n\u001b[1;32m 922\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m url \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 923\u001b[0m ext \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_find_ext\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 924\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 925\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo image data found. Expecting filename, url, or data.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[0;32m/nix/store/6514avl2wlhjgdhvvw80f3qm8ps8kv5b-python3-3.11.9-env/lib/python3.11/site-packages/IPython/core/display.py:1074\u001b[0m, in \u001b[0;36mImage._find_ext\u001b[0;34m(self, s)\u001b[0m\n\u001b[1;32m 1073\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_find_ext\u001b[39m(\u001b[38;5;28mself\u001b[39m, s):\n\u001b[0;32m-> 1074\u001b[0m base, ext \u001b[38;5;241m=\u001b[39m \u001b[43msplitext\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1076\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m ext:\n\u001b[1;32m 1077\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m base\n", - "File \u001b[0;32m:118\u001b[0m, in \u001b[0;36msplitext\u001b[0;34m(p)\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: expected str, bytes or os.PathLike object, not list" - ] + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "display(Image(url=viz_images))" + "display(Image(url=\"assets/demo.gif\"))" ] }, { From 380ed0abb778a3f81776bba972a164c63596d95c Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 13:57:59 +0200 Subject: [PATCH 45/52] chore: restructure project Fix how flake's python is composed together. 1. We can now easily build all python packages against different versions of python. 2. `python.withPackages` now also properly includes this flake's packages. * Move flake parts into a dedicated ./parts folder. * Move python packages into ./python-packages. * Document the Nix code. * Keep the top-level flake.nix small. * Automatically expose all python packages. --- devshell.nix | 34 ----- flake.nix | 124 +++++++----------- parts/devshell.nix | 37 ++++++ parts/lib.nix | 5 + parts/pkgs.nix | 32 +++++ parts/python.nix | 20 +++ pkgs/python-packages.nix | 25 ---- ...thon-overrides.nix => python-overrides.nix | 6 +- python-packages.nix | 7 + .../bayes3d/default.nix | 0 .../bayes3d/optional-cuda.patch | 0 .../distinctipy/default.nix | 0 .../distributions/default.nix | 0 .../distributions/distributions-shared.nix | 0 .../distributions/gnu-sed-on-darwin.patch | 0 .../use-imread-instead-of-scipy.patch | 0 .../dm-tree/default.nix | 0 .../genjax/default.nix | 0 .../genjax/set-pyproject-version.patch | 0 .../genjax/use-beartype-0.18.0-version.patch | 0 .../goftests/default.nix | 0 .../loom/default.nix | 0 .../loom/test.nix | 0 .../open3d/default.nix | 0 .../opencv-python/default.nix | 0 .../relax-dependency-ranges.patch | 0 .../oryx/default.nix | 0 .../parsable/default.nix | 0 .../plum-dispatch/default.nix | 0 .../pymetis/default.nix | 0 .../pyransac3d/default.nix | 0 .../sppl/default.nix | 0 .../tensorflow-probability/default.nix | 0 33 files changed, 151 insertions(+), 139 deletions(-) delete mode 100644 devshell.nix create mode 100644 parts/devshell.nix create mode 100644 parts/lib.nix create mode 100644 parts/pkgs.nix create mode 100644 parts/python.nix delete mode 100644 pkgs/python-packages.nix rename pkgs/python-overrides.nix => python-overrides.nix (69%) create mode 100644 python-packages.nix rename {pkgs/python-modules => python-packages}/bayes3d/default.nix (100%) rename {pkgs/python-modules => python-packages}/bayes3d/optional-cuda.patch (100%) rename {pkgs/python-modules => python-packages}/distinctipy/default.nix (100%) rename {pkgs/python-modules => python-packages}/distributions/default.nix (100%) rename {pkgs/python-modules => python-packages}/distributions/distributions-shared.nix (100%) rename {pkgs/python-modules => python-packages}/distributions/gnu-sed-on-darwin.patch (100%) rename {pkgs/python-modules => python-packages}/distributions/use-imread-instead-of-scipy.patch (100%) rename {pkgs/python-modules => python-packages}/dm-tree/default.nix (100%) rename {pkgs/python-modules => python-packages}/genjax/default.nix (100%) rename {pkgs/python-modules => python-packages}/genjax/set-pyproject-version.patch (100%) rename {pkgs/python-modules => python-packages}/genjax/use-beartype-0.18.0-version.patch (100%) rename {pkgs/python-modules => python-packages}/goftests/default.nix (100%) rename {pkgs/python-modules => python-packages}/loom/default.nix (100%) rename {pkgs/python-modules => python-packages}/loom/test.nix (100%) rename {pkgs/python-modules => python-packages}/open3d/default.nix (100%) rename {pkgs/python-modules => python-packages}/opencv-python/default.nix (100%) rename {pkgs/python-modules => python-packages}/opencv-python/relax-dependency-ranges.patch (100%) rename {pkgs/python-modules => python-packages}/oryx/default.nix (100%) rename {pkgs/python-modules => python-packages}/parsable/default.nix (100%) rename {pkgs/python-modules => python-packages}/plum-dispatch/default.nix (100%) rename {pkgs/python-modules => python-packages}/pymetis/default.nix (100%) rename {pkgs/python-modules => python-packages}/pyransac3d/default.nix (100%) rename {pkgs/python-modules => python-packages}/sppl/default.nix (100%) rename {pkgs/python-modules => python-packages}/tensorflow-probability/default.nix (100%) diff --git a/devshell.nix b/devshell.nix deleted file mode 100644 index 030e305..0000000 --- a/devshell.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ ... }: { - perSystem = { self', pkgs, ... }: - let - inherit (pkgs) lib config; - - devshellPython = ( - self'.legacyPackages.python3Packages.python.withPackages (p: [ - self'.legacyPackages.python3Packages.bayes3d - self'.legacyPackages.python3Packages.jax - p.jupyter - p.scipy - ]) - ); - - cudaShellHook = '' - export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" - export CUDA_PATH=${pkgs.cudatoolkit_11} - ''; - in - { - devShells.default = pkgs.mkShell { - packages = [ - self'.legacyPackages.python3Packages.python-lsp-server - devshellPython - ]; - - shellHook = '' - ${lib.optionalString config.cudaSupport cudaShellHook} - export EXTRA_CCFLAGS="-I/usr/include" - export B3D_ASSET_PATH="${self'.packages.bayes3d.src}/assets" - ''; - }; - }; -} diff --git a/flake.nix b/flake.nix index f1324d8..6f48df0 100644 --- a/flake.nix +++ b/flake.nix @@ -8,105 +8,73 @@ nixpkgs.url = "github:NixOS/nixpkgs?ref=d8724afca4565614164dd81345f6137c4c6eab21"; nixpkgs-llvm-10.url = "github:NixOS/nixpkgs?rev=222c1940fafeda4dea161858ffe6ebfc853d3db5"; + # This is a private dependency and requires Nix's ~/.config/nix/nix.conf + # to include something like: + # + # access-tokens = github.com=gh_ + # genjax.url = "github:probcomp/genjax?ref=v0.1.1"; genjax.flake = false; }; - nixConfig.extra-substituters = [ "https://numtide.cachix.org" ]; - nixConfig.extra-trusted-public-keys = [ - "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" - ]; - nixConfig.sandbox = "relaxed"; + nixConfig = { + # Currently configured with numtide's binary cache. + extra-substituters = [ "https://numtide.cachix.org" ]; + extra-trusted-public-keys = [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" ]; + + # Needed for __noChroot impure builds. + sandbox = "relaxed"; + }; outputs = - inputs@{ - self, - nixpkgs, - flake-parts, - ... - }: - flake-parts.lib.mkFlake { inherit inputs; } { - imports = [ - # To import a flake module - # 1. Add foo to inputs - # 2. Add foo as a parameter to the outputs function - # 3. Add here: foo.flakeModule - ./lib - ./devshell.nix - ./pkgs/python-packages.nix - inputs.flake-parts.flakeModules.easyOverlay - ]; + inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + # List of architectures we support systems = [ - "aarch64-darwin" - # "aarch64-linux" - "x86_64-darwin" - "x86_64-linux" + "x86_64-linux" # fully supported + "aarch64-darwin" # partially supported + "x86_64-darwin" # partially supported + # "aarch64-linux" # not supported yet ]; - # NOTE: This property is consumed by flake-parts.mkFlake to specify outputs of - # the flake that are replicated for each supported system. Typically packages, - # apps, and devshells are per system. - perSystem = - { - config, - self', - pkgs, - system, - ... - }: - let - ociImgBase = pkgs.callPackage ./pkgs/ociBase { - inherit nixpkgs; - basicTools = self.lib.basicTools; - }; - - packages = rec { - inherit ociImgBase; - - inherit (self'.legacyPackages.python3Packages) loom sppl bayes3d; + # This flake is composed with the following files: + imports = [ + ./parts/devshell.nix + ./parts/lib.nix + ./parts/pkgs.nix + ./parts/python.nix + ]; - loomOCI = pkgs.dockerTools.buildLayeredImage { - name = "probcomp/loom"; - contents = - [ loom pkgs.bashInteractive ] ++ - (self.lib.basicTools pkgs) - ; - }; - }; - in + perSystem = + { pkgs, system, ... }: { + # Configure our instance of nixpkgs. _module.args.pkgs = import inputs.nixpkgs { inherit system; config = { + # Build with unfree packages. allowUnfree = true; - # Only enable CUDA on Linux + # Only enable CUDA on Linux. cudaSupport = (system == "x86_64-linux" || system == "aarch64-linux"); }; - overlays = [ - (_final: _prev: { - # This was added due to llvmPackages_10 requirement by Open3d - # and it having been removed from Nixpkgs. - inherit (inputs.nixpkgs-llvm-10.legacyPackages.${system}) llvmPackages_10; - }) - ]; - }; - - inherit packages; - - checks = packages // { - devshellPython = self'.devShells.default; + # Patches for nixpkgs (see below) + overlays = [ inputs.self.overlays.default ]; }; }; - # NOTE: this property is consumed by flake-parts.mkFlake to define fields - # of the flake that are NOT per system, such as generic `lib` code or other - # universal exports. Note that in our case, the lib is equivalently declared - # by modules that are imported (see ./lib/devtools/default.nix) - flake = { - # The usual flake attributes can be defined here, including system- - # agnostic ones like nixosModule and system-enumerating ones, although - # those are more easily expressed in perSystem. + # This function describe changes to apply to nixpkgs that we need. + flake.overlays.default = final: prev: { + # This was added due to llvmPackages_10 requirement by Open3d + # and it having been removed from Nixpkgs. + inherit (inputs.nixpkgs-llvm-10.legacyPackages.${prev.system}) llvmPackages_10; + # Patches to the python declaration. + pythonPackagesExtensions = [ + # Changes to python that we need. + (import ./python-overrides.nix { inherit inputs; }) + # Add our own python packages. + (import ./python-packages.nix) + ]; }; }; } diff --git a/parts/devshell.nix b/parts/devshell.nix new file mode 100644 index 0000000..3a653ac --- /dev/null +++ b/parts/devshell.nix @@ -0,0 +1,37 @@ +{ ... }: +{ + perSystem = + { self', pkgs, ... }: + let + inherit (pkgs) lib config; + + devshellPython = self'.packages.python.withPackages (p: [ + p.bayes3d + p.jax + p.jupyter + p.scipy + ]); + + cudaShellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export CUDA_PATH=${pkgs.cudatoolkit_11} + ''; + in + { + # This developer shell is loaded with `nix develop`. + devShells.default = pkgs.mkShell { + packages = [ + self'.packages.python.pkgs.python-lsp-server + devshellPython + ]; + + shellHook = '' + ${lib.optionalString config.cudaSupport cudaShellHook} + export EXTRA_CCFLAGS="-I/usr/include" + export B3D_ASSET_PATH="${self'.packages.python.pkgs.bayes3d.src}/assets" + ''; + }; + + checks.devShell = self'.devShells.default; + }; +} diff --git a/parts/lib.nix b/parts/lib.nix new file mode 100644 index 0000000..fcf4c9f --- /dev/null +++ b/parts/lib.nix @@ -0,0 +1,5 @@ +_: { + flake.lib = { + basicTools = import ../lib/devtools; + }; +} diff --git a/parts/pkgs.nix b/parts/pkgs.nix new file mode 100644 index 0000000..ea5cea8 --- /dev/null +++ b/parts/pkgs.nix @@ -0,0 +1,32 @@ +{ self, inputs, ... }: +{ + perSystem = + { + config, + self', + pkgs, + system, + ... + }: + { + packages = rec { + ociImgBase = pkgs.callPackage ../pkgs/ociBase { + inherit (inputs) nixpkgs; + basicTools = self.lib.basicTools; + }; + + loomOCI = pkgs.dockerTools.buildLayeredImage { + name = "probcomp/loom"; + contents = [ + self'.packages.python.pkgs.loom + pkgs.bashInteractive + ] ++ (self.lib.basicTools pkgs); + }; + }; + + checks = pkgs.lib.mapAttrs' (name: value: { + name = "pkg-${name}"; + inherit value; + }) self'.packages; + }; +} diff --git a/parts/python.nix b/parts/python.nix new file mode 100644 index 0000000..e1d2b29 --- /dev/null +++ b/parts/python.nix @@ -0,0 +1,20 @@ +{ inputs, ... }: +{ + perSystem = + { pkgs, self', ... }: + let + # We use python 3.11 + python = pkgs.python311; + + # Get the list of python packages from the folder structure + pythonPackages = builtins.attrNames (builtins.readDir ../python-packages); + in + { + packages = + { + inherit python; + } + # Expose all the python modules as packages directly + // (pkgs.lib.genAttrs pythonPackages (name: python.pkgs.${name})); + }; +} diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix deleted file mode 100644 index 1f9d157..0000000 --- a/pkgs/python-packages.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ inputs, ... }: { - perSystem = { pkgs, ... }: - let - loadPackages = - callPackage: path: - let - entries = builtins.readDir path; - in - pkgs.lib.mapAttrs ( - name: type: - if type != "directory" then - (throw "${toString path}/${name} is not a directory") - else - callPackage "${toString path}/${name}" { } - ) entries; - - # For fixing existing packages that live in nixpkgs - pythonOverrides = import ./python-overrides.nix { inherit inputs; }; - in - { - legacyPackages.python3Packages = - (pkgs.python311Packages.overrideScope pythonOverrides).overrideScope - (final: _prev: loadPackages final.callPackage ./python-modules); - }; -} diff --git a/pkgs/python-overrides.nix b/python-overrides.nix similarity index 69% rename from pkgs/python-overrides.nix rename to python-overrides.nix index 24c5a0b..aa3238a 100644 --- a/pkgs/python-overrides.nix +++ b/python-overrides.nix @@ -1,5 +1,7 @@ -{ inputs }: final: _prev: { - # so we can pull from flake inputs +# Python overlay that includes patches to upstream nixpkgs python. +{ inputs }: +final: _prev: { + # So we can pull from flake inputs inherit inputs; # Use the pre-built version of tensorflow diff --git a/python-packages.nix b/python-packages.nix new file mode 100644 index 0000000..48193bc --- /dev/null +++ b/python-packages.nix @@ -0,0 +1,7 @@ +final: prev: +let + dir = toString ./python-packages; + dirEntries = builtins.readDir dir; +in +# Loads all the python packages +builtins.mapAttrs (name: _: final.callPackage "${dir}/${name}" { }) dirEntries diff --git a/pkgs/python-modules/bayes3d/default.nix b/python-packages/bayes3d/default.nix similarity index 100% rename from pkgs/python-modules/bayes3d/default.nix rename to python-packages/bayes3d/default.nix diff --git a/pkgs/python-modules/bayes3d/optional-cuda.patch b/python-packages/bayes3d/optional-cuda.patch similarity index 100% rename from pkgs/python-modules/bayes3d/optional-cuda.patch rename to python-packages/bayes3d/optional-cuda.patch diff --git a/pkgs/python-modules/distinctipy/default.nix b/python-packages/distinctipy/default.nix similarity index 100% rename from pkgs/python-modules/distinctipy/default.nix rename to python-packages/distinctipy/default.nix diff --git a/pkgs/python-modules/distributions/default.nix b/python-packages/distributions/default.nix similarity index 100% rename from pkgs/python-modules/distributions/default.nix rename to python-packages/distributions/default.nix diff --git a/pkgs/python-modules/distributions/distributions-shared.nix b/python-packages/distributions/distributions-shared.nix similarity index 100% rename from pkgs/python-modules/distributions/distributions-shared.nix rename to python-packages/distributions/distributions-shared.nix diff --git a/pkgs/python-modules/distributions/gnu-sed-on-darwin.patch b/python-packages/distributions/gnu-sed-on-darwin.patch similarity index 100% rename from pkgs/python-modules/distributions/gnu-sed-on-darwin.patch rename to python-packages/distributions/gnu-sed-on-darwin.patch diff --git a/pkgs/python-modules/distributions/use-imread-instead-of-scipy.patch b/python-packages/distributions/use-imread-instead-of-scipy.patch similarity index 100% rename from pkgs/python-modules/distributions/use-imread-instead-of-scipy.patch rename to python-packages/distributions/use-imread-instead-of-scipy.patch diff --git a/pkgs/python-modules/dm-tree/default.nix b/python-packages/dm-tree/default.nix similarity index 100% rename from pkgs/python-modules/dm-tree/default.nix rename to python-packages/dm-tree/default.nix diff --git a/pkgs/python-modules/genjax/default.nix b/python-packages/genjax/default.nix similarity index 100% rename from pkgs/python-modules/genjax/default.nix rename to python-packages/genjax/default.nix diff --git a/pkgs/python-modules/genjax/set-pyproject-version.patch b/python-packages/genjax/set-pyproject-version.patch similarity index 100% rename from pkgs/python-modules/genjax/set-pyproject-version.patch rename to python-packages/genjax/set-pyproject-version.patch diff --git a/pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch b/python-packages/genjax/use-beartype-0.18.0-version.patch similarity index 100% rename from pkgs/python-modules/genjax/use-beartype-0.18.0-version.patch rename to python-packages/genjax/use-beartype-0.18.0-version.patch diff --git a/pkgs/python-modules/goftests/default.nix b/python-packages/goftests/default.nix similarity index 100% rename from pkgs/python-modules/goftests/default.nix rename to python-packages/goftests/default.nix diff --git a/pkgs/python-modules/loom/default.nix b/python-packages/loom/default.nix similarity index 100% rename from pkgs/python-modules/loom/default.nix rename to python-packages/loom/default.nix diff --git a/pkgs/python-modules/loom/test.nix b/python-packages/loom/test.nix similarity index 100% rename from pkgs/python-modules/loom/test.nix rename to python-packages/loom/test.nix diff --git a/pkgs/python-modules/open3d/default.nix b/python-packages/open3d/default.nix similarity index 100% rename from pkgs/python-modules/open3d/default.nix rename to python-packages/open3d/default.nix diff --git a/pkgs/python-modules/opencv-python/default.nix b/python-packages/opencv-python/default.nix similarity index 100% rename from pkgs/python-modules/opencv-python/default.nix rename to python-packages/opencv-python/default.nix diff --git a/pkgs/python-modules/opencv-python/relax-dependency-ranges.patch b/python-packages/opencv-python/relax-dependency-ranges.patch similarity index 100% rename from pkgs/python-modules/opencv-python/relax-dependency-ranges.patch rename to python-packages/opencv-python/relax-dependency-ranges.patch diff --git a/pkgs/python-modules/oryx/default.nix b/python-packages/oryx/default.nix similarity index 100% rename from pkgs/python-modules/oryx/default.nix rename to python-packages/oryx/default.nix diff --git a/pkgs/python-modules/parsable/default.nix b/python-packages/parsable/default.nix similarity index 100% rename from pkgs/python-modules/parsable/default.nix rename to python-packages/parsable/default.nix diff --git a/pkgs/python-modules/plum-dispatch/default.nix b/python-packages/plum-dispatch/default.nix similarity index 100% rename from pkgs/python-modules/plum-dispatch/default.nix rename to python-packages/plum-dispatch/default.nix diff --git a/pkgs/python-modules/pymetis/default.nix b/python-packages/pymetis/default.nix similarity index 100% rename from pkgs/python-modules/pymetis/default.nix rename to python-packages/pymetis/default.nix diff --git a/pkgs/python-modules/pyransac3d/default.nix b/python-packages/pyransac3d/default.nix similarity index 100% rename from pkgs/python-modules/pyransac3d/default.nix rename to python-packages/pyransac3d/default.nix diff --git a/pkgs/python-modules/sppl/default.nix b/python-packages/sppl/default.nix similarity index 100% rename from pkgs/python-modules/sppl/default.nix rename to python-packages/sppl/default.nix diff --git a/pkgs/python-modules/tensorflow-probability/default.nix b/python-packages/tensorflow-probability/default.nix similarity index 100% rename from pkgs/python-modules/tensorflow-probability/default.nix rename to python-packages/tensorflow-probability/default.nix From 0a18c7253effa87d866e111024ad1eeb76613905 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 14:04:39 +0200 Subject: [PATCH 46/52] chore: remove unused tcmalloc package --- README.md | 1 - pkgs/tcmalloc/default.nix | 51 --------------------------------------- 2 files changed, 52 deletions(-) delete mode 100644 pkgs/tcmalloc/default.nix diff --git a/README.md b/README.md index a2b14ed..4e96323 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,6 @@ Native library for probability distributions in python used by Loom. NOTE: this #### `.#loom.morePackages.parsable` #### `.#loom.morePackages.pymetis` #### `.#loom.morePackages.goftests` -#### `.#loom.morePackages.tcmalloc` Other upstream python packages required by Distributions and/or Loom. diff --git a/pkgs/tcmalloc/default.nix b/pkgs/tcmalloc/default.nix deleted file mode 100644 index 3c43c83..0000000 --- a/pkgs/tcmalloc/default.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ - fetchFromGitHub, - buildBazelPackage, - bazel, -}: -let - src = fetchFromGitHub { - owner = "google"; - repo = "tcmalloc"; - rev = "6722fbb0dd5dc735989ea0a1b5a4549c8cb062ad"; - hash = "sha256-TH3oVxJMd1kQ3yGsPXNJxIYIebMn0cjKAfLkKsTn2ZI="; - }; -in -buildBazelPackage { - inherit src; - pname = "tcmalloc"; - # No tags published for this repo - version = builtins.substring 0 8 src.rev; - - bazel = bazel; - - preConfigure = '' - mkdir -p /build/output/external - ''; - - fetchAttrs = { - sha256 = "sha256-CJBwjdCqzAkD0atktPslyPF4Ez6reWYJEeyb+/UQIB0="; - }; - - buildAttrs = { - dontUseCmakeConfigure = true; - - installPhase = '' - echo "installing..." - ls -alspH bazel-bin/* - - mkdir -p $out/lib/tcmalloc - install -v -Dm0755 bazel-bin/tcmalloc/*.lo $out/lib/tcmalloc/ - ''; - #ls -alspH bazel-bin/tcmalloc - }; - - removeRulesCC = false; - #removeLocalConfigCc = false; - #removeLocal = false; - - bazelTargets = [ "//tcmalloc:tcmalloc" ]; - - dontAddBazelOpts = true; - dontAddPrefix = true; -} From 5fa8b475978a950db4f85d3545096755cd990c2e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 14:19:20 +0200 Subject: [PATCH 47/52] chore: rename ociImgBase to baseOCI --- envs-flake/images/gensql.loom/default.nix | 2 +- envs-flake/images/gensql.query/default.nix | 2 +- parts/pkgs.nix | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/envs-flake/images/gensql.loom/default.nix b/envs-flake/images/gensql.loom/default.nix index 5a4c918..1896e33 100644 --- a/envs-flake/images/gensql.loom/default.nix +++ b/envs-flake/images/gensql.loom/default.nix @@ -10,7 +10,7 @@ let crossPkgsLinux = nixpkgs.legacyPackages.${systemWithLinux}; python = crossPkgsLinux.python3; - base = opengen.packages.${system}.ociImgBase; + base = opengen.packages.${system}.baseOCI; loom = opengen.packages.${systemWithLinux}.loom; in diff --git a/envs-flake/images/gensql.query/default.nix b/envs-flake/images/gensql.query/default.nix index 54d7a14..27fdc69 100644 --- a/envs-flake/images/gensql.query/default.nix +++ b/envs-flake/images/gensql.query/default.nix @@ -9,7 +9,7 @@ let # in OCI context, whatever our host platform we want to build same arch but linux systemWithLinux = builtins.replaceStrings [ "darwin" ] [ "linux" ] system; - base = opengen.packages.${system}.ociImgBase; + base = opengen.packages.${system}.baseOCI; ociBin = gensqlquery.packages.${systemWithLinux}.bin; in pkgs.dockerTools.buildImage { diff --git a/parts/pkgs.nix b/parts/pkgs.nix index ea5cea8..7e0fb68 100644 --- a/parts/pkgs.nix +++ b/parts/pkgs.nix @@ -10,7 +10,7 @@ }: { packages = rec { - ociImgBase = pkgs.callPackage ../pkgs/ociBase { + baseOCI = pkgs.callPackage ../pkgs/ociBase { inherit (inputs) nixpkgs; basicTools = self.lib.basicTools; }; From 3942402db4b63cfe2864b9f6c39eac80c43ae5f8 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 14:19:33 +0200 Subject: [PATCH 48/52] chore(README): list all the packages --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 4e96323..f8938e4 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,40 @@ To access the `lib` code exported by this flake, declare this repo as a flake in ## Packages -### `.#sppl` +List of Nix packages available in this repo. -Python [library by ProbSys](https://github.com/probsys/sppl) packaged for python3.9 . +### `.#baseOCI` + +#### `.#loomOCI` + +A Loom container image is also provided. It can be built and loaded into your local Docker registry with the following command: + +```console +$ docker load -i $(nix build '.#loomOCI' --no-link --print-out-paths) +``` + +## Python Packages + +Here are all the python packages this flake provides, on top of all the ones +available in nixpkgs. + +All the packages are compiled against Python 3.11. + +### `.#bayes3d` + +. + +### `.#distinctipy` + +### `.#distributions` + +Native library for probability distributions in python used by Loom. NOTE: this ONLY builds for `x86_64` architectures and only runs on linux. + +### `.#dm-tree` + +### `.#genjax` + +### `.#goftests` ### `.#loom` @@ -52,26 +83,32 @@ platform-dependent `distributions`. Your options are: -```bash -nix build '.#packages.x86_64-linux.loom' # same as `.#loom` if that is your OS/arch -nix build './envs-flake#packages.x86_64-darwin.ociImgLoom' +```console +$ nix build '.#packages.x86_64-linux.loom' # same as `.#loom` if that is your OS/arch +$ nix build './envs-flake#packages.x86_64-darwin.ociImgLoom' ``` If you are running on Mac silicon (`aarch64-darwin`), that OCI image will run but behavior is not defined or supported. -#### `.#loom.morePackages.distributions` +### `.#open3d` -Native library for probability distributions in python used by Loom. NOTE: this ONLY builds for `x86_64` architectures and only runs on linux. +### `.#opencv-python` -#### `.#loom.morePackages.parsable` -#### `.#loom.morePackages.pymetis` -#### `.#loom.morePackages.goftests` +### `.#orxy` -Other upstream python packages required by Distributions and/or Loom. +### `.#parsable` -#### `.#loom.ociImg` +### `.#plum-dispatch` + +### `.#pymetis` + +### `.#pyransac3d` + +### `.#sppl` + +Python [library by ProbSys](https://github.com/probsys/sppl) packaged for python3.9 . + +### `.#tensorflow-probability` + +### `.#loom` -A Loom container image is also provided as a passthru attribute of `loom`. It can be built and loaded into your local Docker registry with the following command: -```sh -docker load -i $(nix build 'github:OpenGen/nix/loom-oci-img-attribute#loom.ociImg' --no-link --print-out-paths) -``` From fc9bb4fb611fbf2b600b8c16df6220e0672278b6 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 14:23:48 +0200 Subject: [PATCH 49/52] chore: format the nix code with nixfmt-rfc-style --- flake.nix | 1 + parts/formatter.nix | 8 ++ python-packages/bayes3d/default.nix | 17 +-- python-packages/dm-tree/default.nix | 32 ++--- python-packages/open3d/default.nix | 125 +++++++++--------- .../tensorflow-probability/default.nix | 10 +- 6 files changed, 96 insertions(+), 97 deletions(-) create mode 100644 parts/formatter.nix diff --git a/flake.nix b/flake.nix index 6f48df0..45caa07 100644 --- a/flake.nix +++ b/flake.nix @@ -40,6 +40,7 @@ # This flake is composed with the following files: imports = [ ./parts/devshell.nix + ./parts/formatter.nix ./parts/lib.nix ./parts/pkgs.nix ./parts/python.nix diff --git a/parts/formatter.nix b/parts/formatter.nix new file mode 100644 index 0000000..5320128 --- /dev/null +++ b/parts/formatter.nix @@ -0,0 +1,8 @@ +{ + perSystem = + { pkgs, ... }: + { + # Run `nix fmt` to invoke the nix code formatter. + formatter = pkgs.nixfmt-rfc-style; + }; +} diff --git a/python-packages/bayes3d/default.nix b/python-packages/bayes3d/default.nix index 47b5b5c..6b8abc5 100644 --- a/python-packages/bayes3d/default.nix +++ b/python-packages/bayes3d/default.nix @@ -63,9 +63,7 @@ buildPythonPackage rec { hash = "sha256-6AtxR8ZsByliDTQE/hEJs5+LKwdfS/sRGYXf+mgFHxw="; }; - patches = [ - ./optional-cuda.patch - ]; + patches = [ ./optional-cuda.patch ]; pyproject = true; @@ -78,10 +76,7 @@ buildPythonPackage rec { buildInputs = [ libglvnd libGLU - ] - ++ (lib.optionals cudaSupport [ - cudaPackages_11.cudatoolkit.lib - ]); + ] ++ (lib.optionals cudaSupport [ cudaPackages_11.cudatoolkit.lib ]); propagatedBuildInputs = [ distinctipy @@ -103,9 +98,11 @@ buildPythonPackage rec { timm ]; - preBuild = "" + (lib.optionalString cudaSupport '' - export CUDA_HOME=${cuda-native-redist} - ''); + preBuild = + "" + + (lib.optionalString cudaSupport '' + export CUDA_HOME=${cuda-native-redist} + ''); env.WITH_CUDA = if cudaSupport then "1" else "0"; diff --git a/python-packages/dm-tree/default.nix b/python-packages/dm-tree/default.nix index f818648..d5db75e 100644 --- a/python-packages/dm-tree/default.nix +++ b/python-packages/dm-tree/default.nix @@ -1,11 +1,11 @@ - -{ autoPatchelfHook -, buildPythonPackage -, fetchPypi -, python -, isPy311 -, lib -, stdenv +{ + autoPatchelfHook, + buildPythonPackage, + fetchPypi, + python, + isPy311, + lib, + stdenv, }: let prebuiltWheels = { @@ -30,7 +30,7 @@ let hash = "sha256-N4zIrZPF/jWQ9AWjCZgHIfAhx5DKG9+bFbsdWdrsV/U="; }; }; - + pyVersion = lib.versions.majorMinor python.version; srcInputs = prebuiltWheels."${pyVersion}-${stdenv.system}" @@ -55,9 +55,7 @@ buildPythonPackage rec { format = "wheel"; }; - nativeBuildInputs = (lib.optionals stdenv.isLinux [ - autoPatchelfHook - ]); + nativeBuildInputs = (lib.optionals stdenv.isLinux [ autoPatchelfHook ]); # Dynamic link dependencies buildInputs = [ stdenv.cc.cc ]; @@ -66,11 +64,13 @@ buildPythonPackage rec { meta = with lib; { description = "Tree is a library for working with nested data structures."; - homepage = "https://github.com/deepmind/tree"; - license = licenses.asl20; + homepage = "https://github.com/deepmind/tree"; + license = licenses.asl20; platforms = [ - "aarch64-linux" "x86_64-linux" - "aarch64-darwin" "x86_64-darwin" + "aarch64-linux" + "x86_64-linux" + "aarch64-darwin" + "x86_64-darwin" ]; }; } diff --git a/python-packages/open3d/default.nix b/python-packages/open3d/default.nix index 8e979b6..aa53a75 100644 --- a/python-packages/open3d/default.nix +++ b/python-packages/open3d/default.nix @@ -1,40 +1,41 @@ -{ stdenv -, lib -, config -, pkgs -, fetchPypi -, unzip -, zip -, cudaSupport ? config.cudaSupport - -, autoPatchelfHook -, python -, tensorflow-bin -, libusb -, cudaPackages_11 -, buildPythonPackage -, ipywidgets -, matplotlib -, numpy -, pandas -, plyfile -, torch -, pyyaml -, scikitlearn -, scipy -, tqdm -, plotly -, dash -, addict - -, libGL -, libglvnd -, libdrm -, expat -, xorg -, llvmPackages_10 -, buildEnv -, runCommand +{ + stdenv, + lib, + config, + pkgs, + fetchPypi, + unzip, + zip, + cudaSupport ? config.cudaSupport, + + autoPatchelfHook, + python, + tensorflow-bin, + libusb, + cudaPackages_11, + buildPythonPackage, + ipywidgets, + matplotlib, + numpy, + pandas, + plyfile, + torch, + pyyaml, + scikitlearn, + scipy, + tqdm, + plotly, + dash, + addict, + + libGL, + libglvnd, + libdrm, + expat, + xorg, + llvmPackages_10, + buildEnv, + runCommand, }: let libllvm-wrapped = @@ -108,32 +109,26 @@ buildPythonPackage { cd ../ ''; - nativeBuildInputs = [ ] - ++ (lib.optionals stdenv.isLinux [ - autoPatchelfHook - ]); - - buildInputs = [ - # so deps - stdenv.cc.cc.lib - libusb.out - tensorflow-bin - libGL - libglvnd - expat - xorg.libXxf86vm - xorg.libXfixes - libllvm-wrapped - pkgs.mesa - pkgs.zstd - torch - ] - ++ (lib.optionals stdenv.isLinux [ - libdrm - ]) - ++ (lib.optionals cudaSupport [ - cudaPackages_11.cudatoolkit.lib - ]); + nativeBuildInputs = [ ] ++ (lib.optionals stdenv.isLinux [ autoPatchelfHook ]); + + buildInputs = + [ + # so deps + stdenv.cc.cc.lib + libusb.out + tensorflow-bin + libGL + libglvnd + expat + xorg.libXxf86vm + xorg.libXfixes + libllvm-wrapped + pkgs.mesa + pkgs.zstd + torch + ] + ++ (lib.optionals stdenv.isLinux [ libdrm ]) + ++ (lib.optionals cudaSupport [ cudaPackages_11.cudatoolkit.lib ]); propagatedBuildInputs = [ # py deps @@ -151,9 +146,7 @@ buildPythonPackage { addict ]; - pythonImportsCheck = [ - "open3d" - ]; + pythonImportsCheck = [ "open3d" ]; preFixup = '' echo "OUTPUT TO: $out" diff --git a/python-packages/tensorflow-probability/default.nix b/python-packages/tensorflow-probability/default.nix index 1e9742f..723a1f0 100644 --- a/python-packages/tensorflow-probability/default.nix +++ b/python-packages/tensorflow-probability/default.nix @@ -62,11 +62,11 @@ let LIBTOOL = lib.optionalString stdenv.isDarwin "${cctools}/bin/libtool"; fetchAttrs = { - sha256 = if builtins.hasAttr stdenv.system fetchAttrsBySystem then - fetchAttrsBySystem.${stdenv.system} - else - throw "No hash for system" - ; + sha256 = + if builtins.hasAttr stdenv.system fetchAttrsBySystem then + fetchAttrsBySystem.${stdenv.system} + else + throw "No hash for system"; }; buildAttrs = { From 56149b7b0134dbc2906405f995881163c5441d71 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Jul 2024 14:48:01 +0200 Subject: [PATCH 50/52] chore: remove dead code with deadnix --- envs-flake/images/gensql.query/default.nix | 1 - flake.nix | 2 +- parts/pkgs.nix | 2 -- parts/python.nix | 4 ++-- python-packages.nix | 2 +- python-packages/bayes3d/default.nix | 1 - 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/envs-flake/images/gensql.query/default.nix b/envs-flake/images/gensql.query/default.nix index 27fdc69..4806d1f 100644 --- a/envs-flake/images/gensql.query/default.nix +++ b/envs-flake/images/gensql.query/default.nix @@ -1,6 +1,5 @@ { pkgs, - nixpkgs, system, opengen, gensqlquery, diff --git a/flake.nix b/flake.nix index 45caa07..292a00e 100644 --- a/flake.nix +++ b/flake.nix @@ -64,7 +64,7 @@ }; # This function describe changes to apply to nixpkgs that we need. - flake.overlays.default = final: prev: { + flake.overlays.default = _final: prev: { # This was added due to llvmPackages_10 requirement by Open3d # and it having been removed from Nixpkgs. inherit (inputs.nixpkgs-llvm-10.legacyPackages.${prev.system}) llvmPackages_10; diff --git a/parts/pkgs.nix b/parts/pkgs.nix index 7e0fb68..95570a3 100644 --- a/parts/pkgs.nix +++ b/parts/pkgs.nix @@ -2,10 +2,8 @@ { perSystem = { - config, self', pkgs, - system, ... }: { diff --git a/parts/python.nix b/parts/python.nix index e1d2b29..57ecd0a 100644 --- a/parts/python.nix +++ b/parts/python.nix @@ -1,7 +1,7 @@ -{ inputs, ... }: +{ ... }: { perSystem = - { pkgs, self', ... }: + { pkgs, ... }: let # We use python 3.11 python = pkgs.python311; diff --git a/python-packages.nix b/python-packages.nix index 48193bc..f8125cd 100644 --- a/python-packages.nix +++ b/python-packages.nix @@ -1,4 +1,4 @@ -final: prev: +final: _prev: let dir = toString ./python-packages; dirEntries = builtins.readDir dir; diff --git a/python-packages/bayes3d/default.nix b/python-packages/bayes3d/default.nix index 6b8abc5..5503f12 100644 --- a/python-packages/bayes3d/default.nix +++ b/python-packages/bayes3d/default.nix @@ -1,6 +1,5 @@ { lib, - stdenv, config, fetchFromGitHub, buildPythonPackage, From ecd6acfcee2c5d22c951e6a57ccb3de6d0e64edf Mon Sep 17 00:00:00 2001 From: Jonas Chevalier Date: Mon, 29 Jul 2024 15:29:53 +0200 Subject: [PATCH 51/52] feat: add jupyter-bayes3d package (#13) Make it easy to run a jupyter notebook with the Bayes3D environment available. --- README.md | 11 ++++++++- flake.nix | 2 +- parts/packages.nix | 57 ++++++++++++++++++++++++++++++++++++++++++++++ parts/pkgs.nix | 30 ------------------------ 4 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 parts/packages.nix delete mode 100644 parts/pkgs.nix diff --git a/README.md b/README.md index f8938e4..7dd64f9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ List of Nix packages available in this repo. ### `.#baseOCI` -#### `.#loomOCI` +### `.#loomOCI` A Loom container image is also provided. It can be built and loaded into your local Docker registry with the following command: @@ -53,6 +53,15 @@ A Loom container image is also provided. It can be built and loaded into your lo $ docker load -i $(nix build '.#loomOCI' --no-link --print-out-paths) ``` +### `.#jupyter-bayes3d` + +A jupyter environment with bayes libraries available. + +Example: +```console +$ nix run .#jupyter-bayes3d notebook ./notebooks/demo.ipynb +``` + ## Python Packages Here are all the python packages this flake provides, on top of all the ones diff --git a/flake.nix b/flake.nix index 292a00e..a8c8170 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,7 @@ ./parts/devshell.nix ./parts/formatter.nix ./parts/lib.nix - ./parts/pkgs.nix + ./parts/packages.nix ./parts/python.nix ]; diff --git a/parts/packages.nix b/parts/packages.nix new file mode 100644 index 0000000..309bc94 --- /dev/null +++ b/parts/packages.nix @@ -0,0 +1,57 @@ +{ self, inputs, ... }: +{ + perSystem = + { self', pkgs, lib, ... }: + { + packages = rec { + baseOCI = pkgs.callPackage ../pkgs/ociBase { + inherit (inputs) nixpkgs; + basicTools = self.lib.basicTools; + }; + + loomOCI = pkgs.dockerTools.buildLayeredImage { + name = "probcomp/loom"; + contents = [ + self'.packages.python.pkgs.loom + pkgs.bashInteractive + ] ++ (self.lib.basicTools pkgs); + }; + + jupyter-bayes3d = + let + devshellPython = self'.packages.python.withPackages (p: [ + p.bayes3d + p.jax + p.jupyter + p.scipy + ]); + + cudaShellHook = '' + export EXTRA_LDFLAGS="-L/lib -L${pkgs.linuxPackages.nvidia_x11}/lib" + export CUDA_PATH=${pkgs.cudatoolkit_11} + ''; + in + pkgs.writeShellApplication { + name = "jupyter-bayes3d"; + runtimeInputs = [ + devshellPython + self'.packages.python.pkgs.python-lsp-server + ]; + text = '' + set -euo pipefail + + ${lib.optionalString pkgs.config.cudaSupport cudaShellHook} + export EXTRA_CCFLAGS="-I/usr/include" + export B3D_ASSET_PATH="${self'.packages.python.pkgs.bayes3d.src}/assets" + + exec jupyter "$@" + ''; + }; + }; + + checks = pkgs.lib.mapAttrs' (name: value: { + name = "pkg-${name}"; + inherit value; + }) self'.packages; + }; +} diff --git a/parts/pkgs.nix b/parts/pkgs.nix deleted file mode 100644 index 95570a3..0000000 --- a/parts/pkgs.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ self, inputs, ... }: -{ - perSystem = - { - self', - pkgs, - ... - }: - { - packages = rec { - baseOCI = pkgs.callPackage ../pkgs/ociBase { - inherit (inputs) nixpkgs; - basicTools = self.lib.basicTools; - }; - - loomOCI = pkgs.dockerTools.buildLayeredImage { - name = "probcomp/loom"; - contents = [ - self'.packages.python.pkgs.loom - pkgs.bashInteractive - ] ++ (self.lib.basicTools pkgs); - }; - }; - - checks = pkgs.lib.mapAttrs' (name: value: { - name = "pkg-${name}"; - inherit value; - }) self'.packages; - }; -} From adb8294b6efb4dc0e6494e116d3d0ace246274f1 Mon Sep 17 00:00:00 2001 From: Samuel Rounce Date: Mon, 29 Jul 2024 14:26:55 +0100 Subject: [PATCH 52/52] chore: Add package descriptions and links in README.md --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7dd64f9..423410d 100644 --- a/README.md +++ b/README.md @@ -71,19 +71,9 @@ All the packages are compiled against Python 3.11. ### `.#bayes3d` -. +Bayes3D is a 3D scene perception system based on probabilistic inverse graphics. -### `.#distinctipy` - -### `.#distributions` - -Native library for probability distributions in python used by Loom. NOTE: this ONLY builds for `x86_64` architectures and only runs on linux. - -### `.#dm-tree` - -### `.#genjax` - -### `.#goftests` +* [GitHub](https://github.com/probcomp/bayes3d) ### `.#loom` @@ -99,25 +89,94 @@ $ nix build './envs-flake#packages.x86_64-darwin.ociImgLoom' If you are running on Mac silicon (`aarch64-darwin`), that OCI image will run but behavior is not defined or supported. +### `.#distinctipy` + +distinctipy is a lightweight python package providing functions to generate colours that are visually distinct from one another. + +* [GitHub](https://github.com/alan-turing-institute/distinctipy) +* [PyPi](https://pypi.org/project/distinctipy) + +### `.#distributions` + +Native library for probability distributions in python used by Loom. NOTE: this ONLY builds for `x86_64` architectures and only runs on linux. + +* [GitHub](https://github.com/posterior/distributions) + +### `.#dm-tree` + +Tree is a library for working with nested data structures. In a way, tree generalizes the builtin map function which only supports flat sequences, and allows to apply a function to each "leaf" preserving the overall structure. + +* [GitHub](https://github.com/deepmind/tree) +* [PyPi](https://pypi.org/project/dm-tree) + +### `.#genjax` + +GenJAX is an implementation of Gen on top of JAX - exposing the ability to programmatically construct and manipulate generative functions, as well as JIT compile + auto-batch inference computations using generative functions onto GPU devices. + +* [GitHub](https://github.com/probcomp/genjax) + +### `.#goftests` + +Goftests is intended for unit testing random samplers that generate arbitrary plain-old-data, and focuses on robustness rather than statistical efficiency. In contrast to scipy.stats and statsmodels, goftests does not make assumptions on the distribution being tested, and requires only a simple (sample, prob) interface provided by MCMC samplers. + +* [GitHub](https://github.com/posterior/goftests) + ### `.#open3d` +Open3D is an open-source library that supports rapid development of software that deals with 3D data. + +* [GitHub](https://github.com/isl-org/Open3D) +* [PyPi](https://pypi.org/project/open3d) + ### `.#opencv-python` -### `.#orxy` +Wrapper package for OpenCV python bindings. + +* [GitHub](https://github.com/opencv/opencv-python) +* [PyPi](https://pypi.org/project/opencv-python) + +### `.#oryx` + +Oryx is a library for probabilistic programming and deep learning built on top of Jax. + +* [GitHub](https://github.com/jax-ml/oryx) +* [PyPi](https://pypi.org/project/oryx) ### `.#parsable` +Parsable is a lightweight decorator-based command line parser library. Parsable was written to be simpler than argparse, optparse, and argh. + ### `.#plum-dispatch` +Multiple dispatch in Python. + +* [GitHub](https://github.com/beartype/plum) +* [PyPi](https://pypi.org/project/plum-dispatch) + ### `.#pymetis` +PyMetis is a Python wrapper for the Metis graph partititioning software. + +* [GitHub](https://github.com/inducer/pymetis) +* [PyPi](https://pypi.org/project/PyMetis) + ### `.#pyransac3d` +pyRANSAC-3D is an open source implementation of Random sample consensus (RANSAC) method. It fits primitive shapes such as planes, cuboids and cylinder in a point cloud to many aplications: 3D slam, 3D reconstruction, object tracking and many others. + +* [GitHub](https://github.com/leomariga/pyRANSAC-3D) +* [PyPi](https://pypi.org/project/pyransac3d) + ### `.#sppl` -Python [library by ProbSys](https://github.com/probsys/sppl) packaged for python3.9 . +Probabilistic programming system for fast and exact symbolic inference. + +* [GitHub](https://github.com/probsys/sppl) +* [PyPi](https://pypi.org/project/sppl) ### `.#tensorflow-probability` -### `.#loom` +TensorFlow Probability is a library for probabilistic reasoning and statistical analysis in TensorFlow. +* [GitHub](https://github.com/tensorflow/probability) +* [PyPi](https://pypi.org/project/tensorflow-probability)