From f6e5b3a8eb774b299528db01a883d85b8b9f2c8b Mon Sep 17 00:00:00 2001 From: Lance-Drane Date: Thu, 23 Jul 2026 21:08:47 -0400 Subject: [PATCH 1/4] make gpax and sable optional dependencies Signed-off-by: Lance-Drane --- pyproject.toml | 10 +- uv.lock | 386 +++++++++++++++++++++++++------------------------ 2 files changed, 200 insertions(+), 196 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b36ae29..eb50546 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,12 +20,10 @@ classifiers = ["Programming Language :: Python :: 3"] dependencies = [ "intersect_sdk>=0.9.3,<0.10.0", "numpy", - "scikit-learn>=1.4.0,<2.0.0", # TODO consider making an optional dependency group + "scikit-learn>=1.4.0,<2.0.0", # TODO consider making an optional dependency group "scipy>=1.12.0,<2.0.0", - "gpax>=0.1.8", # TODO consider making an optional dependency group - "numpyro<0.20.1", # depend on older numpyro for gpax (numpyro.contrib.module: random_haiku_module) - "pymongo>=4.12.1", # TODO - this is only needed for dial_service, dial_dataclass can use a simple fixture for ObjectID representation which allows it to skip this dependency - "sable @ git+https://code.ornl.gov/sable/sable.git", # TODO this references the internal repo, transition to public version + "numpyro<0.20.1", # depend on older numpyro for gpax (numpyro.contrib.module: random_haiku_module) + "pymongo>=4.12.1", # TODO - this is only needed for dial_service, dial_dataclass can use a simple fixture for ObjectID representation which allows it to skip this dependency ] [project.urls] @@ -36,6 +34,8 @@ Issues = "https://github.com/INTERSECT-DIAL/dial/issues" [project.optional-dependencies] docs = ["sphinx>=5.3.0", "furo>=2023.3.27"] +gpax = ["gpax>=0.1.8"] +sable = ["sable @ git+https://code.ornl.gov/sable/sable.git"] [dependency-groups] dev = [ diff --git a/uv.lock b/uv.lock index 9a8f535..66d3f1b 100644 --- a/uv.lock +++ b/uv.lock @@ -374,7 +374,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ @@ -450,7 +450,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -650,7 +650,7 @@ name = "cuda-bindings" version = "13.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder", marker = "python_full_version < '3.15'" }, + { name = "cuda-pathfinder" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/a9/21/8464d133752951c154feafb3b65c297e7d80f301183d220bec4c830f1441/cuda_bindings-13.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:120fcc53d57903df529c3486962c56528cba5b7d6c57c99537320ed9922c8b86", size = 6073403, upload-time = "2026-05-29T23:11:36.22Z" }, @@ -685,43 +685,43 @@ wheels = [ [package.optional-dependencies] cublas = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cudart = [ - { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cuda-runtime", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cufft = [ - { name = "nvidia-cufft", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cufft", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cufile = [ - { name = "nvidia-cufile", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cufile", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cupti = [ - { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cuda-cupti", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] curand = [ - { name = "nvidia-curand", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-curand", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cusolver = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-cusolver", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-cusparse", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-cusolver", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] cusparse = [ - { name = "nvidia-cusparse", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, + { name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] nvtx = [ - { name = "nvidia-nvtx", marker = "(python_full_version < '3.15' and platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvtx", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" }, ] [[package]] @@ -738,14 +738,11 @@ name = "dial" version = "0.1.6" source = { editable = "." } dependencies = [ - { name = "gpax", version = "0.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "gpax", version = "0.1.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, { name = "intersect-sdk" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpyro" }, { name = "pymongo" }, - { name = "sable" }, { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -759,6 +756,13 @@ docs = [ { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] +gpax = [ + { name = "gpax", version = "0.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "gpax", version = "0.1.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, +] +sable = [ + { name = "sable" }, +] [package.dev-dependencies] dev = [ @@ -775,17 +779,17 @@ dev = [ [package.metadata] requires-dist = [ { name = "furo", marker = "extra == 'docs'", specifier = ">=2023.3.27" }, - { name = "gpax", specifier = ">=0.1.8" }, + { name = "gpax", marker = "extra == 'gpax'", specifier = ">=0.1.8" }, { name = "intersect-sdk", specifier = ">=0.9.3,<0.10.0" }, { name = "numpy" }, { name = "numpyro", specifier = "<0.20.1" }, { name = "pymongo", specifier = ">=4.12.1" }, - { name = "sable", git = "https://code.ornl.gov/sable/sable.git" }, + { name = "sable", marker = "extra == 'sable'", git = "https://code.ornl.gov/sable/sable.git" }, { name = "scikit-learn", specifier = ">=1.4.0,<2.0.0" }, { name = "scipy", specifier = ">=1.12.0,<2.0.0" }, { name = "sphinx", marker = "extra == 'docs'", specifier = ">=5.3.0" }, ] -provides-extras = ["docs"] +provides-extras = ["docs", "gpax", "sable"] [package.metadata.requires-dev] dev = [ @@ -877,13 +881,13 @@ wheels = [ [package.optional-dependencies] epath = [ - { name = "fsspec", marker = "python_full_version < '3.11'" }, - { name = "importlib-resources", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, - { name = "zipp", marker = "python_full_version < '3.11'" }, + { name = "fsspec" }, + { name = "importlib-resources" }, + { name = "typing-extensions" }, + { name = "zipp" }, ] epy = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] [[package]] @@ -901,12 +905,12 @@ wheels = [ [package.optional-dependencies] epath = [ - { name = "fsspec", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "zipp", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "fsspec" }, + { name = "typing-extensions" }, + { name = "zipp" }, ] epy = [ - { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] [[package]] @@ -914,7 +918,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -938,15 +942,15 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "msgpack", marker = "python_full_version < '3.11'" }, - { name = "optax", marker = "python_full_version < '3.11'" }, - { name = "orbax-checkpoint", marker = "python_full_version < '3.11'" }, - { name = "pyyaml", marker = "python_full_version < '3.11'" }, - { name = "rich", marker = "python_full_version < '3.11'" }, - { name = "tensorstore", version = "0.1.78", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "treescope", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" } }, + { name = "msgpack" }, + { name = "optax" }, + { name = "orbax-checkpoint" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "tensorstore", version = "0.1.78", source = { registry = "https://pypi.org/simple" } }, + { name = "treescope" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e6/76/4ea55a60a47e98fcff591238ee26ed4624cb4fdc4893aa3ebf78d0d021f4/flax-0.10.7.tar.gz", hash = "sha256:2930d6671e23076f6db3b96afacf45c5060898f5c189ecab6dda7e05d26c2085", size = 5136099, upload-time = "2025-07-02T06:10:07.819Z" } wheels = [ @@ -962,16 +966,16 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "msgpack", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "optax", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "orbax-checkpoint", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "pyyaml", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "rich", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "treescope", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" } }, + { name = "msgpack" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "optax" }, + { name = "orbax-checkpoint" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" } }, + { name = "treescope" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8b/02/2d4fbc31dcf5fb02e2b8ce733f8eecaffc7f24e905df2da39539809b195b/flax-0.12.0.tar.gz", hash = "sha256:cb3d4a2028666640c1a2e5b267dadfbd42ef14c6db41896fb4c765a7f05ebe74", size = 5038989, upload-time = "2025-09-25T23:59:00.919Z" } wheels = [ @@ -1083,10 +1087,10 @@ resolution-markers = [ "python_full_version == '3.13.*'", ] dependencies = [ - { name = "dm-haiku", marker = "python_full_version >= '3.13'" }, - { name = "jax", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "matplotlib", marker = "python_full_version >= '3.13'" }, - { name = "numpyro", marker = "python_full_version >= '3.13'" }, + { name = "dm-haiku" }, + { name = "jax", version = "0.5.3", source = { registry = "https://pypi.org/simple" } }, + { name = "matplotlib" }, + { name = "numpyro" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/25/74b8c1c77bad231a92674be105a5fd9152fe3a9eb77efa27d342d77fce20/gpax-0.1.8.tar.gz", hash = "sha256:7bc7b89bd58db2d31f9c6007515d3883810e064c69d5b8895543647422a368aa", size = 73263, upload-time = "2024-03-20T06:39:56.206Z" } wheels = [ @@ -1103,16 +1107,16 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "dm-haiku", marker = "python_full_version < '3.13'" }, - { name = "flax", version = "0.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "dm-haiku" }, + { name = "flax", version = "0.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "flax", version = "0.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jaxlib", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "jaxopt", marker = "python_full_version < '3.13'" }, - { name = "matplotlib", marker = "python_full_version < '3.13'" }, - { name = "numpyro", marker = "python_full_version < '3.13'" }, + { name = "jaxopt" }, + { name = "matplotlib" }, + { name = "numpyro" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/b8/b45ecc6fe6c6d8363f1c66fd07b3a14dc1c1d09c1d0b18d5202a74fb4e91/gpax-0.1.9.tar.gz", hash = "sha256:d86d2738ae3f83a5c5fdb65f6bc421a27d49c46198a9677464f1879a95fcfcca", size = 61256, upload-time = "2025-07-04T06:11:06.435Z" } wheels = [ @@ -1233,11 +1237,11 @@ resolution-markers = [ "python_full_version == '3.13.*'", ] dependencies = [ - { name = "jaxlib", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "opt-einsum", marker = "python_full_version >= '3.13'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "jaxlib", version = "0.5.3", source = { registry = "https://pypi.org/simple" } }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "opt-einsum" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/13/e5/dabb73ab10330e9535aba14fc668b04a46fcd8e78f06567c4f4f1adce340/jax-0.5.3.tar.gz", hash = "sha256:f17fcb0fd61dc289394af6ce4de2dada2312f2689bb0d73642c6f026a95fbb2c", size = 2072748, upload-time = "2025-03-19T18:23:40.901Z" } wheels = [ @@ -1252,11 +1256,11 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "opt-einsum", marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" } }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "opt-einsum" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/cf/1e/267f59c8fb7f143c3f778c76cb7ef1389db3fd7e4540f04b9f42ca90764d/jax-0.6.2.tar.gz", hash = "sha256:a437d29038cbc8300334119692744704ca7941490867b9665406b7f90665cd96", size = 2334091, upload-time = "2025-06-17T23:10:27.186Z" } wheels = [ @@ -1272,11 +1276,11 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "jaxlib", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "opt-einsum", marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "jaxlib", version = "0.7.1", source = { registry = "https://pypi.org/simple" } }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "opt-einsum" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/e8/b393ee314d3b042bd66b986d38e52f4e6046590399d916381265c20467d3/jax-0.7.1.tar.gz", hash = "sha256:118f56338c503361d2791f069d24339d8d44a8db442ed851d2e591222fb7a56d", size = 2428411, upload-time = "2025-08-20T15:55:46.098Z" } wheels = [ @@ -1295,9 +1299,9 @@ resolution-markers = [ "python_full_version == '3.13.*'", ] dependencies = [ - { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "ml-dtypes", version = "0.4.1", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/2e/12/b1da8468ad843b30976b0e87c6b344ee621fb75ef8bbd39156a303f59059/jaxlib-0.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48ff5c89fb8a0fe04d475e9ddc074b4879a91d7ab68a51cec5cd1e87f81e6c47", size = 63694868, upload-time = "2025-03-19T18:23:52.193Z" }, @@ -1327,9 +1331,9 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/15/c5/41598634c99cbebba46e6777286fb76abc449d33d50aeae5d36128ca8803/jaxlib-0.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4601b2b5dc8c23d6afb293eacfb9aec4e1d1871cb2f29c5a151d103e73b0f8", size = 54298019, upload-time = "2025-06-17T23:10:36.916Z" }, @@ -1361,9 +1365,9 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/af/5058d545e95f99a54289648f5430cc3c23263dd70a1391e7491f24ed328d/jaxlib-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f32c3e4c167b7327c342e82d3df84079714ea0b43718be871d039999670b3c9", size = 57686934, upload-time = "2025-08-20T15:55:58.989Z" }, @@ -1395,13 +1399,13 @@ name = "jaxopt" version = "0.8.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jaxlib", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3a/da/ff7d7fbd13b8ed5e8458e80308d075fc649062b9f8676d3fc56f2dc99a82/jaxopt-0.8.5.tar.gz", hash = "sha256:2790bd68ef132b216c083a8bc7a2704eceb35a92c0fc0a1e652e79dfb1e9e9ab", size = 121709, upload-time = "2025-04-14T17:59:01.618Z" } @@ -1715,7 +1719,7 @@ name = "markdown-it-py" version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdurl", marker = "python_full_version < '3.13'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ @@ -1919,7 +1923,7 @@ resolution-markers = [ "python_full_version == '3.13.*'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/fd/15/76f86faa0902836cc133939732f7611ace68cf54148487a99c539c272dc8/ml_dtypes-0.4.1.tar.gz", hash = "sha256:fad5f2de464fd09127e49b7fd1252b9006fb43d2edc1ff112d390c324af5ca7a", size = 692594, upload-time = "2024-09-13T19:07:11.624Z" } wheels = [ @@ -1947,7 +1951,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } @@ -2361,7 +2365,7 @@ name = "nvidia-cublas" version = "13.1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cuda-nvrtc" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, @@ -2400,7 +2404,7 @@ name = "nvidia-cudnn-cu13" version = "9.20.0.48" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cublas" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, @@ -2412,7 +2416,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -2442,9 +2446,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-cusparse", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -2456,7 +2460,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.15' and sys_platform == 'emscripten') or (python_full_version < '3.15' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -2522,12 +2526,12 @@ name = "optax" version = "0.2.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "absl-py", marker = "python_full_version < '3.13'" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "absl-py" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "jaxlib", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jaxlib", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8c/f9/e3d11ae6f298ee941a0690e353a323d158ba5dedc436e75621c310845c5c/optax-0.2.8.tar.gz", hash = "sha256:5b225b35066fc3eebaa4d798f1b4173b4d57d1a480610908981f8343b50af0b0", size = 301193, upload-time = "2026-03-20T23:30:05.465Z" } @@ -2540,25 +2544,25 @@ name = "orbax-checkpoint" version = "0.11.36" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "absl-py", marker = "python_full_version < '3.13'" }, - { name = "aiofiles", marker = "python_full_version < '3.13'" }, - { name = "etils", version = "1.13.0", source = { registry = "https://pypi.org/simple" }, extra = ["epath", "epy"], marker = "python_full_version < '3.11'" }, + { name = "absl-py" }, + { name = "aiofiles" }, + { name = "etils", version = "1.13.0", source = { registry = "https://pypi.org/simple" }, extra = ["epath", "epy"], marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "etils", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, extra = ["epath", "epy"], marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "humanize", marker = "python_full_version < '3.13'" }, - { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "humanize" }, + { name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "jax", version = "0.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "msgpack", marker = "python_full_version < '3.13'" }, - { name = "nest-asyncio", marker = "python_full_version < '3.13' and sys_platform == 'win32'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "msgpack" }, + { name = "nest-asyncio", marker = "sys_platform == 'win32'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "protobuf", marker = "python_full_version < '3.13'" }, - { name = "psutil", marker = "python_full_version < '3.13'" }, - { name = "pyyaml", marker = "python_full_version < '3.13'" }, - { name = "simplejson", marker = "python_full_version < '3.13'" }, - { name = "tensorstore", version = "0.1.78", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "simplejson" }, + { name = "tensorstore", version = "0.1.78", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "tensorstore", version = "0.1.82", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, - { name = "uvloop", marker = "python_full_version < '3.13' and sys_platform != 'win32'" }, + { name = "typing-extensions" }, + { name = "uvloop", marker = "sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a9/95/2eb9afead8b1aaa5a5184ced09f57c7c0ee473ea283be8e36f22ea92680f/orbax_checkpoint-0.11.36.tar.gz", hash = "sha256:60ed7084a9b79385fb5b9e4b05d98c2db6f5892d05ee8d82df680cfac1622312", size = 585461, upload-time = "2026-04-14T17:03:47.475Z" } wheels = [ @@ -3312,8 +3316,8 @@ name = "rich" version = "15.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", marker = "python_full_version < '3.13'" }, - { name = "pygments", marker = "python_full_version < '3.13'" }, + { name = "markdown-it-py" }, + { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ @@ -3494,10 +3498,10 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, + { name = "joblib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -3547,10 +3551,10 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, + { name = "joblib" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } wheels = [ @@ -3600,7 +3604,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } wheels = [ @@ -3665,7 +3669,7 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -3850,23 +3854,23 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version < '3.11'" }, - { name = "babel", marker = "python_full_version < '3.11'" }, - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "imagesize", marker = "python_full_version < '3.11'" }, - { name = "jinja2", marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "requests", marker = "python_full_version < '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" } }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, + { name = "tomli" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } wheels = [ @@ -3881,23 +3885,23 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version == '3.11.*'" }, - { name = "babel", marker = "python_full_version == '3.11.*'" }, - { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, - { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "imagesize", marker = "python_full_version == '3.11.*'" }, - { name = "jinja2", marker = "python_full_version == '3.11.*'" }, - { name = "packaging", marker = "python_full_version == '3.11.*'" }, - { name = "pygments", marker = "python_full_version == '3.11.*'" }, - { name = "requests", marker = "python_full_version == '3.11.*'" }, - { name = "roman-numerals", marker = "python_full_version == '3.11.*'" }, - { name = "snowballstemmer", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.11.*'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.11.*'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ @@ -3917,23 +3921,23 @@ resolution-markers = [ "python_full_version == '3.12.*'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.12'" }, - { name = "babel", marker = "python_full_version >= '3.12'" }, - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "imagesize", marker = "python_full_version >= '3.12'" }, - { name = "jinja2", marker = "python_full_version >= '3.12'" }, - { name = "packaging", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "requests", marker = "python_full_version >= '3.12'" }, - { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.4", source = { registry = "https://pypi.org/simple" } }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ @@ -4084,8 +4088,8 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/ee/05eb424437f4db63331c90e4605025eedc0f71da3faff97161d5d7b405af/tensorstore-0.1.78.tar.gz", hash = "sha256:e26074ffe462394cf54197eb76d6569b500f347573cd74da3f4dd5f510a4ad7c", size = 6913502, upload-time = "2025-10-06T17:44:29.649Z" } wheels = [ @@ -4120,8 +4124,8 @@ resolution-markers = [ "python_full_version == '3.11.*'", ] dependencies = [ - { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, - { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, + { name = "ml-dtypes", version = "0.5.4", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/9b/43aedb544937f214dd7c665a7edf1b8b74f2f55d53ebd351c0ce69acf81a/tensorstore-0.1.82.tar.gz", hash = "sha256:ccfceffb7611fc61330f6da24b8b0abd9251d480ac8a5bac5a1729f9ed0c3a9f", size = 7160364, upload-time = "2026-03-13T00:22:16.888Z" } wheels = [ @@ -4279,7 +4283,7 @@ name = "treescope" version = "0.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f0/2a/d13d3c38862632742d2fe2f7ae307c431db06538fd05ca03020d207b5dcc/treescope-0.1.10.tar.gz", hash = "sha256:20f74656f34ab2d8716715013e8163a0da79bdc2554c16d5023172c50d27ea95", size = 138870, upload-time = "2025-08-08T05:43:48.048Z" } From f64c33d24e7a2ae079d9b25467aa8dc606f2822c Mon Sep 17 00:00:00 2001 From: Lance-Drane Date: Thu, 23 Jul 2026 21:31:03 -0400 Subject: [PATCH 2/4] numpyro version restriction only applies to gpax Signed-off-by: Lance-Drane --- pyproject.toml | 6 ++++-- uv.lock | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eb50546..42f3188 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,6 @@ dependencies = [ "numpy", "scikit-learn>=1.4.0,<2.0.0", # TODO consider making an optional dependency group "scipy>=1.12.0,<2.0.0", - "numpyro<0.20.1", # depend on older numpyro for gpax (numpyro.contrib.module: random_haiku_module) "pymongo>=4.12.1", # TODO - this is only needed for dial_service, dial_dataclass can use a simple fixture for ObjectID representation which allows it to skip this dependency ] @@ -34,7 +33,10 @@ Issues = "https://github.com/INTERSECT-DIAL/dial/issues" [project.optional-dependencies] docs = ["sphinx>=5.3.0", "furo>=2023.3.27"] -gpax = ["gpax>=0.1.8"] +gpax = [ + "gpax>=0.1.8", + "numpyro<0.20.1", # depend on older numpyro for gpax (numpyro.contrib.module: random_haiku_module) +] sable = ["sable @ git+https://code.ornl.gov/sable/sable.git"] [dependency-groups] diff --git a/uv.lock b/uv.lock index 66d3f1b..89d216d 100644 --- a/uv.lock +++ b/uv.lock @@ -741,7 +741,6 @@ dependencies = [ { name = "intersect-sdk" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "numpyro" }, { name = "pymongo" }, { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -759,6 +758,7 @@ docs = [ gpax = [ { name = "gpax", version = "0.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, { name = "gpax", version = "0.1.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "numpyro" }, ] sable = [ { name = "sable" }, @@ -782,7 +782,7 @@ requires-dist = [ { name = "gpax", marker = "extra == 'gpax'", specifier = ">=0.1.8" }, { name = "intersect-sdk", specifier = ">=0.9.3,<0.10.0" }, { name = "numpy" }, - { name = "numpyro", specifier = "<0.20.1" }, + { name = "numpyro", marker = "extra == 'gpax'", specifier = "<0.20.1" }, { name = "pymongo", specifier = ">=4.12.1" }, { name = "sable", marker = "extra == 'sable'", git = "https://code.ornl.gov/sable/sable.git" }, { name = "scikit-learn", specifier = ">=1.4.0,<2.0.0" }, From be7b2f6012cf0270533a63f768552e1aadc03310 Mon Sep 17 00:00:00 2001 From: Lance-Drane Date: Fri, 24 Jul 2026 02:57:41 -0400 Subject: [PATCH 3/4] add guard to skip tests if backends are not installed Signed-off-by: Lance-Drane --- tests/__init__.py | 0 tests/benchmarks/__init__.py | 0 tests/benchmarks/test_strainmap.py | 1736 ++++++++++++++-------------- tests/helpers.py | 30 + tests/unit/__init__.py | 0 tests/unit/test_internals.py | 121 +- 6 files changed, 1025 insertions(+), 862 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/benchmarks/__init__.py create mode 100644 tests/helpers.py create mode 100644 tests/unit/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/benchmarks/__init__.py b/tests/benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/benchmarks/test_strainmap.py b/tests/benchmarks/test_strainmap.py index e91fa3e..3dd9725 100644 --- a/tests/benchmarks/test_strainmap.py +++ b/tests/benchmarks/test_strainmap.py @@ -1,846 +1,890 @@ -""" -Benchmark which is meant to test core DIAL functionality (without the INTERSECT or MONGO pieces) for the Strain-Mapping benchmark problem. -""" - -import logging -import sys -from dataclasses import dataclass -from pathlib import Path - -import numpy as np -import pandas as pd -import pytest -from scipy.interpolate import LinearNDInterpolator - -# from pytest_benchmark.fixture import BenchmarkFixture -from dial_dataclass import ( - DialInputPredictions, - DialInputSingleOtherStrategy, - Normal, -) -from dial_service import ( - core as dial_core, -) -from dial_service.serverside_data import ( - ServersideInputBase, - ServersideInputPrediction, - ServersideInputSingle, -) -from dial_service.service_specific_dataclasses import DialWorkflowCreationParamsService - -logger = logging.getLogger(__name__) - -MOCK_WORKFLOW_ID = '6984e6a6ef6e6290dabced91' -"""fake ObjectID for testing purposes, we do not actually interact with a DB in these tests.""" - - -############### -low_f_data_name = ( - Path(__file__).parents[1] / 'fixtures' / 'adaptive_strain_manufacturing_low_fidelity.csv' -) -high_f_data_name = ( - Path(__file__).parents[1] / 'fixtures' / 'adaptive_strain_manufacturing_high_fidelity.csv' -) - - -def read_and_prepare_data(): - # normalization helper - def normalize_data(in_data): - m_data = (in_data.max() + in_data.min()) * 0.5 - d_data = (in_data.max() - in_data.min()) * 0.5 - return (in_data - m_data) / d_data - - # define constants and read data - # wall_st_index = 0 - # wall_st_end = 676 - x_col_index = 0 # z follow next - e_col_index = 2 # e11, e22 and e33 - nrows = 26 # Rows in sim strain map of the wall - ncols = 26 # Cols in sim strain map of the wall - data = pd.read_csv(low_f_data_name) - - Y, Z, E11, E22, E33, R11, R22, R33 = np.array( - [ - data.values[:, x_col_index], - data.values[:, x_col_index + 1], - data.values[:, e_col_index], - data.values[:, e_col_index + 1], - data.values[:, e_col_index + 2], - data.values[:, e_col_index + 3], - data.values[:, e_col_index + 4], - data.values[:, e_col_index + 5], - ] - ) - - # Convert data - x1 = Y.astype(np.float32).reshape(nrows, ncols) - x2 = Z.astype(np.float32).reshape(nrows, ncols) - - # Sim_e11 = np.array(E11).astype(np.float32).reshape(nrows, ncols) - # Real_e11 = np.array(R11).astype(np.float32).reshape(nrows, ncols) - - # Sim_e22 = np.array(E22).astype(np.float32).reshape(nrows, ncols) - # Real_e22 = np.array(R22).astype(np.float32).reshape(nrows, ncols) - - # Sim_e33 = np.array(E33).astype(np.float32).reshape(nrows, ncols) - Real_e33 = np.array(R33).astype(np.float32).reshape(nrows, ncols) - - # Normalize data - x1_norm = normalize_data(x1) - x2_norm = normalize_data(x2) - - # Sim_e11_norm = normalize_data(Sim_e11) - # Real_e11_norm = normalize_data(Real_e11) - - # Sim_e22_norm = normalize_data(Sim_e22) - # Real_e22_norm = normalize_data(Real_e22) - - # Sim_e33_norm = normalize_data(Sim_e33) - Real_e33_norm = normalize_data(Real_e33) - - # read high fidelity data - # data = pd.read_csv(high_f_data_name) - - # Y, Z, E11, E22, E33, R11, R22, R33 = np.array( - # [ - # data.values[:, x_col_index], - # data.values[:, x_col_index + 1], - # data.values[:, e_col_index], - # data.values[:, e_col_index + 1], - # data.values[:, e_col_index + 2], - # data.values[:, e_col_index + 3], - # data.values[:, e_col_index + 4], - # data.values[:, e_col_index + 5], - # ] - # ) - - # Sim_hi_e11 = np.array(E11).astype(np.float32).reshape(nrows, ncols) - # Sim_hi_e22 = np.array(E22).astype(np.float32).reshape(nrows, ncols) - # Sim_hi_e33 = np.array(E33).astype(np.float32).reshape(nrows, ncols) - - # Transpose the high-fidelity simulation data - # TODO, figure out why this is transposed - # Sim_hi_e11 = Sim_hi_e11.T - # Sim_hi_e22 = Sim_hi_e22.T - # Sim_hi_e33 = Sim_hi_e33.T - - # Normalize data - # Sim_hi_e11_norm = normalize_data(Sim_hi_e11) - # Sim_hi_e22_norm = normalize_data(Sim_hi_e22) - # Sim_hi_e33_norm = normalize_data(Sim_hi_e33) - - # return data that will be used for the benchmark - return nrows, (x1_norm, x2_norm), Real_e33_norm - - -############### - -## Select data for ground truth - -ngrid, x_grids, y_grid = read_and_prepare_data() - -# default inputs -INITIAL_BOUNDS = [[-1.0, 1.0], [-1.0, 1.0]] -NUM_DIMS = len(INITIAL_BOUNDS) - -MESHGRID_SIZE = ngrid -INITIAL_MESHGRIDS = x_grids -INITIAL_POINTS_TO_PREDICT = np.hstack([mg.reshape(-1, 1) for mg in INITIAL_MESHGRIDS]) - -INITIAL_PREDICTIONS = y_grid.reshape(-1, 1) - -############### - -NOISE_LEVEL = 1.0e-2 - -truth_interp = LinearNDInterpolator(INITIAL_POINTS_TO_PREDICT, INITIAL_PREDICTIONS) - - -@dataclass -class StrainMap: - """Strain Map function.""" - - noise_level: float - - truth_interp: LinearNDInterpolator - """Strain interpolator that is used as 'ground_truth'""" - - def strain_map(self, x) -> float: - """ - Represents a measured strain as a function of two simulation parameters. - """ - x = np.asarray(x).reshape((-1, 2)) - x1, x2 = x[:, 0], x[:, 1] - - y_true = truth_interp(np.asarray(x1), np.asarray(x2)) - - # print("Evaluated truth model", np.hstack((x, y_true))) - - y_noise = y_true + self.noise_level * np.random.normal(size=y_true.shape) - return y_noise - - -# build a strain map from the selected truth values -truth_strain_map = StrainMap(truth_interp=truth_interp, noise_level=NOISE_LEVEL) - -############### - -# test parameters -INITIAL_NUM_POINTS = 25 -MAX_ITERATIONS = 150 # only allow a maximum of this many iterations in tests - -INITIAL_DATASET_X = np.random.uniform(-1.0, 1.0, size=(INITIAL_NUM_POINTS, NUM_DIMS)).tolist() - -TARGET_ERROR = 0.11 - - -def run_simulation( - dataset_x: list[list[float]], dataset_y: list[float], strategy: str, strategy_args: object -) -> tuple[np.ndarray, np.ndarray]: - # important "Hyper-parameters" - length_scale = 0.4 - prior_std = 1.0 - - # Assume that there is some small noise in the measurements to stabilize the fit - statistics_y = Normal(loc='y', scale=NOISE_LEVEL) - - # modify a local copy of strategy_args - strategy_args = strategy_args.copy() - backend = strategy_args.pop('backend', 'sklearn') - - if backend == 'sable': - # train model with new data - kernel = 'rbf' - kernel_args = { - 'sigma_range': [5e-2, 1.0], - 'gamma': 0.5, - } - backend_args = { - 'prior_std': prior_std, - } - - else: - # train model with new data - kernel = 'matern' - kernel_args = { - 'length_scale': length_scale, - 'constant_value': prior_std**2, - } - backend_args = {} - - client_state = DialWorkflowCreationParamsService( - dataset_x=dataset_x, - dataset_y=dataset_y, - statistics_y=statistics_y, - bounds=INITIAL_BOUNDS, - kernel=kernel, - kernel_args=kernel_args, - y_is_good=False, - backend=backend, - backend_args=backend_args, - seed=-1, - dim_x=2, - ) - - data = ServersideInputBase(client_state) - model = dial_core.train_model(data) - - # use get_surrogate_values to predict mean and standard deviation on the inital points grid - data = ServersideInputPrediction( - client_state, - DialInputPredictions( - workflow_id=MOCK_WORKFLOW_ID, - points_to_predict=INITIAL_POINTS_TO_PREDICT, - ), - ) - surrogate_mean, surrogate_std, _ = dial_core.get_surrogate_values(data, model) - mean_grid = np.array(surrogate_mean).reshape((-1, 1)) - - # subtract the true values and save mean absolute error and standard deviation - err_grid = np.abs(mean_grid - INITIAL_PREDICTIONS).reshape((MESHGRID_SIZE,) * NUM_DIMS) - std_grid = np.array(surrogate_std).reshape((MESHGRID_SIZE,) * NUM_DIMS) - - # get_next_point - data = ServersideInputSingle( - client_state, - DialInputSingleOtherStrategy( - workflow_id=MOCK_WORKFLOW_ID, - bounds=INITIAL_BOUNDS, - y_is_good=False, - seed=-1, - strategy=strategy, - strategy_args=strategy_args, - discrete_measurements=True, - discrete_measurement_grid_size=[MESHGRID_SIZE, MESHGRID_SIZE], - ), - ) - next_point = dial_core.get_next_point(data, model) - - dataset_x.append(next_point) - - # compute at next point - next_point_y = truth_strain_map.strain_map(next_point).reshape(-1).tolist() - dataset_y.append(next_point_y[0]) - - return err_grid, std_grid - - -def graph(err_grid, dataset_x, strategy): - try: - import matplotlib as mpl - import matplotlib.pyplot as plt - - mpl.use('Agg') # Use non-interactive backend - except ImportError: - logger.error( # noqa: TRY400 - 'Error: matplotlib is required for generating plots. Install it with: pip install matplotlib' - ) - return - - plt.clf() - data = np.array(err_grid) - plt.contourf( - INITIAL_MESHGRIDS[0], - INITIAL_MESHGRIDS[1], - data, - levels=np.logspace(-2, 0, 10), - norm='log', - extend='both', - ) - cbar = plt.colorbar() - cbar.set_ticks(np.logspace(-2, 0, 7)) - cbar.set_label('Overall error') - plt.xlabel('Simulation Parameter #1') - plt.ylabel('Simulation Parameter #2') - # add black dots for data points and a red marker for the recommendation: - X_train = np.array(dataset_x) - plt.scatter(X_train[:, 0], X_train[:, 1], color='black', marker='o') - plt.scatter(1.0, 1.0, s=300, color='None', edgecolors='black', marker='o') - - plt.savefig(f'graph_{strategy}.png') - - -def accuracy_benchmark( - strategy: str, strategy_args: object, max_iterations=MAX_ITERATIONS -) -> tuple[int, float]: - """ - returns: - - number of iterations taken to reach an acceptably accurate target - - target value achieved - """ - - iterations = 0 - - initial_dataset_x = np.random.uniform(-1.0, 1.0, size=(INITIAL_NUM_POINTS, NUM_DIMS)).tolist() - - dataset_x = initial_dataset_x - dataset_y = truth_strain_map.strain_map(dataset_x).reshape(-1).tolist() - - # run simulations until we reach an acceptable target range - while iterations < max_iterations: - try: - err_grid, std_grid = run_simulation(dataset_x, dataset_y, strategy, strategy_args) - except Exception as e: - logger.exception('Error during simulation') - raise AssertionError from e - - # guess has no meaning here, take the last acquired datapoint - guess = dataset_x[-1] - - # compute the RMSE and MAD on the grid and the average - # rmse = np.sqrt(err_grid**2 + std_grid**2) - mad = np.abs(err_grid) - # total_rmse = np.sqrt(np.mean(rmse**2)) - total_mad = np.sqrt(np.mean(mad**2)) - - # pick the MAD over the RMSE as an error criterion, it only measures the mean deviation: - # it is less sensitive to wrong std and hyperparameter calibration, but does not measure how well - # the "error bars" (std_grid) quantify the actual ty - error = total_mad - print(iterations, error) - - graph(mad, dataset_x, f'{strategy}_{strategy_args.get("backend", "sklearn")}') - - if error <= TARGET_ERROR: - break - iterations += 1 - - return iterations, error, guess - - -TEST_PARAMS = ( - ('strategy', 'strategy_args', 'max_iterations'), - [ - ('uncertainty', {}, 2), - ('uncertainty', {'backend': 'sable'}, 2), - ('random', {}, 2), - # use low number of iterations for unit test - ], -) - - -@pytest.mark.parametrize(*TEST_PARAMS) -def test_benchmark_strainmap_accuracy( - # benchmark: BenchmarkFixture, - strategy: str, - strategy_args: dict, - max_iterations: int, -) -> None: - # NUM_RUNS = 20 - # for _ in range(NUM_RUNS): - # iterations, target = benchmark( - # partial(accuracy_benchmark, strategy, strategy_args) - # ) - iterations, target, guess = accuracy_benchmark(strategy, strategy_args, max_iterations) - print( - 'Iterations for', - strategy, - ': ', - iterations, - ' best guess:', - guess, - ' with target value:', - target, - ) - print( - 'Maximum early terminus value', - TARGET_ERROR, - ' with ', - max_iterations, - ' maximum iterations.', - ) - print( - 'Accuracy benchmark for strategy:', - strategy, - 'reached' if iterations <= max_iterations else 'not reached', - ) - - -if __name__ == '__main__': - """Generate HTML benchmark report with plots comparing different strategies.""" - import argparse - import datetime - import json - from pathlib import Path - - logger = logging.getLogger(f'{__name__}_runner') - - try: - import matplotlib as mpl - import matplotlib.pyplot as plt - - mpl.use('Agg') # Use non-interactive backend - except ImportError: - logger.error( # noqa: TRY400 - 'Error: matplotlib is required for generating plots. Install it with: pip install matplotlib' - ) - sys.exit(1) - - def positive_int_type(arg): - try: - val = int(arg) - except ValueError as e: - msg = 'Must be an integer' - raise argparse.ArgumentTypeError(msg) from e - if val < 1: - msg = 'Argument must be a positive number' - raise argparse.ArgumentTypeError(msg) - return val - - parser = argparse.ArgumentParser(description='Generate the Strainmap HTML benchmark pages.') - parser.add_argument( - '--num-runs', - '-n', - type=positive_int_type, - default=3, - help='Number of runs for each strategy.', - ) - args = parser.parse_args() - - strategies = TEST_PARAMS[1] - - # Run multiple iterations for statistical analysis - NUM_RUNS = args.num_runs - logger.info('Running benchmarks with %d iterations per strategy...', NUM_RUNS) - - results = {} - for strategy, strategy_args, _ in strategies: - strategy_name = f'{strategy}' + (f' {json.dumps(strategy_args)}' if strategy_args else '') - logger.info('\nBenchmarking: %s', strategy_name) - - iterations_list = [] - targets_list = [] - guesses_list = [] - - for run in range(NUM_RUNS): - iterations, target, guess = accuracy_benchmark(strategy, strategy_args) - iterations_list.append(iterations) - targets_list.append(target) - guesses_list.append(guess) - logger.info( - ' Run %d/%d: iterations=%d, target=%.4f', run + 1, NUM_RUNS, iterations, target - ) - - results[strategy_name] = { - 'strategy': strategy, - 'strategy_args': strategy_args, - 'iterations': iterations_list, - 'targets': targets_list, - 'guesses': guesses_list, - 'avg_iterations': np.mean(iterations_list), - 'std_iterations': np.std(iterations_list), - 'avg_target': np.mean(targets_list), - 'std_target': np.std(targets_list), - 'success_rate': sum(1 for t in targets_list if t <= TARGET_ERROR) / NUM_RUNS * 100, - } - - # Generate plots - output_dir = Path('reports/benchmarks') - output_dir.mkdir(parents=True, exist_ok=True) - - fig, axes = plt.subplots(2, 2, figsize=(14, 10)) - fig.suptitle('Strainmap Optimization Benchmark Comparison', fontsize=16, fontweight='bold') - - # Plot 1: Average iterations to convergence - ax1 = axes[0, 0] - strategy_names = list(results.keys()) - avg_iterations = [results[s]['avg_iterations'] for s in strategy_names] - std_iterations = [results[s]['std_iterations'] for s in strategy_names] - bars1 = ax1.bar( - range(len(strategy_names)), avg_iterations, yerr=std_iterations, capsize=5, alpha=0.7 - ) - ax1.set_xlabel('Strategy') - ax1.set_ylabel('Average Iterations') - ax1.set_title('Iterations to Convergence (Lower is Better)') - ax1.set_xticks(range(len(strategy_names))) - ax1.set_xticklabels( - [s.replace(' ', '\n') for s in strategy_names], rotation=0, ha='center', fontsize=8 - ) - ax1.grid(axis='y', alpha=0.3) - - # Add value labels on bars - for _i, (bar, val, std) in enumerate(zip(bars1, avg_iterations, std_iterations, strict=False)): - height = bar.get_height() - ax1.text( - bar.get_x() + bar.get_width() / 2.0, - height, - f'{val:.1f}±{std:.1f}', - ha='center', - va='bottom', - fontsize=9, - ) - - # Plot 2: Average target value achieved - ax2 = axes[0, 1] - avg_targets = [results[s]['avg_target'] for s in strategy_names] - std_targets = [results[s]['std_target'] for s in strategy_names] - bars2 = ax2.bar( - range(len(strategy_names)), - avg_targets, - yerr=std_targets, - capsize=5, - alpha=0.7, - color='orange', - ) - ax2.set_xlabel('Strategy') - ax2.set_ylabel('Average Target Value') - ax2.set_title('Final Target Value (Lower is Better)') - ax2.set_xticks(range(len(strategy_names))) - ax2.set_xticklabels( - [s.replace(' ', '\n') for s in strategy_names], rotation=0, ha='center', fontsize=8 - ) - ax2.axhline( - y=TARGET_ERROR, color='r', linestyle='--', label=f'Target Threshold ({TARGET_ERROR})' - ) - ax2.legend() - ax2.grid(axis='y', alpha=0.3) - - # Add value labels on bars - for _i, (bar, val, std) in enumerate(zip(bars2, avg_targets, std_targets, strict=False)): - height = bar.get_height() - ax2.text( - bar.get_x() + bar.get_width() / 2.0, - height, - f'{val:.2f}±{std:.2f}', - ha='center', - va='bottom', - fontsize=9, - ) - - # Plot 3: Success rate - ax3 = axes[1, 0] - success_rates = [results[s]['success_rate'] for s in strategy_names] - bars3 = ax3.bar(range(len(strategy_names)), success_rates, alpha=0.7, color='green') - ax3.set_xlabel('Strategy') - ax3.set_ylabel('Success Rate (%)') - ax3.set_title(f'Success Rate (Target ≤ {TARGET_ERROR})') - ax3.set_xticks(range(len(strategy_names))) - ax3.set_xticklabels( - [s.replace(' ', '\n') for s in strategy_names], rotation=0, ha='center', fontsize=8 - ) - ax3.set_ylim([0, 105]) - ax3.grid(axis='y', alpha=0.3) - - # Add value labels on bars - for bar, val in zip(bars3, success_rates, strict=False): - height = bar.get_height() - ax3.text( - bar.get_x() + bar.get_width() / 2.0, - height, - f'{val:.1f}%', - ha='center', - va='bottom', - fontsize=9, - ) - - # Plot 4: Box plot of iterations distribution - ax4 = axes[1, 1] - iterations_data = [results[s]['iterations'] for s in strategy_names] - bp = ax4.boxplot(iterations_data, patch_artist=True) - for patch in bp['boxes']: - patch.set_facecolor('lightblue') - ax4.set_xlabel('Strategy') - ax4.set_ylabel('Iterations') - ax4.set_title('Iterations Distribution') - ax4.set_xticklabels([s.replace(' ', '\n') for s in strategy_names], fontsize=8) - ax4.grid(axis='y', alpha=0.3) - - plt.tight_layout() - plot_path = output_dir / 'strainmap_benchmark.png' - plt.savefig(plot_path, dpi=150, bbox_inches='tight') - logger.info('✓ Plot saved to %s', plot_path) - plt.close() - - # Generate HTML report - html_content = f""" - - - - - Strainmap Optimization Benchmark Report - - - -

📊 Strainmap Optimization Benchmark Report

- - - -
-

🎯 Test Objective

-

This benchmark evaluates different acquisition strategies for Bayesian optimization on a strain mapping experiment dataset. - The goal is to minimize the total surrogate error (over all inputs) within {MAX_ITERATIONS} iterations, starting from {INITIAL_NUM_POINTS} initial points.

-
- -

📈 Benchmark Results

- -
-

Performance Comparison

- Benchmark comparison plots -
- -

📋 Detailed Statistics

- - - - - - - - - - - - - - -""" - - # Find best performers - best_iterations_idx = min(range(len(strategy_names)), key=lambda i: avg_iterations[i]) - best_target_idx = min(range(len(strategy_names)), key=lambda i: avg_targets[i]) - best_success_idx = max(range(len(strategy_names)), key=lambda i: success_rates[i]) - - for i, strategy_name in enumerate(strategy_names): - result = results[strategy_name] - row_class = '' - if i in (best_iterations_idx, best_target_idx, best_success_idx): - row_class = ' class="best"' - - args_str = json.dumps(result['strategy_args']) if result['strategy_args'] else 'None' - - html_content += f""" - - - - - - - - -""" - - html_content += """ -
StrategyStrategy ArgsAvg IterationsStd IterationsAvg Target ValueStd Target ValueSuccess Rate
{result['strategy']}{args_str}{result['avg_iterations']:.2f}{result['std_iterations']:.2f}{result['avg_target']:.4f}{result['std_target']:.4f}{result['success_rate']:.1f}%
- -

🏆 Key Findings

-
-""" - - # Add findings - best_strategy = strategy_names[best_iterations_idx] - html_content += f"""

Fastest Convergence: {best_strategy} with {avg_iterations[best_iterations_idx]:.2f} ± {std_iterations[best_iterations_idx]:.2f} iterations on average

-""" - - best_accuracy_strategy = strategy_names[best_target_idx] - html_content += f"""

Best Accuracy: {best_accuracy_strategy} with average target value {avg_targets[best_target_idx]:.4f} ± {std_targets[best_target_idx]:.4f}

-""" - - best_reliability_strategy = strategy_names[best_success_idx] - html_content += f"""

Most Reliable: {best_reliability_strategy} with {success_rates[best_success_idx]:.1f}% success rate

-""" - - html_content += """
- -

📊 Raw Data

-
- Click to expand raw results JSON -
-"""
-
-    # Prepare JSON-serializable results
-    json_results = {}
-    for strategy_name, result in results.items():
-        json_results[strategy_name] = {
-            'strategy': result['strategy'],
-            'strategy_args': result['strategy_args'],
-            'iterations': result['iterations'],
-            'targets': result['targets'],
-            'guesses': [[float(x) for x in guess] for guess in result['guesses']],
-            'statistics': {
-                'avg_iterations': float(result['avg_iterations']),
-                'std_iterations': float(result['std_iterations']),
-                'avg_target': float(result['avg_target']),
-                'std_target': float(result['std_target']),
-                'success_rate': float(result['success_rate']),
-            },
-        }
-
-    html_content += json.dumps(json_results, indent=2)
-    html_content += """
-        
-
- -
-

Generated by test_strainmap.py benchmark suite

-
- - -""" - - # Save HTML report - html_path = output_dir / 'strainmap_benchmark.html' - html_path.write_text(html_content) - logger.info('✓ HTML report saved to %s', html_path) - - # Save JSON data - json_path = output_dir / 'strainmap_benchmark.json' - json_path.write_text(json.dumps(json_results, indent=2)) - logger.info('✓ JSON data saved to %s', json_path) - - logger.info('\n%s', '=' * 60) - logger.info('📊 Benchmark Summary') - logger.info('%s', '=' * 60) - for strategy_name in strategy_names: - result = results[strategy_name] - logger.info('\n%s:', strategy_name) - logger.info( - ' Avg Iterations: %.2f ± %.2f', result['avg_iterations'], result['std_iterations'] - ) - logger.info(' Avg Target: %.4f ± %.4f', result['avg_target'], result['std_target']) - logger.info(' Success Rate: %.1f%%', result['success_rate']) - - logger.info('\n%s', '=' * 60) - logger.info('✓ Open %s in your browser to view the full report', html_path) - logger.info('%s', '=' * 60) - # assert iterations <= MAX_ITERATIONS +""" +Benchmark which is meant to test core DIAL functionality (without the INTERSECT or MONGO pieces) for the Strain-Mapping benchmark problem. +""" + +import logging +import sys +from dataclasses import dataclass +from pathlib import Path + +import numpy as np +import pandas as pd +import pytest +from scipy.interpolate import LinearNDInterpolator + +# from pytest_benchmark.fixture import BenchmarkFixture +from dial_dataclass import ( + DialInputPredictions, + DialInputSingleOtherStrategy, + Normal, +) +from dial_service import ( + core as dial_core, +) +from dial_service.serverside_data import ( + ServersideInputBase, + ServersideInputPrediction, + ServersideInputSingle, +) +from dial_service.service_specific_dataclasses import ( + DialWorkflowCreationParamsService, +) + +from ..helpers import generate_pytest_parameters + +logger = logging.getLogger(__name__) + +MOCK_WORKFLOW_ID = '6984e6a6ef6e6290dabced91' +"""fake ObjectID for testing purposes, we do not actually interact with a DB in these tests.""" + + +############### +low_f_data_name = ( + Path(__file__).parents[1] / 'fixtures' / 'adaptive_strain_manufacturing_low_fidelity.csv' +) +high_f_data_name = ( + Path(__file__).parents[1] / 'fixtures' / 'adaptive_strain_manufacturing_high_fidelity.csv' +) + + +def read_and_prepare_data(): + # normalization helper + def normalize_data(in_data): + m_data = (in_data.max() + in_data.min()) * 0.5 + d_data = (in_data.max() - in_data.min()) * 0.5 + return (in_data - m_data) / d_data + + # define constants and read data + # wall_st_index = 0 + # wall_st_end = 676 + x_col_index = 0 # z follow next + e_col_index = 2 # e11, e22 and e33 + nrows = 26 # Rows in sim strain map of the wall + ncols = 26 # Cols in sim strain map of the wall + data = pd.read_csv(low_f_data_name) + + Y, Z, E11, E22, E33, R11, R22, R33 = np.array( + [ + data.values[:, x_col_index], + data.values[:, x_col_index + 1], + data.values[:, e_col_index], + data.values[:, e_col_index + 1], + data.values[:, e_col_index + 2], + data.values[:, e_col_index + 3], + data.values[:, e_col_index + 4], + data.values[:, e_col_index + 5], + ] + ) + + # Convert data + x1 = Y.astype(np.float32).reshape(nrows, ncols) + x2 = Z.astype(np.float32).reshape(nrows, ncols) + + # Sim_e11 = np.array(E11).astype(np.float32).reshape(nrows, ncols) + # Real_e11 = np.array(R11).astype(np.float32).reshape(nrows, ncols) + + # Sim_e22 = np.array(E22).astype(np.float32).reshape(nrows, ncols) + # Real_e22 = np.array(R22).astype(np.float32).reshape(nrows, ncols) + + # Sim_e33 = np.array(E33).astype(np.float32).reshape(nrows, ncols) + Real_e33 = np.array(R33).astype(np.float32).reshape(nrows, ncols) + + # Normalize data + x1_norm = normalize_data(x1) + x2_norm = normalize_data(x2) + + # Sim_e11_norm = normalize_data(Sim_e11) + # Real_e11_norm = normalize_data(Real_e11) + + # Sim_e22_norm = normalize_data(Sim_e22) + # Real_e22_norm = normalize_data(Real_e22) + + # Sim_e33_norm = normalize_data(Sim_e33) + Real_e33_norm = normalize_data(Real_e33) + + # read high fidelity data + # data = pd.read_csv(high_f_data_name) + + # Y, Z, E11, E22, E33, R11, R22, R33 = np.array( + # [ + # data.values[:, x_col_index], + # data.values[:, x_col_index + 1], + # data.values[:, e_col_index], + # data.values[:, e_col_index + 1], + # data.values[:, e_col_index + 2], + # data.values[:, e_col_index + 3], + # data.values[:, e_col_index + 4], + # data.values[:, e_col_index + 5], + # ] + # ) + + # Sim_hi_e11 = np.array(E11).astype(np.float32).reshape(nrows, ncols) + # Sim_hi_e22 = np.array(E22).astype(np.float32).reshape(nrows, ncols) + # Sim_hi_e33 = np.array(E33).astype(np.float32).reshape(nrows, ncols) + + # Transpose the high-fidelity simulation data + # TODO, figure out why this is transposed + # Sim_hi_e11 = Sim_hi_e11.T + # Sim_hi_e22 = Sim_hi_e22.T + # Sim_hi_e33 = Sim_hi_e33.T + + # Normalize data + # Sim_hi_e11_norm = normalize_data(Sim_hi_e11) + # Sim_hi_e22_norm = normalize_data(Sim_hi_e22) + # Sim_hi_e33_norm = normalize_data(Sim_hi_e33) + + # return data that will be used for the benchmark + return nrows, (x1_norm, x2_norm), Real_e33_norm + + +############### + +## Select data for ground truth + +ngrid, x_grids, y_grid = read_and_prepare_data() + +# default inputs +INITIAL_BOUNDS = [[-1.0, 1.0], [-1.0, 1.0]] +NUM_DIMS = len(INITIAL_BOUNDS) + +MESHGRID_SIZE = ngrid +INITIAL_MESHGRIDS = x_grids +INITIAL_POINTS_TO_PREDICT = np.hstack([mg.reshape(-1, 1) for mg in INITIAL_MESHGRIDS]) + +INITIAL_PREDICTIONS = y_grid.reshape(-1, 1) + +############### + +NOISE_LEVEL = 1.0e-2 + +truth_interp = LinearNDInterpolator(INITIAL_POINTS_TO_PREDICT, INITIAL_PREDICTIONS) + + +@dataclass +class StrainMap: + """Strain Map function.""" + + noise_level: float + + truth_interp: LinearNDInterpolator + """Strain interpolator that is used as 'ground_truth'""" + + def strain_map(self, x) -> float: + """ + Represents a measured strain as a function of two simulation parameters. + """ + x = np.asarray(x).reshape((-1, 2)) + x1, x2 = x[:, 0], x[:, 1] + + y_true = truth_interp(np.asarray(x1), np.asarray(x2)) + + # print("Evaluated truth model", np.hstack((x, y_true))) + + y_noise = y_true + self.noise_level * np.random.normal(size=y_true.shape) + return y_noise + + +# build a strain map from the selected truth values +truth_strain_map = StrainMap(truth_interp=truth_interp, noise_level=NOISE_LEVEL) + +############### + +# test parameters +INITIAL_NUM_POINTS = 25 +MAX_ITERATIONS = 150 # only allow a maximum of this many iterations in tests + +INITIAL_DATASET_X = np.random.uniform(-1.0, 1.0, size=(INITIAL_NUM_POINTS, NUM_DIMS)).tolist() + +TARGET_ERROR = 0.11 + + +def run_simulation( + dataset_x: list[list[float]], + dataset_y: list[float], + strategy: str, + strategy_args: object, +) -> tuple[np.ndarray, np.ndarray]: + # important "Hyper-parameters" + length_scale = 0.4 + prior_std = 1.0 + + # Assume that there is some small noise in the measurements to stabilize the fit + statistics_y = Normal(loc='y', scale=NOISE_LEVEL) + + # modify a local copy of strategy_args + strategy_args = strategy_args.copy() + backend = strategy_args.pop('backend', 'sklearn') + + if backend == 'sable': + # train model with new data + kernel = 'rbf' + kernel_args = { + 'sigma_range': [5e-2, 1.0], + 'gamma': 0.5, + } + backend_args = { + 'prior_std': prior_std, + } + + else: + # train model with new data + kernel = 'matern' + kernel_args = { + 'length_scale': length_scale, + 'constant_value': prior_std**2, + } + backend_args = {} + + client_state = DialWorkflowCreationParamsService( + dataset_x=dataset_x, + dataset_y=dataset_y, + statistics_y=statistics_y, + bounds=INITIAL_BOUNDS, + kernel=kernel, + kernel_args=kernel_args, + y_is_good=False, + backend=backend, + backend_args=backend_args, + seed=-1, + dim_x=2, + ) + + data = ServersideInputBase(client_state) + model = dial_core.train_model(data) + + # use get_surrogate_values to predict mean and standard deviation on the inital points grid + data = ServersideInputPrediction( + client_state, + DialInputPredictions( + workflow_id=MOCK_WORKFLOW_ID, + points_to_predict=INITIAL_POINTS_TO_PREDICT, + ), + ) + surrogate_mean, surrogate_std, _ = dial_core.get_surrogate_values(data, model) + mean_grid = np.array(surrogate_mean).reshape((-1, 1)) + + # subtract the true values and save mean absolute error and standard deviation + err_grid = np.abs(mean_grid - INITIAL_PREDICTIONS).reshape((MESHGRID_SIZE,) * NUM_DIMS) + std_grid = np.array(surrogate_std).reshape((MESHGRID_SIZE,) * NUM_DIMS) + + # get_next_point + data = ServersideInputSingle( + client_state, + DialInputSingleOtherStrategy( + workflow_id=MOCK_WORKFLOW_ID, + bounds=INITIAL_BOUNDS, + y_is_good=False, + seed=-1, + strategy=strategy, + strategy_args=strategy_args, + discrete_measurements=True, + discrete_measurement_grid_size=[MESHGRID_SIZE, MESHGRID_SIZE], + ), + ) + next_point = dial_core.get_next_point(data, model) + + dataset_x.append(next_point) + + # compute at next point + next_point_y = truth_strain_map.strain_map(next_point).reshape(-1).tolist() + dataset_y.append(next_point_y[0]) + + return err_grid, std_grid + + +def graph(err_grid, dataset_x, strategy): + try: + import matplotlib as mpl + import matplotlib.pyplot as plt + + mpl.use('Agg') # Use non-interactive backend + except ImportError: + logger.error( # noqa: TRY400 + 'Error: matplotlib is required for generating plots. Install it with: pip install matplotlib' + ) + return + + plt.clf() + data = np.array(err_grid) + plt.contourf( + INITIAL_MESHGRIDS[0], + INITIAL_MESHGRIDS[1], + data, + levels=np.logspace(-2, 0, 10), + norm='log', + extend='both', + ) + cbar = plt.colorbar() + cbar.set_ticks(np.logspace(-2, 0, 7)) + cbar.set_label('Overall error') + plt.xlabel('Simulation Parameter #1') + plt.ylabel('Simulation Parameter #2') + # add black dots for data points and a red marker for the recommendation: + X_train = np.array(dataset_x) + plt.scatter(X_train[:, 0], X_train[:, 1], color='black', marker='o') + plt.scatter(1.0, 1.0, s=300, color='None', edgecolors='black', marker='o') + + plt.savefig(f'graph_{strategy}.png') + + +def accuracy_benchmark( + strategy: str, strategy_args: object, max_iterations=MAX_ITERATIONS +) -> tuple[int, float, float]: + """ + returns: + - number of iterations taken to reach an acceptably accurate target + - target value achieved + """ + + iterations = 0 + + initial_dataset_x = np.random.uniform(-1.0, 1.0, size=(INITIAL_NUM_POINTS, NUM_DIMS)).tolist() + + dataset_x = initial_dataset_x + dataset_y = truth_strain_map.strain_map(dataset_x).reshape(-1).tolist() + + # run simulations until we reach an acceptable target range + while iterations < max_iterations: + try: + err_grid, std_grid = run_simulation(dataset_x, dataset_y, strategy, strategy_args) + except Exception as e: + logger.exception('Error during simulation') + raise AssertionError from e + + # guess has no meaning here, take the last acquired datapoint + guess = dataset_x[-1] + + # compute the RMSE and MAD on the grid and the average + # rmse = np.sqrt(err_grid**2 + std_grid**2) + mad = np.abs(err_grid) + # total_rmse = np.sqrt(np.mean(rmse**2)) + total_mad = np.sqrt(np.mean(mad**2)) + + # pick the MAD over the RMSE as an error criterion, it only measures the mean deviation: + # it is less sensitive to wrong std and hyperparameter calibration, but does not measure how well + # the "error bars" (std_grid) quantify the actual ty + error = total_mad + print(iterations, error) + + graph( + mad, + dataset_x, + f'{strategy}_{strategy_args.get("backend", "sklearn")}', + ) + + if error <= TARGET_ERROR: + break + iterations += 1 + + return iterations, error, guess + + +TEST_PARAMS = ( + ('backend', 'strategy', 'strategy_args', 'max_iterations'), + [ + ('sklearn', 'uncertainty', {}, 2), + ('sable', 'uncertainty', {}, 2), + ('sklearn', 'random', {}, 2), + # use low number of iterations for unit test + ], +) + + +@pytest.mark.parametrize(*generate_pytest_parameters(TEST_PARAMS, 0)) +def test_benchmark_strainmap_accuracy( + # benchmark: BenchmarkFixture, + backend: str, + strategy: str, + strategy_args: dict, + max_iterations: int, +) -> None: + # NUM_RUNS = 20 + # for _ in range(NUM_RUNS): + # iterations, target = benchmark( + # partial(accuracy_benchmark, strategy, strategy_args) + # ) + iterations, target, guess = accuracy_benchmark( + strategy, {'backend': backend, **strategy_args}, max_iterations + ) + print( + 'Iterations for', + strategy, + ': ', + iterations, + ' best guess:', + guess, + ' with target value:', + target, + ) + print( + 'Maximum early terminus value', + TARGET_ERROR, + ' with ', + max_iterations, + ' maximum iterations.', + ) + print( + 'Accuracy benchmark for strategy:', + strategy, + 'reached' if iterations <= max_iterations else 'not reached', + ) + + +if __name__ == '__main__': + """Generate HTML benchmark report with plots comparing different strategies.""" + import argparse + import datetime + import json + from pathlib import Path + + logger = logging.getLogger(f'{__name__}_runner') + + try: + import matplotlib as mpl + import matplotlib.pyplot as plt + + mpl.use('Agg') # Use non-interactive backend + except ImportError: + logger.error( # noqa: TRY400 + 'Error: matplotlib is required for generating plots. Install it with: pip install matplotlib' + ) + sys.exit(1) + + def positive_int_type(arg): + try: + val = int(arg) + except ValueError as e: + msg = 'Must be an integer' + raise argparse.ArgumentTypeError(msg) from e + if val < 1: + msg = 'Argument must be a positive number' + raise argparse.ArgumentTypeError(msg) + return val + + parser = argparse.ArgumentParser(description='Generate the Strainmap HTML benchmark pages.') + parser.add_argument( + '--num-runs', + '-n', + type=positive_int_type, + default=3, + help='Number of runs for each strategy.', + ) + args = parser.parse_args() + + strategies = TEST_PARAMS[1] + + # Run multiple iterations for statistical analysis + NUM_RUNS = args.num_runs + logger.info('Running benchmarks with %d iterations per strategy...', NUM_RUNS) + + results = {} + for strategy, strategy_args, _ in strategies: + strategy_name = f'{strategy}' + (f' {json.dumps(strategy_args)}' if strategy_args else '') + logger.info('\nBenchmarking: %s', strategy_name) + + iterations_list = [] + targets_list = [] + guesses_list = [] + + for run in range(NUM_RUNS): + iterations, target, guess = accuracy_benchmark(strategy, strategy_args) + iterations_list.append(iterations) + targets_list.append(target) + guesses_list.append(guess) + logger.info( + ' Run %d/%d: iterations=%d, target=%.4f', + run + 1, + NUM_RUNS, + iterations, + target, + ) + + results[strategy_name] = { + 'strategy': strategy, + 'strategy_args': strategy_args, + 'iterations': iterations_list, + 'targets': targets_list, + 'guesses': guesses_list, + 'avg_iterations': np.mean(iterations_list), + 'std_iterations': np.std(iterations_list), + 'avg_target': np.mean(targets_list), + 'std_target': np.std(targets_list), + 'success_rate': sum(1 for t in targets_list if t <= TARGET_ERROR) / NUM_RUNS * 100, + } + + # Generate plots + output_dir = Path('reports/benchmarks') + output_dir.mkdir(parents=True, exist_ok=True) + + fig, axes = plt.subplots(2, 2, figsize=(14, 10)) + fig.suptitle( + 'Strainmap Optimization Benchmark Comparison', + fontsize=16, + fontweight='bold', + ) + + # Plot 1: Average iterations to convergence + ax1 = axes[0, 0] + strategy_names = list(results.keys()) + avg_iterations = [results[s]['avg_iterations'] for s in strategy_names] + std_iterations = [results[s]['std_iterations'] for s in strategy_names] + bars1 = ax1.bar( + range(len(strategy_names)), + avg_iterations, + yerr=std_iterations, + capsize=5, + alpha=0.7, + ) + ax1.set_xlabel('Strategy') + ax1.set_ylabel('Average Iterations') + ax1.set_title('Iterations to Convergence (Lower is Better)') + ax1.set_xticks(range(len(strategy_names))) + ax1.set_xticklabels( + [s.replace(' ', '\n') for s in strategy_names], + rotation=0, + ha='center', + fontsize=8, + ) + ax1.grid(axis='y', alpha=0.3) + + # Add value labels on bars + for _i, (bar, val, std) in enumerate(zip(bars1, avg_iterations, std_iterations, strict=False)): + height = bar.get_height() + ax1.text( + bar.get_x() + bar.get_width() / 2.0, + height, + f'{val:.1f}±{std:.1f}', + ha='center', + va='bottom', + fontsize=9, + ) + + # Plot 2: Average target value achieved + ax2 = axes[0, 1] + avg_targets = [results[s]['avg_target'] for s in strategy_names] + std_targets = [results[s]['std_target'] for s in strategy_names] + bars2 = ax2.bar( + range(len(strategy_names)), + avg_targets, + yerr=std_targets, + capsize=5, + alpha=0.7, + color='orange', + ) + ax2.set_xlabel('Strategy') + ax2.set_ylabel('Average Target Value') + ax2.set_title('Final Target Value (Lower is Better)') + ax2.set_xticks(range(len(strategy_names))) + ax2.set_xticklabels( + [s.replace(' ', '\n') for s in strategy_names], + rotation=0, + ha='center', + fontsize=8, + ) + ax2.axhline( + y=TARGET_ERROR, + color='r', + linestyle='--', + label=f'Target Threshold ({TARGET_ERROR})', + ) + ax2.legend() + ax2.grid(axis='y', alpha=0.3) + + # Add value labels on bars + for _i, (bar, val, std) in enumerate(zip(bars2, avg_targets, std_targets, strict=False)): + height = bar.get_height() + ax2.text( + bar.get_x() + bar.get_width() / 2.0, + height, + f'{val:.2f}±{std:.2f}', + ha='center', + va='bottom', + fontsize=9, + ) + + # Plot 3: Success rate + ax3 = axes[1, 0] + success_rates = [results[s]['success_rate'] for s in strategy_names] + bars3 = ax3.bar(range(len(strategy_names)), success_rates, alpha=0.7, color='green') + ax3.set_xlabel('Strategy') + ax3.set_ylabel('Success Rate (%)') + ax3.set_title(f'Success Rate (Target ≤ {TARGET_ERROR})') + ax3.set_xticks(range(len(strategy_names))) + ax3.set_xticklabels( + [s.replace(' ', '\n') for s in strategy_names], + rotation=0, + ha='center', + fontsize=8, + ) + ax3.set_ylim([0, 105]) + ax3.grid(axis='y', alpha=0.3) + + # Add value labels on bars + for bar, val in zip(bars3, success_rates, strict=False): + height = bar.get_height() + ax3.text( + bar.get_x() + bar.get_width() / 2.0, + height, + f'{val:.1f}%', + ha='center', + va='bottom', + fontsize=9, + ) + + # Plot 4: Box plot of iterations distribution + ax4 = axes[1, 1] + iterations_data = [results[s]['iterations'] for s in strategy_names] + bp = ax4.boxplot(iterations_data, patch_artist=True) + for patch in bp['boxes']: + patch.set_facecolor('lightblue') + ax4.set_xlabel('Strategy') + ax4.set_ylabel('Iterations') + ax4.set_title('Iterations Distribution') + ax4.set_xticklabels([s.replace(' ', '\n') for s in strategy_names], fontsize=8) + ax4.grid(axis='y', alpha=0.3) + + plt.tight_layout() + plot_path = output_dir / 'strainmap_benchmark.png' + plt.savefig(plot_path, dpi=150, bbox_inches='tight') + logger.info('✓ Plot saved to %s', plot_path) + plt.close() + + # Generate HTML report + html_content = f""" + + + + + Strainmap Optimization Benchmark Report + + + +

📊 Strainmap Optimization Benchmark Report

+ + + +
+

🎯 Test Objective

+

This benchmark evaluates different acquisition strategies for Bayesian optimization on a strain mapping experiment dataset. + The goal is to minimize the total surrogate error (over all inputs) within {MAX_ITERATIONS} iterations, starting from {INITIAL_NUM_POINTS} initial points.

+
+ +

📈 Benchmark Results

+ +
+

Performance Comparison

+ Benchmark comparison plots +
+ +

📋 Detailed Statistics

+ + + + + + + + + + + + + + +""" + + # Find best performers + best_iterations_idx = min(range(len(strategy_names)), key=lambda i: avg_iterations[i]) + best_target_idx = min(range(len(strategy_names)), key=lambda i: avg_targets[i]) + best_success_idx = max(range(len(strategy_names)), key=lambda i: success_rates[i]) + + for i, strategy_name in enumerate(strategy_names): + result = results[strategy_name] + row_class = '' + if i in (best_iterations_idx, best_target_idx, best_success_idx): + row_class = ' class="best"' + + args_str = json.dumps(result['strategy_args']) if result['strategy_args'] else 'None' + + html_content += f""" + + + + + + + + +""" + + html_content += """ +
StrategyStrategy ArgsAvg IterationsStd IterationsAvg Target ValueStd Target ValueSuccess Rate
{result['strategy']}{args_str}{result['avg_iterations']:.2f}{result['std_iterations']:.2f}{result['avg_target']:.4f}{result['std_target']:.4f}{result['success_rate']:.1f}%
+ +

🏆 Key Findings

+
+""" + + # Add findings + best_strategy = strategy_names[best_iterations_idx] + html_content += f"""

Fastest Convergence: {best_strategy} with {avg_iterations[best_iterations_idx]:.2f} ± {std_iterations[best_iterations_idx]:.2f} iterations on average

+""" + + best_accuracy_strategy = strategy_names[best_target_idx] + html_content += f"""

Best Accuracy: {best_accuracy_strategy} with average target value {avg_targets[best_target_idx]:.4f} ± {std_targets[best_target_idx]:.4f}

+""" + + best_reliability_strategy = strategy_names[best_success_idx] + html_content += f"""

Most Reliable: {best_reliability_strategy} with {success_rates[best_success_idx]:.1f}% success rate

+""" + + html_content += """
+ +

📊 Raw Data

+
+ Click to expand raw results JSON +
+"""
+
+    # Prepare JSON-serializable results
+    json_results = {}
+    for strategy_name, result in results.items():
+        json_results[strategy_name] = {
+            'strategy': result['strategy'],
+            'strategy_args': result['strategy_args'],
+            'iterations': result['iterations'],
+            'targets': result['targets'],
+            'guesses': [[float(x) for x in guess] for guess in result['guesses']],
+            'statistics': {
+                'avg_iterations': float(result['avg_iterations']),
+                'std_iterations': float(result['std_iterations']),
+                'avg_target': float(result['avg_target']),
+                'std_target': float(result['std_target']),
+                'success_rate': float(result['success_rate']),
+            },
+        }
+
+    html_content += json.dumps(json_results, indent=2)
+    html_content += """
+        
+
+ +
+

Generated by test_strainmap.py benchmark suite

+
+ + +""" + + # Save HTML report + html_path = output_dir / 'strainmap_benchmark.html' + html_path.write_text(html_content) + logger.info('✓ HTML report saved to %s', html_path) + + # Save JSON data + json_path = output_dir / 'strainmap_benchmark.json' + json_path.write_text(json.dumps(json_results, indent=2)) + logger.info('✓ JSON data saved to %s', json_path) + + logger.info('\n%s', '=' * 60) + logger.info('📊 Benchmark Summary') + logger.info('%s', '=' * 60) + for strategy_name in strategy_names: + result = results[strategy_name] + logger.info('\n%s:', strategy_name) + logger.info( + ' Avg Iterations: %.2f ± %.2f', + result['avg_iterations'], + result['std_iterations'], + ) + logger.info( + ' Avg Target: %.4f ± %.4f', + result['avg_target'], + result['std_target'], + ) + logger.info(' Success Rate: %.1f%%', result['success_rate']) + + logger.info('\n%s', '=' * 60) + logger.info('✓ Open %s in your browser to view the full report', html_path) + logger.info('%s', '=' * 60) + # assert iterations <= MAX_ITERATIONS diff --git a/tests/helpers.py b/tests/helpers.py new file mode 100644 index 0000000..930ea72 --- /dev/null +++ b/tests/helpers.py @@ -0,0 +1,30 @@ +from copy import deepcopy + +import pytest + +from dial_service.service_specific_dataclasses import AVAILABLE_DIAL_BACKENDS + + +def generate_pytest_parameters( + old_params: tuple[tuple[str], tuple[list[object]]], backend_idx: int +) -> tuple[tuple[str], tuple[list[object]]]: + """ + Generates test safeguards from generic parameters. + + This should only really be used if you simultaneously want to use generic parameters in contexts other than Pytest. + + Params: + - old_params = your normal pytest.mark.parametrize parameters (don't use pytest.param) + - backend_idx = the index of the "backend" parameter in your args list""" + new_params = deepcopy(old_params) + for i, test in enumerate(old_params[1]): + backend_name = test[backend_idx] + if backend_name != 'sklearn': + new_params[1][i] = pytest.param( + *test, + marks=pytest.mark.skipif( + backend_name not in AVAILABLE_DIAL_BACKENDS, + reason=f'{backend_name} not installed', + ), + ) + return new_params diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_internals.py b/tests/unit/test_internals.py index 1b6f9c1..7d32e16 100644 --- a/tests/unit/test_internals.py +++ b/tests/unit/test_internals.py @@ -18,6 +18,7 @@ ServersideInputSingle, ) from dial_service.service_specific_dataclasses import ( + AVAILABLE_DIAL_BACKENDS, DialWorkflowCreationParamsService, ) @@ -332,7 +333,13 @@ def prediction_1D_heteroscedastic(backend): ('backend', 'approx'), [ ('sklearn', 1.842309), - # ('gpax', 2.0), + # pytest.param( + # 'gpax', 2.0, + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_EI_1D(backend, approx): @@ -349,7 +356,13 @@ def test_EI_1D(backend, approx): ('backend', 'val'), [ ('sklearn', 1.0), - # ('gpax', 2.0), + # pytest.param( + # 'gpax', 2.0, + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_EI_1D_discrete(backend, val): @@ -370,7 +383,13 @@ def test_EI_1D_discrete(backend, val): ('backend', 'approx'), [ ('sklearn', [1.705352, -1.682829]), - # ('gpax', [2.0, 2.0]), + # pytest.param( + # 'gpax', [2.0, 2,0], + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_EI_2D(backend, approx): @@ -387,7 +406,12 @@ def test_EI_2D(backend, approx): ('backend', 'approx'), [ ('sklearn', [2.000000, -1.143727, -1.859496]), - # ('gpax', [2.0, 2.0, -2.0], # WAS: [2.0, 2.0, 2.0] + # pytest.param( + # 'gpax', [2.0, 2,0, 2.0,], # WAS: [2.0,2.0,2,0] + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), # ), ], ) @@ -405,7 +429,13 @@ def test_EI_3D(backend, approx): ('backend', 'approx'), [ ('sklearn', [1.5]), - # ('gpax',), + # pytest.param( + # 'gpax' [2.0], + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_uncertainty(backend, approx): @@ -419,7 +449,13 @@ def test_uncertainty(backend, approx): ('backend', 'approx'), [ ('sklearn', [1.790396262]), - # ('gpax', [2.0]), + # pytest.param( + # 'gpax' [2.0], + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_preprocessing_standardize(backend, approx): @@ -435,7 +471,13 @@ def test_preprocessing_standardize(backend, approx): ('backend', 'val'), [ ('sklearn', [1.0]), - # ('gpax', [2.0]), + # pytest.param( + # 'gpax' [2.0], + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_preprocessing_standardize_discrete(backend, val): @@ -459,7 +501,13 @@ def test_preprocessing_standardize_discrete(backend, val): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_random(backend): @@ -475,7 +523,13 @@ def test_random(backend): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_hypercube_single_point(backend): @@ -505,7 +559,13 @@ def test_hypercube_single_point(backend): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_random_discrete(backend): @@ -530,7 +590,13 @@ def test_random_discrete(backend): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_hypercube_single_point_discrete(backend): @@ -566,7 +632,13 @@ def test_hypercube_single_point_discrete(backend): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_hypercube_single_point_discrete_2D(backend): @@ -601,7 +673,13 @@ def test_hypercube_single_point_discrete_2D(backend): ('backend'), [ ('sklearn'), - ('gpax'), + pytest.param( + 'gpax', + marks=pytest.mark.skipif( + 'gpax' not in AVAILABLE_DIAL_BACKENDS, + reason='gpax not installed', + ), + ), ], ) def test_random_points(backend): @@ -616,7 +694,13 @@ def test_random_points(backend): ('backend'), [ ('sklearn'), - # ('gpax'), + # pytest.param( + # 'gpax', + # marks=pytest.mark.skipif( + # 'gpax' not in AVAILABLE_DIAL_BACKENDS, + # reason='gpax not installed', + # ), + # ), ], ) def test_hypercube_multiple_points(backend): @@ -644,7 +728,7 @@ def test_hypercube_multiple_points(backend): ], [2.11126987e01, 2.96625069e01, 2.11126987e01], ), - # ( + # pytest.param( # 'gpax', # [ # 76.99987768175089, @@ -654,6 +738,7 @@ def test_hypercube_multiple_points(backend): # 82.26569221517353, # ], # [3335.7290084812175, 3327.202331393974, 3335.7290084812175], + # marks=pytest.mark.skipif('gpax' not in AVAILABLE_DIAL_BACKENDS, reason='gpax not installed') # ), ], ) @@ -673,10 +758,14 @@ def test_surrogate(backend, expected_means, expected_stddevs): [-1.0, -0.65, 0.0, 0.65, 1.0], [1e-2, 0.42, 0.59, 0.42, 1e-5], ), - ( + pytest.param( 'sable', [-1.0, -0.56, 0.0, 0.56, 1.0], [1e-2, 0.29, 0.34, 0.29, 1e-5], + marks=pytest.mark.skipif( + 'sable' not in AVAILABLE_DIAL_BACKENDS, + reason='sable not installed', + ), ), ], ) From bc8ec52d6c51923ee3a39881232f7ab88e419f7f Mon Sep 17 00:00:00 2001 From: Lance-Drane Date: Fri, 24 Jul 2026 11:10:49 -0400 Subject: [PATCH 4/4] add note about installing dependencies through pip in README Signed-off-by: Lance-Drane --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f17f8d4..a568e92 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,12 @@ To install Dial from source, git clone this repository and run the following fro `pip install .` -Alternatively, both intersect-sdk and Dial may be installed with the following: +By default, **only scikit-learn is available as a backend**. We also support GPax and Sable as backends. To install these: -`pip install -e .` +- for GPax: `pip install ".[gpax]"` +- for Sable: `pip install ".[sable]"` + +Use commas to add multiple backends: `pip install ".[gpax,sable]"` ## Installing (developers)