From 7c44049815a9fdcc52c4b573fcc5ed47523f0aba Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Wed, 17 Dec 2025 05:56:03 +0000 Subject: [PATCH 01/11] update and fix --- docs/index.md | 6 +- pyproject.toml | 14 +--- uv.lock | 211 +++++++++++++++++++++++++++++++++++-------------- 3 files changed, 157 insertions(+), 74 deletions(-) diff --git a/docs/index.md b/docs/index.md index 108ec21..d8e45ee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,10 +8,10 @@ This repository contains the Pytorch code in our paper [SNRAware: Improved Deep author = {Xue, Hui and Hooper, Sarah M. and Pierce, Iain and Davies, Rhodri H. and Stairs, John and Naegele, Joseph and Campbell-Washburn, Adrienne E. and Manisty, Charlotte and Moon, James C. and Treibel, Thomas A. and Hansen, Michael S. and Kellman, Peter}, title = {SNRAware: Improved Deep Learning MRI Denoising with Signal-to-noise Ratio Unit Training and G-factor Map Augmentation}, journal = {Radiology: Artificial Intelligence}, - volume = {0}, - number = {ja}, + volume = {7}, + number = {6}, pages = {e250227}, - year = {0}, + year = {2025}, doi = {10.1148/ryai.250227}, note ={PMID: 41123451}, URL = {https://doi.org/10.1148/ryai.250227} diff --git a/pyproject.toml b/pyproject.toml index 51dcbd5..5eecb9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,9 @@ authors = [ keywords = [] license = "MIT" dependencies = [ - "mkl-fft>=2.0.0", - "mkl-service>=2.4.2", + "mkl", + "mkl-fft", + "mkl-service", "typing_extensions", "click", "h5py", @@ -78,15 +79,6 @@ build-backend = "uv_build" [tool.uv.build-backend] module-name = ["snraware"] -[[tool.uv.index]] -name = "mkl" -url = "https://urob.github.io/numpy-mkl" - -[tool.uv.sources] -numpy = { index = "mkl" } -scipy = { index = "mkl" } -mkl-service = { index = "mkl" } - [project.scripts] [tool.pyright] diff --git a/uv.lock b/uv.lock index 59f20d9..c7dbed0 100644 --- a/uv.lock +++ b/uv.lock @@ -1393,25 +1393,25 @@ wheels = [ [[package]] name = "intel-cmplr-lib-ur" version = "2025.3.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "umf" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/71/33/94785f0575f7319e57788169ef15e504c1e9e9744f04e0b9e00390e1f533/intel_cmplr_lib_ur-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:35e45764907c100090d8dcb75d4d31aa7dc330e5f2d887032c9eb47d3e29d43e" }, - { url = "https://files.pythonhosted.org/packages/c7/a4/a7f61cc9beca3b6aba669381dfcaf3d36cd1f5f5f7716ba32ca091258c3d/intel_cmplr_lib_ur-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:4557a0281128e60628d9fcdca4ce38de40f13b32e8f4bf38ba82045f147c58e2" }, + { url = "https://files.pythonhosted.org/packages/71/33/94785f0575f7319e57788169ef15e504c1e9e9744f04e0b9e00390e1f533/intel_cmplr_lib_ur-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:35e45764907c100090d8dcb75d4d31aa7dc330e5f2d887032c9eb47d3e29d43e", size = 30770342, upload-time = "2025-10-22T17:59:38.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a4/a7f61cc9beca3b6aba669381dfcaf3d36cd1f5f5f7716ba32ca091258c3d/intel_cmplr_lib_ur-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:4557a0281128e60628d9fcdca4ce38de40f13b32e8f4bf38ba82045f147c58e2", size = 1256187, upload-time = "2025-10-22T18:00:38.419Z" }, ] [[package]] name = "intel-openmp" version = "2025.3.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "intel-cmplr-lib-ur" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/87/68241d0b532f0e41d5b2928b640b00f9bb48945df9921df1d878f42c1d38/intel_openmp-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:948d9d6a22ed7643305a717c2ac2678bae8c11adafd5e4d30f903cf9933b4a60" }, - { url = "https://files.pythonhosted.org/packages/79/69/05addedd727061b61a85b4fd989754edb628b5be1cd5d161727f98cf4d86/intel_openmp-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:8ff899d01a41b92ffe0e618b221ea633127d16df726086082d381c431c31339d" }, + { url = "https://files.pythonhosted.org/packages/6a/87/68241d0b532f0e41d5b2928b640b00f9bb48945df9921df1d878f42c1d38/intel_openmp-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:948d9d6a22ed7643305a717c2ac2678bae8c11adafd5e4d30f903cf9933b4a60", size = 74331543, upload-time = "2025-10-22T17:59:28.77Z" }, + { url = "https://files.pythonhosted.org/packages/79/69/05addedd727061b61a85b4fd989754edb628b5be1cd5d161727f98cf4d86/intel_openmp-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:8ff899d01a41b92ffe0e618b221ea633127d16df726086082d381c431c31339d", size = 32083814, upload-time = "2025-10-22T18:00:51.397Z" }, ] [[package]] @@ -2013,15 +2013,15 @@ wheels = [ [[package]] name = "mkl" version = "2025.3.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "intel-openmp" }, { name = "onemkl-license" }, { name = "tbb" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b4/ef531295ed33b929c6c5214421eeebe370f1be22536b6956b4aaf18fdbc5/mkl-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7b81f5d2e034637b187ca58666878c4e8889988a78e39e0b6ee92e846568f660" }, - { url = "https://files.pythonhosted.org/packages/75/0e/b863a60889643e21d5d8d6b619f913703257878979b1df22d344a56276d0/mkl-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:2dbf81b016150c2ce418d80707dcce11d0e3c56f20e1d54813174e7665405561" }, + { url = "https://files.pythonhosted.org/packages/6d/b4/ef531295ed33b929c6c5214421eeebe370f1be22536b6956b4aaf18fdbc5/mkl-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7b81f5d2e034637b187ca58666878c4e8889988a78e39e0b6ee92e846568f660", size = 195022935, upload-time = "2025-10-22T17:56:20.854Z" }, + { url = "https://files.pythonhosted.org/packages/75/0e/b863a60889643e21d5d8d6b619f913703257878979b1df22d344a56276d0/mkl-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:2dbf81b016150c2ce418d80707dcce11d0e3c56f20e1d54813174e7665405561", size = 155530420, upload-time = "2025-10-22T17:57:46.918Z" }, ] [[package]] @@ -2042,17 +2042,15 @@ wheels = [ [[package]] name = "mkl-service" version = "2.6.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mkl" }, ] wheels = [ - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15908dd44cc83aa822222c83cc15fa2f31201a43cc0a37949a64cf377bf27887" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec2728d541547a56ce0b8050f994225ef9ceee994abd711e7b1d9943040ae92" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:415d5b27ec47dfbde316c4fb387f13344917a3fb354324d89d8f5f7da58bcac5" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:54d7e8d984f39c6511ab2c2abceb80ad5f6e278d7f2c0bf9e57d5eb11110cfb5" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20773a79b0a15be7ea2decf3e3a127491a75816f92fce67f6acdd36d01fa4fb2" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/mkl_service-2.6.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d2c725f48aad3935e0ee259f0e156760f612c18eb7336374df8fd26fdc3a700" }, + { url = "https://files.pythonhosted.org/packages/3f/2b/da412ee13861f7b355b431cc1deeaadd08f6e4ebb6b52affb7d264452593/mkl_service-2.6.0-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:5b6616b794691186236d30d383bca4d3fa569ed5d89a9c0c051197f0b20d3df1", size = 82544, upload-time = "2025-10-27T18:06:17.911Z" }, + { url = "https://files.pythonhosted.org/packages/1d/6d/6dc4ce8c23aaaacfd29d4c68331901aca6deeaf6c149e38f658b5a08d287/mkl_service-2.6.0-0-cp312-cp312-win_amd64.whl", hash = "sha256:548e3b45d59d24d6ecb7dad44ae26fb71ddeed8856ed58bedb30aa9a09ccb635", size = 83943, upload-time = "2025-10-27T18:06:42.635Z" }, + { url = "https://files.pythonhosted.org/packages/a9/36/bb9930f49f845c9f9fec943b3cf87052239cd0d2a4c77965268b70455e79/mkl_service-2.6.0-0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:98f5f6a05bce94c8675843d863f1fc0c06fee2a7f14e5f648b221102d44f137e", size = 81590, upload-time = "2025-10-27T18:07:07.21Z" }, + { url = "https://files.pythonhosted.org/packages/40/c6/553c57b3165469824a1d0184b8056402d7f4dd06127111e2111482d2637a/mkl_service-2.6.0-0-cp313-cp313-win_amd64.whl", hash = "sha256:336bea1b2ef4213cf95ed4b40f1feaadb6a692dfd01ce9b87ae297776459d7fe", size = 83229, upload-time = "2025-10-27T18:07:21.82Z" }, ] [[package]] @@ -2260,18 +2258,64 @@ wheels = [ [[package]] name = "numpy" version = "2.3.4" -source = { registry = "https://urob.github.io/numpy-mkl" } -dependencies = [ - { name = "mkl" }, - { name = "mkl-service" }, -] -wheels = [ - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbe8e5ab4fb4e5aeb7f31df1a3fe62b71e386977fde1d197df321d4e828510ea" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:38d950c6072edb1fc544d01348516511ce9a905375b06328bbf86b57da3546b3" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47c06c97541890db9fb9ac80baf8647989385ce46dc7a6b4a945cb71f476e56c" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:d162838135b209704dbf3962144329d65fdebd5d91b3e88debae24bdb2eb5c41" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cabd671a9fc06f331796e6a9eb891435eb7194e74fe82c97e86b165c03780985" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:8d2aeab5102ee4866edec255800bb87c0c8b19828bc48e20c7304dde45c15ed3" }, +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload-time = "2025-10-15T16:18:11.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727, upload-time = "2025-10-15T16:15:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262, upload-time = "2025-10-15T16:15:47.761Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992, upload-time = "2025-10-15T16:15:50.144Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672, upload-time = "2025-10-15T16:15:52.442Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156, upload-time = "2025-10-15T16:15:54.351Z" }, + { url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271, upload-time = "2025-10-15T16:15:56.67Z" }, + { url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531, upload-time = "2025-10-15T16:15:59.412Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983, upload-time = "2025-10-15T16:16:01.804Z" }, + { url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380, upload-time = "2025-10-15T16:16:03.938Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999, upload-time = "2025-10-15T16:16:05.801Z" }, + { url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412, upload-time = "2025-10-15T16:16:07.854Z" }, + { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload-time = "2025-10-15T16:16:10.304Z" }, + { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload-time = "2025-10-15T16:16:12.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload-time = "2025-10-15T16:16:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload-time = "2025-10-15T16:16:16.805Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload-time = "2025-10-15T16:16:18.764Z" }, + { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload-time = "2025-10-15T16:16:21.072Z" }, + { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload-time = "2025-10-15T16:16:23.369Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload-time = "2025-10-15T16:16:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload-time = "2025-10-15T16:16:29.811Z" }, + { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload-time = "2025-10-15T16:16:31.589Z" }, + { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload-time = "2025-10-15T16:16:33.902Z" }, + { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload-time = "2025-10-15T16:16:36.101Z" }, + { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload-time = "2025-10-15T16:16:39.124Z" }, + { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload-time = "2025-10-15T16:16:41.168Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload-time = "2025-10-15T16:16:43.777Z" }, + { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload-time = "2025-10-15T16:16:46.081Z" }, + { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload-time = "2025-10-15T16:16:48.455Z" }, + { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload-time = "2025-10-15T16:16:51.114Z" }, + { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload-time = "2025-10-15T16:16:53.429Z" }, + { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload-time = "2025-10-15T16:16:55.992Z" }, + { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload-time = "2025-10-15T16:16:57.943Z" }, + { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload-time = "2025-10-15T16:17:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload-time = "2025-10-15T16:17:02.509Z" }, + { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload-time = "2025-10-15T16:17:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload-time = "2025-10-15T16:17:07.499Z" }, + { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload-time = "2025-10-15T16:17:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload-time = "2025-10-15T16:17:11.937Z" }, + { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload-time = "2025-10-15T16:17:14.391Z" }, + { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload-time = "2025-10-15T16:17:17.058Z" }, + { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload-time = "2025-10-15T16:17:19.379Z" }, + { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload-time = "2025-10-15T16:17:22.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload-time = "2025-10-15T16:17:24.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload-time = "2025-10-15T16:17:26.935Z" }, + { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload-time = "2025-10-15T16:17:29.638Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload-time = "2025-10-15T16:17:32.384Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload-time = "2025-10-15T16:17:34.515Z" }, + { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload-time = "2025-10-15T16:17:36.128Z" }, + { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload-time = "2025-10-15T16:17:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload-time = "2025-10-15T16:17:41.564Z" }, + { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload-time = "2025-10-15T16:17:43.901Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload-time = "2025-10-15T16:17:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload-time = "2025-10-15T16:17:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload-time = "2025-10-15T16:17:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload-time = "2025-10-15T16:17:53.48Z" }, ] [[package]] @@ -2311,7 +2355,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -2322,7 +2366,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -2349,9 +2393,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -2362,7 +2406,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -2424,10 +2468,10 @@ wheels = [ [[package]] name = "onemkl-license" version = "2025.3.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/11/b43e8cde058c368ce7f8f9b1ca9f812f7397e4309148da7d24cb6b81b513/onemkl_license-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a810b25bb24a90db4492d81c71cde13183d8951ce26960d6dc56d4f4e3dc95ff" }, - { url = "https://files.pythonhosted.org/packages/0e/8e/98ad8181e4af2ea7a6c7827ef6d24c73b6ee96a2890eacf46103f596195d/onemkl_license-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:35a179620bcadbcc3d17d74f7f72cab75889f3736b89685f6a68158325b65e91" }, + { url = "https://files.pythonhosted.org/packages/88/11/b43e8cde058c368ce7f8f9b1ca9f812f7397e4309148da7d24cb6b81b513/onemkl_license-2025.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a810b25bb24a90db4492d81c71cde13183d8951ce26960d6dc56d4f4e3dc95ff", size = 58936, upload-time = "2025-10-22T17:55:41.998Z" }, + { url = "https://files.pythonhosted.org/packages/0e/8e/98ad8181e4af2ea7a6c7827ef6d24c73b6ee96a2890eacf46103f596195d/onemkl_license-2025.3.0-py2.py3-none-win_amd64.whl", hash = "sha256:35a179620bcadbcc3d17d74f7f72cab75889f3736b89685f6a68158325b65e91", size = 58961, upload-time = "2025-10-22T17:57:54.095Z" }, ] [[package]] @@ -3412,6 +3456,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/150fdb96b8fab27fe08d8a59fe67554568727981806e6bc2677a16081ec7/ruamel_yaml_clib-0.2.14-cp314-cp314-win32.whl", hash = "sha256:9b4104bf43ca0cd4e6f738cb86326a3b2f6eef00f417bd1e7efb7bdffe74c539", size = 102394, upload-time = "2025-11-14T21:57:36.703Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/a3fa40084558c7e1dc9546385f22a93949c890a8b2e445b2ba43935f51da/ruamel_yaml_clib-0.2.14-cp314-cp314-win_amd64.whl", hash = "sha256:13997d7d354a9890ea1ec5937a219817464e5cc344805b37671562a401ca3008", size = 122673, upload-time = "2025-11-14T21:57:38.177Z" }, ] [[package]] @@ -3472,19 +3518,62 @@ wheels = [ [[package]] name = "scipy" version = "1.16.3" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mkl" }, - { name = "mkl-service" }, { name = "numpy" }, ] -wheels = [ - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82cdf597dce708a7e5ad21f8f7e2382f18c750866c7fea84156ffc5100eeca85" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:69892facb9b4201079fd6c73c646d6e0ab2376e64e1213e3de62941def53e2fa" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6296838860731f71ca6bd44e18a6b6a649b405b52eb48ba05bb994db06d8520c" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:f2ad7f0b53030e48b7003bd1d596abffd53251e2513bb5371674a590d3486ffe" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92756f22310ece2e27df010f1dc30ae5c473bafb7f9f5d5e9e04a532e4399bfd" }, - { url = "https://github.com/urob/numpy-mkl/releases/download/0.1.2/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:90af63e42b02812edd3eae50236de9953d439b2cc4b153aecaec586df9d944fb" }, +sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" }, + { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" }, + { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" }, + { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" }, + { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" }, + { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" }, + { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" }, + { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" }, + { url = "https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c", size = 36617856, upload-time = "2025-10-28T17:33:31.375Z" }, + { url = "https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d", size = 28874306, upload-time = "2025-10-28T17:33:36.516Z" }, + { url = "https://files.pythonhosted.org/packages/15/65/3a9400efd0228a176e6ec3454b1fa998fbbb5a8defa1672c3f65706987db/scipy-1.16.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9", size = 20865371, upload-time = "2025-10-28T17:33:42.094Z" }, + { url = "https://files.pythonhosted.org/packages/33/d7/eda09adf009a9fb81827194d4dd02d2e4bc752cef16737cc4ef065234031/scipy-1.16.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4", size = 23524877, upload-time = "2025-10-28T17:33:48.483Z" }, + { url = "https://files.pythonhosted.org/packages/7d/6b/3f911e1ebc364cb81320223a3422aab7d26c9c7973109a9cd0f27c64c6c0/scipy-1.16.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959", size = 33342103, upload-time = "2025-10-28T17:33:56.495Z" }, + { url = "https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88", size = 35697297, upload-time = "2025-10-28T17:34:04.722Z" }, + { url = "https://files.pythonhosted.org/packages/04/e1/6496dadbc80d8d896ff72511ecfe2316b50313bfc3ebf07a3f580f08bd8c/scipy-1.16.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234", size = 36021756, upload-time = "2025-10-28T17:34:13.482Z" }, + { url = "https://files.pythonhosted.org/packages/fe/bd/a8c7799e0136b987bda3e1b23d155bcb31aec68a4a472554df5f0937eef7/scipy-1.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d", size = 38696566, upload-time = "2025-10-28T17:34:22.384Z" }, + { url = "https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304", size = 38529877, upload-time = "2025-10-28T17:35:51.076Z" }, + { url = "https://files.pythonhosted.org/packages/7f/14/9d9fbcaa1260a94f4bb5b64ba9213ceb5d03cd88841fe9fd1ffd47a45b73/scipy-1.16.3-cp313-cp313-win_arm64.whl", hash = "sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2", size = 25455366, upload-time = "2025-10-28T17:35:59.014Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a3/9ec205bd49f42d45d77f1730dbad9ccf146244c1647605cf834b3a8c4f36/scipy-1.16.3-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b", size = 37027931, upload-time = "2025-10-28T17:34:31.451Z" }, + { url = "https://files.pythonhosted.org/packages/25/06/ca9fd1f3a4589cbd825b1447e5db3a8ebb969c1eaf22c8579bd286f51b6d/scipy-1.16.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079", size = 29400081, upload-time = "2025-10-28T17:34:39.087Z" }, + { url = "https://files.pythonhosted.org/packages/6a/56/933e68210d92657d93fb0e381683bc0e53a965048d7358ff5fbf9e6a1b17/scipy-1.16.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a", size = 21391244, upload-time = "2025-10-28T17:34:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/a8/7e/779845db03dc1418e215726329674b40576879b91814568757ff0014ad65/scipy-1.16.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119", size = 23929753, upload-time = "2025-10-28T17:34:51.793Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/f756cf8161d5365dcdef9e5f460ab226c068211030a175d2fc7f3f41ca64/scipy-1.16.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c", size = 33496912, upload-time = "2025-10-28T17:34:59.8Z" }, + { url = "https://files.pythonhosted.org/packages/09/b5/222b1e49a58668f23839ca1542a6322bb095ab8d6590d4f71723869a6c2c/scipy-1.16.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e", size = 35802371, upload-time = "2025-10-28T17:35:08.173Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8d/5964ef68bb31829bde27611f8c9deeac13764589fe74a75390242b64ca44/scipy-1.16.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135", size = 36190477, upload-time = "2025-10-28T17:35:16.7Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f2/b31d75cb9b5fa4dd39a0a931ee9b33e7f6f36f23be5ef560bf72e0f92f32/scipy-1.16.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6", size = 38796678, upload-time = "2025-10-28T17:35:26.354Z" }, + { url = "https://files.pythonhosted.org/packages/b4/1e/b3723d8ff64ab548c38d87055483714fefe6ee20e0189b62352b5e015bb1/scipy-1.16.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc", size = 38640178, upload-time = "2025-10-28T17:35:35.304Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f3/d854ff38789aca9b0cc23008d607ced9de4f7ab14fa1ca4329f86b3758ca/scipy-1.16.3-cp313-cp313t-win_arm64.whl", hash = "sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a", size = 25803246, upload-time = "2025-10-28T17:35:42.155Z" }, + { url = "https://files.pythonhosted.org/packages/99/f6/99b10fd70f2d864c1e29a28bbcaa0c6340f9d8518396542d9ea3b4aaae15/scipy-1.16.3-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6", size = 36606469, upload-time = "2025-10-28T17:36:08.741Z" }, + { url = "https://files.pythonhosted.org/packages/4d/74/043b54f2319f48ea940dd025779fa28ee360e6b95acb7cd188fad4391c6b/scipy-1.16.3-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657", size = 28872043, upload-time = "2025-10-28T17:36:16.599Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e1/24b7e50cc1c4ee6ffbcb1f27fe9f4c8b40e7911675f6d2d20955f41c6348/scipy-1.16.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26", size = 20862952, upload-time = "2025-10-28T17:36:22.966Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3a/3e8c01a4d742b730df368e063787c6808597ccb38636ed821d10b39ca51b/scipy-1.16.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc", size = 23508512, upload-time = "2025-10-28T17:36:29.731Z" }, + { url = "https://files.pythonhosted.org/packages/1f/60/c45a12b98ad591536bfe5330cb3cfe1850d7570259303563b1721564d458/scipy-1.16.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22", size = 33413639, upload-time = "2025-10-28T17:36:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/71/bc/35957d88645476307e4839712642896689df442f3e53b0fa016ecf8a3357/scipy-1.16.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc", size = 35704729, upload-time = "2025-10-28T17:36:46.547Z" }, + { url = "https://files.pythonhosted.org/packages/3b/15/89105e659041b1ca11c386e9995aefacd513a78493656e57789f9d9eab61/scipy-1.16.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0", size = 36086251, upload-time = "2025-10-28T17:36:55.161Z" }, + { url = "https://files.pythonhosted.org/packages/1a/87/c0ea673ac9c6cc50b3da2196d860273bc7389aa69b64efa8493bdd25b093/scipy-1.16.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800", size = 38716681, upload-time = "2025-10-28T17:37:04.1Z" }, + { url = "https://files.pythonhosted.org/packages/91/06/837893227b043fb9b0d13e4bd7586982d8136cb249ffb3492930dab905b8/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d", size = 39358423, upload-time = "2025-10-28T17:38:20.005Z" }, + { url = "https://files.pythonhosted.org/packages/95/03/28bce0355e4d34a7c034727505a02d19548549e190bedd13a721e35380b7/scipy-1.16.3-cp314-cp314-win_arm64.whl", hash = "sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f", size = 26135027, upload-time = "2025-10-28T17:38:24.966Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6f/69f1e2b682efe9de8fe9f91040f0cd32f13cfccba690512ba4c582b0bc29/scipy-1.16.3-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c", size = 37028379, upload-time = "2025-10-28T17:37:14.061Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2d/e826f31624a5ebbab1cd93d30fd74349914753076ed0593e1d56a98c4fb4/scipy-1.16.3-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40", size = 29400052, upload-time = "2025-10-28T17:37:21.709Z" }, + { url = "https://files.pythonhosted.org/packages/69/27/d24feb80155f41fd1f156bf144e7e049b4e2b9dd06261a242905e3bc7a03/scipy-1.16.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d", size = 21391183, upload-time = "2025-10-28T17:37:29.559Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d3/1b229e433074c5738a24277eca520a2319aac7465eea7310ea6ae0e98ae2/scipy-1.16.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa", size = 23930174, upload-time = "2025-10-28T17:37:36.306Z" }, + { url = "https://files.pythonhosted.org/packages/16/9d/d9e148b0ec680c0f042581a2be79a28a7ab66c0c4946697f9e7553ead337/scipy-1.16.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8", size = 33497852, upload-time = "2025-10-28T17:37:42.228Z" }, + { url = "https://files.pythonhosted.org/packages/2f/22/4e5f7561e4f98b7bea63cf3fd7934bff1e3182e9f1626b089a679914d5c8/scipy-1.16.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353", size = 35798595, upload-time = "2025-10-28T17:37:48.102Z" }, + { url = "https://files.pythonhosted.org/packages/83/42/6644d714c179429fc7196857866f219fef25238319b650bb32dde7bf7a48/scipy-1.16.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146", size = 36186269, upload-time = "2025-10-28T17:37:53.72Z" }, + { url = "https://files.pythonhosted.org/packages/ac/70/64b4d7ca92f9cf2e6fc6aaa2eecf80bb9b6b985043a9583f32f8177ea122/scipy-1.16.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d", size = 38802779, upload-time = "2025-10-28T17:37:59.393Z" }, + { url = "https://files.pythonhosted.org/packages/61/82/8d0e39f62764cce5ffd5284131e109f07cf8955aef9ab8ed4e3aa5e30539/scipy-1.16.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7", size = 39471128, upload-time = "2025-10-28T17:38:05.259Z" }, + { url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" }, ] [[package]] @@ -3643,6 +3732,7 @@ dependencies = [ { name = "imageio" }, { name = "lightning" }, { name = "matplotlib" }, + { name = "mkl" }, { name = "mkl-fft" }, { name = "mkl-service" }, { name = "nibabel" }, @@ -3700,10 +3790,11 @@ requires-dist = [ { name = "imageio" }, { name = "lightning" }, { name = "matplotlib" }, - { name = "mkl-fft", specifier = ">=2.0.0" }, - { name = "mkl-service", specifier = ">=2.4.2", index = "https://urob.github.io/numpy-mkl" }, + { name = "mkl" }, + { name = "mkl-fft" }, + { name = "mkl-service" }, { name = "nibabel" }, - { name = "numpy", specifier = ">=2.2.6", index = "https://urob.github.io/numpy-mkl" }, + { name = "numpy", specifier = ">=2.2.6" }, { name = "omegaconf" }, { name = "opencv-python" }, { name = "pandas" }, @@ -3715,7 +3806,7 @@ requires-dist = [ { name = "pywavelets" }, { name = "pyyaml" }, { name = "scikit-image" }, - { name = "scipy", index = "https://urob.github.io/numpy-mkl" }, + { name = "scipy" }, { name = "setuptools" }, { name = "torch", specifier = ">=2.8.0" }, { name = "torch-interpol" }, @@ -3803,22 +3894,22 @@ wheels = [ [[package]] name = "tbb" version = "2022.3.0" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tcmlib" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/9e/b7f1f7af53580e4e8cf39cf51b14c8e295d767b3ae9d78b5007d6058cfc8/tbb-2022.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a7e122cb98a6f8823940341aa323dcdfffa77e36712a22a1ae3a76430fa9f412" }, - { url = "https://files.pythonhosted.org/packages/19/e7/8a3ae90c12186033dfd38a7292b2caef3ed10813162ab4713149191d70ef/tbb-2022.3.0-py3-none-win_amd64.whl", hash = "sha256:bb893f5772855ff2fd21867cb5c2806cb7d2960a178e660b342bc6a706a6ebf4" }, + { url = "https://files.pythonhosted.org/packages/e3/9e/b7f1f7af53580e4e8cf39cf51b14c8e295d767b3ae9d78b5007d6058cfc8/tbb-2022.3.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a7e122cb98a6f8823940341aa323dcdfffa77e36712a22a1ae3a76430fa9f412", size = 4178631, upload-time = "2025-10-22T18:00:00.181Z" }, + { url = "https://files.pythonhosted.org/packages/19/e7/8a3ae90c12186033dfd38a7292b2caef3ed10813162ab4713149191d70ef/tbb-2022.3.0-py3-none-win_amd64.whl", hash = "sha256:bb893f5772855ff2fd21867cb5c2806cb7d2960a178e660b342bc6a706a6ebf4", size = 422733, upload-time = "2025-10-22T18:01:20.903Z" }, ] [[package]] name = "tcmlib" version = "1.4.1" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/a4/38e8b5a27b66ab286168ba6c449771ed71d71ec76524e7f12401474a5151/tcmlib-1.4.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:0d5bd98db48d31bec7fedba5c23599bf9ae43c7016d4c3946d25242d320cee89" }, - { url = "https://files.pythonhosted.org/packages/eb/4d/2b1d55dba475f602197e284b9a5e5460139a9c5ef2bfb63c8d2a1fafc163/tcmlib-1.4.1-py2.py3-none-win_amd64.whl", hash = "sha256:5202a1fd8541182fbd4ef72848784e4bf94340152d7ddff33d401d30930fa53c" }, + { url = "https://files.pythonhosted.org/packages/a1/a4/38e8b5a27b66ab286168ba6c449771ed71d71ec76524e7f12401474a5151/tcmlib-1.4.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:0d5bd98db48d31bec7fedba5c23599bf9ae43c7016d4c3946d25242d320cee89", size = 2731276, upload-time = "2025-10-22T17:57:02.392Z" }, + { url = "https://files.pythonhosted.org/packages/eb/4d/2b1d55dba475f602197e284b9a5e5460139a9c5ef2bfb63c8d2a1fafc163/tcmlib-1.4.1-py2.py3-none-win_amd64.whl", hash = "sha256:5202a1fd8541182fbd4ef72848784e4bf94340152d7ddff33d401d30930fa53c", size = 370347, upload-time = "2025-10-22T18:00:36.37Z" }, ] [[package]] @@ -4053,13 +4144,13 @@ wheels = [ [[package]] name = "umf" version = "1.0.2" -source = { registry = "https://urob.github.io/numpy-mkl" } +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tcmlib" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/27/8e/4a90b6aa955268988e7491f502b7ac2bd65cb954b4979bfcc892cf019b50/umf-1.0.2-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:ef4d144a2007a73a1a22ee575ee4f5a1894a206532c6f6d77b4cf548643502fb" }, - { url = "https://files.pythonhosted.org/packages/4c/8c/dfbcedb97b56b2e87a9fe61e43e70b09abb439ed90e06e08d4365fc60c5f/umf-1.0.2-py2.py3-none-win_amd64.whl", hash = "sha256:695b6be076a7bd2037b0cdbe5ee025f9ae74934cc78bdf25df8b52e8f746e457" }, + { url = "https://files.pythonhosted.org/packages/27/8e/4a90b6aa955268988e7491f502b7ac2bd65cb954b4979bfcc892cf019b50/umf-1.0.2-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:ef4d144a2007a73a1a22ee575ee4f5a1894a206532c6f6d77b4cf548643502fb", size = 359878, upload-time = "2025-10-22T17:58:50.929Z" }, + { url = "https://files.pythonhosted.org/packages/4c/8c/dfbcedb97b56b2e87a9fe61e43e70b09abb439ed90e06e08d4365fc60c5f/umf-1.0.2-py2.py3-none-win_amd64.whl", hash = "sha256:695b6be076a7bd2037b0cdbe5ee025f9ae74934cc78bdf25df8b52e8f746e457", size = 249097, upload-time = "2025-10-22T17:57:00.279Z" }, ] [[package]] From bf3e822d710e028831db26168bd5de1b84366e7d Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Wed, 17 Dec 2025 06:03:21 +0000 Subject: [PATCH 02/11] no need to run many tasks --- .github/workflows/main.yaml | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 7d02e93..a05c3c4 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -28,38 +28,12 @@ jobs: secrets: inherit with: sha: ${{ inputs.sha || github.sha }} - build-wheels: - uses: ./.github/workflows/build-wheels.yaml - secrets: inherit - with: - sha: ${{ inputs.sha || github.sha }} - build-containers: - uses: ./.github/workflows/build-containers.yaml - secrets: inherit - with: - sha: ${{ inputs.sha || github.sha }} build-docs: uses: ./.github/workflows/build-docs.yaml secrets: inherit with: sha: ${{ inputs.sha || github.sha }} - codeql: - uses: ./.github/workflows/codeql.yaml - secrets: inherit - with: - sha: ${{ inputs.sha || github.sha }} auto-merge: if: github.event_name == 'pull_request' uses: ./.github/workflows/auto-merge.yaml - secrets: inherit - release: - uses: ./.github/workflows/release.yaml - secrets: inherit - needs: - - build-containers - - build-docs - - build-wheels - - checks - - codeql - with: - publish-docs: ${{ github.ref_name == 'main' }} + secrets: inherit \ No newline at end of file From 1ea849700f60e2993e0991684dcc40efbc103757 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Wed, 17 Dec 2025 06:12:30 +0000 Subject: [PATCH 03/11] update docs/index.md --- docs/index.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/index.md b/docs/index.md index d8e45ee..6aa1d3c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,30 +23,39 @@ This repository contains the Pytorch code in our paper [SNRAware: Improved Deep ## Get started -[just](https://github.com/casey/just) is used in this project. If not, please install this tool: +[just](https://github.com/casey/just), [uv]() and [gif-lfs]() are used in this project. If not, please install this tool: ```bash # install just -wget -qO - 'https://proget.makedeb.org/debian-feeds/prebuilt-mpr.pub' | gpg --dearmor | sudo tee /usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg 1> /dev/null -echo "deb [arch=all,$(dpkg --print-architecture) signed-by=/usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg] https://proget.makedeb.org prebuilt-mpr $(lsb_release -cs)" | sudo tee /etc/apt/sources.list.d/prebuilt-mpr.list +curl -fsSL https://just.systems/install.sh | sudo bash -s -- --to /usr/local/bin + +# install uv +curl -LsSf https://astral.sh/uv/install.sh | sh + +# install git-lfs sudo apt update -sudo apt install just -y +sudo apt install git-lfs ``` -Then, please set up the virtual environment and run tests: +Make sure commands `just`, `us` are on your path. + +Also, this project requires NVIDIA GPU. To check whether your GPU is available and is working: ```bash -# show the list -just --list +nvidia-smi +``` + +Then, please set up the virtual environment and run tests: -# set up virtual environment -just setup-env +```bash +# set up env +uv sync -# review documentation -just serve-docs +# pull down test data +git lfs pull -# run test -just test +# run the test +uv run pytest -m gpu ./test ``` ## Data From 9a015e888688dc539852d982bd739452661fd9f8 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 04:53:02 +0000 Subject: [PATCH 04/11] add test phantom data --- docs/index.md | 26 +++++++++++++++++++++++++- docs/mri_denoising.md | 12 +----------- test/data/phantom/gmap.npy | 3 +++ test/data/phantom/input_imag.npy | 3 +++ test/data/phantom/input_real.npy | 3 +++ 5 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 test/data/phantom/gmap.npy create mode 100644 test/data/phantom/input_imag.npy create mode 100644 test/data/phantom/input_real.npy diff --git a/docs/index.md b/docs/index.md index 6aa1d3c..3028ab0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,6 +44,7 @@ Also, this project requires NVIDIA GPU. To check whether your GPU is available a ```bash nvidia-smi ``` +If the GPU is working correctly, this command will display detailed information, including driver version, GPU usage, memory usage, and temperature. Then, please set up the virtual environment and run tests: @@ -58,7 +59,8 @@ git lfs pull uv run pytest -m gpu ./test ``` -## Data +## Training data + Dataset for MR denoising training is not opened at this moment. ## Model @@ -68,6 +70,28 @@ Three models are released at https://huggingface.co/microsoft/SNRAware - SNRAware-medium: 55.1million parameters - SNRAware-large: 109million parameters +To test the model, +```bash +# download the model from the huggingface +# small model +wget https://huggingface.co/microsoft/SNRAware/blob/main/small/snraware_small_model.pts +wget https://huggingface.co/microsoft/SNRAware/blob/main/small/snraware_small_model.yaml + +# a test data is provided at ./test/data/inference +# input data are [H, W, Frame] 3D complex tensor, input_real.npy and input_imag.npy store the +# real and imaginary part +# gmap.npy is the g-factor map for all frames or for every frame, [H, W, 1 or Frame] + +# let's use the small model to run a inference +export model_file=snraware_small_model.pts +export config_file=snraware_small_model.yaml + +# run the inference +uv run python3 ./src/snraware/projects/mri/denoising/run_inference.py --input_dir ./test/data/phantom --output_dir /tmp/phantom_res_inference --saved_model_path $model_file --saved_config_path $config_file --batch_size 1 --input_fname input --gmap_fname gmap +``` + +After the run, the result is saved in the `/tmp/phantom_res_inference` as numpy file. + ## Direct intended uses SNRAware is shared for research and technical development purposes only, to denoise MR images. diff --git a/docs/mri_denoising.md b/docs/mri_denoising.md index 7269fcb..ce69a8a 100644 --- a/docs/mri_denoising.md +++ b/docs/mri_denoising.md @@ -45,14 +45,4 @@ User needs to log into the wandb as `wandb login`. Training and validation sampl # Run inference -After training the model, user can run inference with `run_inference.py` in the `src/snraware/projects/mri/denoising` folder: - -```bash -export model_file="SNRAware-small.pts" -export config_file="SNRAware-small_config.yaml" -export res_dir="res" -export data_dir="/data" - -python3 ./src/snraware/projects/mri/denoising/run_inference.py --input_dir ${data_dir} --output_dir ${data_dir}/${res_dir} --saved_model_path $model_file --saved_config_path $config_file --batch_size 1 --input_fname input --gmap_fname gmap - -``` \ No newline at end of file +After training the model, user can run inference with `run_inference.py` in the `src/snraware/projects/mri/denoising` folder. Examples to run the model inference is given in the [README](../README.md). \ No newline at end of file diff --git a/test/data/phantom/gmap.npy b/test/data/phantom/gmap.npy new file mode 100644 index 0000000..04041fe --- /dev/null +++ b/test/data/phantom/gmap.npy @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d18c5ac43714b669a4e19bdef2ef2a07ba91f60b1cb13d908597064d137b2319 +size 138320 diff --git a/test/data/phantom/input_imag.npy b/test/data/phantom/input_imag.npy new file mode 100644 index 0000000..8619529 --- /dev/null +++ b/test/data/phantom/input_imag.npy @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a23af0017fa255c780bec9a8f5858035b79f0c3e02b35c93214c43a91cafec6a +size 17694800 diff --git a/test/data/phantom/input_real.npy b/test/data/phantom/input_real.npy new file mode 100644 index 0000000..f92620e --- /dev/null +++ b/test/data/phantom/input_real.npy @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b6c50d9d060c3435923736eed21f7629aa3f643282a4e473c9b2b5bf410c676 +size 17694800 From 6d238fc35631967feef874fb4876ebde52cc7024 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 05:00:41 +0000 Subject: [PATCH 05/11] update python version to <=3.14 --- pyproject.toml | 2 +- uv.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5eecb9d..2ae7b86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "snraware" description = "A deep learning imaging AI framework" version = "0.1.0" readme = "README.md" -requires-python = ">=3.12,<4.0" +requires-python = ">=3.12,<=3.14" authors = [ {name = "Hui Xue", email = "xueh@microsoft.com"}, ] diff --git a/uv.lock b/uv.lock index c7dbed0..83b016b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.12, <4.0" +requires-python = ">=3.12, <=3.14" resolution-markers = [ "sys_platform == 'darwin'", "platform_machine == 'aarch64' and sys_platform == 'linux'", @@ -2355,7 +2355,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -2366,7 +2366,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -2393,9 +2393,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -2406,7 +2406,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'aarch64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, From 1ec15c35c9e355d0c03af0da2ed0f66fbdc0bdc1 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 05:18:08 +0000 Subject: [PATCH 06/11] fix typo --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 3028ab0..9872ee8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,7 +37,7 @@ sudo apt update sudo apt install git-lfs ``` -Make sure commands `just`, `us` are on your path. +Make sure commands `just` and `uv` are on your path. Also, this project requires NVIDIA GPU. To check whether your GPU is available and is working: @@ -61,7 +61,7 @@ uv run pytest -m gpu ./test ## Training data -Dataset for MR denoising training is not opened at this moment. +Dataset for MR denoising training is not opened at this moment. More information will be provided once training data is released. ## Model Three models are released at https://huggingface.co/microsoft/SNRAware From ba17315a743af1b8ae585af0436e41284e8b9d60 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 05:30:38 +0000 Subject: [PATCH 07/11] remove just and unused testing files --- .github/workflows/build-containers.yaml | 37 ---------------------- .github/workflows/build-wheels.yaml | 38 ---------------------- .github/workflows/codeql.yaml | 42 ------------------------- .github/workflows/release.yaml | 18 ----------- justfile | 29 ----------------- 5 files changed, 164 deletions(-) delete mode 100644 .github/workflows/build-containers.yaml delete mode 100644 .github/workflows/build-wheels.yaml delete mode 100644 .github/workflows/codeql.yaml delete mode 100644 .github/workflows/release.yaml delete mode 100644 justfile diff --git a/.github/workflows/build-containers.yaml b/.github/workflows/build-containers.yaml deleted file mode 100644 index 0ab1c9b..0000000 --- a/.github/workflows/build-containers.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build Containers -on: - workflow_dispatch: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string - workflow_call: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string -jobs: - build-containers: - runs-on: "ubuntu-24.04" - steps: - # Checkout the repository code - - name: Checkout Code - uses: actions/checkout@v4 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 - fetch-tags: true - - uses: ./.github/actions/setup-environment - id: runner-context - # Build the Docker image - - name: Build Docker Image - run: | - uv version "$PACKAGE_VERSION" --no-sync - echo "building image: snraware:${PACKAGE_VERSION}" - docker build -f Containerfile -t snraware:${PACKAGE_VERSION} . - echo "testing installation in image" - docker run snraware:${PACKAGE_VERSION} python -c "import snraware" - env: - PACKAGE_VERSION: "${{ steps.runner-context.outputs.package-version }}" diff --git a/.github/workflows/build-wheels.yaml b/.github/workflows/build-wheels.yaml deleted file mode 100644 index e3d813f..0000000 --- a/.github/workflows/build-wheels.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build Wheels -on: - workflow_dispatch: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string - workflow_call: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string -jobs: - build-components: - runs-on: "ubuntu-24.04" - name: "build-wheels" - steps: - - uses: actions/checkout@v5 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 - fetch-tags: true - - uses: ./.github/actions/setup-environment - id: runner-context - - name: build-wheel - env: - PACKAGE_VERSION: "${{ steps.runner-context.outputs.package-version }}" - run: | - uv version "$PACKAGE_VERSION" - uv sync - uv build - - name: "upload-artifacts" - uses: actions/upload-artifact@v4 - with: - name: "pkg-wheels" - path: "dist/*.whl" diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml deleted file mode 100644 index bd4d96c..0000000 --- a/.github/workflows/codeql.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: "CodeQL" -on: - workflow_dispatch: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string - workflow_call: - inputs: - sha: - description: 'the git sha to checkout' - required: true - type: string -jobs: - analyze: - name: Analyze - runs-on: "ubuntu-24.04" - permissions: - security-events: write - actions: read - contents: read - strategy: - fail-fast: false - matrix: - language: [ "javascript-typescript", "python" ] - steps: - - uses: actions/checkout@v5 - with: - ref: ${{ inputs.sha }} - fetch-depth: 0 - fetch-tags: true - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index d45921a..0000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: Release -on: - workflow_call: - inputs: - publish-docs: - description: 'whether to publish docs to the github pages' - required: true - type: boolean -jobs: - publish-docs: - name: "deploy-docs" - runs-on: "ubuntu-latest" - if: "${{ inputs.publish-docs }}" - timeout-minutes: 10 - steps: - - name: "deploy-gh-pages" - id: deployment - uses: actions/deploy-pages@v4 diff --git a/justfile b/justfile deleted file mode 100644 index 9e4f666..0000000 --- a/justfile +++ /dev/null @@ -1,29 +0,0 @@ -set shell := ['bash', '-ceuo', 'pipefail'] - -@default: lint test - -@lint: - ruff check - ruff format --check - ruff check - pyright - -@test: - pytest - -@fix: - ruff check --fix - ruff format - -@build-package: - uv build - -@build-docs: - mkdocs build - -@serve-docs: - mkdocs serve - -@setup-env: - uv sync - direnv allow . From 032406cf066129df8e98e8c456b64997b67f2037 Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 17:44:50 +0000 Subject: [PATCH 08/11] add R4 phantom data --- docs/images/image.png | Bin 0 -> 123953 bytes docs/index.md | 19 ++++++++++--------- test/data/phantom/gmap.npy | 2 +- test/data/phantom/input_imag.npy | 2 +- test/data/phantom/input_real.npy | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 docs/images/image.png diff --git a/docs/images/image.png b/docs/images/image.png new file mode 100644 index 0000000000000000000000000000000000000000..12fcaa490dc5456f1a4b75d1eac8a99439dd99cf GIT binary patch literal 123953 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 literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 9872ee8..c5a5be4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,21 +23,18 @@ This repository contains the Pytorch code in our paper [SNRAware: Improved Deep ## Get started -[just](https://github.com/casey/just), [uv]() and [gif-lfs]() are used in this project. If not, please install this tool: +`uv` is used in this project. Please install it as: ```bash -# install just -curl -fsSL https://just.systems/install.sh | sudo bash -s -- --to /usr/local/bin - # install uv curl -LsSf https://astral.sh/uv/install.sh | sh # install git-lfs sudo apt update -sudo apt install git-lfs +sudo apt install git-lfs direnv ``` -Make sure commands `just` and `uv` are on your path. +Make sure commands `uv` are on your path. Also, this project requires NVIDIA GPU. To check whether your GPU is available and is working: @@ -74,8 +71,8 @@ To test the model, ```bash # download the model from the huggingface # small model -wget https://huggingface.co/microsoft/SNRAware/blob/main/small/snraware_small_model.pts -wget https://huggingface.co/microsoft/SNRAware/blob/main/small/snraware_small_model.yaml +wget https://huggingface.co/microsoft/SNRAware/resolve/main/small/snraware_small_model.pts +wget https://huggingface.co/microsoft/SNRAware/resolve/main/small/snraware_small_model.yaml # a test data is provided at ./test/data/inference # input data are [H, W, Frame] 3D complex tensor, input_real.npy and input_imag.npy store the @@ -90,7 +87,11 @@ export config_file=snraware_small_model.yaml uv run python3 ./src/snraware/projects/mri/denoising/run_inference.py --input_dir ./test/data/phantom --output_dir /tmp/phantom_res_inference --saved_model_path $model_file --saved_config_path $config_file --batch_size 1 --input_fname input --gmap_fname gmap ``` -After the run, the result is saved in the `/tmp/phantom_res_inference` as numpy file. +After the run, the result is saved in the `/tmp/phantom_res_inference` as numpy files. + +![alt text](./docs/images/image.png) + +raw, model output, difference ## Direct intended uses SNRAware is shared for research and technical development purposes only, to denoise MR images. diff --git a/test/data/phantom/gmap.npy b/test/data/phantom/gmap.npy index 04041fe..3ef8026 100644 --- a/test/data/phantom/gmap.npy +++ b/test/data/phantom/gmap.npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d18c5ac43714b669a4e19bdef2ef2a07ba91f60b1cb13d908597064d137b2319 +oid sha256:d5d05b086b9d30a19c4a6224be8018730dafb39fb48258f1b099e7ca119e24fb size 138320 diff --git a/test/data/phantom/input_imag.npy b/test/data/phantom/input_imag.npy index 8619529..39cb532 100644 --- a/test/data/phantom/input_imag.npy +++ b/test/data/phantom/input_imag.npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a23af0017fa255c780bec9a8f5858035b79f0c3e02b35c93214c43a91cafec6a +oid sha256:5001992c3d05c8c1de2e5860a31bd071af045440153316f7a0d23d97b9ec44d4 size 17694800 diff --git a/test/data/phantom/input_real.npy b/test/data/phantom/input_real.npy index f92620e..e7bf7ce 100644 --- a/test/data/phantom/input_real.npy +++ b/test/data/phantom/input_real.npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b6c50d9d060c3435923736eed21f7629aa3f643282a4e473c9b2b5bf410c676 +oid sha256:04daf8e69cce5c2039b207eb2266f263f451aafbb9f389359caba9729b17d718 size 17694800 From eb5c200652aa769dde129f15ee0e8c6d1400f80e Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 18:36:31 +0000 Subject: [PATCH 09/11] add back codeql and release; add check for gpus --- .github/workflows/codeql.yaml | 42 +++++++++++++++++++ .github/workflows/main.yaml | 16 ++++++- .github/workflows/release.yaml | 18 ++++++++ docs/index.md | 9 +++- test/components/test_backbone_hrnet.py | 3 ++ test/components/test_backbone_soanet.py | 4 +- test/components/test_backbone_unet.py | 2 + test/components/test_block.py | 3 ++ test/components/test_cell.py | 3 ++ test/components/test_convolution_module.py | 2 + test/components/test_global_3d_attention.py | 7 +++- test/components/test_local_3d_attention.py | 7 +++- .../test_spatial_global_attention.py | 6 ++- .../test_spatial_local_attention.py | 5 ++- test/components/test_spatial_vit_attention.py | 6 +-- test/components/test_swin_3d_attention.py | 6 ++- test/components/test_temporal_attention.py | 5 ++- test/components/test_vit_3d_attention.py | 6 +-- ...ut_conv_False_False_False_False_[1, 1].npy | 2 +- ...out_conv_False_False_False_True_[1, 1].npy | 2 +- ...out_conv_False_False_True_False_[1, 1].npy | 2 +- ..._out_conv_False_False_True_True_[1, 1].npy | 2 +- ...out_conv_False_True_False_False_[1, 1].npy | 2 +- ..._out_conv_False_True_False_True_[1, 1].npy | 2 +- ..._out_conv_False_True_True_False_[1, 1].npy | 2 +- ...t_out_conv_False_True_True_True_[1, 1].npy | 2 +- ...out_conv_True_False_False_False_[1, 1].npy | 2 +- ..._out_conv_True_False_False_True_[1, 1].npy | 2 +- ..._out_conv_True_False_True_False_[1, 1].npy | 2 +- ...t_out_conv_True_False_True_True_[1, 1].npy | 2 +- ..._out_conv_True_True_False_False_[1, 1].npy | 2 +- ...t_out_conv_True_True_False_True_[1, 1].npy | 2 +- ...t_out_conv_True_True_True_False_[1, 1].npy | 2 +- ...st_out_conv_True_True_True_True_[1, 1].npy | 2 +- ...out_lin_False_False_False_False_[1, 1].npy | 2 +- ..._out_lin_False_False_False_True_[1, 1].npy | 2 +- ..._out_lin_False_False_True_False_[1, 1].npy | 2 +- ...t_out_lin_False_False_True_True_[1, 1].npy | 2 +- ..._out_lin_False_True_False_False_[1, 1].npy | 2 +- ...t_out_lin_False_True_False_True_[1, 1].npy | 2 +- ...t_out_lin_False_True_True_False_[1, 1].npy | 2 +- ...st_out_lin_False_True_True_True_[1, 1].npy | 2 +- ..._out_lin_True_False_False_False_[1, 1].npy | 2 +- ...t_out_lin_True_False_False_True_[1, 1].npy | 2 +- ...t_out_lin_True_False_True_False_[1, 1].npy | 2 +- ...st_out_lin_True_False_True_True_[1, 1].npy | 2 +- ...t_out_lin_True_True_False_False_[1, 1].npy | 2 +- ...st_out_lin_True_True_False_True_[1, 1].npy | 2 +- ...st_out_lin_True_True_True_False_[1, 1].npy | 2 +- ...est_out_lin_True_True_True_True_[1, 1].npy | 2 +- test/test_e2e_mri_denoising.py | 10 ++++- test/test_e2e_simple_regression.py | 5 +++ 52 files changed, 174 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/codeql.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000..310dc7a --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,42 @@ +name: "CodeQL" +on: + workflow_dispatch: + inputs: + sha: + description: 'the git sha to checkout' + required: true + type: string + workflow_call: + inputs: + sha: + description: 'the git sha to checkout' + required: true + type: string +jobs: + analyze: + name: Analyze + runs-on: "ubuntu-24.04" + permissions: + security-events: write + actions: read + contents: read + strategy: + fail-fast: false + matrix: + language: [ "python" ] + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ inputs.sha }} + fetch-depth: 0 + fetch-tags: true + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a05c3c4..cdce46e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -33,7 +33,21 @@ jobs: secrets: inherit with: sha: ${{ inputs.sha || github.sha }} + codeql: + uses: ./.github/workflows/codeql.yaml + secrets: inherit + with: + sha: ${{ inputs.sha || github.sha }} auto-merge: if: github.event_name == 'pull_request' uses: ./.github/workflows/auto-merge.yaml - secrets: inherit \ No newline at end of file + secrets: inherit + release: + uses: ./.github/workflows/release.yaml + secrets: inherit + needs: + - build-docs + - checks + - codeql + with: + publish-docs: ${{ github.ref_name == 'main' }} \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..d45921a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,18 @@ +name: Release +on: + workflow_call: + inputs: + publish-docs: + description: 'whether to publish docs to the github pages' + required: true + type: boolean +jobs: + publish-docs: + name: "deploy-docs" + runs-on: "ubuntu-latest" + if: "${{ inputs.publish-docs }}" + timeout-minutes: 10 + steps: + - name: "deploy-gh-pages" + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docs/index.md b/docs/index.md index c5a5be4..6402912 100644 --- a/docs/index.md +++ b/docs/index.md @@ -43,10 +43,15 @@ nvidia-smi ``` If the GPU is working correctly, this command will display detailed information, including driver version, GPU usage, memory usage, and temperature. -Then, please set up the virtual environment and run tests: +Then, please clone the repo, set up the virtual environment and run tests: ```bash +# clone the repo +git clone git@github.com:microsoft/SNRAware.git + # set up env +direnv allow +cd ./SNRAware uv sync # pull down test data @@ -94,7 +99,7 @@ After the run, the result is saved in the `/tmp/phantom_res_inference` as numpy raw, model output, difference ## Direct intended uses -SNRAware is shared for research and technical development purposes only, to denoise MR images. +SNRAware is shared for research and technical development purposes only, to denoisegit MR images. ## License and Usage Notices The data, code, and model checkpoints described in this repository is provided for research and technical development use diff --git a/test/components/test_backbone_hrnet.py b/test/components/test_backbone_hrnet.py index f5813e4..d521404 100644 --- a/test/components/test_backbone_hrnet.py +++ b/test/components/test_backbone_hrnet.py @@ -57,6 +57,9 @@ def test(self, backbone): with_timer = True device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + _B, C, T, H, W = 1, 2, 16, 16, 16 test_in = torch.from_numpy(self.test_in).to(dtype=torch.float32, device=device) assert np.linalg.norm(self.test_in - test_in.cpu().numpy()) < 1e-3 diff --git a/test/components/test_backbone_soanet.py b/test/components/test_backbone_soanet.py index 34690d5..ea085ed 100644 --- a/test/components/test_backbone_soanet.py +++ b/test/components/test_backbone_soanet.py @@ -55,7 +55,9 @@ def teardown_class(self): def test(self, downsample, backbone): with_timer = True device = get_device() - + if device != "cuda": + pytest.skip("GPU only test") + _B, C, T, H, W = 1, 4, 8, 32, 32 test_in = torch.from_numpy(self.test_in).to(dtype=torch.float32, device=device) diff --git a/test/components/test_backbone_unet.py b/test/components/test_backbone_unet.py index 60ab945..96d2e7e 100644 --- a/test/components/test_backbone_unet.py +++ b/test/components/test_backbone_unet.py @@ -55,6 +55,8 @@ def teardown_class(self): def test(self, backbone): with_timer = True device = get_device() + if device != "cuda": + pytest.skip("GPU only test") _B, C, T, H, W = 1, 2, 16, 32, 32 test_in = torch.from_numpy(self.test_in).to(dtype=torch.float32, device=device) diff --git a/test/components/test_block.py b/test/components/test_block.py index 085a51a..cfbeeee 100644 --- a/test/components/test_block.py +++ b/test/components/test_block.py @@ -72,6 +72,9 @@ def test(self, block_str): test_in = torch.rand(B, T, C, H, W) device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + test_in = test_in.to(device=device, dtype=torch.float32) test_in = torch.permute(test_in, [0, 2, 1, 3, 4]) diff --git a/test/components/test_cell.py b/test/components/test_cell.py index 73b2c68..505eaa6 100644 --- a/test/components/test_cell.py +++ b/test/components/test_cell.py @@ -31,6 +31,9 @@ def test(self): test_in = torch.rand(B, T, C, H, W).to(torch.float32) device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + test_in = test_in.to(device=device) att_types = [ diff --git a/test/components/test_convolution_module.py b/test/components/test_convolution_module.py index d9de796..cef038e 100644 --- a/test/components/test_convolution_module.py +++ b/test/components/test_convolution_module.py @@ -39,6 +39,8 @@ def test(self): with_timer = True device = get_device() + if device != "cuda": + pytest.skip("GPU only test") test_in = torch.rand(B, T, C, H, W, device=device) assert np.linalg.norm(self.test_in - test_in.cpu().numpy()) < 1e-3 diff --git a/test/components/test_global_3d_attention.py b/test/components/test_global_3d_attention.py index 356dd70..d05665d 100644 --- a/test/components/test_global_3d_attention.py +++ b/test/components/test_global_3d_attention.py @@ -29,6 +29,11 @@ def teardown_class(self): @pytest.mark.gpu def test(self): + + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -72,8 +77,6 @@ def test(self): with_timer = True - device = get_device() - B, T, C, H1, W1 = 1, 16, 2, 64, 64 C_out = 8 test_in = torch.rand(B, T, C, H1, W1).to(device=device) diff --git a/test/components/test_local_3d_attention.py b/test/components/test_local_3d_attention.py index 1c7f005..c9458b7 100644 --- a/test/components/test_local_3d_attention.py +++ b/test/components/test_local_3d_attention.py @@ -29,6 +29,11 @@ def teardown_class(self): @pytest.mark.gpu def test(self): + + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -72,8 +77,6 @@ def test(self): with_timer = True - device = get_device() - B, T, C, H1, W1 = 1, 16, 2, 64, 64 C_out = 8 test_in = torch.rand(B, T, C, H1, W1).to(device=device) diff --git a/test/components/test_spatial_global_attention.py b/test/components/test_spatial_global_attention.py index d361c33..b819966 100644 --- a/test/components/test_spatial_global_attention.py +++ b/test/components/test_spatial_global_attention.py @@ -29,7 +29,10 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - print("Begin Testing") + + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -102,7 +105,6 @@ def test(self): stride_qks = [[1, 1]] with_timer = True - device = get_device() B, C, T, H1, W1 = 1, 2, 16, 64, 64 C_out = 4 diff --git a/test/components/test_spatial_local_attention.py b/test/components/test_spatial_local_attention.py index 4912c4f..67d4632 100644 --- a/test/components/test_spatial_local_attention.py +++ b/test/components/test_spatial_local_attention.py @@ -26,7 +26,9 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - print("Begin Testing") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -102,7 +104,6 @@ def test(self): stride_qks = [[1, 1], [2, 2]] with_timer = True - device = get_device() B, T, C, H1, W1 = 1, 4, 2, 64, 64 C_out = 16 diff --git a/test/components/test_spatial_vit_attention.py b/test/components/test_spatial_vit_attention.py index 5f07850..69d6769 100644 --- a/test/components/test_spatial_vit_attention.py +++ b/test/components/test_spatial_vit_attention.py @@ -29,7 +29,9 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - print("Begin Testing") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -75,8 +77,6 @@ def test(self): with_timer = True - device = get_device() - B, T, C, H1, W1 = 1, 16, 2, 32, 32 C_out = 8 test_in = torch.rand(B, T, C, H1, W1).to(device=device) diff --git a/test/components/test_swin_3d_attention.py b/test/components/test_swin_3d_attention.py index 7591ca5..ea60cac 100644 --- a/test/components/test_swin_3d_attention.py +++ b/test/components/test_swin_3d_attention.py @@ -30,6 +30,10 @@ def teardown_class(self): @pytest.mark.gpu def test(self): + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -71,8 +75,6 @@ def test(self): with_timer = True - device = get_device() - B, T, C, H1, W1 = 1, 16, 2, 32, 32 C_out = 32 test_in = torch.rand(B, T, C, H1, W1).to(device=device) diff --git a/test/components/test_temporal_attention.py b/test/components/test_temporal_attention.py index 33bb9d9..6cec977 100644 --- a/test/components/test_temporal_attention.py +++ b/test/components/test_temporal_attention.py @@ -28,13 +28,14 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - print("Begin Testing") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") B, T, C, H, W = 2, 16, 3, 16, 16 C_out = 32 with_timer = True - device = get_device() test_in = torch.rand(B, T, C, H, W, device=device) assert np.linalg.norm(self.test_in - test_in.cpu().numpy()) < 1e-3 diff --git a/test/components/test_vit_3d_attention.py b/test/components/test_vit_3d_attention.py index d3141d6..58c1ca1 100644 --- a/test/components/test_vit_3d_attention.py +++ b/test/components/test_vit_3d_attention.py @@ -29,7 +29,9 @@ def teardown_class(self): @pytest.mark.gpu def test_Vit3D(self): - print("Begin Testing") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") t = np.arange(256) t = np.reshape(t, (16, 16)) @@ -76,8 +78,6 @@ def test_Vit3D(self): with_timer = True - device = get_device() - B, T, C, H1, W1 = 1, 16, 2, 32, 32 C_out = 8 test_in = torch.rand(B, T, C, H1, W1).to(device=device) diff --git a/test/data/spatial_global/test_out_conv_False_False_False_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_False_False_False_[1, 1].npy index 3b3817c..407d2cd 100644 --- a/test/data/spatial_global/test_out_conv_False_False_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_False_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:413115d899b8e1364df8cc02cf1369fbd9ab9c7fdbd0dcc2146f9fecb62109a2 +oid sha256:758727abd313808e33267c84eb7fa720bdf0ebfe01ce33d7151acc58b3ad2ad4 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_False_False_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_False_False_True_[1, 1].npy index 298ed80..2841ba7 100644 --- a/test/data/spatial_global/test_out_conv_False_False_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_False_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:673065cc4763369eebad9c5c35ada176ebb64306af8e92c62b8e83bde216f258 +oid sha256:72109d0355c6b13a9a34f1aa20fc12639ae0314d02867543e3ca844198ea27c7 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_False_True_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_False_True_False_[1, 1].npy index e07dc8c..2a96b0f 100644 --- a/test/data/spatial_global/test_out_conv_False_False_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_False_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f290ead7a707fe13a9b1aca755f6e1fb9c31aa3dcbd5f9d66417613bdd9d476f +oid sha256:a1d4ffeeeaf652c270b761fd3044491e63ab85b125d3acea4c482bc4f8c4e5fa size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_False_True_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_False_True_True_[1, 1].npy index b4cd2db..b77d648 100644 --- a/test/data/spatial_global/test_out_conv_False_False_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_False_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06d5d99bf5f7b4d527c6ffab51b0f91df9d1a9586c3f0181314141f1bad5920d +oid sha256:273eb256d7ead4bf76b7cbdfcb4dfea14a123b1c50000ee5ea5034a2b6eb309f size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_True_False_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_True_False_False_[1, 1].npy index 4c528ad..ac2f6cd 100644 --- a/test/data/spatial_global/test_out_conv_False_True_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_True_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08a768890204ca4206f2a415a6e674bc581f51671ae1a1e6d3aceebef9b3ff29 +oid sha256:7883272ac1a4d78920ed754651ecc6152590e44447be2ee4d7d3e187b6e5a3aa size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_True_False_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_True_False_True_[1, 1].npy index 79eca6f..0dc4834 100644 --- a/test/data/spatial_global/test_out_conv_False_True_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_True_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eda751b92bebf16b7445f2e9f45a4913e156eb9d40ba98346af029726565dc90 +oid sha256:e8d5742cad7636a1b2c3611f5659315ba995eccf3cbaa818876bac50c58b4dc7 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_True_True_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_True_True_False_[1, 1].npy index d770522..1036b28 100644 --- a/test/data/spatial_global/test_out_conv_False_True_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_True_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b750d213707fab9c22c6a6861f3c47d6aa61d7f2b4169709233f6767c1b1847 +oid sha256:06aea2595881797781f5fda0d99e090fa791b3dcf1756337955ba2b756740812 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_False_True_True_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_False_True_True_True_[1, 1].npy index 4041dc1..75006ba 100644 --- a/test/data/spatial_global/test_out_conv_False_True_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_False_True_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:921d1254ef2678202d41e89367c36c850b172d222e8c0ac92c1c4c18307df885 +oid sha256:3a5a8797afd70c70fa102fd0ab848a0391d2a24d09f4eadbd57b56a7d9031bc3 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_False_False_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_False_False_False_[1, 1].npy index f24774e..2c1558a 100644 --- a/test/data/spatial_global/test_out_conv_True_False_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_False_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:949961516ab6ba12ee2c9413aa5e86d938ee254056d98adc3aa7e55b61529207 +oid sha256:54a27f6718435f25e1cfe00b1462b5c24ed2f6d41adfaaa81e09433bae7cf26e size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_False_False_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_False_False_True_[1, 1].npy index 5400729..9adc5b2 100644 --- a/test/data/spatial_global/test_out_conv_True_False_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_False_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3bab0cef0f305e9cb410362e6bb7df5b1d915610853ccc6689722e395ecec0e +oid sha256:d97dbcadb983fda0f35324675ddb2f710a8db170cbb985354904541110f9dfd6 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_False_True_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_False_True_False_[1, 1].npy index 3781d75..5c00698 100644 --- a/test/data/spatial_global/test_out_conv_True_False_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_False_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfc0b6b3b71413f182d7ef4f5d999dd1ef19e318055af1b61bff6a34b097fc3d +oid sha256:7c3a19bdacb0058fd38f6ac0203c8d898268996e09db9d221747b6b73889b053 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_False_True_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_False_True_True_[1, 1].npy index 4e4180c..d73cc3f 100644 --- a/test/data/spatial_global/test_out_conv_True_False_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_False_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3381d77df2d699e9b2d9083f356fde1edfdf317ea9ab1b642cd42e172d5ea8a0 +oid sha256:7236b38ec07a8f5925629f1c1bdbad5c3396abe775eed428ecff5f16317a1229 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_True_False_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_True_False_False_[1, 1].npy index 6cce159..a5238c0 100644 --- a/test/data/spatial_global/test_out_conv_True_True_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_True_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c9cab03d8df15d6f6e4a7bc08e544b0812090a3990ce07cf836066faf4855b1 +oid sha256:61d72f6a43e1ccc416fbbeaf4bd3ca016737ff1b34eccfb0a833348e19c6daa4 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_True_False_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_True_False_True_[1, 1].npy index 4955abd..274d055 100644 --- a/test/data/spatial_global/test_out_conv_True_True_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_True_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b73292c46b03473dfa5d2b180f07a2275061d1a79fb004f6dbc751217c07933 +oid sha256:918852dadc630894bfe7a8d553ced4316e975132e3cf2bba7986fa7386b2dfcf size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_True_True_False_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_True_True_False_[1, 1].npy index cce94d7..142bef7 100644 --- a/test/data/spatial_global/test_out_conv_True_True_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_True_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d6df2ad858d6bdad3b81b66c25a23095a0a9d09c883e0bbcc01feaa553cdfeb +oid sha256:ab2ef679574552549f949fa8c546dcfe887906b4be1a8246508a10f6c82dde48 size 1048704 diff --git a/test/data/spatial_global/test_out_conv_True_True_True_True_[1, 1].npy b/test/data/spatial_global/test_out_conv_True_True_True_True_[1, 1].npy index f6741f5..9e38fee 100644 --- a/test/data/spatial_global/test_out_conv_True_True_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_conv_True_True_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee9fe269213f1c755bf8801bda67160c9dc48019ef3db715946e533a0262ceda +oid sha256:5a681081002cefe9be74145a8dcfa1be6795d49c5485592e8829d00b3c8fe105 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_False_False_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_False_False_False_[1, 1].npy index 491767b..2dff820 100644 --- a/test/data/spatial_global/test_out_lin_False_False_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_False_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b138e8032e57430f4d47671524787212a455c75cbd7bbed7348f3a42e845d6c6 +oid sha256:3668d6b8fa2bc6cbd859a7d779701c9012fbd70c6a14edfbc91937e24b18b619 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_False_False_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_False_False_True_[1, 1].npy index ba5bc42..06ab95d 100644 --- a/test/data/spatial_global/test_out_lin_False_False_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_False_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d69b38f14a2d67535247d5e9f55e2a4c80f565c480e445354a2186d855eb9bf7 +oid sha256:ed437e3848db4b2baac565078ae3700639362393faec851225f4052d5fdac3cd size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_False_True_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_False_True_False_[1, 1].npy index 0735912..b1cba1a 100644 --- a/test/data/spatial_global/test_out_lin_False_False_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_False_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55e16ba54a225a4e925bf7ec64541ca1a69d602c84718516c35f0ca9a88b341a +oid sha256:499a99061592cee4daf11a8a36342c78cfb28b5ed7a218ad49bc37140d6d4440 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_False_True_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_False_True_True_[1, 1].npy index e51ff93..f906cba 100644 --- a/test/data/spatial_global/test_out_lin_False_False_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_False_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fadf140deabafb5bc182b119f57e19fc1ee7c2eea6e52eaa1fb10d3663dc1c4 +oid sha256:d790d3beea2aed26dabd0cc9276ff236c6653032f2c448a9c57bf46018ea5e94 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_True_False_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_True_False_False_[1, 1].npy index 0c06d06..4cfd5b9 100644 --- a/test/data/spatial_global/test_out_lin_False_True_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_True_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a97c4d7dac4d24e22d3ba9560d0d7a4aa5a0bfe964ea19d707de16482f2e508a +oid sha256:08c92a30c69566d98b58ea672faded7d85ac9c4f539a44fd3a1cceaa61cb5d5b size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_True_False_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_True_False_True_[1, 1].npy index 13196fa..23b99ab 100644 --- a/test/data/spatial_global/test_out_lin_False_True_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_True_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01ef895728dba65083fbb10026c3bf8aeae05bffd7467612d87ff5450d4520d4 +oid sha256:43029550307a9a4ff8b79d13b8826e551254de038ab797f9c9fcbf6b3427cec9 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_True_True_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_True_True_False_[1, 1].npy index 6a50900..bfa0274 100644 --- a/test/data/spatial_global/test_out_lin_False_True_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_True_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afa0a5583fab48f59017112ba857b9b89cf6a6b3d68a23f515eb08074af91dd0 +oid sha256:71da79e587c3caaea6107ad85eb2d72433e517f397107ac4a693b5132744f77c size 1048704 diff --git a/test/data/spatial_global/test_out_lin_False_True_True_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_False_True_True_True_[1, 1].npy index b6ff268..5c8be35 100644 --- a/test/data/spatial_global/test_out_lin_False_True_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_False_True_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:260c2c3e417a9a4a2bae51b4a8005851dee8f68aad5f71e819ac988a7b26f11a +oid sha256:7bd5ed22da0dfac89a3cf4d390ef17452e6e68ac6e47ecd5087641c6b294ae87 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_False_False_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_False_False_False_[1, 1].npy index 3340d62..c1415aa 100644 --- a/test/data/spatial_global/test_out_lin_True_False_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_False_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9bfada73326ca2cf26721cda327da9415ded3e4f4fc8547636c3fde307ed061 +oid sha256:3ddc62901ee8cd7382e62abaf452edc07ea3204804e92b55804770af9761701f size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_False_False_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_False_False_True_[1, 1].npy index 036b9ad..389ac89 100644 --- a/test/data/spatial_global/test_out_lin_True_False_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_False_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f4a1fa07a3b7c755f773862783a192d2d3eb0c5feaf1effc7670b378d60d006 +oid sha256:910b74c2daa22183acf1a887a3e02230a2a6e22836479c56a1559561bb2d8e51 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_False_True_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_False_True_False_[1, 1].npy index 238b719..97f5441 100644 --- a/test/data/spatial_global/test_out_lin_True_False_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_False_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:172f6b353ad3418b8b9b6f2b4ad927adbcfd3eb8e6bbd2b419c9184c3ee5c770 +oid sha256:c6f9de8ca93edc1154860887d69bbc3a94acb6d2f7c62dd9692f7dcbef945e7a size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_False_True_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_False_True_True_[1, 1].npy index 796a116..cf21def 100644 --- a/test/data/spatial_global/test_out_lin_True_False_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_False_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:963dd0b6c14d7d84422782eba6390d3897c6914231728c81b7f569b16a9b6e12 +oid sha256:c0354e73a86e0fa44cb9d34f6e8736c54a162b827ff7a12ad3abccbe4a6fb698 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_True_False_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_True_False_False_[1, 1].npy index 7e7aac8..3a268fc 100644 --- a/test/data/spatial_global/test_out_lin_True_True_False_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_True_False_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:113649a311e22e145f046e29f38bbdcc0fef14bb6be19e463784eb43c704eecb +oid sha256:5c8b5d9738816ecf330d42fe6c9ee320bb51ba95d1600b28fb343f811061ebec size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_True_False_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_True_False_True_[1, 1].npy index 7ab744d..df1da86 100644 --- a/test/data/spatial_global/test_out_lin_True_True_False_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_True_False_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:203a87352a615084f8f68842c9b0c36e019263a70fd7b53cfd8bef94b075f72f +oid sha256:28d5fae89c047380ea4cfc0cb9d2a18a9a49db3eb0c2dad2d7a0880664fd9efd size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_True_True_False_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_True_True_False_[1, 1].npy index 5675a35..204ff54 100644 --- a/test/data/spatial_global/test_out_lin_True_True_True_False_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_True_True_False_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:596b048683b944035a9530cfb54be973e2e1c22bb0534418de345247e2527ff2 +oid sha256:7b206f3df2faea393dd6edb0630e12b4af0d0482432e55c8c86a404b822abdf1 size 1048704 diff --git a/test/data/spatial_global/test_out_lin_True_True_True_True_[1, 1].npy b/test/data/spatial_global/test_out_lin_True_True_True_True_[1, 1].npy index 3a31f3b..423841d 100644 --- a/test/data/spatial_global/test_out_lin_True_True_True_True_[1, 1].npy +++ b/test/data/spatial_global/test_out_lin_True_True_True_True_[1, 1].npy @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bddf221f501f8398995006b20b900844909fc1580dd99966558ccb8431ad0c53 +oid sha256:7eb141e8f72040b5ded554efcdb608105a7c667d536bea35a4eec8fbb1e811e2 size 1048704 diff --git a/test/test_e2e_mri_denoising.py b/test/test_e2e_mri_denoising.py index 87e629f..1b24919 100644 --- a/test/test_e2e_mri_denoising.py +++ b/test/test_e2e_mri_denoising.py @@ -7,7 +7,7 @@ import torch from hydra import compose, initialize -from snraware.components.setup import end_timer, start_timer +from snraware.components.setup import end_timer, start_timer, get_device from snraware.projects.mri.denoising.inference_model import ( load_lit_model, load_model, @@ -126,6 +126,10 @@ def test_training(self, request): if "gpu" not in selected_markers or "slow" not in selected_markers: pytest.skip("Skipping because both markers 'gpu' and 'slow' are not set") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + with initialize( version_base=None, config_path="../src/snraware/projects/mri/denoising/configs" ): @@ -167,6 +171,10 @@ def test_training_single_epoch(self, request): if os.path.exists(self.data_root) is False or os.path.exists(self.test_root) is False: pytest.skip("Skipping because test data not found") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + with initialize( version_base=None, config_path="../src/snraware/projects/mri/denoising/configs" ): diff --git a/test/test_e2e_simple_regression.py b/test/test_e2e_simple_regression.py index c79325d..91e27f2 100644 --- a/test/test_e2e_simple_regression.py +++ b/test/test_e2e_simple_regression.py @@ -8,6 +8,7 @@ from lightning.pytorch.callbacks import ModelCheckpoint, ModelSummary, TQDMProgressBar from omegaconf import OmegaConf +from snraware.components.setup import get_device from snraware.components.heads import PreConv2D, SimpleConv2d from snraware.components.model import HRnet, SOAnet, Unet from snraware.components.optim import OptimScheduler @@ -217,6 +218,10 @@ def test_trainer(self, backbone, request): if "gpu" not in selected_markers: pytest.skip("Skipping because marker 'gpu' is not set") + device = get_device() + if device != "cuda": + pytest.skip("GPU only test") + overrides = [ f"backbone={backbone}", "backbone.block.cell.window_size=[4,4,1]", From b078fb11a5d8cd5a3e032eea7ce44082670df28c Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 19:30:36 +0000 Subject: [PATCH 10/11] fix ruff --- test/components/test_backbone_soanet.py | 2 +- test/components/test_cell.py | 2 +- test/components/test_global_3d_attention.py | 3 +-- test/components/test_local_3d_attention.py | 3 +-- test/components/test_spatial_global_attention.py | 3 +-- test/test_e2e_mri_denoising.py | 2 +- test/test_e2e_simple_regression.py | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/test/components/test_backbone_soanet.py b/test/components/test_backbone_soanet.py index ea085ed..4fae7fb 100644 --- a/test/components/test_backbone_soanet.py +++ b/test/components/test_backbone_soanet.py @@ -57,7 +57,7 @@ def test(self, downsample, backbone): device = get_device() if device != "cuda": pytest.skip("GPU only test") - + _B, C, T, H, W = 1, 4, 8, 32, 32 test_in = torch.from_numpy(self.test_in).to(dtype=torch.float32, device=device) diff --git a/test/components/test_cell.py b/test/components/test_cell.py index 505eaa6..f725365 100644 --- a/test/components/test_cell.py +++ b/test/components/test_cell.py @@ -33,7 +33,7 @@ def test(self): device = get_device() if device != "cuda": pytest.skip("GPU only test") - + test_in = test_in.to(device=device) att_types = [ diff --git a/test/components/test_global_3d_attention.py b/test/components/test_global_3d_attention.py index d05665d..70133bd 100644 --- a/test/components/test_global_3d_attention.py +++ b/test/components/test_global_3d_attention.py @@ -29,8 +29,7 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - - device = get_device() + device = get_device() if device != "cuda": pytest.skip("GPU only test") diff --git a/test/components/test_local_3d_attention.py b/test/components/test_local_3d_attention.py index c9458b7..a92756b 100644 --- a/test/components/test_local_3d_attention.py +++ b/test/components/test_local_3d_attention.py @@ -29,8 +29,7 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - - device = get_device() + device = get_device() if device != "cuda": pytest.skip("GPU only test") diff --git a/test/components/test_spatial_global_attention.py b/test/components/test_spatial_global_attention.py index b819966..d4f12bf 100644 --- a/test/components/test_spatial_global_attention.py +++ b/test/components/test_spatial_global_attention.py @@ -29,7 +29,6 @@ def teardown_class(self): @pytest.mark.gpu def test(self): - device = get_device() if device != "cuda": pytest.skip("GPU only test") @@ -146,7 +145,7 @@ def test(self): fname = f"{attention_type}_{normalize_Q_K}_{att_with_output_proj}_{cosine_att}_{att_with_relative_position_bias}_{stride_qk}" gt_fname = os.path.join(self.data_root, f"test_out_{fname}.npy") - np.save(gt_fname, test_out.detach().cpu().numpy()) + # np.save(gt_fname, test_out.detach().cpu().numpy()) assert os.path.exists(gt_fname) test_out_gt = np.load( os.path.join(self.data_root, f"test_out_{fname}.npy") diff --git a/test/test_e2e_mri_denoising.py b/test/test_e2e_mri_denoising.py index 1b24919..6af39b5 100644 --- a/test/test_e2e_mri_denoising.py +++ b/test/test_e2e_mri_denoising.py @@ -7,7 +7,7 @@ import torch from hydra import compose, initialize -from snraware.components.setup import end_timer, start_timer, get_device +from snraware.components.setup import end_timer, get_device, start_timer from snraware.projects.mri.denoising.inference_model import ( load_lit_model, load_model, diff --git a/test/test_e2e_simple_regression.py b/test/test_e2e_simple_regression.py index 91e27f2..50b784c 100644 --- a/test/test_e2e_simple_regression.py +++ b/test/test_e2e_simple_regression.py @@ -8,10 +8,10 @@ from lightning.pytorch.callbacks import ModelCheckpoint, ModelSummary, TQDMProgressBar from omegaconf import OmegaConf -from snraware.components.setup import get_device from snraware.components.heads import PreConv2D, SimpleConv2d from snraware.components.model import HRnet, SOAnet, Unet from snraware.components.optim import OptimScheduler +from snraware.components.setup import get_device # ----------------------------------------------------------------- From ef8f713c84438acdfb0261995fc846e1c0634f9a Mon Sep 17 00:00:00 2001 From: Hui Xue Date: Fri, 19 Dec 2025 20:42:47 +0000 Subject: [PATCH 11/11] fix the typo --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 6402912..ed36bcd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -43,7 +43,7 @@ nvidia-smi ``` If the GPU is working correctly, this command will display detailed information, including driver version, GPU usage, memory usage, and temperature. -Then, please clone the repo, set up the virtual environment and run tests: +Make sure the command `uv` are on your path. Then please clone the repo, set up the virtual environment and run tests: ```bash # clone the repo