From 3f17cf8393e74baeda3aa8afafc505f21bfa1a39 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 2 May 2025 06:59:46 -0700 Subject: [PATCH 01/54] migrate TokenizerStats to uv --- opt/TokenizerStats/.python-version | 1 + opt/TokenizerStats/poetry.toml | 3 - opt/TokenizerStats/pyproject.toml | 31 +- opt/TokenizerStats/uv.lock | 2753 ++++++++++++++++++++++++++++ 4 files changed, 2771 insertions(+), 17 deletions(-) create mode 100644 opt/TokenizerStats/.python-version delete mode 100644 opt/TokenizerStats/poetry.toml create mode 100644 opt/TokenizerStats/uv.lock diff --git a/opt/TokenizerStats/.python-version b/opt/TokenizerStats/.python-version new file mode 100644 index 00000000..e4fba218 --- /dev/null +++ b/opt/TokenizerStats/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/opt/TokenizerStats/poetry.toml b/opt/TokenizerStats/poetry.toml deleted file mode 100644 index 53b35d37..00000000 --- a/opt/TokenizerStats/poetry.toml +++ /dev/null @@ -1,3 +0,0 @@ -[virtualenvs] -create = true -in-project = true diff --git a/opt/TokenizerStats/pyproject.toml b/opt/TokenizerStats/pyproject.toml index 098c5099..58faafa7 100644 --- a/opt/TokenizerStats/pyproject.toml +++ b/opt/TokenizerStats/pyproject.toml @@ -1,15 +1,18 @@ -[tool.poetry] -package-mode = false -authors = ["Alexius Wadell "] +[project] +name = "TokenizerStats" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +authors = [ + {name = "Alexius Wadell", email = "awadell@umich.edu"}, +] +requires-python = ">=3.10, <3.14" +dependencies = [ + "mendeleev >= 0.16", + "selfies == 2.1.2", + "jsonnet ~= 0.20.0", + "electrolyte-fm", +] -[tool.poetry.dependencies] -python = ">=3.10,<3.13" -mendeleev = "^0.16" -electrolyte_fm = {path = "../..", extras = ["tokenizers"], develop = true} -selfies = "^2.1.2" -jsonnet = "^0.20.0" - - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +[tool.uv.sources] +electrolyte-fm = { path = "../../" } diff --git a/opt/TokenizerStats/uv.lock b/opt/TokenizerStats/uv.lock new file mode 100644 index 00000000..6488ce45 --- /dev/null +++ b/opt/TokenizerStats/uv.lock @@ -0,0 +1,2753 @@ +version = 1 +revision = 1 +requires-python = ">=3.10, <3.14" +resolution-markers = [ + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform != 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform != 'linux'", + "python_full_version < '3.11' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform != 'linux'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.11.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/c3/e5f64af7e97a02f547020e6ff861595766bb5ecb37c7492fac9fe3c14f6c/aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4", size = 711703 }, + { url = "https://files.pythonhosted.org/packages/5f/2f/53c26e96efa5fd01ebcfe1fefdfb7811f482bb21f4fa103d85eca4dcf888/aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6", size = 471348 }, + { url = "https://files.pythonhosted.org/packages/80/47/dcc248464c9b101532ee7d254a46f6ed2c1fd3f4f0f794cf1f2358c0d45b/aiohttp-3.11.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5691dc38750fcb96a33ceef89642f139aa315c8a193bbd42a0c33476fd4a1609", size = 457611 }, + { url = "https://files.pythonhosted.org/packages/4c/ca/67d816ef075e8ac834b5f1f6b18e8db7d170f7aebaf76f1be462ea10cab0/aiohttp-3.11.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554c918ec43f8480b47a5ca758e10e793bd7410b83701676a4782672d670da55", size = 1591976 }, + { url = "https://files.pythonhosted.org/packages/46/00/0c120287aa51c744438d99e9aae9f8c55ca5b9911c42706966c91c9d68d6/aiohttp-3.11.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a4076a2b3ba5b004b8cffca6afe18a3b2c5c9ef679b4d1e9859cf76295f8d4f", size = 1632819 }, + { url = "https://files.pythonhosted.org/packages/54/a3/3923c9040cd4927dfee1aa017513701e35adcfc35d10729909688ecaa465/aiohttp-3.11.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:767a97e6900edd11c762be96d82d13a1d7c4fc4b329f054e88b57cdc21fded94", size = 1666567 }, + { url = "https://files.pythonhosted.org/packages/e0/ab/40dacb15c0c58f7f17686ea67bc186e9f207341691bdb777d1d5ff4671d5/aiohttp-3.11.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ddc9337a0fb0e727785ad4f41163cc314376e82b31846d3835673786420ef1", size = 1594959 }, + { url = "https://files.pythonhosted.org/packages/0d/98/d40c2b7c4a5483f9a16ef0adffce279ced3cc44522e84b6ba9e906be5168/aiohttp-3.11.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f414f37b244f2a97e79b98d48c5ff0789a0b4b4609b17d64fa81771ad780e415", size = 1538516 }, + { url = "https://files.pythonhosted.org/packages/cf/10/e0bf3a03524faac45a710daa034e6f1878b24a1fef9c968ac8eb786ae657/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb239f47328581e2ec7744ab5911f97afb10752332a6dd3d98e14e429e1a9e7", size = 1529037 }, + { url = "https://files.pythonhosted.org/packages/ad/d6/5ff5282e00e4eb59c857844984cbc5628f933e2320792e19f93aff518f52/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2c50bad73ed629cc326cc0f75aed8ecfb013f88c5af116f33df556ed47143eb", size = 1546813 }, + { url = "https://files.pythonhosted.org/packages/de/96/f1014f84101f9b9ad2d8acf3cc501426475f7f0cc62308ae5253e2fac9a7/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a8d8f20c39d3fa84d1c28cdb97f3111387e48209e224408e75f29c6f8e0861d", size = 1523852 }, + { url = "https://files.pythonhosted.org/packages/a5/86/ec772c6838dd6bae3229065af671891496ac1834b252f305cee8152584b2/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:106032eaf9e62fd6bc6578c8b9e6dc4f5ed9a5c1c7fb2231010a1b4304393421", size = 1603766 }, + { url = "https://files.pythonhosted.org/packages/84/38/31f85459c9402d409c1499284fc37a96f69afadce3cfac6a1b5ab048cbf1/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b491e42183e8fcc9901d8dcd8ae644ff785590f1727f76ca86e731c61bfe6643", size = 1620647 }, + { url = "https://files.pythonhosted.org/packages/31/2f/54aba0040764dd3d362fb37bd6aae9b3034fcae0b27f51b8a34864e48209/aiohttp-3.11.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad8c745ff9460a16b710e58e06a9dec11ebc0d8f4dd82091cefb579844d69868", size = 1559260 }, + { url = "https://files.pythonhosted.org/packages/ca/d2/a05c7dd9e1b6948c1c5d04f1a8bcfd7e131923fa809bb87477d5c76f1517/aiohttp-3.11.18-cp310-cp310-win32.whl", hash = "sha256:8e57da93e24303a883146510a434f0faf2f1e7e659f3041abc4e3fb3f6702a9f", size = 418051 }, + { url = "https://files.pythonhosted.org/packages/39/e2/796a6179e8abe267dfc84614a50291560a989d28acacbc5dab3bcd4cbec4/aiohttp-3.11.18-cp310-cp310-win_amd64.whl", hash = "sha256:cc93a4121d87d9f12739fc8fab0a95f78444e571ed63e40bfc78cd5abe700ac9", size = 442908 }, + { url = "https://files.pythonhosted.org/packages/2f/10/fd9ee4f9e042818c3c2390054c08ccd34556a3cb209d83285616434cf93e/aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9", size = 712088 }, + { url = "https://files.pythonhosted.org/packages/22/eb/6a77f055ca56f7aae2cd2a5607a3c9e7b9554f1497a069dcfcb52bfc9540/aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b", size = 471450 }, + { url = "https://files.pythonhosted.org/packages/78/dc/5f3c0d27c91abf0bb5d103e9c9b0ff059f60cf6031a5f06f456c90731f42/aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66", size = 457836 }, + { url = "https://files.pythonhosted.org/packages/49/7b/55b65af9ef48b9b811c91ff8b5b9de9650c71147f10523e278d297750bc8/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756", size = 1690978 }, + { url = "https://files.pythonhosted.org/packages/a2/5a/3f8938c4f68ae400152b42742653477fc625d6bfe02e764f3521321c8442/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717", size = 1745307 }, + { url = "https://files.pythonhosted.org/packages/b4/42/89b694a293333ef6f771c62da022163bcf44fb03d4824372d88e3dc12530/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4", size = 1780692 }, + { url = "https://files.pythonhosted.org/packages/e2/ce/1a75384e01dd1bf546898b6062b1b5f7a59b6692ef802e4dd6db64fed264/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f", size = 1676934 }, + { url = "https://files.pythonhosted.org/packages/a5/31/442483276e6c368ab5169797d9873b5875213cbcf7e74b95ad1c5003098a/aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361", size = 1621190 }, + { url = "https://files.pythonhosted.org/packages/7b/83/90274bf12c079457966008a58831a99675265b6a34b505243e004b408934/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1", size = 1658947 }, + { url = "https://files.pythonhosted.org/packages/91/c1/da9cee47a0350b78fdc93670ebe7ad74103011d7778ab4c382ca4883098d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421", size = 1654443 }, + { url = "https://files.pythonhosted.org/packages/c9/f2/73cbe18dc25d624f79a09448adfc4972f82ed6088759ddcf783cd201956c/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e", size = 1644169 }, + { url = "https://files.pythonhosted.org/packages/5b/32/970b0a196c4dccb1b0cfa5b4dc3b20f63d76f1c608f41001a84b2fd23c3d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d", size = 1728532 }, + { url = "https://files.pythonhosted.org/packages/0b/50/b1dc810a41918d2ea9574e74125eb053063bc5e14aba2d98966f7d734da0/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f", size = 1750310 }, + { url = "https://files.pythonhosted.org/packages/95/24/39271f5990b35ff32179cc95537e92499d3791ae82af7dcf562be785cd15/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd", size = 1691580 }, + { url = "https://files.pythonhosted.org/packages/6b/78/75d0353feb77f041460564f12fe58e456436bbc00cbbf5d676dbf0038cc2/aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d", size = 417565 }, + { url = "https://files.pythonhosted.org/packages/ed/97/b912dcb654634a813f8518de359364dfc45976f822116e725dc80a688eee/aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6", size = 443652 }, + { url = "https://files.pythonhosted.org/packages/b5/d2/5bc436f42bf4745c55f33e1e6a2d69e77075d3e768e3d1a34f96ee5298aa/aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2", size = 706671 }, + { url = "https://files.pythonhosted.org/packages/fe/d0/2dbabecc4e078c0474abb40536bbde717fb2e39962f41c5fc7a216b18ea7/aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508", size = 466169 }, + { url = "https://files.pythonhosted.org/packages/70/84/19edcf0b22933932faa6e0be0d933a27bd173da02dc125b7354dff4d8da4/aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e", size = 457554 }, + { url = "https://files.pythonhosted.org/packages/32/d0/e8d1f034ae5624a0f21e4fb3feff79342ce631f3a4d26bd3e58b31ef033b/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f", size = 1690154 }, + { url = "https://files.pythonhosted.org/packages/16/de/2f9dbe2ac6f38f8495562077131888e0d2897e3798a0ff3adda766b04a34/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f", size = 1733402 }, + { url = "https://files.pythonhosted.org/packages/e0/04/bd2870e1e9aef990d14b6df2a695f17807baf5c85a4c187a492bda569571/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec", size = 1783958 }, + { url = "https://files.pythonhosted.org/packages/23/06/4203ffa2beb5bedb07f0da0f79b7d9039d1c33f522e0d1a2d5b6218e6f2e/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6", size = 1695288 }, + { url = "https://files.pythonhosted.org/packages/30/b2/e2285dda065d9f29ab4b23d8bcc81eb881db512afb38a3f5247b191be36c/aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009", size = 1618871 }, + { url = "https://files.pythonhosted.org/packages/57/e0/88f2987885d4b646de2036f7296ebea9268fdbf27476da551c1a7c158bc0/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4", size = 1646262 }, + { url = "https://files.pythonhosted.org/packages/e0/19/4d2da508b4c587e7472a032290b2981f7caeca82b4354e19ab3df2f51d56/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9", size = 1677431 }, + { url = "https://files.pythonhosted.org/packages/eb/ae/047473ea50150a41440f3265f53db1738870b5a1e5406ece561ca61a3bf4/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb", size = 1637430 }, + { url = "https://files.pythonhosted.org/packages/11/32/c6d1e3748077ce7ee13745fae33e5cb1dac3e3b8f8787bf738a93c94a7d2/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda", size = 1703342 }, + { url = "https://files.pythonhosted.org/packages/c5/1d/a3b57bfdbe285f0d45572d6d8f534fd58761da3e9cbc3098372565005606/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1", size = 1740600 }, + { url = "https://files.pythonhosted.org/packages/a5/71/f9cd2fed33fa2b7ce4d412fb7876547abb821d5b5520787d159d0748321d/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea", size = 1695131 }, + { url = "https://files.pythonhosted.org/packages/97/97/d1248cd6d02b9de6aa514793d0dcb20099f0ec47ae71a933290116c070c5/aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8", size = 412442 }, + { url = "https://files.pythonhosted.org/packages/33/9a/e34e65506e06427b111e19218a99abf627638a9703f4b8bcc3e3021277ed/aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8", size = 439444 }, + { url = "https://files.pythonhosted.org/packages/0a/18/be8b5dd6b9cf1b2172301dbed28e8e5e878ee687c21947a6c81d6ceaa15d/aiohttp-3.11.18-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:474215ec618974054cf5dc465497ae9708543cbfc312c65212325d4212525811", size = 699833 }, + { url = "https://files.pythonhosted.org/packages/0d/84/ecdc68e293110e6f6f6d7b57786a77555a85f70edd2b180fb1fafaff361a/aiohttp-3.11.18-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ced70adf03920d4e67c373fd692123e34d3ac81dfa1c27e45904a628567d804", size = 462774 }, + { url = "https://files.pythonhosted.org/packages/d7/85/f07718cca55884dad83cc2433746384d267ee970e91f0dcc75c6d5544079/aiohttp-3.11.18-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d9f6c0152f8d71361905aaf9ed979259537981f47ad099c8b3d81e0319814bd", size = 454429 }, + { url = "https://files.pythonhosted.org/packages/82/02/7f669c3d4d39810db8842c4e572ce4fe3b3a9b82945fdd64affea4c6947e/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a35197013ed929c0aed5c9096de1fc5a9d336914d73ab3f9df14741668c0616c", size = 1670283 }, + { url = "https://files.pythonhosted.org/packages/ec/79/b82a12f67009b377b6c07a26bdd1b81dab7409fc2902d669dbfa79e5ac02/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:540b8a1f3a424f1af63e0af2d2853a759242a1769f9f1ab053996a392bd70118", size = 1717231 }, + { url = "https://files.pythonhosted.org/packages/a6/38/d5a1f28c3904a840642b9a12c286ff41fc66dfa28b87e204b1f242dbd5e6/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9e6710ebebfce2ba21cee6d91e7452d1125100f41b906fb5af3da8c78b764c1", size = 1769621 }, + { url = "https://files.pythonhosted.org/packages/53/2d/deb3749ba293e716b5714dda06e257f123c5b8679072346b1eb28b766a0b/aiohttp-3.11.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8af2ef3b4b652ff109f98087242e2ab974b2b2b496304063585e3d78de0b000", size = 1678667 }, + { url = "https://files.pythonhosted.org/packages/b8/a8/04b6e11683a54e104b984bd19a9790eb1ae5f50968b601bb202d0406f0ff/aiohttp-3.11.18-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28c3f975e5ae3dbcbe95b7e3dcd30e51da561a0a0f2cfbcdea30fc1308d72137", size = 1601592 }, + { url = "https://files.pythonhosted.org/packages/5e/9d/c33305ae8370b789423623f0e073d09ac775cd9c831ac0f11338b81c16e0/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c28875e316c7b4c3e745172d882d8a5c835b11018e33432d281211af35794a93", size = 1621679 }, + { url = "https://files.pythonhosted.org/packages/56/45/8e9a27fff0538173d47ba60362823358f7a5f1653c6c30c613469f94150e/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:13cd38515568ae230e1ef6919e2e33da5d0f46862943fcda74e7e915096815f3", size = 1656878 }, + { url = "https://files.pythonhosted.org/packages/84/5b/8c5378f10d7a5a46b10cb9161a3aac3eeae6dba54ec0f627fc4ddc4f2e72/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0e2a92101efb9f4c2942252c69c63ddb26d20f46f540c239ccfa5af865197bb8", size = 1620509 }, + { url = "https://files.pythonhosted.org/packages/9e/2f/99dee7bd91c62c5ff0aa3c55f4ae7e1bc99c6affef780d7777c60c5b3735/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6d3e32b8753c8d45ac550b11a1090dd66d110d4ef805ffe60fa61495360b3b2", size = 1680263 }, + { url = "https://files.pythonhosted.org/packages/03/0a/378745e4ff88acb83e2d5c884a4fe993a6e9f04600a4560ce0e9b19936e3/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ea4cf2488156e0f281f93cc2fd365025efcba3e2d217cbe3df2840f8c73db261", size = 1715014 }, + { url = "https://files.pythonhosted.org/packages/f6/0b/b5524b3bb4b01e91bc4323aad0c2fcaebdf2f1b4d2eb22743948ba364958/aiohttp-3.11.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d4df95ad522c53f2b9ebc07f12ccd2cb15550941e11a5bbc5ddca2ca56316d7", size = 1666614 }, + { url = "https://files.pythonhosted.org/packages/c7/b7/3d7b036d5a4ed5a4c704e0754afe2eef24a824dfab08e6efbffb0f6dd36a/aiohttp-3.11.18-cp313-cp313-win32.whl", hash = "sha256:cdd1bbaf1e61f0d94aced116d6e95fe25942f7a5f42382195fd9501089db5d78", size = 411358 }, + { url = "https://files.pythonhosted.org/packages/1e/3c/143831b32cd23b5263a995b2a1794e10aa42f8a895aae5074c20fda36c07/aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01", size = 437658 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649 }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471 }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317 }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368 }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491 }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695 }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849 }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091 }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445 }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782 }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "datasets" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/af/8c1d10daf37383c32ab0a7461eaa4d5c7a3c47808fe5a8563744de002df7/datasets-3.5.1.tar.gz", hash = "sha256:f835b45dbbd7065c1191734b6f7c8d96fdf8c5751feaa5aa52b2a0dc43eea58f", size = 568915 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/f5/668b3444a2f487b0052b908af631fe39eeb2bdb2359d9bbc2c3b80b71119/datasets-3.5.1-py3-none-any.whl", hash = "sha256:4074dda8dd6e9ece242b1580a8ef3928777d59ae1db144d911229e443a093cbb", size = 491436 }, +] + +[[package]] +name = "deepspeed" +version = "0.16.4+e2dc3eeb" +source = { git = "https://github.com/microsoft/DeepSpeed.git?rev=v0.16.4#e2dc3eeb1923073e32739596a4fd051417d4ff92" } +dependencies = [ + { name = "einops" }, + { name = "hjson" }, + { name = "msgpack" }, + { name = "ninja" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "py-cpuinfo" }, + { name = "pydantic" }, + { name = "torch" }, + { name = "tqdm" }, +] + +[[package]] +name = "deprecated" +version = "1.2.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 }, +] + +[[package]] +name = "dill" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, +] + +[[package]] +name = "docker-pycreds" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982 }, +] + +[[package]] +name = "docstring-parser" +version = "0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, +] + +[[package]] +name = "einops" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, +] + +[[package]] +name = "electrolyte-fm" +version = "0.1.0" +source = { virtual = "../../" } +dependencies = [ + { name = "datasets" }, + { name = "deepspeed" }, + { name = "jsonargparse", extra = ["jsonnet", "signatures"] }, + { name = "jsonnet" }, + { name = "lightning" }, + { name = "pydoe2" }, + { name = "pytorch-lightning" }, + { name = "rdkit" }, + { name = "rich" }, + { name = "safetensors" }, + { name = "scikit-learn" }, + { name = "selfies" }, + { name = "smirk" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "transformers" }, + { name = "typer" }, + { name = "wandb" }, +] + +[package.metadata] +requires-dist = [ + { name = "datasets", specifier = ">=2.20" }, + { name = "deepspeed", git = "https://github.com/microsoft/DeepSpeed.git?rev=v0.16.4" }, + { name = "jsonargparse", extras = ["signatures", "jsonnet"], specifier = ">=4.27" }, + { name = "jsonnet", specifier = ">=0.20.0" }, + { name = "lightning", specifier = "~=2.5" }, + { name = "pydoe2", specifier = ">=1.3" }, + { name = "pytorch-lightning", specifier = "~=2.5" }, + { name = "rdkit", specifier = ">=2024.3.5" }, + { name = "rich", specifier = ">=13" }, + { name = "safetensors", specifier = ">=0.5.1" }, + { name = "scikit-learn", specifier = ">=1.5.1" }, + { name = "selfies", specifier = ">=2.1.0" }, + { name = "smirk", git = "ssh://git@github.com/BattModels/smirk.git?rev=dfdd9dc27c066f956b69d1f55c2d4717beb7838d" }, + { name = "torch", specifier = "~=2.6" }, + { name = "torchmetrics", specifier = "~=1.7" }, + { name = "transformers", specifier = ">=4.40" }, + { name = "typer", specifier = ">=0.9" }, + { name = "wandb", specifier = ">=0.16" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pre-commit", specifier = ">=3.7" }, + { name = "pytest", specifier = ">=8" }, + { name = "ruff", specifier = ">=0.5.4" }, +] +plotting = [{ name = "matplotlib", specifier = "~=3.9" }] +tokenizers = [{ name = "smilespe", specifier = "==0.0.3" }] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "frozenlist" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/03/22e4eb297981d48468c3d9982ab6076b10895106d3039302a943bb60fd70/frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e", size = 160584 }, + { url = "https://files.pythonhosted.org/packages/2b/b8/c213e35bcf1c20502c6fd491240b08cdd6ceec212ea54873f4cae99a51e4/frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352", size = 124099 }, + { url = "https://files.pythonhosted.org/packages/2b/33/df17b921c2e37b971407b4045deeca6f6de7caf0103c43958da5e1b85e40/frozenlist-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9799257237d0479736e2b4c01ff26b5c7f7694ac9692a426cb717f3dc02fff9b", size = 122106 }, + { url = "https://files.pythonhosted.org/packages/8e/09/93f0293e8a95c05eea7cf9277fef8929fb4d0a2234ad9394cd2a6b6a6bb4/frozenlist-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a7bb0fe1f7a70fb5c6f497dc32619db7d2cdd53164af30ade2f34673f8b1fc", size = 287205 }, + { url = "https://files.pythonhosted.org/packages/5e/34/35612f6f1b1ae0f66a4058599687d8b39352ade8ed329df0890fb553ea1e/frozenlist-1.6.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:36d2fc099229f1e4237f563b2a3e0ff7ccebc3999f729067ce4e64a97a7f2869", size = 295079 }, + { url = "https://files.pythonhosted.org/packages/e5/ca/51577ef6cc4ec818aab94a0034ef37808d9017c2e53158fef8834dbb3a07/frozenlist-1.6.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f27a9f9a86dcf00708be82359db8de86b80d029814e6693259befe82bb58a106", size = 308068 }, + { url = "https://files.pythonhosted.org/packages/36/27/c63a23863b9dcbd064560f0fea41b516bbbf4d2e8e7eec3ff880a96f0224/frozenlist-1.6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ecee69073312951244f11b8627e3700ec2bfe07ed24e3a685a5979f0412d24", size = 305640 }, + { url = "https://files.pythonhosted.org/packages/33/c2/91720b3562a6073ba604547a417c8d3bf5d33e4c8f1231f3f8ff6719e05c/frozenlist-1.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2c7d5aa19714b1b01a0f515d078a629e445e667b9da869a3cd0e6fe7dec78bd", size = 278509 }, + { url = "https://files.pythonhosted.org/packages/d0/6e/1b64671ab2fca1ebf32c5b500205724ac14c98b9bc1574b2ef55853f4d71/frozenlist-1.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bbd454f0fb23b51cadc9bdba616c9678e4114b6f9fa372d462ff2ed9323ec8", size = 287318 }, + { url = "https://files.pythonhosted.org/packages/66/30/589a8d8395d5ebe22a6b21262a4d32876df822c9a152e9f2919967bb8e1a/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7daa508e75613809c7a57136dec4871a21bca3080b3a8fc347c50b187df4f00c", size = 290923 }, + { url = "https://files.pythonhosted.org/packages/4d/e0/2bd0d2a4a7062b7e4b5aad621697cd3579e5d1c39d99f2833763d91e746d/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89ffdb799154fd4d7b85c56d5fa9d9ad48946619e0eb95755723fffa11022d75", size = 304847 }, + { url = "https://files.pythonhosted.org/packages/70/a0/a1a44204398a4b308c3ee1b7bf3bf56b9dcbcc4e61c890e038721d1498db/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:920b6bd77d209931e4c263223381d63f76828bec574440f29eb497cf3394c249", size = 285580 }, + { url = "https://files.pythonhosted.org/packages/78/ed/3862bc9abe05839a6a5f5bab8b6bbdf0fc9369505cb77cd15b8c8948f6a0/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d3ceb265249fb401702fce3792e6b44c1166b9319737d21495d3611028d95769", size = 304033 }, + { url = "https://files.pythonhosted.org/packages/2c/9c/1c48454a9e1daf810aa6d977626c894b406651ca79d722fce0f13c7424f1/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52021b528f1571f98a7d4258c58aa8d4b1a96d4f01d00d51f1089f2e0323cb02", size = 307566 }, + { url = "https://files.pythonhosted.org/packages/35/ef/cb43655c21f1bad5c42bcd540095bba6af78bf1e474b19367f6fd67d029d/frozenlist-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0f2ca7810b809ed0f1917293050163c7654cefc57a49f337d5cd9de717b8fad3", size = 295354 }, + { url = "https://files.pythonhosted.org/packages/9f/59/d8069a688a0f54a968c73300d6013e4786b029bfec308664094130dcea66/frozenlist-1.6.0-cp310-cp310-win32.whl", hash = "sha256:0e6f8653acb82e15e5443dba415fb62a8732b68fe09936bb6d388c725b57f812", size = 115586 }, + { url = "https://files.pythonhosted.org/packages/f9/a6/8f0cef021912ba7aa3b9920fe0a4557f6e85c41bbf71bb568cd744828df5/frozenlist-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a39819a5a3e84304cd286e3dc62a549fe60985415851b3337b6f5cc91907f1", size = 120845 }, + { url = "https://files.pythonhosted.org/packages/53/b5/bc883b5296ec902115c00be161da93bf661199c465ec4c483feec6ea4c32/frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d", size = 160912 }, + { url = "https://files.pythonhosted.org/packages/6f/93/51b058b563d0704b39c56baa222828043aafcac17fd3734bec5dbeb619b1/frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0", size = 124315 }, + { url = "https://files.pythonhosted.org/packages/c9/e0/46cd35219428d350558b874d595e132d1c17a9471a1bd0d01d518a261e7c/frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe", size = 122230 }, + { url = "https://files.pythonhosted.org/packages/d1/0f/7ad2ce928ad06d6dd26a61812b959ded573d3e9d0ee6109d96c2be7172e9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba", size = 314842 }, + { url = "https://files.pythonhosted.org/packages/34/76/98cbbd8a20a5c3359a2004ae5e5b216af84a150ccbad67c8f8f30fb2ea91/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595", size = 304919 }, + { url = "https://files.pythonhosted.org/packages/9a/fa/258e771ce3a44348c05e6b01dffc2bc67603fba95761458c238cd09a2c77/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a", size = 324074 }, + { url = "https://files.pythonhosted.org/packages/d5/a4/047d861fd8c538210e12b208c0479912273f991356b6bdee7ea8356b07c9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626", size = 321292 }, + { url = "https://files.pythonhosted.org/packages/c0/25/cfec8af758b4525676cabd36efcaf7102c1348a776c0d1ad046b8a7cdc65/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff", size = 301569 }, + { url = "https://files.pythonhosted.org/packages/87/2f/0c819372fa9f0c07b153124bf58683b8d0ca7bb73ea5ccde9b9ef1745beb/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a", size = 313625 }, + { url = "https://files.pythonhosted.org/packages/50/5f/f0cf8b0fdedffdb76b3745aa13d5dbe404d63493cc211ce8250f2025307f/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0", size = 312523 }, + { url = "https://files.pythonhosted.org/packages/e1/6c/38c49108491272d3e84125bbabf2c2d0b304899b52f49f0539deb26ad18d/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606", size = 322657 }, + { url = "https://files.pythonhosted.org/packages/bd/4b/3bd3bad5be06a9d1b04b1c22be80b5fe65b502992d62fab4bdb25d9366ee/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584", size = 303414 }, + { url = "https://files.pythonhosted.org/packages/5b/89/7e225a30bef6e85dbfe22622c24afe932e9444de3b40d58b1ea589a14ef8/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a", size = 320321 }, + { url = "https://files.pythonhosted.org/packages/22/72/7e3acef4dd9e86366cb8f4d8f28e852c2b7e116927e9722b31a6f71ea4b0/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1", size = 323975 }, + { url = "https://files.pythonhosted.org/packages/d8/85/e5da03d20507e13c66ce612c9792b76811b7a43e3320cce42d95b85ac755/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e", size = 316553 }, + { url = "https://files.pythonhosted.org/packages/ac/8e/6c609cbd0580ae8a0661c408149f196aade7d325b1ae7adc930501b81acb/frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860", size = 115511 }, + { url = "https://files.pythonhosted.org/packages/f2/13/a84804cfde6de12d44ed48ecbf777ba62b12ff09e761f76cdd1ff9e14bb1/frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603", size = 120863 }, + { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193 }, + { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831 }, + { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862 }, + { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361 }, + { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115 }, + { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505 }, + { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666 }, + { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119 }, + { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788 }, + { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914 }, + { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283 }, + { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264 }, + { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482 }, + { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248 }, + { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161 }, + { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548 }, + { url = "https://files.pythonhosted.org/packages/6f/e5/04c7090c514d96ca00887932417f04343ab94904a56ab7f57861bf63652d/frozenlist-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1d7fb014fe0fbfee3efd6a94fc635aeaa68e5e1720fe9e57357f2e2c6e1a647e", size = 158182 }, + { url = "https://files.pythonhosted.org/packages/e9/8f/60d0555c61eec855783a6356268314d204137f5e0c53b59ae2fc28938c99/frozenlist-1.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01bcaa305a0fdad12745502bfd16a1c75b14558dabae226852f9159364573117", size = 122838 }, + { url = "https://files.pythonhosted.org/packages/5a/a7/d0ec890e3665b4b3b7c05dc80e477ed8dc2e2e77719368e78e2cd9fec9c8/frozenlist-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b314faa3051a6d45da196a2c495e922f987dc848e967d8cfeaee8a0328b1cd4", size = 120980 }, + { url = "https://files.pythonhosted.org/packages/cc/19/9b355a5e7a8eba903a008579964192c3e427444752f20b2144b10bb336df/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da62fecac21a3ee10463d153549d8db87549a5e77eefb8c91ac84bb42bb1e4e3", size = 305463 }, + { url = "https://files.pythonhosted.org/packages/9c/8d/5b4c758c2550131d66935ef2fa700ada2461c08866aef4229ae1554b93ca/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1eb89bf3454e2132e046f9599fbcf0a4483ed43b40f545551a39316d0201cd1", size = 297985 }, + { url = "https://files.pythonhosted.org/packages/48/2c/537ec09e032b5865715726b2d1d9813e6589b571d34d01550c7aeaad7e53/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18689b40cb3936acd971f663ccb8e2589c45db5e2c5f07e0ec6207664029a9c", size = 311188 }, + { url = "https://files.pythonhosted.org/packages/31/2f/1aa74b33f74d54817055de9a4961eff798f066cdc6f67591905d4fc82a84/frozenlist-1.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e67ddb0749ed066b1a03fba812e2dcae791dd50e5da03be50b6a14d0c1a9ee45", size = 311874 }, + { url = "https://files.pythonhosted.org/packages/bf/f0/cfec18838f13ebf4b37cfebc8649db5ea71a1b25dacd691444a10729776c/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc5e64626e6682638d6e44398c9baf1d6ce6bc236d40b4b57255c9d3f9761f1f", size = 291897 }, + { url = "https://files.pythonhosted.org/packages/ea/a5/deb39325cbbea6cd0a46db8ccd76150ae2fcbe60d63243d9df4a0b8c3205/frozenlist-1.6.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:437cfd39564744ae32ad5929e55b18ebd88817f9180e4cc05e7d53b75f79ce85", size = 305799 }, + { url = "https://files.pythonhosted.org/packages/78/22/6ddec55c5243a59f605e4280f10cee8c95a449f81e40117163383829c241/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62dd7df78e74d924952e2feb7357d826af8d2f307557a779d14ddf94d7311be8", size = 302804 }, + { url = "https://files.pythonhosted.org/packages/5d/b7/d9ca9bab87f28855063c4d202936800219e39db9e46f9fb004d521152623/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a66781d7e4cddcbbcfd64de3d41a61d6bdde370fc2e38623f30b2bd539e84a9f", size = 316404 }, + { url = "https://files.pythonhosted.org/packages/a6/3a/1255305db7874d0b9eddb4fe4a27469e1fb63720f1fc6d325a5118492d18/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:482fe06e9a3fffbcd41950f9d890034b4a54395c60b5e61fae875d37a699813f", size = 295572 }, + { url = "https://files.pythonhosted.org/packages/2a/f2/8d38eeee39a0e3a91b75867cc102159ecccf441deb6ddf67be96d3410b84/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e4f9373c500dfc02feea39f7a56e4f543e670212102cc2eeb51d3a99c7ffbde6", size = 307601 }, + { url = "https://files.pythonhosted.org/packages/38/04/80ec8e6b92f61ef085422d7b196822820404f940950dde5b2e367bede8bc/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e69bb81de06827147b7bfbaeb284d85219fa92d9f097e32cc73675f279d70188", size = 314232 }, + { url = "https://files.pythonhosted.org/packages/3a/58/93b41fb23e75f38f453ae92a2f987274c64637c450285577bd81c599b715/frozenlist-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7613d9977d2ab4a9141dde4a149f4357e4065949674c5649f920fec86ecb393e", size = 308187 }, + { url = "https://files.pythonhosted.org/packages/6a/a2/e64df5c5aa36ab3dee5a40d254f3e471bb0603c225f81664267281c46a2d/frozenlist-1.6.0-cp313-cp313-win32.whl", hash = "sha256:4def87ef6d90429f777c9d9de3961679abf938cb6b7b63d4a7eb8a268babfce4", size = 114772 }, + { url = "https://files.pythonhosted.org/packages/a0/77/fead27441e749b2d574bb73d693530d59d520d4b9e9679b8e3cb779d37f2/frozenlist-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:37a8a52c3dfff01515e9bbbee0e6063181362f9de3db2ccf9bc96189b557cbfd", size = 119847 }, + { url = "https://files.pythonhosted.org/packages/df/bd/cc6d934991c1e5d9cafda83dfdc52f987c7b28343686aef2e58a9cf89f20/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:46138f5a0773d064ff663d273b309b696293d7a7c00a0994c5c13a5078134b64", size = 174937 }, + { url = "https://files.pythonhosted.org/packages/f2/a2/daf945f335abdbfdd5993e9dc348ef4507436936ab3c26d7cfe72f4843bf/frozenlist-1.6.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f88bc0a2b9c2a835cb888b32246c27cdab5740059fb3688852bf91e915399b91", size = 136029 }, + { url = "https://files.pythonhosted.org/packages/51/65/4c3145f237a31247c3429e1c94c384d053f69b52110a0d04bfc8afc55fb2/frozenlist-1.6.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:777704c1d7655b802c7850255639672e90e81ad6fa42b99ce5ed3fbf45e338dd", size = 134831 }, + { url = "https://files.pythonhosted.org/packages/77/38/03d316507d8dea84dfb99bdd515ea245628af964b2bf57759e3c9205cc5e/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85ef8d41764c7de0dcdaf64f733a27352248493a85a80661f3c678acd27e31f2", size = 392981 }, + { url = "https://files.pythonhosted.org/packages/37/02/46285ef9828f318ba400a51d5bb616ded38db8466836a9cfa39f3903260b/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:da5cb36623f2b846fb25009d9d9215322318ff1c63403075f812b3b2876c8506", size = 371999 }, + { url = "https://files.pythonhosted.org/packages/0d/64/1212fea37a112c3c5c05bfb5f0a81af4836ce349e69be75af93f99644da9/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cbb56587a16cf0fb8acd19e90ff9924979ac1431baea8681712716a8337577b0", size = 392200 }, + { url = "https://files.pythonhosted.org/packages/81/ce/9a6ea1763e3366e44a5208f76bf37c76c5da570772375e4d0be85180e588/frozenlist-1.6.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6154c3ba59cda3f954c6333025369e42c3acd0c6e8b6ce31eb5c5b8116c07e0", size = 390134 }, + { url = "https://files.pythonhosted.org/packages/bc/36/939738b0b495b2c6d0c39ba51563e453232813042a8d908b8f9544296c29/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e8246877afa3f1ae5c979fe85f567d220f86a50dc6c493b9b7d8191181ae01e", size = 365208 }, + { url = "https://files.pythonhosted.org/packages/b4/8b/939e62e93c63409949c25220d1ba8e88e3960f8ef6a8d9ede8f94b459d27/frozenlist-1.6.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0f6cce16306d2e117cf9db71ab3a9e8878a28176aeaf0dbe35248d97b28d0c", size = 385548 }, + { url = "https://files.pythonhosted.org/packages/62/38/22d2873c90102e06a7c5a3a5b82ca47e393c6079413e8a75c72bff067fa8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1b8e8cd8032ba266f91136d7105706ad57770f3522eac4a111d77ac126a25a9b", size = 391123 }, + { url = "https://files.pythonhosted.org/packages/44/78/63aaaf533ee0701549500f6d819be092c6065cb5c577edb70c09df74d5d0/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e2ada1d8515d3ea5378c018a5f6d14b4994d4036591a52ceaf1a1549dec8e1ad", size = 394199 }, + { url = "https://files.pythonhosted.org/packages/54/45/71a6b48981d429e8fbcc08454dc99c4c2639865a646d549812883e9c9dd3/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:cdb2c7f071e4026c19a3e32b93a09e59b12000751fc9b0b7758da899e657d215", size = 373854 }, + { url = "https://files.pythonhosted.org/packages/3f/f3/dbf2a5e11736ea81a66e37288bf9f881143a7822b288a992579ba1b4204d/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:03572933a1969a6d6ab509d509e5af82ef80d4a5d4e1e9f2e1cdd22c77a3f4d2", size = 395412 }, + { url = "https://files.pythonhosted.org/packages/b3/f1/c63166806b331f05104d8ea385c4acd511598568b1f3e4e8297ca54f2676/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:77effc978947548b676c54bbd6a08992759ea6f410d4987d69feea9cd0919911", size = 394936 }, + { url = "https://files.pythonhosted.org/packages/ef/ea/4f3e69e179a430473eaa1a75ff986526571215fefc6b9281cdc1f09a4eb8/frozenlist-1.6.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a2bda8be77660ad4089caf2223fdbd6db1858462c4b85b67fbfa22102021e497", size = 391459 }, + { url = "https://files.pythonhosted.org/packages/d3/c3/0fc2c97dea550df9afd072a37c1e95421652e3206bbeaa02378b24c2b480/frozenlist-1.6.0-cp313-cp313t-win32.whl", hash = "sha256:a4d96dc5bcdbd834ec6b0f91027817214216b5b30316494d2b1aebffb87c534f", size = 128797 }, + { url = "https://files.pythonhosted.org/packages/ae/f5/79c9320c5656b1965634fe4be9c82b12a3305bdbc58ad9cb941131107b20/frozenlist-1.6.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e18036cb4caa17ea151fd5f3d70be9d354c99eb8cf817a3ccde8a7873b074348", size = 134709 }, + { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404 }, +] + +[[package]] +name = "fsspec" +version = "2025.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/f4/5721faf47b8c499e776bc34c6a8fc17efdf7fdef0b00f398128bc5dcb4ac/fsspec-2025.3.0.tar.gz", hash = "sha256:a935fd1ea872591f2b5148907d103488fc523295e6c64b835cfad8c3eca44972", size = 298491 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl", hash = "sha256:efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3", size = 193615 }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "greenlet" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/74/907bb43af91782e0366b0960af62a8ce1f9398e4291cac7beaeffbee0c04/greenlet-3.2.1.tar.gz", hash = "sha256:9f4dd4b4946b14bb3bf038f81e1d2e535b7d94f1b2a59fdba1293cd9c1a0a4d7", size = 184475 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3e/6332bb2d1e43ec6270e0b97bf253cd704691ee55e4e52196cb7da8f774e9/greenlet-3.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:777c1281aa7c786738683e302db0f55eb4b0077c20f1dc53db8852ffaea0a6b0", size = 267364 }, + { url = "https://files.pythonhosted.org/packages/73/c1/c47cc96878c4eda993a2deaba15af3cfdc87cf8e2e3c4c20726dea541a8c/greenlet-3.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3059c6f286b53ea4711745146ffe5a5c5ff801f62f6c56949446e0f6461f8157", size = 625721 }, + { url = "https://files.pythonhosted.org/packages/c8/65/df1ff1a505a62b08d31da498ddc0c9992e9c536c01944f8b800a7cf17ac6/greenlet-3.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1a40a17e2c7348f5eee5d8e1b4fa6a937f0587eba89411885a36a8e1fc29bd2", size = 636983 }, + { url = "https://files.pythonhosted.org/packages/e8/1d/29944dcaaf5e482f7bff617de15f29e17cc0e74c7393888f8a43d7f6229e/greenlet-3.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5193135b3a8d0017cb438de0d49e92bf2f6c1c770331d24aa7500866f4db4017", size = 632880 }, + { url = "https://files.pythonhosted.org/packages/e4/c6/6c0891fd775b4fc5613593181526ba282771682dfe7bd0206d283403bcbb/greenlet-3.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639a94d001fe874675b553f28a9d44faed90f9864dc57ba0afef3f8d76a18b04", size = 631638 }, + { url = "https://files.pythonhosted.org/packages/c0/50/3d8cadd4dfab17ef72bf0476cc2dacab368273ed29a79bbe66c36c6007a4/greenlet-3.2.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fe303381e7e909e42fb23e191fc69659910909fdcd056b92f6473f80ef18543", size = 580577 }, + { url = "https://files.pythonhosted.org/packages/a5/fe/bb0fc421318c69a840e5b98fdeea29d8dcb38f43ffe8b49664aeb10cc3dc/greenlet-3.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:72c9b668454e816b5ece25daac1a42c94d1c116d5401399a11b77ce8d883110c", size = 1109788 }, + { url = "https://files.pythonhosted.org/packages/89/e9/db23a39effaef855deac9083a9054cbe34e1623dcbabed01e34a9d4174c7/greenlet-3.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6079ae990bbf944cf66bea64a09dcb56085815630955109ffa98984810d71565", size = 1133412 }, + { url = "https://files.pythonhosted.org/packages/6a/86/c33905264b43fe4806720f60124254a149857b42c1bf01bd6e136883c99f/greenlet-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:e63cd2035f49376a23611fbb1643f78f8246e9d4dfd607534ec81b175ce582c2", size = 294958 }, + { url = "https://files.pythonhosted.org/packages/26/80/a6ee52c59f75a387ec1f0c0075cf7981fb4644e4162afd3401dabeaa83ca/greenlet-3.2.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:aa30066fd6862e1153eaae9b51b449a6356dcdb505169647f69e6ce315b9468b", size = 268609 }, + { url = "https://files.pythonhosted.org/packages/ad/11/bd7a900629a4dd0e691dda88f8c2a7bfa44d0c4cffdb47eb5302f87a30d0/greenlet-3.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b0f3a0a67786facf3b907a25db80efe74310f9d63cc30869e49c79ee3fcef7e", size = 628776 }, + { url = "https://files.pythonhosted.org/packages/46/f1/686754913fcc2707addadf815c884fd49c9f00a88e6dac277a1e1a8b8086/greenlet-3.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64a4d0052de53ab3ad83ba86de5ada6aeea8f099b4e6c9ccce70fb29bc02c6a2", size = 640827 }, + { url = "https://files.pythonhosted.org/packages/03/74/bef04fa04125f6bcae2c1117e52f99c5706ac6ee90b7300b49b3bc18fc7d/greenlet-3.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:852ef432919830022f71a040ff7ba3f25ceb9fe8f3ab784befd747856ee58530", size = 636752 }, + { url = "https://files.pythonhosted.org/packages/aa/08/e8d493ab65ae1e9823638b8d0bf5d6b44f062221d424c5925f03960ba3d0/greenlet-3.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4818116e75a0dd52cdcf40ca4b419e8ce5cb6669630cb4f13a6c384307c9543f", size = 635993 }, + { url = "https://files.pythonhosted.org/packages/1f/9d/3a3a979f2b019fb756c9a92cd5e69055aded2862ebd0437de109cf7472a2/greenlet-3.2.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9afa05fe6557bce1642d8131f87ae9462e2a8e8c46f7ed7929360616088a3975", size = 583927 }, + { url = "https://files.pythonhosted.org/packages/59/21/a00d27d9abb914c1213926be56b2a2bf47999cf0baf67d9ef5b105b8eb5b/greenlet-3.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5c12f0d17a88664757e81a6e3fc7c2452568cf460a2f8fb44f90536b2614000b", size = 1112891 }, + { url = "https://files.pythonhosted.org/packages/20/c7/922082bf41f0948a78d703d75261d5297f3db894758317409e4677dc1446/greenlet-3.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dbb4e1aa2000852937dd8f4357fb73e3911da426df8ca9b8df5db231922da474", size = 1138318 }, + { url = "https://files.pythonhosted.org/packages/34/d7/e05aa525d824ec32735ba7e66917e944a64866c1a95365b5bd03f3eb2c08/greenlet-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:cb5ee928ce5fedf9a4b0ccdc547f7887136c4af6109d8f2fe8e00f90c0db47f5", size = 295407 }, + { url = "https://files.pythonhosted.org/packages/f0/d1/e4777b188a04726f6cf69047830d37365b9191017f54caf2f7af336a6f18/greenlet-3.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:0ba2811509a30e5f943be048895a983a8daf0b9aa0ac0ead526dfb5d987d80ea", size = 270381 }, + { url = "https://files.pythonhosted.org/packages/59/e7/b5b738f5679247ddfcf2179c38945519668dced60c3164c20d55c1a7bb4a/greenlet-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4245246e72352b150a1588d43ddc8ab5e306bef924c26571aafafa5d1aaae4e8", size = 637195 }, + { url = "https://files.pythonhosted.org/packages/6c/9f/57968c88a5f6bc371364baf983a2e5549cca8f503bfef591b6dd81332cbc/greenlet-3.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7abc0545d8e880779f0c7ce665a1afc3f72f0ca0d5815e2b006cafc4c1cc5840", size = 651381 }, + { url = "https://files.pythonhosted.org/packages/40/81/1533c9a458e9f2ebccb3ae22f1463b2093b0eb448a88aac36182f1c2cd3d/greenlet-3.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6dcc6d604a6575c6225ac0da39df9335cc0c6ac50725063fa90f104f3dbdb2c9", size = 646110 }, + { url = "https://files.pythonhosted.org/packages/06/66/25f7e4b1468ebe4a520757f2e41c2a36a2f49a12e963431b82e9f98df2a0/greenlet-3.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2273586879affca2d1f414709bb1f61f0770adcabf9eda8ef48fd90b36f15d12", size = 648070 }, + { url = "https://files.pythonhosted.org/packages/d7/4c/49d366565c4c4d29e6f666287b9e2f471a66c3a3d8d5066692e347f09e27/greenlet-3.2.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff38c869ed30fff07f1452d9a204ece1ec6d3c0870e0ba6e478ce7c1515acf22", size = 603816 }, + { url = "https://files.pythonhosted.org/packages/04/15/1612bb61506f44b6b8b6bebb6488702b1fe1432547e95dda57874303a1f5/greenlet-3.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e934591a7a4084fa10ee5ef50eb9d2ac8c4075d5c9cf91128116b5dca49d43b1", size = 1119572 }, + { url = "https://files.pythonhosted.org/packages/cc/2f/002b99dacd1610e825876f5cbbe7f86740aa2a6b76816e5eca41c8457e85/greenlet-3.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:063bcf7f8ee28eb91e7f7a8148c65a43b73fbdc0064ab693e024b5a940070145", size = 1147442 }, + { url = "https://files.pythonhosted.org/packages/c0/ba/82a2c3b9868644ee6011da742156247070f30e952f4d33f33857458450f2/greenlet-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7132e024ebeeeabbe661cf8878aac5d2e643975c4feae833142592ec2f03263d", size = 296207 }, + { url = "https://files.pythonhosted.org/packages/77/2a/581b3808afec55b2db838742527c40b4ce68b9b64feedff0fd0123f4b19a/greenlet-3.2.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:e1967882f0c42eaf42282a87579685c8673c51153b845fde1ee81be720ae27ac", size = 269119 }, + { url = "https://files.pythonhosted.org/packages/b0/f3/1c4e27fbdc84e13f05afc2baf605e704668ffa26e73a43eca93e1120813e/greenlet-3.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e77ae69032a95640a5fe8c857ec7bee569a0997e809570f4c92048691ce4b437", size = 637314 }, + { url = "https://files.pythonhosted.org/packages/fc/1a/9fc43cb0044f425f7252da9847893b6de4e3b20c0a748bce7ab3f063d5bc/greenlet-3.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3227c6ec1149d4520bc99edac3b9bc8358d0034825f3ca7572165cb502d8f29a", size = 651421 }, + { url = "https://files.pythonhosted.org/packages/8a/65/d47c03cdc62c6680206b7420c4a98363ee997e87a5e9da1e83bd7eeb57a8/greenlet-3.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ddda0197c5b46eedb5628d33dad034c455ae77708c7bf192686e760e26d6a0c", size = 645789 }, + { url = "https://files.pythonhosted.org/packages/2f/40/0faf8bee1b106c241780f377b9951dd4564ef0972de1942ef74687aa6bba/greenlet-3.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de62b542e5dcf0b6116c310dec17b82bb06ef2ceb696156ff7bf74a7a498d982", size = 648262 }, + { url = "https://files.pythonhosted.org/packages/e0/a8/73305f713183c2cb08f3ddd32eaa20a6854ba9c37061d682192db9b021c3/greenlet-3.2.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c07a0c01010df42f1f058b3973decc69c4d82e036a951c3deaf89ab114054c07", size = 606770 }, + { url = "https://files.pythonhosted.org/packages/c3/05/7d726e1fb7f8a6ac55ff212a54238a36c57db83446523c763e20cd30b837/greenlet-3.2.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2530bfb0abcd451ea81068e6d0a1aac6dabf3f4c23c8bd8e2a8f579c2dd60d95", size = 1117960 }, + { url = "https://files.pythonhosted.org/packages/bf/9f/2b6cb1bd9f1537e7b08c08705c4a1d7bd4f64489c67d102225c4fd262bda/greenlet-3.2.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c472adfca310f849903295c351d297559462067f618944ce2650a1878b84123", size = 1145500 }, + { url = "https://files.pythonhosted.org/packages/e4/f6/339c6e707062319546598eb9827d3ca8942a3eccc610d4a54c1da7b62527/greenlet-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:24a496479bc8bd01c39aa6516a43c717b4cee7196573c47b1f8e1011f7c12495", size = 295994 }, + { url = "https://files.pythonhosted.org/packages/f1/72/2a251d74a596af7bb1717e891ad4275a3fd5ac06152319d7ad8c77f876af/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175d583f7d5ee57845591fc30d852b75b144eb44b05f38b67966ed6df05c8526", size = 629889 }, + { url = "https://files.pythonhosted.org/packages/29/2e/d7ed8bf97641bf704b6a43907c0e082cdf44d5bc026eb8e1b79283e7a719/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ecc9d33ca9428e4536ea53e79d781792cee114d2fa2695b173092bdbd8cd6d5", size = 635261 }, + { url = "https://files.pythonhosted.org/packages/1e/75/802aa27848a6fcb5e566f69c64534f572e310f0f12d41e9201a81e741551/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f56382ac4df3860ebed8ed838f268f03ddf4e459b954415534130062b16bc32", size = 632523 }, + { url = "https://files.pythonhosted.org/packages/56/09/f7c1c3bab9b4c589ad356503dd71be00935e9c4db4db516ed88fc80f1187/greenlet-3.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc45a7189c91c0f89aaf9d69da428ce8301b0fd66c914a499199cfb0c28420fc", size = 628816 }, + { url = "https://files.pythonhosted.org/packages/79/e0/1bb90d30b5450eac2dffeaac6b692857c4bd642c21883b79faa8fa056cf2/greenlet-3.2.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51a2f49da08cff79ee42eb22f1658a2aed60c72792f0a0a95f5f0ca6d101b1fb", size = 593687 }, + { url = "https://files.pythonhosted.org/packages/c5/b5/adbe03c8b4c178add20cc716021183ae6b0326d56ba8793d7828c94286f6/greenlet-3.2.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:0c68bbc639359493420282d2f34fa114e992a8724481d700da0b10d10a7611b8", size = 1105754 }, + { url = "https://files.pythonhosted.org/packages/39/93/84582d7ef38dec009543ccadec6ab41079a6cbc2b8c0566bcd07bf1aaf6c/greenlet-3.2.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:e775176b5c203a1fa4be19f91da00fd3bff536868b77b237da3f4daa5971ae5d", size = 1125160 }, +] + +[[package]] +name = "hjson" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.30.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "joblib" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, +] + +[[package]] +name = "jsonargparse" +version = "4.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/10/c77a54b0d7f3f7a3263b0f2d5e5697bce417dd08bc483a34369420456d10/jsonargparse-4.39.0.tar.gz", hash = "sha256:d72f9e84c251aeac6cd3cd5d91212f4a7c47be9a58e642dd3bce4b990543b360", size = 201775 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/68/fe1c9ce7acb2155e2d94777dd906a08ba2cc60df2568cdb18c0fed94847c/jsonargparse-4.39.0-py3-none-any.whl", hash = "sha256:62ba3d5a66518b865252cfa285988e2ef8d0339de3cd2ad25f98465dc45d41f1", size = 223002 }, +] + +[package.optional-dependencies] +jsonnet = [ + { name = "jsonnet", marker = "python_full_version < '3.13' and os_name == 'posix'" }, + { name = "jsonnet-binary", marker = "python_full_version < '3.13' and os_name != 'posix'" }, + { name = "jsonnet-sdist", marker = "python_full_version >= '3.13'" }, +] +signatures = [ + { name = "docstring-parser" }, + { name = "typeshed-client" }, +] + +[[package]] +name = "jsonnet" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/0e/76e5029265bc4bc4d99ad521fcb32a83070af29a0babc331ad181c299ee0/jsonnet-0.20.0.tar.gz", hash = "sha256:7e770c7bf3a366b97b650a39430450f77612e74406731eb75c5bd59f3f104d4f", size = 594173 } + +[[package]] +name = "jsonnet-binary" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/1c/9ceeb537e74c6687a3ee3b96f651502061f6b9bd939034cd57038ea15333/jsonnet-binary-0.17.0.tar.gz", hash = "sha256:fbadf25f28161b0ccf29e0b72ef689790d14a9b23a681ab6846bd7cb12e17f1d", size = 294155 } + +[[package]] +name = "jsonnet-sdist" +version = "2024.6.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/58/28693aa624874f260e143fbd3e9c8fa9c9c6c043ecd09cbdb7282fc774b8/jsonnet-sdist-2024.6.23.tar.gz", hash = "sha256:37c88078ab3f3cec8aa2969e88fd2de278d4801abaa52e0a573458ae205370d9", size = 458479 } + +[[package]] +name = "lightning" +version = "2.5.1.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pytorch-lightning" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/d0/fb3c5077efdd74c28ea3a277fd80bbf03738d866013a8637691138bfebca/lightning-2.5.1.post0.tar.gz", hash = "sha256:fda1ac63c283b3b08a54be8d905dd88469cf09e9845d36dd28b699e78911cbc8", size = 631113 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/1b/67201d693a575e8a086831710f33e697fab66166223f792e459ef2b84934/lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:a228a52ca52f0c5006ff327c92b8942f09e1aea3f2d9b0d7c8a209edd5b9e71d", size = 819001 }, +] + +[[package]] +name = "lightning-utilities" +version = "0.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/bb/63a6a8c9e7a96b6ba92647fa5b1595c2dbee29f8178705adb4704d82ecba/lightning_utilities-0.14.3.tar.gz", hash = "sha256:37e2f83f273890052955a44054382c211a303012ee577619efbaa5df9e65e9f5", size = 30346 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/c1/31b3184cba7b257a4a3b5ca5b88b9204ccb7aa02fe3c992280899293ed54/lightning_utilities-0.14.3-py3-none-any.whl", hash = "sha256:4ab9066aa36cd7b93a05713808901909e96cc3f187ea6fd3052b2fd91313b468", size = 28894 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mendeleev" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "deprecated" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pandas" }, + { name = "pydantic" }, + { name = "pyfiglet" }, + { name = "pygments" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/73/233d6fc5a399ebf640999a49c8fd0054ecb94add712011d8b2627217b3b5/mendeleev-1.0.0.tar.gz", hash = "sha256:fb4d5699a02b2d17ae09d2727307b2fe4c50c43f4a3bc750d8cd99f16b4aa83e", size = 1368358 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/2c/5ca2d5c503b788b638c79308f5dfb1f4cb9fad95d070fc3c2e0b81ce8542/mendeleev-1.0.0-py3-none-any.whl", hash = "sha256:8474658ffa7025ac0367c791e715870a6db2756a47698f032178f978b2f1b2f1", size = 1377517 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "msgpack" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/f9/a892a6038c861fa849b11a2bb0502c07bc698ab6ea53359e5771397d883b/msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", size = 150428 }, + { url = "https://files.pythonhosted.org/packages/df/7a/d174cc6a3b6bb85556e6a046d3193294a92f9a8e583cdbd46dc8a1d7e7f4/msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", size = 84131 }, + { url = "https://files.pythonhosted.org/packages/08/52/bf4fbf72f897a23a56b822997a72c16de07d8d56d7bf273242f884055682/msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", size = 81215 }, + { url = "https://files.pythonhosted.org/packages/02/95/dc0044b439b518236aaf012da4677c1b8183ce388411ad1b1e63c32d8979/msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", size = 371229 }, + { url = "https://files.pythonhosted.org/packages/ff/75/09081792db60470bef19d9c2be89f024d366b1e1973c197bb59e6aabc647/msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", size = 378034 }, + { url = "https://files.pythonhosted.org/packages/32/d3/c152e0c55fead87dd948d4b29879b0f14feeeec92ef1fd2ec21b107c3f49/msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", size = 363070 }, + { url = "https://files.pythonhosted.org/packages/d9/2c/82e73506dd55f9e43ac8aa007c9dd088c6f0de2aa19e8f7330e6a65879fc/msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", size = 359863 }, + { url = "https://files.pythonhosted.org/packages/cb/a0/3d093b248837094220e1edc9ec4337de3443b1cfeeb6e0896af8ccc4cc7a/msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", size = 368166 }, + { url = "https://files.pythonhosted.org/packages/e4/13/7646f14f06838b406cf5a6ddbb7e8dc78b4996d891ab3b93c33d1ccc8678/msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", size = 370105 }, + { url = "https://files.pythonhosted.org/packages/67/fa/dbbd2443e4578e165192dabbc6a22c0812cda2649261b1264ff515f19f15/msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", size = 68513 }, + { url = "https://files.pythonhosted.org/packages/24/ce/c2c8fbf0ded750cb63cbcbb61bc1f2dfd69e16dca30a8af8ba80ec182dcd/msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", size = 74687 }, + { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, + { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, + { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, + { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 }, + { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 }, + { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 }, + { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 }, + { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, + { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, + { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, + { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, + { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, + { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, + { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, + { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, + { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, + { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, + { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, + { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, + { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, + { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, +] + +[[package]] +name = "multidict" +version = "6.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/44/45e798d4cd1b5dfe41ddf36266c7aca6d954e3c7a8b0d599ad555ce2b4f8/multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5", size = 65822 }, + { url = "https://files.pythonhosted.org/packages/10/fb/9ea024f928503f8c758f8463759d21958bf27b1f7a1103df73e5022e6a7c/multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188", size = 38706 }, + { url = "https://files.pythonhosted.org/packages/6d/eb/7013316febca37414c0e1469fccadcb1a0e4315488f8f57ca5d29b384863/multidict-6.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a7be07e5df178430621c716a63151165684d3e9958f2bbfcb644246162007ab7", size = 37979 }, + { url = "https://files.pythonhosted.org/packages/64/28/5a7bf4e7422613ea80f9ebc529d3845b20a422cfa94d4355504ac98047ee/multidict-6.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b128dbf1c939674a50dd0b28f12c244d90e5015e751a4f339a96c54f7275e291", size = 220233 }, + { url = "https://files.pythonhosted.org/packages/52/05/b4c58850f71befde6a16548968b48331a155a80627750b150bb5962e4dea/multidict-6.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9cb19dfd83d35b6ff24a4022376ea6e45a2beba8ef3f0836b8a4b288b6ad685", size = 217762 }, + { url = "https://files.pythonhosted.org/packages/99/a3/393e23bba1e9a00f95b3957acd8f5e3ee3446e78c550f593be25f9de0483/multidict-6.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3cf62f8e447ea2c1395afa289b332e49e13d07435369b6f4e41f887db65b40bf", size = 230699 }, + { url = "https://files.pythonhosted.org/packages/9c/a7/52c63069eb1a079f824257bb8045d93e692fa2eb34d08323d1fdbdfc398a/multidict-6.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:909f7d43ff8f13d1adccb6a397094adc369d4da794407f8dd592c51cf0eae4b1", size = 226801 }, + { url = "https://files.pythonhosted.org/packages/2c/e9/40d2b73e7d6574d91074d83477a990e3701affbe8b596010d4f5e6c7a6fa/multidict-6.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bb8f8302fbc7122033df959e25777b0b7659b1fd6bcb9cb6bed76b5de67afef", size = 219833 }, + { url = "https://files.pythonhosted.org/packages/e4/6a/0572b22fe63c632254f55a1c1cb7d29f644002b1d8731d6103a290edc754/multidict-6.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b79471b4f21169ea25ebc37ed6f058040c578e50ade532e2066562597b8a9", size = 212920 }, + { url = "https://files.pythonhosted.org/packages/33/fe/c63735db9dece0053868b2d808bcc2592a83ce1830bc98243852a2b34d42/multidict-6.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a7bd27f7ab3204f16967a6f899b3e8e9eb3362c0ab91f2ee659e0345445e0078", size = 225263 }, + { url = "https://files.pythonhosted.org/packages/47/c2/2db296d64d41525110c27ed38fadd5eb571c6b936233e75a5ea61b14e337/multidict-6.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:99592bd3162e9c664671fd14e578a33bfdba487ea64bcb41d281286d3c870ad7", size = 214249 }, + { url = "https://files.pythonhosted.org/packages/7e/74/8bc26e54c79f9a0f111350b1b28a9cacaaee53ecafccd53c90e59754d55a/multidict-6.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a62d78a1c9072949018cdb05d3c533924ef8ac9bcb06cbf96f6d14772c5cd451", size = 221650 }, + { url = "https://files.pythonhosted.org/packages/af/d7/2ce87606e3799d9a08a941f4c170930a9895886ea8bd0eca75c44baeebe3/multidict-6.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ccdde001578347e877ca4f629450973c510e88e8865d5aefbcb89b852ccc666", size = 231235 }, + { url = "https://files.pythonhosted.org/packages/07/e1/d191a7ad3b90c613fc4b130d07a41c380e249767586148709b54d006ca17/multidict-6.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:eccb67b0e78aa2e38a04c5ecc13bab325a43e5159a181a9d1a6723db913cbb3c", size = 226056 }, + { url = "https://files.pythonhosted.org/packages/24/05/a57490cf6a8d5854f4af2d17dfc54924f37fbb683986e133b76710a36079/multidict-6.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b6fcf6054fc4114a27aa865f8840ef3d675f9316e81868e0ad5866184a6cba5", size = 220014 }, + { url = "https://files.pythonhosted.org/packages/5c/b1/be04fa9f08c684e9e27cca85b4ab94c10f017ec07c4c631af9c8c10bb275/multidict-6.4.3-cp310-cp310-win32.whl", hash = "sha256:f92c7f62d59373cd93bc9969d2da9b4b21f78283b1379ba012f7ee8127b3152e", size = 35042 }, + { url = "https://files.pythonhosted.org/packages/d9/ca/8888f99892513001fa900eef11bafbf38ff3485109510487de009da85748/multidict-6.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:b57e28dbc031d13916b946719f213c494a517b442d7b48b29443e79610acd887", size = 38506 }, + { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259 }, + { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451 }, + { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706 }, + { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669 }, + { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182 }, + { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025 }, + { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481 }, + { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492 }, + { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279 }, + { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733 }, + { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089 }, + { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257 }, + { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728 }, + { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087 }, + { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137 }, + { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959 }, + { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541 }, + { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019 }, + { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925 }, + { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008 }, + { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374 }, + { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869 }, + { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949 }, + { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032 }, + { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517 }, + { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291 }, + { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982 }, + { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823 }, + { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714 }, + { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739 }, + { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809 }, + { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934 }, + { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242 }, + { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635 }, + { url = "https://files.pythonhosted.org/packages/6c/4b/86fd786d03915c6f49998cf10cd5fe6b6ac9e9a071cb40885d2e080fb90d/multidict-6.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a76534263d03ae0cfa721fea40fd2b5b9d17a6f85e98025931d41dc49504474", size = 63831 }, + { url = "https://files.pythonhosted.org/packages/45/05/9b51fdf7aef2563340a93be0a663acba2c428c4daeaf3960d92d53a4a930/multidict-6.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:805031c2f599eee62ac579843555ed1ce389ae00c7e9f74c2a1b45e0564a88dd", size = 37888 }, + { url = "https://files.pythonhosted.org/packages/0b/43/53fc25394386c911822419b522181227ca450cf57fea76e6188772a1bd91/multidict-6.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c56c179839d5dcf51d565132185409d1d5dd8e614ba501eb79023a6cab25576b", size = 36852 }, + { url = "https://files.pythonhosted.org/packages/8a/68/7b99c751e822467c94a235b810a2fd4047d4ecb91caef6b5c60116991c4b/multidict-6.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c64f4ddb3886dd8ab71b68a7431ad4aa01a8fa5be5b11543b29674f29ca0ba3", size = 223644 }, + { url = "https://files.pythonhosted.org/packages/80/1b/d458d791e4dd0f7e92596667784fbf99e5c8ba040affe1ca04f06b93ae92/multidict-6.4.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3002a856367c0b41cad6784f5b8d3ab008eda194ed7864aaa58f65312e2abcac", size = 230446 }, + { url = "https://files.pythonhosted.org/packages/e2/46/9793378d988905491a7806d8987862dc5a0bae8a622dd896c4008c7b226b/multidict-6.4.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d75e621e7d887d539d6e1d789f0c64271c250276c333480a9e1de089611f790", size = 231070 }, + { url = "https://files.pythonhosted.org/packages/a7/b8/b127d3e1f8dd2a5bf286b47b24567ae6363017292dc6dec44656e6246498/multidict-6.4.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:995015cf4a3c0d72cbf453b10a999b92c5629eaf3a0c3e1efb4b5c1f602253bb", size = 229956 }, + { url = "https://files.pythonhosted.org/packages/0c/93/f70a4c35b103fcfe1443059a2bb7f66e5c35f2aea7804105ff214f566009/multidict-6.4.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b0fabae7939d09d7d16a711468c385272fa1b9b7fb0d37e51143585d8e72e0", size = 222599 }, + { url = "https://files.pythonhosted.org/packages/63/8c/e28e0eb2fe34921d6aa32bfc4ac75b09570b4d6818cc95d25499fe08dc1d/multidict-6.4.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61ed4d82f8a1e67eb9eb04f8587970d78fe7cddb4e4d6230b77eda23d27938f9", size = 216136 }, + { url = "https://files.pythonhosted.org/packages/72/f5/fbc81f866585b05f89f99d108be5d6ad170e3b6c4d0723d1a2f6ba5fa918/multidict-6.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:062428944a8dc69df9fdc5d5fc6279421e5f9c75a9ee3f586f274ba7b05ab3c8", size = 228139 }, + { url = "https://files.pythonhosted.org/packages/bb/ba/7d196bad6b85af2307d81f6979c36ed9665f49626f66d883d6c64d156f78/multidict-6.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b90e27b4674e6c405ad6c64e515a505c6d113b832df52fdacb6b1ffd1fa9a1d1", size = 226251 }, + { url = "https://files.pythonhosted.org/packages/cc/e2/fae46a370dce79d08b672422a33df721ec8b80105e0ea8d87215ff6b090d/multidict-6.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7d50d4abf6729921e9613d98344b74241572b751c6b37feed75fb0c37bd5a817", size = 221868 }, + { url = "https://files.pythonhosted.org/packages/26/20/bbc9a3dec19d5492f54a167f08546656e7aef75d181d3d82541463450e88/multidict-6.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:43fe10524fb0a0514be3954be53258e61d87341008ce4914f8e8b92bee6f875d", size = 233106 }, + { url = "https://files.pythonhosted.org/packages/ee/8d/f30ae8f5ff7a2461177f4d8eb0d8f69f27fb6cfe276b54ec4fd5a282d918/multidict-6.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:236966ca6c472ea4e2d3f02f6673ebfd36ba3f23159c323f5a496869bc8e47c9", size = 230163 }, + { url = "https://files.pythonhosted.org/packages/15/e9/2833f3c218d3c2179f3093f766940ded6b81a49d2e2f9c46ab240d23dfec/multidict-6.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:422a5ec315018e606473ba1f5431e064cf8b2a7468019233dcf8082fabad64c8", size = 225906 }, + { url = "https://files.pythonhosted.org/packages/f1/31/6edab296ac369fd286b845fa5dd4c409e63bc4655ed8c9510fcb477e9ae9/multidict-6.4.3-cp313-cp313-win32.whl", hash = "sha256:f901a5aace8e8c25d78960dcc24c870c8d356660d3b49b93a78bf38eb682aac3", size = 35238 }, + { url = "https://files.pythonhosted.org/packages/23/57/2c0167a1bffa30d9a1383c3dab99d8caae985defc8636934b5668830d2ef/multidict-6.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:1c152c49e42277bc9a2f7b78bd5fa10b13e88d1b0328221e7aef89d5c60a99a5", size = 38799 }, + { url = "https://files.pythonhosted.org/packages/c9/13/2ead63b9ab0d2b3080819268acb297bd66e238070aa8d42af12b08cbee1c/multidict-6.4.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:be8751869e28b9c0d368d94f5afcb4234db66fe8496144547b4b6d6a0645cfc6", size = 68642 }, + { url = "https://files.pythonhosted.org/packages/85/45/f1a751e1eede30c23951e2ae274ce8fad738e8a3d5714be73e0a41b27b16/multidict-6.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d4b31f8a68dccbcd2c0ea04f0e014f1defc6b78f0eb8b35f2265e8716a6df0c", size = 40028 }, + { url = "https://files.pythonhosted.org/packages/a7/29/fcc53e886a2cc5595cc4560df333cb9630257bda65003a7eb4e4e0d8f9c1/multidict-6.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:032efeab3049e37eef2ff91271884303becc9e54d740b492a93b7e7266e23756", size = 39424 }, + { url = "https://files.pythonhosted.org/packages/f6/f0/056c81119d8b88703971f937b371795cab1407cd3c751482de5bfe1a04a9/multidict-6.4.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e78006af1a7c8a8007e4f56629d7252668344442f66982368ac06522445e375", size = 226178 }, + { url = "https://files.pythonhosted.org/packages/a3/79/3b7e5fea0aa80583d3a69c9d98b7913dfd4fbc341fb10bb2fb48d35a9c21/multidict-6.4.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:daeac9dd30cda8703c417e4fddccd7c4dc0c73421a0b54a7da2713be125846be", size = 222617 }, + { url = "https://files.pythonhosted.org/packages/06/db/3ed012b163e376fc461e1d6a67de69b408339bc31dc83d39ae9ec3bf9578/multidict-6.4.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f6f90700881438953eae443a9c6f8a509808bc3b185246992c4233ccee37fea", size = 227919 }, + { url = "https://files.pythonhosted.org/packages/b1/db/0433c104bca380989bc04d3b841fc83e95ce0c89f680e9ea4251118b52b6/multidict-6.4.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f84627997008390dd15762128dcf73c3365f4ec0106739cde6c20a07ed198ec8", size = 226097 }, + { url = "https://files.pythonhosted.org/packages/c2/95/910db2618175724dd254b7ae635b6cd8d2947a8b76b0376de7b96d814dab/multidict-6.4.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3307b48cd156153b117c0ea54890a3bdbf858a5b296ddd40dc3852e5f16e9b02", size = 220706 }, + { url = "https://files.pythonhosted.org/packages/d1/af/aa176c6f5f1d901aac957d5258d5e22897fe13948d1e69063ae3d5d0ca01/multidict-6.4.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ead46b0fa1dcf5af503a46e9f1c2e80b5d95c6011526352fa5f42ea201526124", size = 211728 }, + { url = "https://files.pythonhosted.org/packages/e7/42/d51cc5fc1527c3717d7f85137d6c79bb7a93cd214c26f1fc57523774dbb5/multidict-6.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1748cb2743bedc339d63eb1bca314061568793acd603a6e37b09a326334c9f44", size = 226276 }, + { url = "https://files.pythonhosted.org/packages/28/6b/d836dea45e0b8432343ba4acf9a8ecaa245da4c0960fb7ab45088a5e568a/multidict-6.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:acc9fa606f76fc111b4569348cc23a771cb52c61516dcc6bcef46d612edb483b", size = 212069 }, + { url = "https://files.pythonhosted.org/packages/55/34/0ee1a7adb3560e18ee9289c6e5f7db54edc312b13e5c8263e88ea373d12c/multidict-6.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:31469d5832b5885adeb70982e531ce86f8c992334edd2f2254a10fa3182ac504", size = 217858 }, + { url = "https://files.pythonhosted.org/packages/04/08/586d652c2f5acefe0cf4e658eedb4d71d4ba6dfd4f189bd81b400fc1bc6b/multidict-6.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ba46b51b6e51b4ef7bfb84b82f5db0dc5e300fb222a8a13b8cd4111898a869cf", size = 226988 }, + { url = "https://files.pythonhosted.org/packages/82/e3/cc59c7e2bc49d7f906fb4ffb6d9c3a3cf21b9f2dd9c96d05bef89c2b1fd1/multidict-6.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:389cfefb599edf3fcfd5f64c0410da686f90f5f5e2c4d84e14f6797a5a337af4", size = 220435 }, + { url = "https://files.pythonhosted.org/packages/e0/32/5c3a556118aca9981d883f38c4b1bfae646f3627157f70f4068e5a648955/multidict-6.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:64bc2bbc5fba7b9db5c2c8d750824f41c6994e3882e6d73c903c2afa78d091e4", size = 221494 }, + { url = "https://files.pythonhosted.org/packages/b9/3b/1599631f59024b75c4d6e3069f4502409970a336647502aaf6b62fb7ac98/multidict-6.4.3-cp313-cp313t-win32.whl", hash = "sha256:0ecdc12ea44bab2807d6b4a7e5eef25109ab1c82a8240d86d3c1fc9f3b72efd5", size = 41775 }, + { url = "https://files.pythonhosted.org/packages/e8/4e/09301668d675d02ca8e8e1a3e6be046619e30403f5ada2ed5b080ae28d02/multidict-6.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7146a8742ea71b5d7d955bffcef58a9e6e04efba704b52a460134fefd10a8208", size = 45946 }, + { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400 }, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/76/6e712a2623d146d314f17598df5de7224c85c0060ef63fd95cc15a25b3fa/multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee", size = 134980 }, + { url = "https://files.pythonhosted.org/packages/0f/ab/1e6e8009e380e22254ff539ebe117861e5bdb3bff1fc977920972237c6c7/multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec", size = 134982 }, + { url = "https://files.pythonhosted.org/packages/bc/f7/7ec7fddc92e50714ea3745631f79bd9c96424cb2702632521028e57d3a36/multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02", size = 134824 }, + { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519 }, + { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741 }, + { url = "https://files.pythonhosted.org/packages/ea/89/38df130f2c799090c978b366cfdf5b96d08de5b29a4a293df7f7429fa50b/multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435", size = 132628 }, + { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + +[[package]] +name = "ninja" +version = "1.11.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/d4/6b0324541018561c5e73e617bd16f20a4fc17d1179bb3b3520b6ca8beb7b/ninja-1.11.1.4.tar.gz", hash = "sha256:6aa39f6e894e0452e5b297327db00019383ae55d5d9c57c73b04f13bf79d438a", size = 201256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/b1/3a61b348936b62a386465b1937cd778fa3a5748582e26d832dbab844ff27/ninja-1.11.1.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:b33923c8da88e8da20b6053e38deb433f53656441614207e01d283ad02c5e8e7", size = 279071 }, + { url = "https://files.pythonhosted.org/packages/12/42/4c94fdad51fcf1f039a156e97de9e4d564c2a8cc0303782d36f9bd893a4b/ninja-1.11.1.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cede0af00b58e27b31f2482ba83292a8e9171cdb9acc2c867a3b6e40b3353e43", size = 472026 }, + { url = "https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0", size = 422814 }, + { url = "https://files.pythonhosted.org/packages/e3/ad/fb6cca942528e25e8e0ab0f0cf98fe007319bf05cf69d726c564b815c4af/ninja-1.11.1.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3090d4488fadf6047d0d7a1db0c9643a8d391f0d94729554dbb89b5bdc769d7", size = 156965 }, + { url = "https://files.pythonhosted.org/packages/a8/e7/d94a1b60031b115dd88526834b3da69eaacdc3c1a6769773ca8e2b1386b5/ninja-1.11.1.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecce44a00325a93631792974659cf253a815cc6da4ec96f89742925dfc295a0d", size = 179937 }, + { url = "https://files.pythonhosted.org/packages/08/cc/e9316a28235409e9363794fc3d0b3083e48dd80d441006de66421e55f364/ninja-1.11.1.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c29bb66d2aa46a2409ab369ea804c730faec7652e8c22c1e428cc09216543e5", size = 157020 }, + { url = "https://files.pythonhosted.org/packages/e3/30/389b22300541aa5f2e9dad322c4de2f84be4e32aa4e8babd9160d620b5f1/ninja-1.11.1.4-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:055f386fb550c2c9d6157e45e20a84d29c47968876b9c5794ae2aec46f952306", size = 130389 }, + { url = "https://files.pythonhosted.org/packages/a9/10/e27f35cb92813aabbb7ae771b1685b45be1cc8a0798ce7d4bfd08d142b93/ninja-1.11.1.4-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:f6186d7607bb090c3be1e10c8a56b690be238f953616626f5032238c66e56867", size = 372435 }, + { url = "https://files.pythonhosted.org/packages/c2/26/e3559619756739aae124c6abf7fe41f7e546ab1209cfbffb13137bff2d2e/ninja-1.11.1.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:cf4453679d15babc04ba023d68d091bb613091b67101c88f85d2171c6621c6eb", size = 419300 }, + { url = "https://files.pythonhosted.org/packages/35/46/809e4e9572570991b8e6f88f3583807d017371ab4cb09171cbc72a7eb3e4/ninja-1.11.1.4-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:d4a6f159b08b0ac4aca5ee1572e3e402f969139e71d85d37c0e2872129098749", size = 420239 }, + { url = "https://files.pythonhosted.org/packages/e6/64/5cb5710d15f844edf02ada577f8eddfdcd116f47eec15850f3371a3a4b33/ninja-1.11.1.4-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:c3b96bd875f3ef1db782470e9e41d7508905a0986571f219d20ffed238befa15", size = 415986 }, + { url = "https://files.pythonhosted.org/packages/95/b2/0e9ab1d926f423b12b09925f78afcc5e48b3c22e7121be3ddf6c35bf06a3/ninja-1.11.1.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:cf554e73f72c04deb04d0cf51f5fdb1903d9c9ca3d2344249c8ce3bd616ebc02", size = 379657 }, + { url = "https://files.pythonhosted.org/packages/c8/3e/fd6d330d0434168e7fe070d414b57dd99c4c133faa69c05b42a3cbdc6c13/ninja-1.11.1.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:cfdd09776436a1ff3c4a2558d3fc50a689fb9d7f1bdbc3e6f7b8c2991341ddb3", size = 454466 }, + { url = "https://files.pythonhosted.org/packages/e6/df/a25f3ad0b1c59d1b90564096e4fd89a6ca30d562b1e942f23880c3000b89/ninja-1.11.1.4-py3-none-win32.whl", hash = "sha256:2ab67a41c90bea5ec4b795bab084bc0b3b3bb69d3cd21ca0294fc0fc15a111eb", size = 255931 }, + { url = "https://files.pythonhosted.org/packages/5b/10/9b8fe9ac004847490cc7b54896124c01ce2d87d95dc60aabd0b8591addff/ninja-1.11.1.4-py3-none-win_amd64.whl", hash = "sha256:4617b3c12ff64b611a7d93fd9e378275512bb36eff8babff7c83f5116b4f8d66", size = 296461 }, + { url = "https://files.pythonhosted.org/packages/b9/58/612a17593c2d117f96c7f6b7f1e6570246bddc4b1e808519403a1417f217/ninja-1.11.1.4-py3-none-win_arm64.whl", hash = "sha256:5713cf50c5be50084a8693308a63ecf9e55c3132a78a41ab1363a28b6caaaee1", size = 271441 }, +] + +[[package]] +name = "numpy" +version = "1.26.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform != 'linux'", + "python_full_version < '3.11' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform != 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, +] + +[[package]] +name = "numpy" +version = "2.2.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform != 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform != 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/b2/ce4b867d8cd9c0ee84938ae1e6a6f7926ebf928c9090d036fc3c6a04f946/numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", size = 20273920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/4e/3d9e6d16237c2aa5485695f0626cbba82f6481efca2e9132368dea3b885e/numpy-2.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f4a922da1729f4c40932b2af4fe84909c7a6e167e6e99f71838ce3a29f3fe26", size = 21252117 }, + { url = "https://files.pythonhosted.org/packages/38/e4/db91349d4079cd15c02ff3b4b8882a529991d6aca077db198a2f2a670406/numpy-2.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6f91524d31b34f4a5fee24f5bc16dcd1491b668798b6d85585d836c1e633a6a", size = 14424615 }, + { url = "https://files.pythonhosted.org/packages/f8/59/6e5b011f553c37b008bd115c7ba7106a18f372588fbb1b430b7a5d2c41ce/numpy-2.2.5-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:19f4718c9012e3baea91a7dba661dcab2451cda2550678dc30d53acb91a7290f", size = 5428691 }, + { url = "https://files.pythonhosted.org/packages/a2/58/d5d70ebdac82b3a6ddf409b3749ca5786636e50fd64d60edb46442af6838/numpy-2.2.5-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:eb7fd5b184e5d277afa9ec0ad5e4eb562ecff541e7f60e69ee69c8d59e9aeaba", size = 6965010 }, + { url = "https://files.pythonhosted.org/packages/dc/a8/c290394be346d4e7b48a40baf292626fd96ec56a6398ace4c25d9079bc6a/numpy-2.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6413d48a9be53e183eb06495d8e3b006ef8f87c324af68241bbe7a39e8ff54c3", size = 14369885 }, + { url = "https://files.pythonhosted.org/packages/c2/70/fed13c70aabe7049368553e81d7ca40f305f305800a007a956d7cd2e5476/numpy-2.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7451f92eddf8503c9b8aa4fe6aa7e87fd51a29c2cfc5f7dbd72efde6c65acf57", size = 16418372 }, + { url = "https://files.pythonhosted.org/packages/04/ab/c3c14f25ddaecd6fc58a34858f6a93a21eea6c266ba162fa99f3d0de12ac/numpy-2.2.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0bcb1d057b7571334139129b7f941588f69ce7c4ed15a9d6162b2ea54ded700c", size = 15883173 }, + { url = "https://files.pythonhosted.org/packages/50/18/f53710a19042911c7aca824afe97c203728a34b8cf123e2d94621a12edc3/numpy-2.2.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36ab5b23915887543441efd0417e6a3baa08634308894316f446027611b53bf1", size = 18206881 }, + { url = "https://files.pythonhosted.org/packages/6b/ec/5b407bab82f10c65af5a5fe754728df03f960fd44d27c036b61f7b3ef255/numpy-2.2.5-cp310-cp310-win32.whl", hash = "sha256:422cc684f17bc963da5f59a31530b3936f57c95a29743056ef7a7903a5dbdf88", size = 6609852 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/467ca8675c7e6c567f571d8db942cc10a87588bd9e20a909d8af4171edda/numpy-2.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:e4f0b035d9d0ed519c813ee23e0a733db81ec37d2e9503afbb6e54ccfdee0fa7", size = 12944922 }, + { url = "https://files.pythonhosted.org/packages/f5/fb/e4e4c254ba40e8f0c78218f9e86304628c75b6900509b601c8433bdb5da7/numpy-2.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c42365005c7a6c42436a54d28c43fe0e01ca11eb2ac3cefe796c25a5f98e5e9b", size = 21256475 }, + { url = "https://files.pythonhosted.org/packages/81/32/dd1f7084f5c10b2caad778258fdaeedd7fbd8afcd2510672811e6138dfac/numpy-2.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:498815b96f67dc347e03b719ef49c772589fb74b8ee9ea2c37feae915ad6ebda", size = 14461474 }, + { url = "https://files.pythonhosted.org/packages/0e/65/937cdf238ef6ac54ff749c0f66d9ee2b03646034c205cea9b6c51f2f3ad1/numpy-2.2.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6411f744f7f20081b1b4e7112e0f4c9c5b08f94b9f086e6f0adf3645f85d3a4d", size = 5426875 }, + { url = "https://files.pythonhosted.org/packages/25/17/814515fdd545b07306eaee552b65c765035ea302d17de1b9cb50852d2452/numpy-2.2.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9de6832228f617c9ef45d948ec1cd8949c482238d68b2477e6f642c33a7b0a54", size = 6969176 }, + { url = "https://files.pythonhosted.org/packages/e5/32/a66db7a5c8b5301ec329ab36d0ecca23f5e18907f43dbd593c8ec326d57c/numpy-2.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:369e0d4647c17c9363244f3468f2227d557a74b6781cb62ce57cf3ef5cc7c610", size = 14374850 }, + { url = "https://files.pythonhosted.org/packages/ad/c9/1bf6ada582eebcbe8978f5feb26584cd2b39f94ededeea034ca8f84af8c8/numpy-2.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262d23f383170f99cd9191a7c85b9a50970fe9069b2f8ab5d786eca8a675d60b", size = 16430306 }, + { url = "https://files.pythonhosted.org/packages/6a/f0/3f741863f29e128f4fcfdb99253cc971406b402b4584663710ee07f5f7eb/numpy-2.2.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa70fdbdc3b169d69e8c59e65c07a1c9351ceb438e627f0fdcd471015cd956be", size = 15884767 }, + { url = "https://files.pythonhosted.org/packages/98/d9/4ccd8fd6410f7bf2d312cbc98892e0e43c2fcdd1deae293aeb0a93b18071/numpy-2.2.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37e32e985f03c06206582a7323ef926b4e78bdaa6915095ef08070471865b906", size = 18219515 }, + { url = "https://files.pythonhosted.org/packages/b1/56/783237243d4395c6dd741cf16eeb1a9035ee3d4310900e6b17e875d1b201/numpy-2.2.5-cp311-cp311-win32.whl", hash = "sha256:f5045039100ed58fa817a6227a356240ea1b9a1bc141018864c306c1a16d4175", size = 6607842 }, + { url = "https://files.pythonhosted.org/packages/98/89/0c93baaf0094bdaaaa0536fe61a27b1dce8a505fa262a865ec142208cfe9/numpy-2.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:b13f04968b46ad705f7c8a80122a42ae8f620536ea38cf4bdd374302926424dd", size = 12949071 }, + { url = "https://files.pythonhosted.org/packages/e2/f7/1fd4ff108cd9d7ef929b8882692e23665dc9c23feecafbb9c6b80f4ec583/numpy-2.2.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051", size = 20948633 }, + { url = "https://files.pythonhosted.org/packages/12/03/d443c278348371b20d830af155ff2079acad6a9e60279fac2b41dbbb73d8/numpy-2.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc", size = 14176123 }, + { url = "https://files.pythonhosted.org/packages/2b/0b/5ca264641d0e7b14393313304da48b225d15d471250376f3fbdb1a2be603/numpy-2.2.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e", size = 5163817 }, + { url = "https://files.pythonhosted.org/packages/04/b3/d522672b9e3d28e26e1613de7675b441bbd1eaca75db95680635dd158c67/numpy-2.2.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa", size = 6698066 }, + { url = "https://files.pythonhosted.org/packages/a0/93/0f7a75c1ff02d4b76df35079676b3b2719fcdfb39abdf44c8b33f43ef37d/numpy-2.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571", size = 14087277 }, + { url = "https://files.pythonhosted.org/packages/b0/d9/7c338b923c53d431bc837b5b787052fef9ae68a56fe91e325aac0d48226e/numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073", size = 16135742 }, + { url = "https://files.pythonhosted.org/packages/2d/10/4dec9184a5d74ba9867c6f7d1e9f2e0fb5fe96ff2bf50bb6f342d64f2003/numpy-2.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8", size = 15581825 }, + { url = "https://files.pythonhosted.org/packages/80/1f/2b6fcd636e848053f5b57712a7d1880b1565eec35a637fdfd0a30d5e738d/numpy-2.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae", size = 17899600 }, + { url = "https://files.pythonhosted.org/packages/ec/87/36801f4dc2623d76a0a3835975524a84bd2b18fe0f8835d45c8eae2f9ff2/numpy-2.2.5-cp312-cp312-win32.whl", hash = "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb", size = 6312626 }, + { url = "https://files.pythonhosted.org/packages/8b/09/4ffb4d6cfe7ca6707336187951992bd8a8b9142cf345d87ab858d2d7636a/numpy-2.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282", size = 12645715 }, + { url = "https://files.pythonhosted.org/packages/e2/a0/0aa7f0f4509a2e07bd7a509042967c2fab635690d4f48c6c7b3afd4f448c/numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", size = 20935102 }, + { url = "https://files.pythonhosted.org/packages/7e/e4/a6a9f4537542912ec513185396fce52cdd45bdcf3e9d921ab02a93ca5aa9/numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", size = 14191709 }, + { url = "https://files.pythonhosted.org/packages/be/65/72f3186b6050bbfe9c43cb81f9df59ae63603491d36179cf7a7c8d216758/numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", size = 5149173 }, + { url = "https://files.pythonhosted.org/packages/e5/e9/83e7a9432378dde5802651307ae5e9ea07bb72b416728202218cd4da2801/numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", size = 6684502 }, + { url = "https://files.pythonhosted.org/packages/ea/27/b80da6c762394c8ee516b74c1f686fcd16c8f23b14de57ba0cad7349d1d2/numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", size = 14084417 }, + { url = "https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", size = 16133807 }, + { url = "https://files.pythonhosted.org/packages/bf/9b/4cc171a0acbe4666f7775cfd21d4eb6bb1d36d3a0431f48a73e9212d2278/numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", size = 15575611 }, + { url = "https://files.pythonhosted.org/packages/a3/45/40f4135341850df48f8edcf949cf47b523c404b712774f8855a64c96ef29/numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", size = 17895747 }, + { url = "https://files.pythonhosted.org/packages/f8/4c/b32a17a46f0ffbde8cc82df6d3daeaf4f552e346df143e1b188a701a8f09/numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", size = 6309594 }, + { url = "https://files.pythonhosted.org/packages/13/ae/72e6276feb9ef06787365b05915bfdb057d01fceb4a43cb80978e518d79b/numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", size = 12638356 }, + { url = "https://files.pythonhosted.org/packages/79/56/be8b85a9f2adb688e7ded6324e20149a03541d2b3297c3ffc1a73f46dedb/numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", size = 20963778 }, + { url = "https://files.pythonhosted.org/packages/ff/77/19c5e62d55bff507a18c3cdff82e94fe174957bad25860a991cac719d3ab/numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", size = 14207279 }, + { url = "https://files.pythonhosted.org/packages/75/22/aa11f22dc11ff4ffe4e849d9b63bbe8d4ac6d5fae85ddaa67dfe43be3e76/numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", size = 5199247 }, + { url = "https://files.pythonhosted.org/packages/4f/6c/12d5e760fc62c08eded0394f62039f5a9857f758312bf01632a81d841459/numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", size = 6711087 }, + { url = "https://files.pythonhosted.org/packages/ef/94/ece8280cf4218b2bee5cec9567629e61e51b4be501e5c6840ceb593db945/numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", size = 14059964 }, + { url = "https://files.pythonhosted.org/packages/39/41/c5377dac0514aaeec69115830a39d905b1882819c8e65d97fc60e177e19e/numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", size = 16121214 }, + { url = "https://files.pythonhosted.org/packages/db/54/3b9f89a943257bc8e187145c6bc0eb8e3d615655f7b14e9b490b053e8149/numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", size = 15575788 }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2e407e85df35b29f79945751b8f8e671057a13a376497d7fb2151ba0d290/numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", size = 17893672 }, + { url = "https://files.pythonhosted.org/packages/29/7e/d0b44e129d038dba453f00d0e29ebd6eaf2f06055d72b95b9947998aca14/numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", size = 6377102 }, + { url = "https://files.pythonhosted.org/packages/63/be/b85e4aa4bf42c6502851b971f1c326d583fcc68227385f92089cf50a7b45/numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", size = 12750096 }, + { url = "https://files.pythonhosted.org/packages/35/e4/5ef5ef1d4308f96961198b2323bfc7c7afb0ccc0d623b01c79bc87ab496d/numpy-2.2.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4ea7e1cff6784e58fe281ce7e7f05036b3e1c89c6f922a6bfbc0a7e8768adbe", size = 21083404 }, + { url = "https://files.pythonhosted.org/packages/a3/5f/bde9238e8e977652a16a4b114ed8aa8bb093d718c706eeecb5f7bfa59572/numpy-2.2.5-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d7543263084a85fbc09c704b515395398d31d6395518446237eac219eab9e55e", size = 6828578 }, + { url = "https://files.pythonhosted.org/packages/ef/7f/813f51ed86e559ab2afb6a6f33aa6baf8a560097e25e4882a938986c76c2/numpy-2.2.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0255732338c4fdd00996c0421884ea8a3651eea555c3a56b84892b66f696eb70", size = 16234796 }, + { url = "https://files.pythonhosted.org/packages/68/67/1175790323026d3337cc285cc9c50eca637d70472b5e622529df74bb8f37/numpy-2.2.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169", size = 12859001 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.6.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.6.80" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980 }, + { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690 }, + { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.5.1.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632 }, + { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622 }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.11.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.7.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010 }, + { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790 }, + { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367 }, + { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.26.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.6.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276 }, + { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 }, + { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 }, + { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 }, + { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 }, + { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 }, + { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 }, + { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 }, + { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 }, + { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 }, + { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 }, + { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 }, + { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 }, + { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 }, + { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, + { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, + { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, + { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, + { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, + { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, + { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "pillow" +version = "11.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/8b/b158ad57ed44d3cc54db8d68ad7c0a58b8fc0e4c7a3f995f9d62d5b464a1/pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047", size = 3198442 }, + { url = "https://files.pythonhosted.org/packages/b1/f8/bb5d956142f86c2d6cc36704943fa761f2d2e4c48b7436fd0a85c20f1713/pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95", size = 3030553 }, + { url = "https://files.pythonhosted.org/packages/22/7f/0e413bb3e2aa797b9ca2c5c38cb2e2e45d88654e5b12da91ad446964cfae/pillow-11.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61", size = 4405503 }, + { url = "https://files.pythonhosted.org/packages/f3/b4/cc647f4d13f3eb837d3065824aa58b9bcf10821f029dc79955ee43f793bd/pillow-11.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1", size = 4490648 }, + { url = "https://files.pythonhosted.org/packages/c2/6f/240b772a3b35cdd7384166461567aa6713799b4e78d180c555bd284844ea/pillow-11.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c", size = 4508937 }, + { url = "https://files.pythonhosted.org/packages/f3/5e/7ca9c815ade5fdca18853db86d812f2f188212792780208bdb37a0a6aef4/pillow-11.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d", size = 4599802 }, + { url = "https://files.pythonhosted.org/packages/02/81/c3d9d38ce0c4878a77245d4cf2c46d45a4ad0f93000227910a46caff52f3/pillow-11.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97", size = 4576717 }, + { url = "https://files.pythonhosted.org/packages/42/49/52b719b89ac7da3185b8d29c94d0e6aec8140059e3d8adcaa46da3751180/pillow-11.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579", size = 4654874 }, + { url = "https://files.pythonhosted.org/packages/5b/0b/ede75063ba6023798267023dc0d0401f13695d228194d2242d5a7ba2f964/pillow-11.2.1-cp310-cp310-win32.whl", hash = "sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d", size = 2331717 }, + { url = "https://files.pythonhosted.org/packages/ed/3c/9831da3edea527c2ed9a09f31a2c04e77cd705847f13b69ca60269eec370/pillow-11.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad", size = 2676204 }, + { url = "https://files.pythonhosted.org/packages/01/97/1f66ff8a1503d8cbfc5bae4dc99d54c6ec1e22ad2b946241365320caabc2/pillow-11.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2", size = 2414767 }, + { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, + { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, + { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, + { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, + { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, + { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, + { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, + { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, + { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, + { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, + { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, + { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, + { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, + { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, + { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, + { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, + { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, + { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, + { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, + { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, + { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, + { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, + { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, + { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, + { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, + { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, + { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, + { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, + { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, + { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, + { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, + { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, + { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, + { url = "https://files.pythonhosted.org/packages/33/49/c8c21e4255b4f4a2c0c68ac18125d7f5460b109acc6dfdef1a24f9b960ef/pillow-11.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156", size = 3181727 }, + { url = "https://files.pythonhosted.org/packages/6d/f1/f7255c0838f8c1ef6d55b625cfb286835c17e8136ce4351c5577d02c443b/pillow-11.2.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772", size = 2999833 }, + { url = "https://files.pythonhosted.org/packages/e2/57/9968114457bd131063da98d87790d080366218f64fa2943b65ac6739abb3/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363", size = 3437472 }, + { url = "https://files.pythonhosted.org/packages/b2/1b/e35d8a158e21372ecc48aac9c453518cfe23907bb82f950d6e1c72811eb0/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0", size = 3459976 }, + { url = "https://files.pythonhosted.org/packages/26/da/2c11d03b765efff0ccc473f1c4186dc2770110464f2177efaed9cf6fae01/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01", size = 3527133 }, + { url = "https://files.pythonhosted.org/packages/79/1a/4e85bd7cadf78412c2a3069249a09c32ef3323650fd3005c97cca7aa21df/pillow-11.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193", size = 3571555 }, + { url = "https://files.pythonhosted.org/packages/69/03/239939915216de1e95e0ce2334bf17a7870ae185eb390fab6d706aadbfc0/pillow-11.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013", size = 2674713 }, + { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, + { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, + { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, + { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, + { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, + { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, + { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, +] + +[[package]] +name = "propcache" +version = "0.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/56/e27c136101addf877c8291dbda1b3b86ae848f3837ce758510a0d806c92f/propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98", size = 80224 }, + { url = "https://files.pythonhosted.org/packages/63/bd/88e98836544c4f04db97eefd23b037c2002fa173dd2772301c61cd3085f9/propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180", size = 46491 }, + { url = "https://files.pythonhosted.org/packages/15/43/0b8eb2a55753c4a574fc0899885da504b521068d3b08ca56774cad0bea2b/propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71", size = 45927 }, + { url = "https://files.pythonhosted.org/packages/ad/6c/d01f9dfbbdc613305e0a831016844987a1fb4861dd221cd4c69b1216b43f/propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649", size = 206135 }, + { url = "https://files.pythonhosted.org/packages/9a/8a/e6e1c77394088f4cfdace4a91a7328e398ebed745d59c2f6764135c5342d/propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f", size = 220517 }, + { url = "https://files.pythonhosted.org/packages/19/3b/6c44fa59d6418f4239d5db8b1ece757351e85d6f3ca126dfe37d427020c8/propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229", size = 218952 }, + { url = "https://files.pythonhosted.org/packages/7c/e4/4aeb95a1cd085e0558ab0de95abfc5187329616193a1012a6c4c930e9f7a/propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46", size = 206593 }, + { url = "https://files.pythonhosted.org/packages/da/6a/29fa75de1cbbb302f1e1d684009b969976ca603ee162282ae702287b6621/propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7", size = 196745 }, + { url = "https://files.pythonhosted.org/packages/19/7e/2237dad1dbffdd2162de470599fa1a1d55df493b16b71e5d25a0ac1c1543/propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0", size = 203369 }, + { url = "https://files.pythonhosted.org/packages/a4/bc/a82c5878eb3afb5c88da86e2cf06e1fe78b7875b26198dbb70fe50a010dc/propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519", size = 198723 }, + { url = "https://files.pythonhosted.org/packages/17/76/9632254479c55516f51644ddbf747a45f813031af5adcb8db91c0b824375/propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd", size = 200751 }, + { url = "https://files.pythonhosted.org/packages/3e/c3/a90b773cf639bd01d12a9e20c95be0ae978a5a8abe6d2d343900ae76cd71/propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259", size = 210730 }, + { url = "https://files.pythonhosted.org/packages/ed/ec/ad5a952cdb9d65c351f88db7c46957edd3d65ffeee72a2f18bd6341433e0/propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e", size = 213499 }, + { url = "https://files.pythonhosted.org/packages/83/c0/ea5133dda43e298cd2010ec05c2821b391e10980e64ee72c0a76cdbb813a/propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136", size = 207132 }, + { url = "https://files.pythonhosted.org/packages/79/dd/71aae9dec59333064cfdd7eb31a63fa09f64181b979802a67a90b2abfcba/propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42", size = 40952 }, + { url = "https://files.pythonhosted.org/packages/31/0a/49ff7e5056c17dfba62cbdcbb90a29daffd199c52f8e65e5cb09d5f53a57/propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833", size = 45163 }, + { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243 }, + { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503 }, + { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934 }, + { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633 }, + { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124 }, + { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283 }, + { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498 }, + { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486 }, + { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675 }, + { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727 }, + { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878 }, + { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558 }, + { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754 }, + { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088 }, + { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859 }, + { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153 }, + { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430 }, + { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637 }, + { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123 }, + { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031 }, + { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100 }, + { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170 }, + { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000 }, + { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262 }, + { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772 }, + { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133 }, + { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741 }, + { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047 }, + { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467 }, + { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022 }, + { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647 }, + { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784 }, + { url = "https://files.pythonhosted.org/packages/58/60/f645cc8b570f99be3cf46714170c2de4b4c9d6b827b912811eff1eb8a412/propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8", size = 77865 }, + { url = "https://files.pythonhosted.org/packages/6f/d4/c1adbf3901537582e65cf90fd9c26fde1298fde5a2c593f987112c0d0798/propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f", size = 45452 }, + { url = "https://files.pythonhosted.org/packages/d1/b5/fe752b2e63f49f727c6c1c224175d21b7d1727ce1d4873ef1c24c9216830/propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111", size = 44800 }, + { url = "https://files.pythonhosted.org/packages/62/37/fc357e345bc1971e21f76597028b059c3d795c5ca7690d7a8d9a03c9708a/propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5", size = 225804 }, + { url = "https://files.pythonhosted.org/packages/0d/f1/16e12c33e3dbe7f8b737809bad05719cff1dccb8df4dafbcff5575002c0e/propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb", size = 230650 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/018b9f2ed876bf5091e60153f727e8f9073d97573f790ff7cdf6bc1d1fb8/propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7", size = 234235 }, + { url = "https://files.pythonhosted.org/packages/45/5f/3faee66fc930dfb5da509e34c6ac7128870631c0e3582987fad161fcb4b1/propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120", size = 228249 }, + { url = "https://files.pythonhosted.org/packages/62/1e/a0d5ebda5da7ff34d2f5259a3e171a94be83c41eb1e7cd21a2105a84a02e/propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654", size = 214964 }, + { url = "https://files.pythonhosted.org/packages/db/a0/d72da3f61ceab126e9be1f3bc7844b4e98c6e61c985097474668e7e52152/propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e", size = 222501 }, + { url = "https://files.pythonhosted.org/packages/18/6d/a008e07ad7b905011253adbbd97e5b5375c33f0b961355ca0a30377504ac/propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b", size = 217917 }, + { url = "https://files.pythonhosted.org/packages/98/37/02c9343ffe59e590e0e56dc5c97d0da2b8b19fa747ebacf158310f97a79a/propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53", size = 217089 }, + { url = "https://files.pythonhosted.org/packages/53/1b/d3406629a2c8a5666d4674c50f757a77be119b113eedd47b0375afdf1b42/propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5", size = 228102 }, + { url = "https://files.pythonhosted.org/packages/cd/a7/3664756cf50ce739e5f3abd48febc0be1a713b1f389a502ca819791a6b69/propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7", size = 230122 }, + { url = "https://files.pythonhosted.org/packages/35/36/0bbabaacdcc26dac4f8139625e930f4311864251276033a52fd52ff2a274/propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef", size = 226818 }, + { url = "https://files.pythonhosted.org/packages/cc/27/4e0ef21084b53bd35d4dae1634b6d0bad35e9c58ed4f032511acca9d4d26/propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24", size = 40112 }, + { url = "https://files.pythonhosted.org/packages/a6/2c/a54614d61895ba6dd7ac8f107e2b2a0347259ab29cbf2ecc7b94fa38c4dc/propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037", size = 44034 }, + { url = "https://files.pythonhosted.org/packages/5a/a8/0a4fd2f664fc6acc66438370905124ce62e84e2e860f2557015ee4a61c7e/propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f", size = 82613 }, + { url = "https://files.pythonhosted.org/packages/4d/e5/5ef30eb2cd81576256d7b6caaa0ce33cd1d2c2c92c8903cccb1af1a4ff2f/propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c", size = 47763 }, + { url = "https://files.pythonhosted.org/packages/87/9a/87091ceb048efeba4d28e903c0b15bcc84b7c0bf27dc0261e62335d9b7b8/propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc", size = 47175 }, + { url = "https://files.pythonhosted.org/packages/3e/2f/854e653c96ad1161f96194c6678a41bbb38c7947d17768e8811a77635a08/propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de", size = 292265 }, + { url = "https://files.pythonhosted.org/packages/40/8d/090955e13ed06bc3496ba4a9fb26c62e209ac41973cb0d6222de20c6868f/propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6", size = 294412 }, + { url = "https://files.pythonhosted.org/packages/39/e6/d51601342e53cc7582449e6a3c14a0479fab2f0750c1f4d22302e34219c6/propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7", size = 294290 }, + { url = "https://files.pythonhosted.org/packages/3b/4d/be5f1a90abc1881884aa5878989a1acdafd379a91d9c7e5e12cef37ec0d7/propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458", size = 282926 }, + { url = "https://files.pythonhosted.org/packages/57/2b/8f61b998c7ea93a2b7eca79e53f3e903db1787fca9373af9e2cf8dc22f9d/propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11", size = 267808 }, + { url = "https://files.pythonhosted.org/packages/11/1c/311326c3dfce59c58a6098388ba984b0e5fb0381ef2279ec458ef99bd547/propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c", size = 290916 }, + { url = "https://files.pythonhosted.org/packages/4b/74/91939924b0385e54dc48eb2e4edd1e4903ffd053cf1916ebc5347ac227f7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf", size = 262661 }, + { url = "https://files.pythonhosted.org/packages/c2/d7/e6079af45136ad325c5337f5dd9ef97ab5dc349e0ff362fe5c5db95e2454/propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27", size = 264384 }, + { url = "https://files.pythonhosted.org/packages/b7/d5/ba91702207ac61ae6f1c2da81c5d0d6bf6ce89e08a2b4d44e411c0bbe867/propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757", size = 291420 }, + { url = "https://files.pythonhosted.org/packages/58/70/2117780ed7edcd7ba6b8134cb7802aada90b894a9810ec56b7bb6018bee7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18", size = 290880 }, + { url = "https://files.pythonhosted.org/packages/4a/1f/ecd9ce27710021ae623631c0146719280a929d895a095f6d85efb6a0be2e/propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a", size = 287407 }, + { url = "https://files.pythonhosted.org/packages/3e/66/2e90547d6b60180fb29e23dc87bd8c116517d4255240ec6d3f7dc23d1926/propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d", size = 42573 }, + { url = "https://files.pythonhosted.org/packages/cb/8f/50ad8599399d1861b4d2b6b45271f0ef6af1b09b0a2386a46dbaf19c9535/propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e", size = 46757 }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, +] + +[[package]] +name = "protobuf" +version = "6.30.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148 }, + { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003 }, + { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579 }, + { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319 }, + { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212 }, + { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335 }, +] + +[[package]] +name = "pyarrow" +version = "20.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/23/77094eb8ee0dbe88441689cb6afc40ac312a1e15d3a7acc0586999518222/pyarrow-20.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c7dd06fd7d7b410ca5dc839cc9d485d2bc4ae5240851bcd45d85105cc90a47d7", size = 30832591 }, + { url = "https://files.pythonhosted.org/packages/c3/d5/48cc573aff00d62913701d9fac478518f693b30c25f2c157550b0b2565cb/pyarrow-20.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5382de8dc34c943249b01c19110783d0d64b207167c728461add1ecc2db88e4", size = 32273686 }, + { url = "https://files.pythonhosted.org/packages/37/df/4099b69a432b5cb412dd18adc2629975544d656df3d7fda6d73c5dba935d/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6415a0d0174487456ddc9beaead703d0ded5966129fa4fd3114d76b5d1c5ceae", size = 41337051 }, + { url = "https://files.pythonhosted.org/packages/4c/27/99922a9ac1c9226f346e3a1e15e63dee6f623ed757ff2893f9d6994a69d3/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15aa1b3b2587e74328a730457068dc6c89e6dcbf438d4369f572af9d320a25ee", size = 42404659 }, + { url = "https://files.pythonhosted.org/packages/21/d1/71d91b2791b829c9e98f1e0d85be66ed93aff399f80abb99678511847eaa/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5605919fbe67a7948c1f03b9f3727d82846c053cd2ce9303ace791855923fd20", size = 40695446 }, + { url = "https://files.pythonhosted.org/packages/f1/ca/ae10fba419a6e94329707487835ec721f5a95f3ac9168500bcf7aa3813c7/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a5704f29a74b81673d266e5ec1fe376f060627c2e42c5c7651288ed4b0db29e9", size = 42278528 }, + { url = "https://files.pythonhosted.org/packages/7a/a6/aba40a2bf01b5d00cf9cd16d427a5da1fad0fb69b514ce8c8292ab80e968/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:00138f79ee1b5aca81e2bdedb91e3739b987245e11fa3c826f9e57c5d102fb75", size = 42918162 }, + { url = "https://files.pythonhosted.org/packages/93/6b/98b39650cd64f32bf2ec6d627a9bd24fcb3e4e6ea1873c5e1ea8a83b1a18/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f2d67ac28f57a362f1a2c1e6fa98bfe2f03230f7e15927aecd067433b1e70ce8", size = 44550319 }, + { url = "https://files.pythonhosted.org/packages/ab/32/340238be1eb5037e7b5de7e640ee22334417239bc347eadefaf8c373936d/pyarrow-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4a8b029a07956b8d7bd742ffca25374dd3f634b35e46cc7a7c3fa4c75b297191", size = 25770759 }, + { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035 }, + { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552 }, + { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704 }, + { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836 }, + { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789 }, + { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124 }, + { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060 }, + { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640 }, + { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491 }, + { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067 }, + { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128 }, + { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890 }, + { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775 }, + { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231 }, + { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639 }, + { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549 }, + { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216 }, + { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496 }, + { url = "https://files.pythonhosted.org/packages/9b/aa/daa413b81446d20d4dad2944110dcf4cf4f4179ef7f685dd5a6d7570dc8e/pyarrow-20.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a15532e77b94c61efadde86d10957950392999503b3616b2ffcef7621a002893", size = 30798501 }, + { url = "https://files.pythonhosted.org/packages/ff/75/2303d1caa410925de902d32ac215dc80a7ce7dd8dfe95358c165f2adf107/pyarrow-20.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dd43f58037443af715f34f1322c782ec463a3c8a94a85fdb2d987ceb5658e061", size = 32277895 }, + { url = "https://files.pythonhosted.org/packages/92/41/fe18c7c0b38b20811b73d1bdd54b1fccba0dab0e51d2048878042d84afa8/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0d288143a8585806e3cc7c39566407aab646fb9ece164609dac1cfff45f6ae", size = 41327322 }, + { url = "https://files.pythonhosted.org/packages/da/ab/7dbf3d11db67c72dbf36ae63dcbc9f30b866c153b3a22ef728523943eee6/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6953f0114f8d6f3d905d98e987d0924dabce59c3cda380bdfaa25a6201563b4", size = 42411441 }, + { url = "https://files.pythonhosted.org/packages/90/c3/0c7da7b6dac863af75b64e2f827e4742161128c350bfe7955b426484e226/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:991f85b48a8a5e839b2128590ce07611fae48a904cae6cab1f089c5955b57eb5", size = 40677027 }, + { url = "https://files.pythonhosted.org/packages/be/27/43a47fa0ff9053ab5203bb3faeec435d43c0d8bfa40179bfd076cdbd4e1c/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:97c8dc984ed09cb07d618d57d8d4b67a5100a30c3818c2fb0b04599f0da2de7b", size = 42281473 }, + { url = "https://files.pythonhosted.org/packages/bc/0b/d56c63b078876da81bbb9ba695a596eabee9b085555ed12bf6eb3b7cab0e/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b71daf534f4745818f96c214dbc1e6124d7daf059167330b610fc69b6f3d3e3", size = 42893897 }, + { url = "https://files.pythonhosted.org/packages/92/ac/7d4bd020ba9145f354012838692d48300c1b8fe5634bfda886abcada67ed/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8b88758f9303fa5a83d6c90e176714b2fd3852e776fc2d7e42a22dd6c2fb368", size = 44543847 }, + { url = "https://files.pythonhosted.org/packages/9d/07/290f4abf9ca702c5df7b47739c1b2c83588641ddfa2cc75e34a301d42e55/pyarrow-20.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:30b3051b7975801c1e1d387e17c588d8ab05ced9b1e14eec57915f79869b5031", size = 25653219 }, + { url = "https://files.pythonhosted.org/packages/95/df/720bb17704b10bd69dde086e1400b8eefb8f58df3f8ac9cff6c425bf57f1/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ca151afa4f9b7bc45bcc791eb9a89e90a9eb2772767d0b1e5389609c7d03db63", size = 30853957 }, + { url = "https://files.pythonhosted.org/packages/d9/72/0d5f875efc31baef742ba55a00a25213a19ea64d7176e0fe001c5d8b6e9a/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:4680f01ecd86e0dd63e39eb5cd59ef9ff24a9d166db328679e36c108dc993d4c", size = 32247972 }, + { url = "https://files.pythonhosted.org/packages/d5/bc/e48b4fa544d2eea72f7844180eb77f83f2030b84c8dad860f199f94307ed/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4c8534e2ff059765647aa69b75d6543f9fef59e2cd4c6d18015192565d2b70", size = 41256434 }, + { url = "https://files.pythonhosted.org/packages/c3/01/974043a29874aa2cf4f87fb07fd108828fc7362300265a2a64a94965e35b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e1f8a47f4b4ae4c69c4d702cfbdfe4d41e18e5c7ef6f1bb1c50918c1e81c57b", size = 42353648 }, + { url = "https://files.pythonhosted.org/packages/68/95/cc0d3634cde9ca69b0e51cbe830d8915ea32dda2157560dda27ff3b3337b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a1f60dc14658efaa927f8214734f6a01a806d7690be4b3232ba526836d216122", size = 40619853 }, + { url = "https://files.pythonhosted.org/packages/29/c2/3ad40e07e96a3e74e7ed7cc8285aadfa84eb848a798c98ec0ad009eb6bcc/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:204a846dca751428991346976b914d6d2a82ae5b8316a6ed99789ebf976551e6", size = 42241743 }, + { url = "https://files.pythonhosted.org/packages/eb/cb/65fa110b483339add6a9bc7b6373614166b14e20375d4daa73483755f830/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f3b117b922af5e4c6b9a9115825726cac7d8b1421c37c2b5e24fbacc8930612c", size = 42839441 }, + { url = "https://files.pythonhosted.org/packages/98/7b/f30b1954589243207d7a0fbc9997401044bf9a033eec78f6cb50da3f304a/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e724a3fd23ae5b9c010e7be857f4405ed5e679db5c93e66204db1a69f733936a", size = 44503279 }, + { url = "https://files.pythonhosted.org/packages/37/40/ad395740cd641869a13bcf60851296c89624662575621968dcfafabaa7f6/pyarrow-20.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:82f1ee5133bd8f49d31be1299dc07f585136679666b502540db854968576faf9", size = 25944982 }, +] + +[[package]] +name = "pydantic" +version = "2.11.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817 }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357 }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011 }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730 }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178 }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462 }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652 }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306 }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720 }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915 }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884 }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496 }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019 }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982 }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412 }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749 }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225 }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490 }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525 }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446 }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678 }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, +] + +[[package]] +name = "pydoe2" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/1f/8a2e1b57f6dd22cd2002e4332e229e87a3858d560c516b50ab7fe5bb075c/pyDOE2-1.3.0.tar.gz", hash = "sha256:5492b0f984af52da3af20b1cd61deb21b067c858e65243ec3ba573375f0d6720", size = 19967 } + +[[package]] +name = "pyfiglet" +version = "0.8.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/02/48293654fb2e4fdeb4d927f00a380230a832744b6c9af757373a72d018d1/pyfiglet-0.8.post1.tar.gz", hash = "sha256:c6c2321755d09267b438ec7b936825a4910fec696292139e664ca8670e103639", size = 634618 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/07/fcfdd7a2872f5b348953de35acce1544dab0c1e8368dca54279b1cde5c15/pyfiglet-0.8.post1-py2.py3-none-any.whl", hash = "sha256:d555bcea17fbeaf70eaefa48bb119352487e629c9b56f30f383e2c62dd67a01c", size = 865831 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytorch-lightning" +version = "2.5.1.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec", extra = ["http"] }, + { name = "lightning-utilities" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "torch" }, + { name = "torchmetrics" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/0c/cfa6223c525f67ea3a7a2907e36e9e9a9653300f82cfd9af88f8136514ab/pytorch_lightning-2.5.1.post0.tar.gz", hash = "sha256:abc3d5a804d41f941b14e3fd7db5572a1270cd1e9889b50e962984c87d498d94", size = 634368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/a9/e14821cfaf08e8d78185cca0477c9d3a62bafe1b4b530100f7b66bb1f7bb/pytorch_lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:873fb21392c8b79908218f5ca8f65bd835439216e52550c36ff55d849e99c93e", size = 823084 }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "rdkit" +version = "2024.9.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pillow" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/44/8908dc5e26248a1e27d5a0f149e17af2b5e85585a227e57d2d4f53f68d07/rdkit-2024.9.6-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:015c7200fffefdae53b59a4658e0323e2d16ac16b2b00174188f769a0cabe52a", size = 29960044 }, + { url = "https://files.pythonhosted.org/packages/8f/2e/36a2cee64073df724c7fc635c969de604440d754c65ac1dd2627e5aacc16/rdkit-2024.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffe05be4354178b48bfd60ea40c847e6364e75e71893bd2c659da8819c8afab6", size = 27718856 }, + { url = "https://files.pythonhosted.org/packages/13/28/c9abf09ade43a8ed6165bc1e7660cbefdf449f89cb400eb20377e84ceb2c/rdkit-2024.9.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6e6bc9b6edc3e5efa88f796d8a86192b72b5d54ce9488f8ccb043e6b74308b79", size = 33528869 }, + { url = "https://files.pythonhosted.org/packages/a3/f6/3a2278acc1d3831fd1d5b81ae355297be1c2ea8d8d0e200291aa2edfbdeb/rdkit-2024.9.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2b5573055c8defbad7ce25db10786a56e0699faf3282daea21100c59f7af7298", size = 34344527 }, + { url = "https://files.pythonhosted.org/packages/cb/61/18b1fec82a2c4ab60234eab6673174c7dbcfd82490fc834a4d2b10d42d8b/rdkit-2024.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:9d64c865de57c15bbe7726f20a967016f2ac04f738889c83210091be944b512b", size = 22472377 }, + { url = "https://files.pythonhosted.org/packages/cc/c1/b21d793ab572545917af8d00c7519ea1e0fc10d3e74ac936de4a06fc3649/rdkit-2024.9.6-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:bbb3fe860e5ccb8debad592ded991b2aca611c997640fc6e68b0f41beb648639", size = 29960558 }, + { url = "https://files.pythonhosted.org/packages/72/64/f96f4a72052be7580ab825279cc71653a476c023c53828765be520db47e6/rdkit-2024.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a28971024ba0300f7c566874212b8c2c8f7db984e85418eaceec5cb739bcab1d", size = 27719061 }, + { url = "https://files.pythonhosted.org/packages/1d/2f/3befa6b3a061665351d5a58aba66e318175777c40cbfbfc511bd57d2aa7b/rdkit-2024.9.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6e236da74308b19f695ed259b12b75e100e56593333f2b47cff2420e9746e571", size = 33524980 }, + { url = "https://files.pythonhosted.org/packages/cc/3f/472c33312ca8a55242fc2cf6179809f4a967185e9dc6e76ea28ddd37a097/rdkit-2024.9.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7b473a715711e7f1f747173fc18a16b02d5952eadbeedb09d05d1f0a6879c282", size = 34343341 }, + { url = "https://files.pythonhosted.org/packages/94/85/d8d7da7ba8d00ad8977193f30d0062324aa9279e8af67e413674d3f60483/rdkit-2024.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:f302e8a347debe24ffeeb303b8b031ef2e61265872a6450f46aa819b25f5c684", size = 22472536 }, + { url = "https://files.pythonhosted.org/packages/b0/b4/54a543114b58bfbb0f659d30c7902d10e7fa908544691455b36e0783fa77/rdkit-2024.9.6-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:f57a2ad6ceff54917586f47a01ecbe43bbdf4a6f00defd746b500228752ccf84", size = 30035515 }, + { url = "https://files.pythonhosted.org/packages/d1/5a/83deae41ff455e417f67e91393d3fae9f604e15fc633544412fa16d9bcc6/rdkit-2024.9.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f3931ff192aeee29ce41cf7c30650883fc10f1504af2a9dcc67d8554a0779d3", size = 27760806 }, + { url = "https://files.pythonhosted.org/packages/95/41/c52930491f52d7bdb50e297d5064fdc959a82adea9f735a9ab7c412ac874/rdkit-2024.9.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:97661c84a9dc10797d197db1024757676e0ca29a306d5eec289bbfdf4c53c80c", size = 33414832 }, + { url = "https://files.pythonhosted.org/packages/e9/f1/2f978f26b2c58716e70ca1e442e51c260b43e55c2eb64bd2ad76584b60da/rdkit-2024.9.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:82e91020e700979f6fc94a4722b47d770912d54cd9accb1c85961c57550cb945", size = 34276560 }, + { url = "https://files.pythonhosted.org/packages/36/a7/738b3eb302b95ece0efea4d9b9c90d8dd7bb9ef529a56ee3bbbcfd239cde/rdkit-2024.9.6-cp312-cp312-win_amd64.whl", hash = "sha256:995df4e0b09a866fc628b6c9f1c10dca32726964ff1c7506cbf7975af0a9ba60", size = 22487552 }, + { url = "https://files.pythonhosted.org/packages/07/be/e0fb0bf1301ce3add94a248a24b29c7784b7d504de4776554fafe3372c54/rdkit-2024.9.6-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:5001786e1f06749fa4cf7d6a9d23b36afc6d9b028d500777a3861c8a4d91238b", size = 30034429 }, + { url = "https://files.pythonhosted.org/packages/d4/0e/918410c8975cb365ad51d3e487965c3c5ccfc3dea5715c81f0e89339cec0/rdkit-2024.9.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b2578458414600beb3a5691fc900e0640da3c571b30ed3dc76381cfe5caadc9e", size = 27759699 }, + { url = "https://files.pythonhosted.org/packages/41/ee/9758cbcde26bacb820579ecbfbd3069ab8bd10b55e08f6731c208ce2eb3f/rdkit-2024.9.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:4fbf4d680a1070a15f6d9c7cf93218bed87c7110e4e7a467d1b449e430e20ff5", size = 33413200 }, + { url = "https://files.pythonhosted.org/packages/d9/f9/6e5b8f6445450a3b01e28915f749f9d644a8b855f6e7b26c09f193b1978d/rdkit-2024.9.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4b0efe61d9eee664dc19203478e3312e596be0d200c3bbd4ef1b584aef7146d3", size = 34275798 }, + { url = "https://files.pythonhosted.org/packages/3f/9b/3c32fd6dbb1210a8e9dc83feee82778c8bf2ad483c9e8394138c25789110/rdkit-2024.9.6-cp313-cp313-win_amd64.whl", hash = "sha256:6c740c543b55f99d8d3bdea92c68617ef5669bcf2cc1652752a65f156f26dbc0", size = 22486705 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, + { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, + { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, + { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, + { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, + { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, + { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, + { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, + { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, + { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "14.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "scikit-learn" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/3a/f4597eb41049110b21ebcbb0bcb43e4035017545daa5eedcfeb45c08b9c5/scikit_learn-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e", size = 12067702 }, + { url = "https://files.pythonhosted.org/packages/37/19/0423e5e1fd1c6ec5be2352ba05a537a473c1677f8188b9306097d684b327/scikit_learn-1.6.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36", size = 11112765 }, + { url = "https://files.pythonhosted.org/packages/70/95/d5cb2297a835b0f5fc9a77042b0a2d029866379091ab8b3f52cc62277808/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8634c4bd21a2a813e0a7e3900464e6d593162a29dd35d25bdf0103b3fce60ed5", size = 12643991 }, + { url = "https://files.pythonhosted.org/packages/b7/91/ab3c697188f224d658969f678be86b0968ccc52774c8ab4a86a07be13c25/scikit_learn-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:775da975a471c4f6f467725dff0ced5c7ac7bda5e9316b260225b48475279a1b", size = 13497182 }, + { url = "https://files.pythonhosted.org/packages/17/04/d5d556b6c88886c092cc989433b2bab62488e0f0dafe616a1d5c9cb0efb1/scikit_learn-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:8a600c31592bd7dab31e1c61b9bbd6dea1b3433e67d264d17ce1017dbdce8002", size = 11125517 }, + { url = "https://files.pythonhosted.org/packages/6c/2a/e291c29670795406a824567d1dfc91db7b699799a002fdaa452bceea8f6e/scikit_learn-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72abc587c75234935e97d09aa4913a82f7b03ee0b74111dcc2881cba3c5a7b33", size = 12102620 }, + { url = "https://files.pythonhosted.org/packages/25/92/ee1d7a00bb6b8c55755d4984fd82608603a3cc59959245068ce32e7fb808/scikit_learn-1.6.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b3b00cdc8f1317b5f33191df1386c0befd16625f49d979fe77a8d44cae82410d", size = 11116234 }, + { url = "https://files.pythonhosted.org/packages/30/cd/ed4399485ef364bb25f388ab438e3724e60dc218c547a407b6e90ccccaef/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc4765af3386811c3ca21638f63b9cf5ecf66261cc4815c1db3f1e7dc7b79db2", size = 12592155 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/62fc9a5a659bb58a03cdd7e258956a5824bdc9b4bb3c5d932f55880be569/scikit_learn-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25fc636bdaf1cc2f4a124a116312d837148b5e10872147bdaf4887926b8c03d8", size = 13497069 }, + { url = "https://files.pythonhosted.org/packages/a1/a6/c5b78606743a1f28eae8f11973de6613a5ee87366796583fb74c67d54939/scikit_learn-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:fa909b1a36e000a03c382aade0bd2063fd5680ff8b8e501660c0f59f021a6415", size = 11139809 }, + { url = "https://files.pythonhosted.org/packages/0a/18/c797c9b8c10380d05616db3bfb48e2a3358c767affd0857d56c2eb501caa/scikit_learn-1.6.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:926f207c804104677af4857b2c609940b743d04c4c35ce0ddc8ff4f053cddc1b", size = 12104516 }, + { url = "https://files.pythonhosted.org/packages/c4/b7/2e35f8e289ab70108f8cbb2e7a2208f0575dc704749721286519dcf35f6f/scikit_learn-1.6.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2c2cae262064e6a9b77eee1c8e768fc46aa0b8338c6a8297b9b6759720ec0ff2", size = 11167837 }, + { url = "https://files.pythonhosted.org/packages/a4/f6/ff7beaeb644bcad72bcfd5a03ff36d32ee4e53a8b29a639f11bcb65d06cd/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1061b7c028a8663fb9a1a1baf9317b64a257fcb036dae5c8752b2abef31d136f", size = 12253728 }, + { url = "https://files.pythonhosted.org/packages/29/7a/8bce8968883e9465de20be15542f4c7e221952441727c4dad24d534c6d99/scikit_learn-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e69fab4ebfc9c9b580a7a80111b43d214ab06250f8a7ef590a4edf72464dd86", size = 13147700 }, + { url = "https://files.pythonhosted.org/packages/62/27/585859e72e117fe861c2079bcba35591a84f801e21bc1ab85bce6ce60305/scikit_learn-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:70b1d7e85b1c96383f872a519b3375f92f14731e279a7b4c6cfd650cf5dffc52", size = 11110613 }, + { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, + { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, + { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, + { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, + { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, + { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, + { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, + { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, +] + +[[package]] +name = "scipy" +version = "1.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/b9/31ba9cd990e626574baf93fbc1ac61cf9ed54faafd04c479117517661637/scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec", size = 59417316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/df/ef233fff6838fe6f7840d69b5ef9f20d2b5c912a8727b21ebf876cb15d54/scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9", size = 38692502 }, + { url = "https://files.pythonhosted.org/packages/5c/20/acdd4efb8a68b842968f7bc5611b1aeb819794508771ad104de418701422/scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5", size = 30085508 }, + { url = "https://files.pythonhosted.org/packages/42/55/39cf96ca7126f1e78ee72a6344ebdc6702fc47d037319ad93221063e6cf4/scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e", size = 22359166 }, + { url = "https://files.pythonhosted.org/packages/51/48/708d26a4ab8a1441536bf2dfcad1df0ca14a69f010fba3ccbdfc02df7185/scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9", size = 25112047 }, + { url = "https://files.pythonhosted.org/packages/dd/65/f9c5755b995ad892020381b8ae11f16d18616208e388621dfacc11df6de6/scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3", size = 35536214 }, + { url = "https://files.pythonhosted.org/packages/de/3c/c96d904b9892beec978562f64d8cc43f9cca0842e65bd3cd1b7f7389b0ba/scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d", size = 37646981 }, + { url = "https://files.pythonhosted.org/packages/3d/74/c2d8a24d18acdeae69ed02e132b9bc1bb67b7bee90feee1afe05a68f9d67/scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58", size = 37230048 }, + { url = "https://files.pythonhosted.org/packages/42/19/0aa4ce80eca82d487987eff0bc754f014dec10d20de2f66754fa4ea70204/scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa", size = 40010322 }, + { url = "https://files.pythonhosted.org/packages/d0/d2/f0683b7e992be44d1475cc144d1f1eeae63c73a14f862974b4db64af635e/scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65", size = 41233385 }, + { url = "https://files.pythonhosted.org/packages/40/1f/bf0a5f338bda7c35c08b4ed0df797e7bafe8a78a97275e9f439aceb46193/scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4", size = 38703651 }, + { url = "https://files.pythonhosted.org/packages/de/54/db126aad3874601048c2c20ae3d8a433dbfd7ba8381551e6f62606d9bd8e/scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1", size = 30102038 }, + { url = "https://files.pythonhosted.org/packages/61/d8/84da3fffefb6c7d5a16968fe5b9f24c98606b165bb801bb0b8bc3985200f/scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971", size = 22375518 }, + { url = "https://files.pythonhosted.org/packages/44/78/25535a6e63d3b9c4c90147371aedb5d04c72f3aee3a34451f2dc27c0c07f/scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655", size = 25142523 }, + { url = "https://files.pythonhosted.org/packages/e0/22/4b4a26fe1cd9ed0bc2b2cb87b17d57e32ab72c346949eaf9288001f8aa8e/scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e", size = 35491547 }, + { url = "https://files.pythonhosted.org/packages/32/ea/564bacc26b676c06a00266a3f25fdfe91a9d9a2532ccea7ce6dd394541bc/scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0", size = 37634077 }, + { url = "https://files.pythonhosted.org/packages/43/c2/bfd4e60668897a303b0ffb7191e965a5da4056f0d98acfb6ba529678f0fb/scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40", size = 37231657 }, + { url = "https://files.pythonhosted.org/packages/4a/75/5f13050bf4f84c931bcab4f4e83c212a36876c3c2244475db34e4b5fe1a6/scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462", size = 40035857 }, + { url = "https://files.pythonhosted.org/packages/b9/8b/7ec1832b09dbc88f3db411f8cdd47db04505c4b72c99b11c920a8f0479c3/scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737", size = 41217654 }, + { url = "https://files.pythonhosted.org/packages/4b/5d/3c78815cbab499610f26b5bae6aed33e227225a9fa5290008a733a64f6fc/scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd", size = 38756184 }, + { url = "https://files.pythonhosted.org/packages/37/20/3d04eb066b471b6e171827548b9ddb3c21c6bbea72a4d84fc5989933910b/scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301", size = 30163558 }, + { url = "https://files.pythonhosted.org/packages/a4/98/e5c964526c929ef1f795d4c343b2ff98634ad2051bd2bbadfef9e772e413/scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93", size = 22437211 }, + { url = "https://files.pythonhosted.org/packages/1d/cd/1dc7371e29195ecbf5222f9afeedb210e0a75057d8afbd942aa6cf8c8eca/scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20", size = 25232260 }, + { url = "https://files.pythonhosted.org/packages/f0/24/1a181a9e5050090e0b5138c5f496fee33293c342b788d02586bc410c6477/scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e", size = 35198095 }, + { url = "https://files.pythonhosted.org/packages/c0/53/eaada1a414c026673eb983f8b4a55fe5eb172725d33d62c1b21f63ff6ca4/scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8", size = 37297371 }, + { url = "https://files.pythonhosted.org/packages/e9/06/0449b744892ed22b7e7b9a1994a866e64895363572677a316a9042af1fe5/scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11", size = 36872390 }, + { url = "https://files.pythonhosted.org/packages/6a/6f/a8ac3cfd9505ec695c1bc35edc034d13afbd2fc1882a7c6b473e280397bb/scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53", size = 39700276 }, + { url = "https://files.pythonhosted.org/packages/f5/6f/e6e5aff77ea2a48dd96808bb51d7450875af154ee7cbe72188afb0b37929/scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded", size = 40942317 }, + { url = "https://files.pythonhosted.org/packages/53/40/09319f6e0f276ea2754196185f95cd191cb852288440ce035d5c3a931ea2/scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf", size = 38717587 }, + { url = "https://files.pythonhosted.org/packages/fe/c3/2854f40ecd19585d65afaef601e5e1f8dbf6758b2f95b5ea93d38655a2c6/scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37", size = 30100266 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/f9fe6e3c828cb5930b5fe74cb479de5f3d66d682fa8adb77249acaf545b8/scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d", size = 22373768 }, + { url = "https://files.pythonhosted.org/packages/15/9d/a60db8c795700414c3f681908a2b911e031e024d93214f2d23c6dae174ab/scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb", size = 25154719 }, + { url = "https://files.pythonhosted.org/packages/37/3b/9bda92a85cd93f19f9ed90ade84aa1e51657e29988317fabdd44544f1dd4/scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27", size = 35163195 }, + { url = "https://files.pythonhosted.org/packages/03/5a/fc34bf1aa14dc7c0e701691fa8685f3faec80e57d816615e3625f28feb43/scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0", size = 37255404 }, + { url = "https://files.pythonhosted.org/packages/4a/71/472eac45440cee134c8a180dbe4c01b3ec247e0338b7c759e6cd71f199a7/scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32", size = 36860011 }, + { url = "https://files.pythonhosted.org/packages/01/b3/21f890f4f42daf20e4d3aaa18182dddb9192771cd47445aaae2e318f6738/scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d", size = 39657406 }, + { url = "https://files.pythonhosted.org/packages/0d/76/77cf2ac1f2a9cc00c073d49e1e16244e389dd88e2490c91d84e1e3e4d126/scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f", size = 40961243 }, + { url = "https://files.pythonhosted.org/packages/4c/4b/a57f8ddcf48e129e6054fa9899a2a86d1fc6b07a0e15c7eebff7ca94533f/scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9", size = 38870286 }, + { url = "https://files.pythonhosted.org/packages/0c/43/c304d69a56c91ad5f188c0714f6a97b9c1fed93128c691148621274a3a68/scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f", size = 30141634 }, + { url = "https://files.pythonhosted.org/packages/44/1a/6c21b45d2548eb73be9b9bff421aaaa7e85e22c1f9b3bc44b23485dfce0a/scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6", size = 22415179 }, + { url = "https://files.pythonhosted.org/packages/74/4b/aefac4bba80ef815b64f55da06f62f92be5d03b467f2ce3668071799429a/scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af", size = 25126412 }, + { url = "https://files.pythonhosted.org/packages/b1/53/1cbb148e6e8f1660aacd9f0a9dfa2b05e9ff1cb54b4386fe868477972ac2/scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274", size = 34952867 }, + { url = "https://files.pythonhosted.org/packages/2c/23/e0eb7f31a9c13cf2dca083828b97992dd22f8184c6ce4fec5deec0c81fcf/scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776", size = 36890009 }, + { url = "https://files.pythonhosted.org/packages/03/f3/e699e19cabe96bbac5189c04aaa970718f0105cff03d458dc5e2b6bd1e8c/scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828", size = 36545159 }, + { url = "https://files.pythonhosted.org/packages/af/f5/ab3838e56fe5cc22383d6fcf2336e48c8fe33e944b9037fbf6cbdf5a11f8/scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28", size = 39136566 }, + { url = "https://files.pythonhosted.org/packages/0a/c8/b3f566db71461cabd4b2d5b39bcc24a7e1c119535c8361f81426be39bb47/scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db", size = 40477705 }, +] + +[[package]] +name = "selfies" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/5d/0c464898da5630661e4e8fa55a3a73c4bcc84f00c929990552f1102de6bd/selfies-2.1.2.tar.gz", hash = "sha256:bbb7b28a7cb562471a9db732e34a1486fd033a3086f892d4b0362764b396696c", size = 41881 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/ea/ddf401b64a42bebb28bf83319740725e4de418760bb4a23757e9f89393a6/selfies-2.1.2-py3-none-any.whl", hash = "sha256:30f675b9b0e9ac71d8489b36cc19e82389dbf319260743d90c9de5d19587b98f", size = 35860 }, +] + +[[package]] +name = "sentry-sdk" +version = "2.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786 }, +] + +[[package]] +name = "setproctitle" +version = "1.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/db/8214810cae49e2e474ea741aaa7d6111486f27377e864f0eb6d297c9be56/setproctitle-1.3.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ebcf34b69df4ca0eabaaaf4a3d890f637f355fed00ba806f7ebdd2d040658c26", size = 17412 }, + { url = "https://files.pythonhosted.org/packages/a4/45/909b0dcd68b16d2e58de0e861c0c0b67748ccc87ff9b59136e9710b528b1/setproctitle-1.3.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1aa1935aa2195b76f377e5cb018290376b7bf085f0b53f5a95c0c21011b74367", size = 11966 }, + { url = "https://files.pythonhosted.org/packages/8a/f4/f1cd54fedae1cdacf1d1db833dc096bfb1f029451f60e68563e4c26ed2f7/setproctitle-1.3.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13624d9925bb481bc0ccfbc7f533da38bfbfe6e80652314f789abc78c2e513bd", size = 31350 }, + { url = "https://files.pythonhosted.org/packages/5a/5f/f159b22d286a349633d4090090b8e6632fb84575a64f189b68e70a613c65/setproctitle-1.3.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97a138fa875c6f281df7720dac742259e85518135cd0e3551aba1c628103d853", size = 32704 }, + { url = "https://files.pythonhosted.org/packages/9c/25/e5ea2673d951dafc04b6544d7b33dd9283733d715c8f40e93d39ae35d6a0/setproctitle-1.3.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c86e9e82bfab579327dbe9b82c71475165fbc8b2134d24f9a3b2edaf200a5c3d", size = 29833 }, + { url = "https://files.pythonhosted.org/packages/67/2b/c3cbd4a4462c1143465d8c151f1d51bbfb418e60a96a754329d28d416575/setproctitle-1.3.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6af330ddc2ec05a99c3933ab3cba9365357c0b8470a7f2fa054ee4b0984f57d1", size = 30884 }, + { url = "https://files.pythonhosted.org/packages/27/04/b43a622a9fbf0f216a50b523067d3b07739ede2106a7226223e33abf6659/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:109fc07b1cd6cef9c245b2028e3e98e038283342b220def311d0239179810dbe", size = 30798 }, + { url = "https://files.pythonhosted.org/packages/41/60/8eb197b56b0a3110eef2a1d2ddb61b3f5809dbab9d975aa40c86e5d4b312/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7df5fcc48588f82b6cc8073db069609ddd48a49b1e9734a20d0efb32464753c4", size = 29758 }, + { url = "https://files.pythonhosted.org/packages/db/1d/c394322a5425c12f4ada0696eb6d194768752d4e3acaca0c9d593025feb4/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2407955dc359d735a20ac6e797ad160feb33d529a2ac50695c11a1ec680eafab", size = 32157 }, + { url = "https://files.pythonhosted.org/packages/e7/24/ce080682b144f057814efbe95daac09149e90f7689e2515897817a941686/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:38ca045626af693da042ac35d7332e7b9dbd52e6351d6973b310612e3acee6d6", size = 30291 }, + { url = "https://files.pythonhosted.org/packages/a7/4f/4db265493567865428dcec376f8142a9c65d27c10c3ac915d173b4053afb/setproctitle-1.3.6-cp310-cp310-win32.whl", hash = "sha256:9483aa336687463f5497dd37a070094f3dff55e2c888994f8440fcf426a1a844", size = 11492 }, + { url = "https://files.pythonhosted.org/packages/38/b0/64c3948f7957db44b4c5edfe9c197de493144dc915ddf95cf36aeca0dc52/setproctitle-1.3.6-cp310-cp310-win_amd64.whl", hash = "sha256:4efc91b437f6ff2578e89e3f17d010c0a0ff01736606473d082913ecaf7859ba", size = 12204 }, + { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412 }, + { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963 }, + { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718 }, + { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027 }, + { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223 }, + { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204 }, + { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181 }, + { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101 }, + { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438 }, + { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625 }, + { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488 }, + { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201 }, + { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399 }, + { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966 }, + { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017 }, + { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419 }, + { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711 }, + { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742 }, + { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925 }, + { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981 }, + { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209 }, + { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587 }, + { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487 }, + { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208 }, + { url = "https://files.pythonhosted.org/packages/89/76/f1a2fdbf9b9602945a7489ba5c52e9863de37381ef1a85a2b9ed0ff8bc79/setproctitle-1.3.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e2a9e62647dc040a76d55563580bf3bb8fe1f5b6ead08447c2ed0d7786e5e794", size = 17392 }, + { url = "https://files.pythonhosted.org/packages/5c/5b/4e0db8b10b4543afcb3dbc0827793d46e43ec1de6b377e313af3703d08e0/setproctitle-1.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:751ba352ed922e0af60458e961167fa7b732ac31c0ddd1476a2dfd30ab5958c5", size = 11951 }, + { url = "https://files.pythonhosted.org/packages/dc/fe/d5d00aaa700fe1f6160b6e95c225b29c01f4d9292176d48fd968815163ea/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7890e291bf4708e3b61db9069ea39b3ab0651e42923a5e1f4d78a7b9e4b18301", size = 32087 }, + { url = "https://files.pythonhosted.org/packages/9f/b3/894b827b93ef813c082479bebf88185860f01ac243df737823dd705e7fff/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2b17855ed7f994f3f259cf2dfbfad78814538536fa1a91b50253d84d87fd88d", size = 33502 }, + { url = "https://files.pythonhosted.org/packages/b2/cd/5330734cca1a4cfcb721432c22cb7899ff15a4101ba868b2ef452ffafea1/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e51ec673513465663008ce402171192a053564865c2fc6dc840620871a9bd7c", size = 30713 }, + { url = "https://files.pythonhosted.org/packages/fa/d3/c2590c5daa2e9a008d3f2b16c0f4a351826193be55f147cb32af49c6d814/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63cc10352dc6cf35a33951656aa660d99f25f574eb78132ce41a85001a638aa7", size = 31792 }, + { url = "https://files.pythonhosted.org/packages/e6/b1/c553ed5af8cfcecd5ae7737e63af58a17a03d26f3d61868c7eb20bf7e3cf/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dba8faee2e4a96e934797c9f0f2d093f8239bf210406a99060b3eabe549628e", size = 31927 }, + { url = "https://files.pythonhosted.org/packages/70/78/2d5385206540127a3dca0ff83225b1ac66873f5cc89d4a6d3806c92f5ae2/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e3e44d08b61de0dd6f205528498f834a51a5c06689f8fb182fe26f3a3ce7dca9", size = 30981 }, + { url = "https://files.pythonhosted.org/packages/31/62/e3e4a4e006d0e549748e53cded4ff3b667be0602860fc61b7de8b412b667/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:de004939fc3fd0c1200d26ea9264350bfe501ffbf46c8cf5dc7f345f2d87a7f1", size = 33244 }, + { url = "https://files.pythonhosted.org/packages/aa/05/4b223fd4ef94e105dc7aff27fa502fb7200cf52be2bb0c064bd2406b5611/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3f8194b4d631b003a1176a75d1acd545e04b1f54b821638e098a93e6e62830ef", size = 31630 }, + { url = "https://files.pythonhosted.org/packages/1b/ba/5f68eb969f7336f54b54a599fd3ffbd7662f9733b080bc8598705971b3dd/setproctitle-1.3.6-cp313-cp313-win32.whl", hash = "sha256:d714e002dd3638170fe7376dc1b686dbac9cb712cde3f7224440af722cc9866a", size = 11480 }, + { url = "https://files.pythonhosted.org/packages/ba/f5/7f47f0ca35c9c357f16187cee9229f3eda0237bc6fdd3061441336f361c0/setproctitle-1.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:b70c07409d465f3a8b34d52f863871fb8a00755370791d2bd1d4f82b3cdaf3d5", size = 12198 }, + { url = "https://files.pythonhosted.org/packages/39/ad/c3941b8fc6b32a976c9e2d9615a90ae793b69cd010ca8c3575dbc822104f/setproctitle-1.3.6-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:23a57d3b8f1549515c2dbe4a2880ebc1f27780dc126c5e064167563e015817f5", size = 17401 }, + { url = "https://files.pythonhosted.org/packages/04/38/a184f857b988d3a9c401e470a4e38182a5c99ee77bf90432d7665e9d35a3/setproctitle-1.3.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:81c443310831e29fabbd07b75ebbfa29d0740b56f5907c6af218482d51260431", size = 11959 }, + { url = "https://files.pythonhosted.org/packages/b7/b9/4878ef9d8483adfd1edf6bf95151362aaec0d05aac306a97ff0383f491b5/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d88c63bd395c787b0aa81d8bbc22c1809f311032ce3e823a6517b711129818e4", size = 33463 }, + { url = "https://files.pythonhosted.org/packages/cc/60/3ef49d1931aff2a36a7324a49cca10d77ef03e0278452fd468c33a52d7e3/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73f14b86d0e2858ece6bf5807c9889670e392c001d414b4293d0d9b291942c3", size = 34959 }, + { url = "https://files.pythonhosted.org/packages/81/c6/dee0a973acecefb0db6c9c2e0ea7f18b7e4db773a72e534741ebdee8bbb8/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3393859eb8f19f5804049a685bf286cb08d447e28ba5c6d8543c7bf5500d5970", size = 32055 }, + { url = "https://files.pythonhosted.org/packages/ea/a5/5dd5c4192cf18d16349a32a07f728a9a48a2a05178e16966cabd6645903e/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:785cd210c0311d9be28a70e281a914486d62bfd44ac926fcd70cf0b4d65dff1c", size = 32986 }, + { url = "https://files.pythonhosted.org/packages/df/a6/1508d37eb8008670d33f13fcdb91cbd8ef54697276469abbfdd3d4428c59/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c051f46ed1e13ba8214b334cbf21902102807582fbfaf0fef341b9e52f0fafbf", size = 32736 }, + { url = "https://files.pythonhosted.org/packages/1a/73/c84ec8880d543766a12fcd6b65dbd013770974a40577889f357409b0441e/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:49498ebf68ca3e75321ffe634fcea5cc720502bfaa79bd6b03ded92ce0dc3c24", size = 31945 }, + { url = "https://files.pythonhosted.org/packages/95/0a/126b9ff7a406a69a62825fe5bd6d1ba8671919a7018c4f9e2c63f49bfcb6/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4431629c178193f23c538cb1de3da285a99ccc86b20ee91d81eb5f1a80e0d2ba", size = 34333 }, + { url = "https://files.pythonhosted.org/packages/9a/fd/5474b04f1c013ff460129d2bc774557dd6e186da4667865efef9a83bf378/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d136fbf8ad4321716e44d6d6b3d8dffb4872626010884e07a1db54b7450836cf", size = 32508 }, + { url = "https://files.pythonhosted.org/packages/32/21/2503e38520cb076a7ecaef6a35d6a6fa89cf02af3541c84c811fd7500d20/setproctitle-1.3.6-cp313-cp313t-win32.whl", hash = "sha256:d483cc23cc56ab32911ea0baa0d2d9ea7aa065987f47de847a0a93a58bf57905", size = 11482 }, + { url = "https://files.pythonhosted.org/packages/65/23/7833d75a27fba25ddc5cd3b54cd03c4bf8e18b8e2dbec622eb6326278ce8/setproctitle-1.3.6-cp313-cp313t-win_amd64.whl", hash = "sha256:74973aebea3543ad033b9103db30579ec2b950a466e09f9c2180089e8346e0ec", size = 12209 }, + { url = "https://files.pythonhosted.org/packages/d0/2b/f19977b646b64c1440dade2c385c89c39f74ce5254defa102dfd9c163e0b/setproctitle-1.3.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3cde5b83ec4915cd5e6ae271937fd60d14113c8f7769b4a20d51769fe70d8717", size = 11471 }, + { url = "https://files.pythonhosted.org/packages/78/46/db58cf700f1408cf0f63d3f939f7b077bd450da8e037310f70e74c41262f/setproctitle-1.3.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:797f2846b546a8741413c57d9fb930ad5aa939d925c9c0fa6186d77580035af7", size = 13520 }, + { url = "https://files.pythonhosted.org/packages/5c/46/0b89e7ebe77543e721c67077ad93fc8c7c3c31a8db3b12e00d02950f6adc/setproctitle-1.3.6-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac3eb04bcf0119aadc6235a2c162bae5ed5f740e3d42273a7228b915722de20", size = 13094 }, + { url = "https://files.pythonhosted.org/packages/f7/78/03f2e42eb83bce6f853d7751ae95f8a3ae7408145a0b6cdd717be01497d7/setproctitle-1.3.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e6b5633c94c5111f7137f875e8f1ff48f53b991d5d5b90932f27dc8c1fa9ae4", size = 12241 }, +] + +[[package]] +name = "setuptools" +version = "80.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b2/bd26ed086b842b68c8fe9aac380ad7e5118cf84fa7abd45bb059a88368a8/setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd", size = 1354038 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/f6/126c9309c8fe93e5d6bb850593cd58d591daf2da45cc78b61e48d8d95879/setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4", size = 1240689 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smirk" +version = "0.2.0" +source = { git = "ssh://git@github.com/BattModels/smirk.git?rev=dfdd9dc27c066f956b69d1f55c2d4717beb7838d#dfdd9dc27c066f956b69d1f55c2d4717beb7838d" } +dependencies = [ + { name = "transformers" }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.40" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/c3/3f2bfa5e4dcd9938405fe2fab5b6ab94a9248a4f9536ea2fd497da20525f/sqlalchemy-2.0.40.tar.gz", hash = "sha256:d827099289c64589418ebbcaead0145cd19f4e3e8a93919a0100247af245fa00", size = 9664299 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/fa/8e8fd93684b04e65816be864bebf0000fe1602e5452d006f9acc5db14ce5/sqlalchemy-2.0.40-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1ea21bef99c703f44444ad29c2c1b6bd55d202750b6de8e06a955380f4725d7", size = 2112843 }, + { url = "https://files.pythonhosted.org/packages/ba/87/06992f78a9ce545dfd1fea3dd99262bec5221f6f9d2d2066c3e94662529f/sqlalchemy-2.0.40-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:afe63b208153f3a7a2d1a5b9df452b0673082588933e54e7c8aac457cf35e758", size = 2104032 }, + { url = "https://files.pythonhosted.org/packages/92/ee/57dc77282e8be22d686bd4681825299aa1069bbe090564868ea270ed5214/sqlalchemy-2.0.40-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8aae085ea549a1eddbc9298b113cffb75e514eadbb542133dd2b99b5fb3b6af", size = 3086406 }, + { url = "https://files.pythonhosted.org/packages/94/3f/ceb9ab214b2e42d2e74a9209b3a2f2f073504eee16cddd2df81feeb67c2f/sqlalchemy-2.0.40-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ea9181284754d37db15156eb7be09c86e16e50fbe77610e9e7bee09291771a1", size = 3094652 }, + { url = "https://files.pythonhosted.org/packages/00/0a/3401232a5b6d91a2df16c1dc39c6504c54575744c2faafa1e5a50de96621/sqlalchemy-2.0.40-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5434223b795be5c5ef8244e5ac98056e290d3a99bdcc539b916e282b160dda00", size = 3050503 }, + { url = "https://files.pythonhosted.org/packages/93/c2/ea7171415ab131397f71a2673645c2fe29ebe9a93063d458eb89e42bf051/sqlalchemy-2.0.40-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15d08d5ef1b779af6a0909b97be6c1fd4298057504eb6461be88bd1696cb438e", size = 3076011 }, + { url = "https://files.pythonhosted.org/packages/3d/ee/d8e229280d621bed8c51eebf1dd413aa09ca89e309b1fff40d881dd149af/sqlalchemy-2.0.40-cp310-cp310-win32.whl", hash = "sha256:cd2f75598ae70bcfca9117d9e51a3b06fe29edd972fdd7fd57cc97b4dbf3b08a", size = 2085136 }, + { url = "https://files.pythonhosted.org/packages/60/7f/ea1086136bc648cd4713a1e01869f7fc31979d67b3a8f973f5d9ab8de7e1/sqlalchemy-2.0.40-cp310-cp310-win_amd64.whl", hash = "sha256:2cbafc8d39ff1abdfdda96435f38fab141892dc759a2165947d1a8fffa7ef596", size = 2109421 }, + { url = "https://files.pythonhosted.org/packages/77/7e/55044a9ec48c3249bb38d5faae93f09579c35e862bb318ebd1ed7a1994a5/sqlalchemy-2.0.40-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6bacab7514de6146a1976bc56e1545bee247242fab030b89e5f70336fc0003e", size = 2114025 }, + { url = "https://files.pythonhosted.org/packages/77/0f/dcf7bba95f847aec72f638750747b12d37914f71c8cc7c133cf326ab945c/sqlalchemy-2.0.40-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5654d1ac34e922b6c5711631f2da497d3a7bffd6f9f87ac23b35feea56098011", size = 2104419 }, + { url = "https://files.pythonhosted.org/packages/75/70/c86a5c20715e4fe903dde4c2fd44fc7e7a0d5fb52c1b954d98526f65a3ea/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35904d63412db21088739510216e9349e335f142ce4a04b69e2528020ee19ed4", size = 3222720 }, + { url = "https://files.pythonhosted.org/packages/12/cf/b891a8c1d0c27ce9163361664c2128c7a57de3f35000ea5202eb3a2917b7/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7a80ed86d6aaacb8160a1caef6680d4ddd03c944d985aecee940d168c411d1", size = 3222682 }, + { url = "https://files.pythonhosted.org/packages/15/3f/7709d8c8266953d945435a96b7f425ae4172a336963756b58e996fbef7f3/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:519624685a51525ddaa7d8ba8265a1540442a2ec71476f0e75241eb8263d6f51", size = 3159542 }, + { url = "https://files.pythonhosted.org/packages/85/7e/717eaabaf0f80a0132dc2032ea8f745b7a0914451c984821a7c8737fb75a/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ee5f9999a5b0e9689bed96e60ee53c3384f1a05c2dd8068cc2e8361b0df5b7a", size = 3179864 }, + { url = "https://files.pythonhosted.org/packages/e4/cc/03eb5dfcdb575cbecd2bd82487b9848f250a4b6ecfb4707e834b4ce4ec07/sqlalchemy-2.0.40-cp311-cp311-win32.whl", hash = "sha256:c0cae71e20e3c02c52f6b9e9722bca70e4a90a466d59477822739dc31ac18b4b", size = 2084675 }, + { url = "https://files.pythonhosted.org/packages/9a/48/440946bf9dc4dc231f4f31ef0d316f7135bf41d4b86aaba0c0655150d370/sqlalchemy-2.0.40-cp311-cp311-win_amd64.whl", hash = "sha256:574aea2c54d8f1dd1699449f332c7d9b71c339e04ae50163a3eb5ce4c4325ee4", size = 2110099 }, + { url = "https://files.pythonhosted.org/packages/92/06/552c1f92e880b57d8b92ce6619bd569b25cead492389b1d84904b55989d8/sqlalchemy-2.0.40-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d3b31d0a1c44b74d3ae27a3de422dfccd2b8f0b75e51ecb2faa2bf65ab1ba0d", size = 2112620 }, + { url = "https://files.pythonhosted.org/packages/01/72/a5bc6e76c34cebc071f758161dbe1453de8815ae6e662393910d3be6d70d/sqlalchemy-2.0.40-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37f7a0f506cf78c80450ed1e816978643d3969f99c4ac6b01104a6fe95c5490a", size = 2103004 }, + { url = "https://files.pythonhosted.org/packages/bf/fd/0e96c8e6767618ed1a06e4d7a167fe13734c2f8113c4cb704443e6783038/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bb933a650323e476a2e4fbef8997a10d0003d4da996aad3fd7873e962fdde4d", size = 3252440 }, + { url = "https://files.pythonhosted.org/packages/cd/6a/eb82e45b15a64266a2917a6833b51a334ea3c1991728fd905bfccbf5cf63/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959738971b4745eea16f818a2cd086fb35081383b078272c35ece2b07012716", size = 3263277 }, + { url = "https://files.pythonhosted.org/packages/45/97/ebe41ab4530f50af99e3995ebd4e0204bf1b0dc0930f32250dde19c389fe/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:110179728e442dae85dd39591beb74072ae4ad55a44eda2acc6ec98ead80d5f2", size = 3198591 }, + { url = "https://files.pythonhosted.org/packages/e6/1c/a569c1b2b2f5ac20ba6846a1321a2bf52e9a4061001f282bf1c5528dcd69/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8040680eaacdce4d635f12c55c714f3d4c7f57da2bc47a01229d115bd319191", size = 3225199 }, + { url = "https://files.pythonhosted.org/packages/8f/91/87cc71a6b10065ca0209d19a4bb575378abda6085e72fa0b61ffb2201b84/sqlalchemy-2.0.40-cp312-cp312-win32.whl", hash = "sha256:650490653b110905c10adac69408380688cefc1f536a137d0d69aca1069dc1d1", size = 2082959 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/14c511cda174aa1ad9b0e42b64ff5a71db35d08b0d80dc044dae958921e5/sqlalchemy-2.0.40-cp312-cp312-win_amd64.whl", hash = "sha256:2be94d75ee06548d2fc591a3513422b873490efb124048f50556369a834853b0", size = 2108526 }, + { url = "https://files.pythonhosted.org/packages/8c/18/4e3a86cc0232377bc48c373a9ba6a1b3fb79ba32dbb4eda0b357f5a2c59d/sqlalchemy-2.0.40-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:915866fd50dd868fdcc18d61d8258db1bf9ed7fbd6dfec960ba43365952f3b01", size = 2107887 }, + { url = "https://files.pythonhosted.org/packages/cb/60/9fa692b1d2ffc4cbd5f47753731fd332afed30137115d862d6e9a1e962c7/sqlalchemy-2.0.40-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a4c5a2905a9ccdc67a8963e24abd2f7afcd4348829412483695c59e0af9a705", size = 2098367 }, + { url = "https://files.pythonhosted.org/packages/4c/9f/84b78357ca641714a439eb3fbbddb17297dacfa05d951dbf24f28d7b5c08/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55028d7a3ebdf7ace492fab9895cbc5270153f75442a0472d8516e03159ab364", size = 3184806 }, + { url = "https://files.pythonhosted.org/packages/4b/7d/e06164161b6bfce04c01bfa01518a20cccbd4100d5c951e5a7422189191a/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cfedff6878b0e0d1d0a50666a817ecd85051d12d56b43d9d425455e608b5ba0", size = 3198131 }, + { url = "https://files.pythonhosted.org/packages/6d/51/354af20da42d7ec7b5c9de99edafbb7663a1d75686d1999ceb2c15811302/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb19e30fdae77d357ce92192a3504579abe48a66877f476880238a962e5b96db", size = 3131364 }, + { url = "https://files.pythonhosted.org/packages/7a/2f/48a41ff4e6e10549d83fcc551ab85c268bde7c03cf77afb36303c6594d11/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16d325ea898f74b26ffcd1cf8c593b0beed8714f0317df2bed0d8d1de05a8f26", size = 3159482 }, + { url = "https://files.pythonhosted.org/packages/33/ac/e5e0a807163652a35be878c0ad5cfd8b1d29605edcadfb5df3c512cdf9f3/sqlalchemy-2.0.40-cp313-cp313-win32.whl", hash = "sha256:a669cbe5be3c63f75bcbee0b266779706f1a54bcb1000f302685b87d1b8c1500", size = 2080704 }, + { url = "https://files.pythonhosted.org/packages/1c/cb/f38c61f7f2fd4d10494c1c135ff6a6ddb63508d0b47bccccd93670637309/sqlalchemy-2.0.40-cp313-cp313-win_amd64.whl", hash = "sha256:641ee2e0834812d657862f3a7de95e0048bdcb6c55496f39c6fa3d435f6ac6ad", size = 2104564 }, + { url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894 }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, +] + +[[package]] +name = "tokenizerstats" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "electrolyte-fm" }, + { name = "jsonnet" }, + { name = "mendeleev" }, + { name = "selfies" }, +] + +[package.metadata] +requires-dist = [ + { name = "electrolyte-fm", virtual = "../../" }, + { name = "jsonnet", specifier = "~=0.20.0" }, + { name = "mendeleev", specifier = ">=0.16" }, + { name = "selfies", specifier = "==2.1.2" }, +] + +[[package]] +name = "torch" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/c2/3fb87940fa160d956ee94d644d37b99a24b9c05a4222bf34f94c71880e28/torch-2.7.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c9afea41b11e1a1ab1b258a5c31afbd646d6319042bfe4f231b408034b51128b", size = 99158447 }, + { url = "https://files.pythonhosted.org/packages/cc/2c/91d1de65573fce563f5284e69d9c56b57289625cffbbb6d533d5d56c36a5/torch-2.7.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b9960183b6e5b71239a3e6c883d8852c304e691c0b2955f7045e8a6d05b9183", size = 865164221 }, + { url = "https://files.pythonhosted.org/packages/7f/7e/1b1cc4e0e7cc2666cceb3d250eef47a205f0821c330392cf45eb08156ce5/torch-2.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:2ad79d0d8c2a20a37c5df6052ec67c2078a2c4e9a96dd3a8b55daaff6d28ea29", size = 212521189 }, + { url = "https://files.pythonhosted.org/packages/dc/0b/b2b83f30b8e84a51bf4f96aa3f5f65fdf7c31c591cc519310942339977e2/torch-2.7.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:34e0168ed6de99121612d72224e59b2a58a83dae64999990eada7260c5dd582d", size = 68559462 }, + { url = "https://files.pythonhosted.org/packages/40/da/7378d16cc636697f2a94f791cb496939b60fb8580ddbbef22367db2c2274/torch-2.7.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2b7813e904757b125faf1a9a3154e1d50381d539ced34da1992f52440567c156", size = 99159397 }, + { url = "https://files.pythonhosted.org/packages/0e/6b/87fcddd34df9f53880fa1f0c23af7b6b96c935856473faf3914323588c40/torch-2.7.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd5cfbb4c3bbadd57ad1b27d56a28008f8d8753733411a140fcfb84d7f933a25", size = 865183681 }, + { url = "https://files.pythonhosted.org/packages/13/85/6c1092d4b06c3db1ed23d4106488750917156af0b24ab0a2d9951830b0e9/torch-2.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:58df8d5c2eeb81305760282b5069ea4442791a6bbf0c74d9069b7b3304ff8a37", size = 212520100 }, + { url = "https://files.pythonhosted.org/packages/aa/3f/85b56f7e2abcfa558c5fbf7b11eb02d78a4a63e6aeee2bbae3bb552abea5/torch-2.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:0a8d43caa342b9986101ec5feb5bbf1d86570b5caa01e9cb426378311258fdde", size = 68569377 }, + { url = "https://files.pythonhosted.org/packages/aa/5e/ac759f4c0ab7c01feffa777bd68b43d2ac61560a9770eeac074b450f81d4/torch-2.7.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:36a6368c7ace41ad1c0f69f18056020b6a5ca47bedaca9a2f3b578f5a104c26c", size = 99013250 }, + { url = "https://files.pythonhosted.org/packages/9c/58/2d245b6f1ef61cf11dfc4aceeaacbb40fea706ccebac3f863890c720ab73/torch-2.7.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:15aab3e31c16feb12ae0a88dba3434a458874636f360c567caa6a91f6bfba481", size = 865042157 }, + { url = "https://files.pythonhosted.org/packages/44/80/b353c024e6b624cd9ce1d66dcb9d24e0294680f95b369f19280e241a0159/torch-2.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:f56d4b2510934e072bab3ab8987e00e60e1262fb238176168f5e0c43a1320c6d", size = 212482262 }, + { url = "https://files.pythonhosted.org/packages/ee/8d/b2939e5254be932db1a34b2bd099070c509e8887e0c5a90c498a917e4032/torch-2.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:30b7688a87239a7de83f269333651d8e582afffce6f591fff08c046f7787296e", size = 68574294 }, + { url = "https://files.pythonhosted.org/packages/14/24/720ea9a66c29151b315ea6ba6f404650834af57a26b2a04af23ec246b2d5/torch-2.7.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:868ccdc11798535b5727509480cd1d86d74220cfdc42842c4617338c1109a205", size = 99015553 }, + { url = "https://files.pythonhosted.org/packages/4b/27/285a8cf12bd7cd71f9f211a968516b07dcffed3ef0be585c6e823675ab91/torch-2.7.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b52347118116cf3dff2ab5a3c3dd97c719eb924ac658ca2a7335652076df708", size = 865046389 }, + { url = "https://files.pythonhosted.org/packages/74/c8/2ab2b6eadc45554af8768ae99668c5a8a8552e2012c7238ded7e9e4395e1/torch-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:434cf3b378340efc87c758f250e884f34460624c0523fe5c9b518d205c91dd1b", size = 212490304 }, + { url = "https://files.pythonhosted.org/packages/28/fd/74ba6fde80e2b9eef4237fe668ffae302c76f0e4221759949a632ca13afa/torch-2.7.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:edad98dddd82220465b106506bb91ee5ce32bd075cddbcf2b443dfaa2cbd83bf", size = 68856166 }, + { url = "https://files.pythonhosted.org/packages/cb/b4/8df3f9fe6bdf59e56a0e538592c308d18638eb5f5dc4b08d02abb173c9f0/torch-2.7.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a885fc25afefb6e6eb18a7d1e8bfa01cc153e92271d980a49243b250d5ab6d9", size = 99091348 }, + { url = "https://files.pythonhosted.org/packages/9d/f5/0bd30e9da04c3036614aa1b935a9f7e505a9e4f1f731b15e165faf8a4c74/torch-2.7.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:176300ff5bc11a5f5b0784e40bde9e10a35c4ae9609beed96b4aeb46a27f5fae", size = 865104023 }, + { url = "https://files.pythonhosted.org/packages/d1/b7/2235d0c3012c596df1c8d39a3f4afc1ee1b6e318d469eda4c8bb68566448/torch-2.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d0ca446a93f474985d81dc866fcc8dccefb9460a29a456f79d99c29a78a66993", size = 212750916 }, + { url = "https://files.pythonhosted.org/packages/90/48/7e6477cf40d48cc0a61fa0d41ee9582b9a316b12772fcac17bc1a40178e7/torch-2.7.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:27f5007bdf45f7bb7af7f11d1828d5c2487e030690afb3d89a651fd7036a390e", size = 68575074 }, +] + +[[package]] +name = "torchmetrics" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lightning-utilities" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/c4/2d921ccf7433ee6d8d6edeaca238674cbbc74528120631c662014795c645/torchmetrics-1.7.1.tar.gz", hash = "sha256:0ac1a0e90d2375866ceb5d3868720c6df7d7d0c5729b7ad36e92c897c6af70c2", size = 565045 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/ee/4d0a7213a6f412afb3483031009a3b970dd7bed3be24de95ab04fba1c05a/torchmetrics-1.7.1-py3-none-any.whl", hash = "sha256:9a4c45edbd0a1844cc1540c9e71bfbe0ee783a2ed997344d947fefecac90dbb9", size = 961489 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.51.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/11/7414d5bc07690002ce4d7553602107bf969af85144bbd02830f9fb471236/transformers-4.51.3.tar.gz", hash = "sha256:e292fcab3990c6defe6328f0f7d2004283ca81a7a07b2de9a46d67fd81ea1409", size = 8941266 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/b6/5257d04ae327b44db31f15cce39e6020cc986333c715660b1315a9724d82/transformers-4.51.3-py3-none-any.whl", hash = "sha256:fd3279633ceb2b777013234bbf0b4f5c2d23c4626b05497691f00cfda55e8a83", size = 10383940 }, +] + +[[package]] +name = "triton" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/04/d54d3a6d077c646624dc9461b0059e23fd5d30e0dbe67471e3654aec81f9/triton-3.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fad99beafc860501d7fcc1fb7045d9496cbe2c882b1674640304949165a916e7", size = 156441993 }, + { url = "https://files.pythonhosted.org/packages/3c/c5/4874a81131cc9e934d88377fbc9d24319ae1fb540f3333b4e9c696ebc607/triton-3.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3161a2bf073d6b22c4e2f33f951f3e5e3001462b2570e6df9cd57565bdec2984", size = 156528461 }, + { url = "https://files.pythonhosted.org/packages/11/53/ce18470914ab6cfbec9384ee565d23c4d1c55f0548160b1c7b33000b11fd/triton-3.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b68c778f6c4218403a6bd01be7484f6dc9e20fe2083d22dd8aef33e3b87a10a3", size = 156504509 }, + { url = "https://files.pythonhosted.org/packages/7d/74/4bf2702b65e93accaa20397b74da46fb7a0356452c1bb94dbabaf0582930/triton-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47bc87ad66fa4ef17968299acacecaab71ce40a238890acc6ad197c3abe2b8f1", size = 156516468 }, + { url = "https://files.pythonhosted.org/packages/0a/93/f28a696fa750b9b608baa236f8225dd3290e5aff27433b06143adc025961/triton-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce4700fc14032af1e049005ae94ba908e71cd6c2df682239aed08e49bc71b742", size = 156580729 }, +] + +[[package]] +name = "typer" +version = "0.15.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/1a/5f36851f439884bcfe8539f6a20ff7516e7b60f319bbaf69a90dc35cc2eb/typer-0.15.3.tar.gz", hash = "sha256:818873625d0569653438316567861899f7e9972f2e6e0c16dab608345ced713c", size = 101641 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/20/9d953de6f4367163d23ec823200eb3ecb0050a2609691e512c8b95827a9b/typer-0.15.3-py3-none-any.whl", hash = "sha256:c86a65ad77ca531f03de08d1b9cb67cd09ad02ddddf4b34745b5008f43b239bd", size = 45253 }, +] + +[[package]] +name = "typeshed-client" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-resources" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/1e/f20e33447be772486acf028295cdd21437454a051adb602d52ddb5334f9e/typeshed_client-2.7.0.tar.gz", hash = "sha256:e63df1e738588ad39f1226de042f4407ab6a99c456f0837063afd83b1415447c", size = 433569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/39/4702c2901899c018189b9aa7eb75aa8eb54527aed71c3f285895190dc664/typeshed_client-2.7.0-py3-none-any.whl", hash = "sha256:97084e5abc58a76ace2c4618ecaebd625f2d19bbd85aa1b3fb86216bf174bbea", size = 624417 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +] + +[[package]] +name = "wandb" +version = "0.19.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "docker-pycreds" }, + { name = "gitpython" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "setproctitle" }, + { name = "setuptools" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/1b/39261dbbfa35b2d4f21656dca87b7b43eea7a8e0cfec6ad1514faed6e8d3/wandb-0.19.10.tar.gz", hash = "sha256:010772d26cd259eee2b7e0c8a8d59ad53de050ba96b0bc56e3ee23ff4f1436bb", size = 39467171 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/9a/0a3f323b4dbf5527ce19203a2adc86afc0db546256cc3dd4c20cf45ef182/wandb-0.19.10-py3-none-any.whl", hash = "sha256:c0ed28c56005fa899eed2f03db50b3c579682797afd0baf925c73e9e5d38e2f6", size = 6411518 }, + { url = "https://files.pythonhosted.org/packages/03/a8/7b38239f352c8b6cf47084d125d0bb9d76e99301748b82cf6856883676b9/wandb-0.19.10-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1dbe1aa6f321a761a6be35f55dedb120c50a37fb807f6cb72714c51a6e987444", size = 20941581 }, + { url = "https://files.pythonhosted.org/packages/5b/dc/01aa44bf5b9831f02643859eff4125a05aac726290a171e9e05e144cac09/wandb-0.19.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5eeaeabcf6e90cc96292ee47d08b2e180e81a560773e3127bafa8c02c96c0029", size = 20341851 }, + { url = "https://files.pythonhosted.org/packages/6d/02/7d29203f0e522fbaaaa52ca0a77c8120fa5f971c6cf521fb5fb887fb9db2/wandb-0.19.10-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:cb3b88eb69d4e17fd7cdfb89970eff06b7328862ec6dd739ae524b7b17cd8a9f", size = 20941301 }, + { url = "https://files.pythonhosted.org/packages/f0/fe/51f9450a449394368018af8b56108ec67c8b7ad32a463daa6957eee9e3b4/wandb-0.19.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c290791a58a2afdf0ec76d02e9f5d750387d52797bc46b12e889cf9baeec2880", size = 19921945 }, + { url = "https://files.pythonhosted.org/packages/21/ef/657260437386bb55b17f93751f8c62e39b8c786da943da8c61cda155e070/wandb-0.19.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a518b94b0b481230e62c069d341a1c35f5bf0ac0011bc06554b33b213ea6d40", size = 21346350 }, + { url = "https://files.pythonhosted.org/packages/be/62/1a6c27f11cea57423ba0466c1b8f2e4183a4a5631a9d2b1064e6f9c14065/wandb-0.19.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3dfabb4fa64b84663d34fd11c53bd6e4a3a42026e7776519966cd45a4c39f93a", size = 19928921 }, + { url = "https://files.pythonhosted.org/packages/7e/97/54f211f880f6f9179819c7ee84172516d6f56dd7ef8c358a8ed71e844969/wandb-0.19.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:92d6889f6f291f954ac27019ea6c4b5115d1670846f2474c875c98baf93f8f51", size = 21426985 }, + { url = "https://files.pythonhosted.org/packages/bb/a5/ff6c1986a0ad0e9b768bb6369a8b461a8707c37685acc2d3f4cd0a880cd4/wandb-0.19.10-py3-none-win32.whl", hash = "sha256:1df6a5ad4f4eea1732453598360e99350b6a14e5d911d305d595bd1c412ff0e8", size = 20732820 }, + { url = "https://files.pythonhosted.org/packages/00/46/46a2e7cf5113671eb7323dc1ae7a33b7e6ecbc1042b078f34f28a8968fa1/wandb-0.19.10-py3-none-win_amd64.whl", hash = "sha256:ae6b09efaa3c5b70146710fc153889f4faf059ea2e0bd2c1fb821dc59a77f50a", size = 20732825 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/d1/1daec934997e8b160040c78d7b31789f19b122110a75eca3d4e8da0049e1/wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", size = 53307 }, + { url = "https://files.pythonhosted.org/packages/1b/7b/13369d42651b809389c1a7153baa01d9700430576c81a2f5c5e460df0ed9/wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", size = 38486 }, + { url = "https://files.pythonhosted.org/packages/62/bf/e0105016f907c30b4bd9e377867c48c34dc9c6c0c104556c9c9126bd89ed/wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", size = 38777 }, + { url = "https://files.pythonhosted.org/packages/27/70/0f6e0679845cbf8b165e027d43402a55494779295c4b08414097b258ac87/wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", size = 83314 }, + { url = "https://files.pythonhosted.org/packages/0f/77/0576d841bf84af8579124a93d216f55d6f74374e4445264cb378a6ed33eb/wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", size = 74947 }, + { url = "https://files.pythonhosted.org/packages/90/ec/00759565518f268ed707dcc40f7eeec38637d46b098a1f5143bff488fe97/wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", size = 82778 }, + { url = "https://files.pythonhosted.org/packages/f8/5a/7cffd26b1c607b0b0c8a9ca9d75757ad7620c9c0a9b4a25d3f8a1480fafc/wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", size = 81716 }, + { url = "https://files.pythonhosted.org/packages/7e/09/dccf68fa98e862df7e6a60a61d43d644b7d095a5fc36dbb591bbd4a1c7b2/wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", size = 74548 }, + { url = "https://files.pythonhosted.org/packages/b7/8e/067021fa3c8814952c5e228d916963c1115b983e21393289de15128e867e/wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", size = 81334 }, + { url = "https://files.pythonhosted.org/packages/4b/0d/9d4b5219ae4393f718699ca1c05f5ebc0c40d076f7e65fd48f5f693294fb/wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", size = 36427 }, + { url = "https://files.pythonhosted.org/packages/72/6a/c5a83e8f61aec1e1aeef939807602fb880e5872371e95df2137142f5c58e/wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", size = 38774 }, + { url = "https://files.pythonhosted.org/packages/cd/f7/a2aab2cbc7a665efab072344a8949a71081eed1d2f451f7f7d2b966594a2/wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58", size = 53308 }, + { url = "https://files.pythonhosted.org/packages/50/ff/149aba8365fdacef52b31a258c4dc1c57c79759c335eff0b3316a2664a64/wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", size = 38488 }, + { url = "https://files.pythonhosted.org/packages/65/46/5a917ce85b5c3b490d35c02bf71aedaa9f2f63f2d15d9949cc4ba56e8ba9/wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", size = 38776 }, + { url = "https://files.pythonhosted.org/packages/ca/74/336c918d2915a4943501c77566db41d1bd6e9f4dbc317f356b9a244dfe83/wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", size = 83776 }, + { url = "https://files.pythonhosted.org/packages/09/99/c0c844a5ccde0fe5761d4305485297f91d67cf2a1a824c5f282e661ec7ff/wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", size = 75420 }, + { url = "https://files.pythonhosted.org/packages/b4/b0/9fc566b0fe08b282c850063591a756057c3247b2362b9286429ec5bf1721/wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", size = 83199 }, + { url = "https://files.pythonhosted.org/packages/9d/4b/71996e62d543b0a0bd95dda485219856def3347e3e9380cc0d6cf10cfb2f/wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", size = 82307 }, + { url = "https://files.pythonhosted.org/packages/39/35/0282c0d8789c0dc9bcc738911776c762a701f95cfe113fb8f0b40e45c2b9/wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", size = 75025 }, + { url = "https://files.pythonhosted.org/packages/4f/6d/90c9fd2c3c6fee181feecb620d95105370198b6b98a0770cba090441a828/wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", size = 81879 }, + { url = "https://files.pythonhosted.org/packages/8f/fa/9fb6e594f2ce03ef03eddbdb5f4f90acb1452221a5351116c7c4708ac865/wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", size = 36419 }, + { url = "https://files.pythonhosted.org/packages/47/f8/fb1773491a253cbc123c5d5dc15c86041f746ed30416535f2a8df1f4a392/wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", size = 38773 }, + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970 }, + { url = "https://files.pythonhosted.org/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801 }, + { url = "https://files.pythonhosted.org/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927 }, + { url = "https://files.pythonhosted.org/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360 }, + { url = "https://files.pythonhosted.org/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528 }, + { url = "https://files.pythonhosted.org/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149 }, + { url = "https://files.pythonhosted.org/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703 }, + { url = "https://files.pythonhosted.org/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255 }, + { url = "https://files.pythonhosted.org/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744 }, + { url = "https://files.pythonhosted.org/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115 }, + { url = "https://files.pythonhosted.org/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247 }, + { url = "https://files.pythonhosted.org/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419 }, + { url = "https://files.pythonhosted.org/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003 }, + { url = "https://files.pythonhosted.org/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773 }, + { url = "https://files.pythonhosted.org/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1", size = 31969 }, + { url = "https://files.pythonhosted.org/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8", size = 30800 }, + { url = "https://files.pythonhosted.org/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166", size = 221566 }, + { url = "https://files.pythonhosted.org/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7", size = 201214 }, + { url = "https://files.pythonhosted.org/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623", size = 429433 }, + { url = "https://files.pythonhosted.org/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a", size = 194822 }, + { url = "https://files.pythonhosted.org/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88", size = 208538 }, + { url = "https://files.pythonhosted.org/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c", size = 216953 }, + { url = "https://files.pythonhosted.org/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2", size = 203594 }, + { url = "https://files.pythonhosted.org/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084", size = 210971 }, + { url = "https://files.pythonhosted.org/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d", size = 415050 }, + { url = "https://files.pythonhosted.org/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839", size = 192216 }, + { url = "https://files.pythonhosted.org/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da", size = 30120 }, + { url = "https://files.pythonhosted.org/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58", size = 30003 }, + { url = "https://files.pythonhosted.org/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3", size = 26777 }, + { url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787 }, + { url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959 }, + { url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006 }, + { url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326 }, + { url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380 }, + { url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934 }, + { url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301 }, + { url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351 }, + { url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294 }, + { url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674 }, + { url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022 }, + { url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170 }, + { url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040 }, + { url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796 }, + { url = "https://files.pythonhosted.org/packages/c9/b8/e4b3ad92d249be5c83fa72916c9091b0965cb0faeff05d9a0a3870ae6bff/xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6", size = 31795 }, + { url = "https://files.pythonhosted.org/packages/fc/d8/b3627a0aebfbfa4c12a41e22af3742cf08c8ea84f5cc3367b5de2d039cce/xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5", size = 30792 }, + { url = "https://files.pythonhosted.org/packages/c3/cc/762312960691da989c7cd0545cb120ba2a4148741c6ba458aa723c00a3f8/xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc", size = 220950 }, + { url = "https://files.pythonhosted.org/packages/fe/e9/cc266f1042c3c13750e86a535496b58beb12bf8c50a915c336136f6168dc/xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3", size = 199980 }, + { url = "https://files.pythonhosted.org/packages/bf/85/a836cd0dc5cc20376de26b346858d0ac9656f8f730998ca4324921a010b9/xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c", size = 428324 }, + { url = "https://files.pythonhosted.org/packages/b4/0e/15c243775342ce840b9ba34aceace06a1148fa1630cd8ca269e3223987f5/xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb", size = 194370 }, + { url = "https://files.pythonhosted.org/packages/87/a1/b028bb02636dfdc190da01951d0703b3d904301ed0ef6094d948983bef0e/xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f", size = 207911 }, + { url = "https://files.pythonhosted.org/packages/80/d5/73c73b03fc0ac73dacf069fdf6036c9abad82de0a47549e9912c955ab449/xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7", size = 216352 }, + { url = "https://files.pythonhosted.org/packages/b6/2a/5043dba5ddbe35b4fe6ea0a111280ad9c3d4ba477dd0f2d1fe1129bda9d0/xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326", size = 203410 }, + { url = "https://files.pythonhosted.org/packages/a2/b2/9a8ded888b7b190aed75b484eb5c853ddd48aa2896e7b59bbfbce442f0a1/xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf", size = 210322 }, + { url = "https://files.pythonhosted.org/packages/98/62/440083fafbc917bf3e4b67c2ade621920dd905517e85631c10aac955c1d2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7", size = 414725 }, + { url = "https://files.pythonhosted.org/packages/75/db/009206f7076ad60a517e016bb0058381d96a007ce3f79fa91d3010f49cc2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c", size = 192070 }, + { url = "https://files.pythonhosted.org/packages/1f/6d/c61e0668943a034abc3a569cdc5aeae37d686d9da7e39cf2ed621d533e36/xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637", size = 30172 }, + { url = "https://files.pythonhosted.org/packages/96/14/8416dce965f35e3d24722cdf79361ae154fa23e2ab730e5323aa98d7919e/xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43", size = 30041 }, + { url = "https://files.pythonhosted.org/packages/27/ee/518b72faa2073f5aa8e3262408d284892cb79cf2754ba0c3a5870645ef73/xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b", size = 26801 }, + { url = "https://files.pythonhosted.org/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732 }, + { url = "https://files.pythonhosted.org/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214 }, + { url = "https://files.pythonhosted.org/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020 }, + { url = "https://files.pythonhosted.org/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515 }, + { url = "https://files.pythonhosted.org/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064 }, +] + +[[package]] +name = "yarl" +version = "1.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/ab/66082639f99d7ef647a86b2ff4ca20f8ae13bd68a6237e6e166b8eb92edf/yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22", size = 145054 }, + { url = "https://files.pythonhosted.org/packages/3d/c2/4e78185c453c3ca02bd11c7907394d0410d26215f9e4b7378648b3522a30/yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62", size = 96811 }, + { url = "https://files.pythonhosted.org/packages/c7/45/91e31dccdcf5b7232dcace78bd51a1bb2d7b4b96c65eece0078b620587d1/yarl-1.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a06701b647c9939d7019acdfa7ebbfbb78ba6aa05985bb195ad716ea759a569", size = 94566 }, + { url = "https://files.pythonhosted.org/packages/c8/21/e0aa650bcee881fb804331faa2c0f9a5d6be7609970b2b6e3cdd414e174b/yarl-1.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7595498d085becc8fb9203aa314b136ab0516c7abd97e7d74f7bb4eb95042abe", size = 327297 }, + { url = "https://files.pythonhosted.org/packages/1a/a4/58f10870f5c17595c5a37da4c6a0b321589b7d7976e10570088d445d0f47/yarl-1.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af5607159085dcdb055d5678fc2d34949bd75ae6ea6b4381e784bbab1c3aa195", size = 323578 }, + { url = "https://files.pythonhosted.org/packages/07/df/2506b1382cc0c4bb0d22a535dc3e7ccd53da9a59b411079013a7904ac35c/yarl-1.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95b50910e496567434cb77a577493c26bce0f31c8a305135f3bda6a2483b8e10", size = 343212 }, + { url = "https://files.pythonhosted.org/packages/ba/4a/d1c901d0e2158ad06bb0b9a92473e32d992f98673b93c8a06293e091bab0/yarl-1.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b594113a301ad537766b4e16a5a6750fcbb1497dcc1bc8a4daae889e6402a634", size = 337956 }, + { url = "https://files.pythonhosted.org/packages/8b/fd/10fcf7d86f49b1a11096d6846257485ef32e3d3d322e8a7fdea5b127880c/yarl-1.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:083ce0393ea173cd37834eb84df15b6853b555d20c52703e21fbababa8c129d2", size = 333889 }, + { url = "https://files.pythonhosted.org/packages/e2/cd/bae926a25154ba31c5fd15f2aa6e50a545c840e08d85e2e2e0807197946b/yarl-1.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1a350a652bbbe12f666109fbddfdf049b3ff43696d18c9ab1531fbba1c977a", size = 322282 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/c3ac3597dfde746c63c637c5422cf3954ebf622a8de7f09892d20a68900d/yarl-1.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fb0caeac4a164aadce342f1597297ec0ce261ec4532bbc5a9ca8da5622f53867", size = 336270 }, + { url = "https://files.pythonhosted.org/packages/dd/42/417fd7b8da5846def29712370ea8916a4be2553de42a2c969815153717be/yarl-1.20.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d88cc43e923f324203f6ec14434fa33b85c06d18d59c167a0637164863b8e995", size = 335500 }, + { url = "https://files.pythonhosted.org/packages/37/aa/c2339683f8f05f4be16831b6ad58d04406cf1c7730e48a12f755da9f5ac5/yarl-1.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e52d6ed9ea8fd3abf4031325dc714aed5afcbfa19ee4a89898d663c9976eb487", size = 339672 }, + { url = "https://files.pythonhosted.org/packages/be/12/ab6c4df95f00d7bc9502bf07a92d5354f11d9d3cb855222a6a8d2bd6e8da/yarl-1.20.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce360ae48a5e9961d0c730cf891d40698a82804e85f6e74658fb175207a77cb2", size = 351840 }, + { url = "https://files.pythonhosted.org/packages/83/3c/08d58c51bbd3899be3e7e83cd7a691fdcf3b9f78b8699d663ecc2c090ab7/yarl-1.20.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:06d06c9d5b5bc3eb56542ceeba6658d31f54cf401e8468512447834856fb0e61", size = 359550 }, + { url = "https://files.pythonhosted.org/packages/8a/15/de7906c506f85fb476f0edac4bd74569f49e5ffdcf98e246a0313bf593b9/yarl-1.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c27d98f4e5c4060582f44e58309c1e55134880558f1add7a87c1bc36ecfade19", size = 351108 }, + { url = "https://files.pythonhosted.org/packages/25/04/c6754f5ae2cdf057ac094ac01137c17875b629b1c29ed75354626a755375/yarl-1.20.0-cp310-cp310-win32.whl", hash = "sha256:f4d3fa9b9f013f7050326e165c3279e22850d02ae544ace285674cb6174b5d6d", size = 86733 }, + { url = "https://files.pythonhosted.org/packages/db/1f/5c1952f3d983ac3f5fb079b5b13b62728f8a73fd27d03e1cef7e476addff/yarl-1.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc906b636239631d42eb8a07df8359905da02704a868983265603887ed68c076", size = 92916 }, + { url = "https://files.pythonhosted.org/packages/60/82/a59d8e21b20ffc836775fa7daedac51d16bb8f3010c4fcb495c4496aa922/yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3", size = 145178 }, + { url = "https://files.pythonhosted.org/packages/ba/81/315a3f6f95947cfbf37c92d6fbce42a1a6207b6c38e8c2b452499ec7d449/yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a", size = 96859 }, + { url = "https://files.pythonhosted.org/packages/ad/17/9b64e575583158551b72272a1023cdbd65af54fe13421d856b2850a6ddb7/yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2", size = 94647 }, + { url = "https://files.pythonhosted.org/packages/2c/29/8f291e7922a58a21349683f6120a85701aeefaa02e9f7c8a2dc24fe3f431/yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e", size = 355788 }, + { url = "https://files.pythonhosted.org/packages/26/6d/b4892c80b805c42c228c6d11e03cafabf81662d371b0853e7f0f513837d5/yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9", size = 344613 }, + { url = "https://files.pythonhosted.org/packages/d7/0e/517aa28d3f848589bae9593717b063a544b86ba0a807d943c70f48fcf3bb/yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a", size = 370953 }, + { url = "https://files.pythonhosted.org/packages/5f/9b/5bd09d2f1ad6e6f7c2beae9e50db78edd2cca4d194d227b958955573e240/yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2", size = 369204 }, + { url = "https://files.pythonhosted.org/packages/9c/85/d793a703cf4bd0d4cd04e4b13cc3d44149470f790230430331a0c1f52df5/yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2", size = 358108 }, + { url = "https://files.pythonhosted.org/packages/6f/54/b6c71e13549c1f6048fbc14ce8d930ac5fb8bafe4f1a252e621a24f3f1f9/yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8", size = 346610 }, + { url = "https://files.pythonhosted.org/packages/a0/1a/d6087d58bdd0d8a2a37bbcdffac9d9721af6ebe50d85304d9f9b57dfd862/yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902", size = 365378 }, + { url = "https://files.pythonhosted.org/packages/02/84/e25ddff4cbc001dbc4af76f8d41a3e23818212dd1f0a52044cbc60568872/yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791", size = 356919 }, + { url = "https://files.pythonhosted.org/packages/04/76/898ae362353bf8f64636495d222c8014c8e5267df39b1a9fe1e1572fb7d0/yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f", size = 364248 }, + { url = "https://files.pythonhosted.org/packages/1b/b0/9d9198d83a622f1c40fdbf7bd13b224a6979f2e1fc2cf50bfb1d8773c495/yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da", size = 378418 }, + { url = "https://files.pythonhosted.org/packages/c7/ce/1f50c1cc594cf5d3f5bf4a9b616fca68680deaec8ad349d928445ac52eb8/yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4", size = 383850 }, + { url = "https://files.pythonhosted.org/packages/89/1e/a59253a87b35bfec1a25bb5801fb69943330b67cfd266278eb07e0609012/yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5", size = 381218 }, + { url = "https://files.pythonhosted.org/packages/85/b0/26f87df2b3044b0ef1a7cf66d321102bdca091db64c5ae853fcb2171c031/yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6", size = 86606 }, + { url = "https://files.pythonhosted.org/packages/33/46/ca335c2e1f90446a77640a45eeb1cd8f6934f2c6e4df7db0f0f36ef9f025/yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb", size = 93374 }, + { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089 }, + { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706 }, + { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719 }, + { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972 }, + { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639 }, + { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745 }, + { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178 }, + { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219 }, + { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266 }, + { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873 }, + { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524 }, + { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370 }, + { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297 }, + { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771 }, + { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355 }, + { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904 }, + { url = "https://files.pythonhosted.org/packages/0f/6f/514c9bff2900c22a4f10e06297714dbaf98707143b37ff0bcba65a956221/yarl-1.20.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2137810a20b933b1b1b7e5cf06a64c3ed3b4747b0e5d79c9447c00db0e2f752f", size = 145030 }, + { url = "https://files.pythonhosted.org/packages/4e/9d/f88da3fa319b8c9c813389bfb3463e8d777c62654c7168e580a13fadff05/yarl-1.20.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:447c5eadd750db8389804030d15f43d30435ed47af1313303ed82a62388176d3", size = 96894 }, + { url = "https://files.pythonhosted.org/packages/cd/57/92e83538580a6968b2451d6c89c5579938a7309d4785748e8ad42ddafdce/yarl-1.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42fbe577272c203528d402eec8bf4b2d14fd49ecfec92272334270b850e9cd7d", size = 94457 }, + { url = "https://files.pythonhosted.org/packages/e9/ee/7ee43bd4cf82dddd5da97fcaddb6fa541ab81f3ed564c42f146c83ae17ce/yarl-1.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e321617de4ab170226cd15006a565d0fa0d908f11f724a2c9142d6b2812ab0", size = 343070 }, + { url = "https://files.pythonhosted.org/packages/4a/12/b5eccd1109e2097bcc494ba7dc5de156e41cf8309fab437ebb7c2b296ce3/yarl-1.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4345f58719825bba29895011e8e3b545e6e00257abb984f9f27fe923afca2501", size = 337739 }, + { url = "https://files.pythonhosted.org/packages/7d/6b/0eade8e49af9fc2585552f63c76fa59ef469c724cc05b29519b19aa3a6d5/yarl-1.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d9b980d7234614bc4674468ab173ed77d678349c860c3af83b1fffb6a837ddc", size = 351338 }, + { url = "https://files.pythonhosted.org/packages/45/cb/aaaa75d30087b5183c7b8a07b4fb16ae0682dd149a1719b3a28f54061754/yarl-1.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af4baa8a445977831cbaa91a9a84cc09debb10bc8391f128da2f7bd070fc351d", size = 353636 }, + { url = "https://files.pythonhosted.org/packages/98/9d/d9cb39ec68a91ba6e66fa86d97003f58570327d6713833edf7ad6ce9dde5/yarl-1.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123393db7420e71d6ce40d24885a9e65eb1edefc7a5228db2d62bcab3386a5c0", size = 348061 }, + { url = "https://files.pythonhosted.org/packages/72/6b/103940aae893d0cc770b4c36ce80e2ed86fcb863d48ea80a752b8bda9303/yarl-1.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab47acc9332f3de1b39e9b702d9c916af7f02656b2a86a474d9db4e53ef8fd7a", size = 334150 }, + { url = "https://files.pythonhosted.org/packages/ef/b2/986bd82aa222c3e6b211a69c9081ba46484cffa9fab2a5235e8d18ca7a27/yarl-1.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4a34c52ed158f89876cba9c600b2c964dfc1ca52ba7b3ab6deb722d1d8be6df2", size = 362207 }, + { url = "https://files.pythonhosted.org/packages/14/7c/63f5922437b873795d9422cbe7eb2509d4b540c37ae5548a4bb68fd2c546/yarl-1.20.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:04d8cfb12714158abf2618f792c77bc5c3d8c5f37353e79509608be4f18705c9", size = 361277 }, + { url = "https://files.pythonhosted.org/packages/81/83/450938cccf732466953406570bdb42c62b5ffb0ac7ac75a1f267773ab5c8/yarl-1.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7dc63ad0d541c38b6ae2255aaa794434293964677d5c1ec5d0116b0e308031f5", size = 364990 }, + { url = "https://files.pythonhosted.org/packages/b4/de/af47d3a47e4a833693b9ec8e87debb20f09d9fdc9139b207b09a3e6cbd5a/yarl-1.20.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d02b591a64e4e6ca18c5e3d925f11b559c763b950184a64cf47d74d7e41877", size = 374684 }, + { url = "https://files.pythonhosted.org/packages/62/0b/078bcc2d539f1faffdc7d32cb29a2d7caa65f1a6f7e40795d8485db21851/yarl-1.20.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95fc9876f917cac7f757df80a5dda9de59d423568460fe75d128c813b9af558e", size = 382599 }, + { url = "https://files.pythonhosted.org/packages/74/a9/4fdb1a7899f1fb47fd1371e7ba9e94bff73439ce87099d5dd26d285fffe0/yarl-1.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb769ae5760cd1c6a712135ee7915f9d43f11d9ef769cb3f75a23e398a92d384", size = 378573 }, + { url = "https://files.pythonhosted.org/packages/fd/be/29f5156b7a319e4d2e5b51ce622b4dfb3aa8d8204cd2a8a339340fbfad40/yarl-1.20.0-cp313-cp313-win32.whl", hash = "sha256:70e0c580a0292c7414a1cead1e076c9786f685c1fc4757573d2967689b370e62", size = 86051 }, + { url = "https://files.pythonhosted.org/packages/52/56/05fa52c32c301da77ec0b5f63d2d9605946fe29defacb2a7ebd473c23b81/yarl-1.20.0-cp313-cp313-win_amd64.whl", hash = "sha256:4c43030e4b0af775a85be1fa0433119b1565673266a70bf87ef68a9d5ba3174c", size = 92742 }, + { url = "https://files.pythonhosted.org/packages/d4/2f/422546794196519152fc2e2f475f0e1d4d094a11995c81a465faf5673ffd/yarl-1.20.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b6c4c3d0d6a0ae9b281e492b1465c72de433b782e6b5001c8e7249e085b69051", size = 163575 }, + { url = "https://files.pythonhosted.org/packages/90/fc/67c64ddab6c0b4a169d03c637fb2d2a212b536e1989dec8e7e2c92211b7f/yarl-1.20.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8681700f4e4df891eafa4f69a439a6e7d480d64e52bf460918f58e443bd3da7d", size = 106121 }, + { url = "https://files.pythonhosted.org/packages/6d/00/29366b9eba7b6f6baed7d749f12add209b987c4cfbfa418404dbadc0f97c/yarl-1.20.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:84aeb556cb06c00652dbf87c17838eb6d92cfd317799a8092cee0e570ee11229", size = 103815 }, + { url = "https://files.pythonhosted.org/packages/28/f4/a2a4c967c8323c03689383dff73396281ced3b35d0ed140580825c826af7/yarl-1.20.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f166eafa78810ddb383e930d62e623d288fb04ec566d1b4790099ae0f31485f1", size = 408231 }, + { url = "https://files.pythonhosted.org/packages/0f/a1/66f7ffc0915877d726b70cc7a896ac30b6ac5d1d2760613603b022173635/yarl-1.20.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5d3d6d14754aefc7a458261027a562f024d4f6b8a798adb472277f675857b1eb", size = 390221 }, + { url = "https://files.pythonhosted.org/packages/41/15/cc248f0504610283271615e85bf38bc014224122498c2016d13a3a1b8426/yarl-1.20.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a8f64df8ed5d04c51260dbae3cc82e5649834eebea9eadfd829837b8093eb00", size = 411400 }, + { url = "https://files.pythonhosted.org/packages/5c/af/f0823d7e092bfb97d24fce6c7269d67fcd1aefade97d0a8189c4452e4d5e/yarl-1.20.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d9949eaf05b4d30e93e4034a7790634bbb41b8be2d07edd26754f2e38e491de", size = 411714 }, + { url = "https://files.pythonhosted.org/packages/83/70/be418329eae64b9f1b20ecdaac75d53aef098797d4c2299d82ae6f8e4663/yarl-1.20.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c366b254082d21cc4f08f522ac201d0d83a8b8447ab562732931d31d80eb2a5", size = 404279 }, + { url = "https://files.pythonhosted.org/packages/19/f5/52e02f0075f65b4914eb890eea1ba97e6fd91dd821cc33a623aa707b2f67/yarl-1.20.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91bc450c80a2e9685b10e34e41aef3d44ddf99b3a498717938926d05ca493f6a", size = 384044 }, + { url = "https://files.pythonhosted.org/packages/6a/36/b0fa25226b03d3f769c68d46170b3e92b00ab3853d73127273ba22474697/yarl-1.20.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c2aa4387de4bc3a5fe158080757748d16567119bef215bec643716b4fbf53f9", size = 416236 }, + { url = "https://files.pythonhosted.org/packages/cb/3a/54c828dd35f6831dfdd5a79e6c6b4302ae2c5feca24232a83cb75132b205/yarl-1.20.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d2cbca6760a541189cf87ee54ff891e1d9ea6406079c66341008f7ef6ab61145", size = 402034 }, + { url = "https://files.pythonhosted.org/packages/10/97/c7bf5fba488f7e049f9ad69c1b8fdfe3daa2e8916b3d321aa049e361a55a/yarl-1.20.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:798a5074e656f06b9fad1a162be5a32da45237ce19d07884d0b67a0aa9d5fdda", size = 407943 }, + { url = "https://files.pythonhosted.org/packages/fd/a4/022d2555c1e8fcff08ad7f0f43e4df3aba34f135bff04dd35d5526ce54ab/yarl-1.20.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f106e75c454288472dbe615accef8248c686958c2e7dd3b8d8ee2669770d020f", size = 423058 }, + { url = "https://files.pythonhosted.org/packages/4c/f6/0873a05563e5df29ccf35345a6ae0ac9e66588b41fdb7043a65848f03139/yarl-1.20.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3b60a86551669c23dc5445010534d2c5d8a4e012163218fc9114e857c0586fdd", size = 423792 }, + { url = "https://files.pythonhosted.org/packages/9e/35/43fbbd082708fa42e923f314c24f8277a28483d219e049552e5007a9aaca/yarl-1.20.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e429857e341d5e8e15806118e0294f8073ba9c4580637e59ab7b238afca836f", size = 422242 }, + { url = "https://files.pythonhosted.org/packages/ed/f7/f0f2500cf0c469beb2050b522c7815c575811627e6d3eb9ec7550ddd0bfe/yarl-1.20.0-cp313-cp313t-win32.whl", hash = "sha256:65a4053580fe88a63e8e4056b427224cd01edfb5f951498bfefca4052f0ce0ac", size = 93816 }, + { url = "https://files.pythonhosted.org/packages/3f/93/f73b61353b2a699d489e782c3f5998b59f974ec3156a2050a52dfd7e8946/yarl-1.20.0-cp313-cp313t-win_amd64.whl", hash = "sha256:53b2da3a6ca0a541c1ae799c349788d480e5144cac47dba0266c7cb6c76151fe", size = 101093 }, + { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124 }, +] From f0f1eceb37943b2d3fa5455651d82fb20951b594 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 16:44:37 -0400 Subject: [PATCH 02/54] fix: ssl error causing download issues Fixed by setting env in activate, but implemented a don't check (unsafe) fix too --- electrolyte_fm/utils/cache.py | 9 ++++++--- opt/TokenizerStats/activate | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/electrolyte_fm/utils/cache.py b/electrolyte_fm/utils/cache.py index a45b2481..e7e2fb99 100644 --- a/electrolyte_fm/utils/cache.py +++ b/electrolyte_fm/utils/cache.py @@ -42,17 +42,20 @@ def cached_github_archive(repo, commit, file): return cached_path -def cached_download(url: str, path: Path) -> Path: +def cached_download(url: str, path: Path, disable_ssl=False) -> Path: path = Path(path) cache = Path(__file__).parent.parent.parent.joinpath(".cache") cached_file = cache.joinpath(path) cached_file.parent.mkdir(exist_ok=True, parents=True) if not cached_file.exists(): - import urllib + import ssl + import urllib.request + + ctx = ssl.create_default_context() if not disable_ssl else ssl._create_unverified_context() user_agent = "Wget/1.19.5" # Pretend to be wget req = urllib.request.Request(url, headers={"User-Agent": user_agent}) - with urllib.request.urlopen(req) as fid: + with urllib.request.urlopen(req, context=ctx) as fid: cached_file.parent.mkdir(parents=True, exist_ok=True) with open(cached_file, "wb") as out: out.write(fid.read()) diff --git a/opt/TokenizerStats/activate b/opt/TokenizerStats/activate index aaab6060..0bcdd953 100755 --- a/opt/TokenizerStats/activate +++ b/opt/TokenizerStats/activate @@ -6,6 +6,8 @@ DIR="$(git rev-parse --show-toplevel)/opt/TokenizerStats" if command -v module > /dev/null; then module purge module --ignore_cache load gcc python/3.11.5 openmpi/4.1.6 + export SSL_CERT_DIR=/etc/pki/tls/certs + export SSL_CERT_FILE=/etc/pki/tls/cert.pem fi # Activate virtual environment From cded7facabb97e117034fdd50bd401e521a2dcb3 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 16:45:31 -0400 Subject: [PATCH 03/54] fixes for running tokenizer stats with uv --- opt/TokenizerStats/pyproject.toml | 4 +- opt/TokenizerStats/src/cli.jl | 3 +- opt/TokenizerStats/src/collect.jl | 7 ++- opt/TokenizerStats/uv.lock | 85 +++++++++++++++++++++++++++---- 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/opt/TokenizerStats/pyproject.toml b/opt/TokenizerStats/pyproject.toml index 58faafa7..d2cc6c99 100644 --- a/opt/TokenizerStats/pyproject.toml +++ b/opt/TokenizerStats/pyproject.toml @@ -12,7 +12,9 @@ dependencies = [ "selfies == 2.1.2", "jsonnet ~= 0.20.0", "electrolyte-fm", + "smilespe==0.0.3", + "apetokenizer @ git+https://github.com/mikemayuare/apetokenizer.git@8b070d6278503bcf8ad2eedc1756ac34f4bbd981", ] [tool.uv.sources] -electrolyte-fm = { path = "../../" } +electrolyte-fm = { path = "../../", editable = true } diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index 13229b12..42633a4f 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -21,6 +21,7 @@ end @annotate function get_dataset(name_or_path, tokenizer, encoding) start = time() + tokenizer = isdir(tokenizer) ? realpath(tokenizer) : tokenizer if isdir(name_or_path) if "tmQM" in splitpath(name_or_path) dm = TokenizerStats.tmqm(name_or_path; tokenizer, encoding) @@ -117,7 +118,7 @@ end # Load the tokenizer tokenizer = args_cmd["tokenizer"] tokenizer_name = isdir(tokenizer) ? basename(tokenizer) : tokenizer - dm, dataset = get_dataset(args_cmd["dataset"], tokenizer, args_cmd["encoding"]) + dm = () -> first(get_dataset(args_cmd["dataset"], tokenizer, args_cmd["encoding"])) model_loss(dm, args_cmd["model"], args_cmd["output"]) elseif args["%COMMAND%"] == "merge" diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 3fd6d9c4..2249fd16 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -162,12 +162,17 @@ function tabulate_dataset(datamodule::Py, out_file::AbstractString; tokenizer_na return 0 end -function model_loss(datamodule::Py, ref_file::String, output::String) +function model_loss(constructor, ref_file::String, output::String) # Init MPI MPI.Init() comm = MPI.COMM_WORLD rank = MPI.Comm_rank(comm) size = MPI.Comm_size(comm) + @info "Rank $rank of $size is ready" now() + + # Create datamodule + datamodule = constructor() + MPI.Barrier(comm) @info "Rank $rank of $size is starting" now() # Load Reference Tokenizer / n-gram model diff --git a/opt/TokenizerStats/uv.lock b/opt/TokenizerStats/uv.lock index 6488ce45..36781d93 100644 --- a/opt/TokenizerStats/uv.lock +++ b/opt/TokenizerStats/uv.lock @@ -124,6 +124,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] +[[package]] +name = "apetokenizer" +version = "0.0.0" +source = { git = "https://github.com/mikemayuare/apetokenizer.git?rev=8b070d6278503bcf8ad2eedc1756ac34f4bbd981#8b070d6278503bcf8ad2eedc1756ac34f4bbd981" } + [[package]] name = "async-timeout" version = "5.0.1" @@ -332,7 +337,7 @@ wheels = [ [[package]] name = "electrolyte-fm" version = "0.1.0" -source = { virtual = "../../" } +source = { editable = "../../" } dependencies = [ { name = "datasets" }, { name = "deepspeed" }, @@ -385,6 +390,15 @@ dev = [ plotting = [{ name = "matplotlib", specifier = "~=3.9" }] tokenizers = [{ name = "smilespe", specifier = "==0.0.3" }] +[[package]] +name = "fastprogress" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/52/77c827febdf8aac3b0840ce0edb7c34cbd330fab9bbe032c2a79ec8f2d51/fastprogress-1.0.3.tar.gz", hash = "sha256:7a17d2b438890f838c048eefce32c4ded47197ecc8ea042cecc33d3deb8022f5", size = 14920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/8f/213223fdee199c55db81e2d0c669f30e8285c5be2526c4ed924de39247da/fastprogress-1.0.3-py3-none-any.whl", hash = "sha256:6dfea88f7a4717b0a8d6ee2048beae5dbed369f932a368c5dd9caff34796f7c5", size = 12744 }, +] + [[package]] name = "filelock" version = "3.18.0" @@ -502,6 +516,30 @@ http = [ { name = "aiohttp" }, ] +[[package]] +name = "gensim" +version = "4.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy" }, + { name = "smart-open" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/68/074333a52f6fa82402332054ca0dfa721f7dcfa7eace313f64cdb44bacde/gensim-4.3.2.tar.gz", hash = "sha256:99ac6af6ffd40682e70155ed9f92ecbf4384d59fb50af120d343ea5ee1b308ab", size = 23263982 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/21/21f993356303c4c92352d9e7c732f715e86e7b0bc04674be71bb1e9bb05b/gensim-4.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:31b3cb313939b6940ee21660177f6405e71b920da462dbf065b2458a24ab33e1", size = 24084903 }, + { url = "https://files.pythonhosted.org/packages/d7/84/ec2f74713475b57b7d882f5160b0b7c9df40ecc409d793e87656a6f3ada0/gensim-4.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67c41b15e19e4950f57124f633c45839b5c84268ffa58079c5b0c0f04d2a9cb9", size = 24035874 }, + { url = "https://files.pythonhosted.org/packages/59/84/ed371ab548a02e16f83669dee5337ad3917d3bf980878608956817e0534b/gensim-4.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9bf1a8ee2e8214499c517008a0fd175ce5c649954a88569358cfae6bfca42dc", size = 26398052 }, + { url = "https://files.pythonhosted.org/packages/e8/d9/104988573fd2c1acdc64e66883b35fb8ae559310d2d9f77db78bf7de9add/gensim-4.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e34ee6f8a318fbf0b65e6d39a985ecf9e9051febfd1221ae6255fff1972c547", size = 26506991 }, + { url = "https://files.pythonhosted.org/packages/ab/b0/d58dc405fd60ab546ca714321235dc2d455b2dc06bfb4fc1092940c749fc/gensim-4.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c46b7395dc57c83329932f3febed9660891fdcc75327d56f55000e3e08898983", size = 24012956 }, + { url = "https://files.pythonhosted.org/packages/99/f5/8d2cb0b2628bb6482baafbf0ff7262c11fc46e98b23ee79234828b927e8d/gensim-4.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a919493339cfad39d5e76768c1bc546cd507f715c5fca93165cc174a97657457", size = 24078719 }, + { url = "https://files.pythonhosted.org/packages/63/46/5feab9c524a380bfa9f9f1c0d065743280dca30b216ab4c7a231f22dbed7/gensim-4.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8dcd1419266bd563c371d25530f4dce3505fe78059b2c0c08724e4f9e5479b38", size = 24030236 }, + { url = "https://files.pythonhosted.org/packages/14/34/f1e056feda95330f7d8beef6771e3441ce0e8e2d1f55bf754b0b0594b234/gensim-4.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e8035ac3f54dca3a8ca56bec526ddfe5b23006e0134b7375ca5f5dbfaef70a", size = 26558413 }, + { url = "https://files.pythonhosted.org/packages/22/40/7d2cce3ad4ad5d02aa68e253e6ea5f0acc381f02f594e235fe00a274faff/gensim-4.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c3b537c1fd4699c8e6d59c3ffa2fdd9918cd4e5555bf5ee7c1fbedd89b2d643", size = 26662667 }, + { url = "https://files.pythonhosted.org/packages/ad/97/b8253236dfedb9094f4273393a3fd03997da81f27f15822e56128da894ae/gensim-4.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5a52001226f9e89f7833503f99c9b4fd028fdf837002f24cdc1bc3cf901a4003", size = 24010152 }, +] + [[package]] name = "gitdb" version = "4.0.12" @@ -1171,7 +1209,7 @@ name = "nvidia-cudnn-cu12" version = "9.5.1.17" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386 }, @@ -1182,7 +1220,7 @@ name = "nvidia-cufft-cu12" version = "11.3.0.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632 }, @@ -1211,9 +1249,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790 }, @@ -1225,7 +1263,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367 }, @@ -2173,6 +2211,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] +[[package]] +name = "smart-open" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/30/1f41c3d3b8cec82024b4b277bfd4e5b18b765ae7279eb9871fa25c503778/smart_open-7.1.0.tar.gz", hash = "sha256:a4f09f84f0f6d3637c6543aca7b5487438877a21360e7368ccf1f704789752ba", size = 72044 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/18/9a8d9f01957aa1f8bbc5676d54c2e33102d247e146c1a3679d3bd5cc2e3a/smart_open-7.1.0-py3-none-any.whl", hash = "sha256:4b8489bb6058196258bafe901730c7db0dcf4f083f316e97269c66f45502055b", size = 61746 }, +] + +[[package]] +name = "smilespe" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastprogress" }, + { name = "gensim" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/5c/a638fd96cdf4499eaed76d5dbcec734d98c4ddaf2a8f9e13e44e5151fa29/SmilesPE-0.0.3.tar.gz", hash = "sha256:7ceebc7d314e456a08f77d45f08fe4b638886901c0eac50f0cdb005b9f0912bc", size = 15625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/f9/273f54d9d4b42779926291c82a5b3ffea30cff2492ebbe4ce08dccdcc949/SmilesPE-0.0.3-py3-none-any.whl", hash = "sha256:9f74279daa14945859546fb2de11c208b5116927ce5fe03b3cf46bcba96f5e58", size = 15723 }, +] + [[package]] name = "smirk" version = "0.2.0" @@ -2286,18 +2349,22 @@ name = "tokenizerstats" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "apetokenizer" }, { name = "electrolyte-fm" }, { name = "jsonnet" }, { name = "mendeleev" }, { name = "selfies" }, + { name = "smilespe" }, ] [package.metadata] requires-dist = [ - { name = "electrolyte-fm", virtual = "../../" }, + { name = "apetokenizer", git = "https://github.com/mikemayuare/apetokenizer.git?rev=8b070d6278503bcf8ad2eedc1756ac34f4bbd981" }, + { name = "electrolyte-fm", editable = "../../" }, { name = "jsonnet", specifier = "~=0.20.0" }, { name = "mendeleev", specifier = ">=0.16" }, { name = "selfies", specifier = "==2.1.2" }, + { name = "smilespe", specifier = "==0.0.3" }, ] [[package]] @@ -2406,7 +2473,7 @@ name = "triton" version = "3.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "setuptools" }, + { name = "setuptools", marker = "sys_platform == 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/76/04/d54d3a6d077c646624dc9461b0059e23fd5d30e0dbe67471e3654aec81f9/triton-3.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fad99beafc860501d7fcc1fb7045d9496cbe2c882b1674640304949165a916e7", size = 156441993 }, From 8f7a8ba1aabf0a1ff40b900d672356c943557094 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 16:48:09 -0400 Subject: [PATCH 04/54] tweak: compute loss using finetuned n-gram model as well --- opt/TokenizerStats/submit_jobs.py | 97 +++++++++++++------------------ 1 file changed, 41 insertions(+), 56 deletions(-) diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 72f00461..185a4c84 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env -S uv run python import time import logging import subprocess @@ -278,12 +278,17 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles"): ) -def ngram_loss(dataset, tokenizer, slurm=None, encoding="smiles", ds_name=None): - input = STATS_DIR.joinpath(tokenizer, "realspace", "usage.jld2") +def ngram_loss(dataset, tokenizer, slurm=None, encoding="smiles", ngram="realspace", ds_name=None): + input = STATS_DIR.joinpath(tokenizer, ngram, "usage.jld2") ds_name = ds_name or str(dataset) - output = STATS_DIR.joinpath(tokenizer, ds_name, "model_loss.jld2") + outfile = "model_loss.jld2" if ngram == "realspace" else f"model_loss_{ngram}.jld2" + output = STATS_DIR.joinpath(tokenizer, ds_name, outfile) slurm = slurm or {} - slurm.setdefault("job-name", f"loss-{ds_name}") + if ngram == "realspace": + slurm.setdefault("job-name", f"loss-{ds_name}") + else: + slurm.setdefault("job-name", f"loss-{ds_name}-{ngram}") + slurm.setdefault("mem-per-cpu", "2G") slurm.setdefault("partition", "venkvis-cpu,venkvis-largemem") slurm.setdefault("ntasks", 4) @@ -373,12 +378,12 @@ def set_logging_level(verbosity): for tok in tokenizers: tok_name = tok["name_or_path"] - # # Tabulate OOVs - # output = STATS_DIR.joinpath(tok_name, "oov.json") - # p = wk.add_process( - # ["submit_oov.sh", "--output", output, tok_name], - # output=output, - # ) + # Tabulate OOVs + output = STATS_DIR.joinpath(tok_name, "oov.json") + p = wk.add_process( + ["submit_oov.sh", "--output", output, tok_name], + output=output, + ) # Tokenize RealSpace wk.add_process( @@ -401,65 +406,45 @@ def set_logging_level(verbosity): ) # Tokenize MoleculeNet - for ds in MOLNET_DATASETS: + for ds in [*MOLNET_DATASETS, "tmqm"]: + dataset = ds if ds != "tmqm" else args.tmqm + ds_name = None if ds != "tmqm" else "tmqm" wk.add_process( usage( - ds, + dataset, tok_name, + ds_name=ds_name, encoding=tok["encoding"], ) ) - wk.add_process( - ngram_loss( - ds, - tok_name, - encoding=tok["encoding"], + for ngram in ["realspace", ds]: + wk.add_process( + ngram_loss( + dataset, + tok_name, + encoding=tok["encoding"], + ngram=ngram, + ds_name=ds_name, + slurm={"time": "8:0:0" if ds == "tmqm" else "4:0:0"}, + ) ) - ) for ref in REF_INFO_LOSS: + if ds == "tmqm" and tok["encoding"] == "selfies": + continue # Majority of selfies fail + if tok["name_or_path"] == "ncfrey/ChemGPT-4.7M": continue # Token Alignment will fail wk.add_process( - ngram_info_loss(ds, tok_name, ref, encoding=tok["encoding"]) - ) - - wk.add_process( - usage( - args.tmqm, - tok_name, - ds_name="tmqm", - encoding=tok["encoding"], - slurm={"ntasks": 4, "time": "8:0:0", "job-name": "usage-tmqm"}, - ) - ) - wk.add_process( - ngram_loss( - args.tmqm, - tok_name, - ds_name="tmqm", - encoding=tok["encoding"], - slurm={"ntasks": 4, "time": "8:0:0", "job-name": "loss-tmqm"}, - ) - ) - for ref in REF_INFO_LOSS: - if tok["encoding"] == "selfies": - continue - - if tok["name_or_path"] == "ncfrey/ChemGPT-4.7M": - continue # Token Alignment will fail - - wk.add_process( - ngram_info_loss( - args.tmqm, - tok_name, - ref, - ds_name="tmqm", - encoding=tok["encoding"], - slurm={"job-name": "dist-tmqm"}, + ngram_info_loss( + dataset, + tok_name, + ref, + ds_name=ds_name, + encoding=tok["encoding"], + ) ) - ) wk.show() wk.run(dry_run=args.dry_run) From 4046bad97cf4a0e058b7db579ef30c79ee2b5c9b Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 16:48:42 -0400 Subject: [PATCH 05/54] fix: correct encoding for ChemGPT --- opt/TokenizerStats/tokenizers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/tokenizers.json b/opt/TokenizerStats/tokenizers.json index db8ca276..f358ae19 100644 --- a/opt/TokenizerStats/tokenizers.json +++ b/opt/TokenizerStats/tokenizers.json @@ -240,7 +240,7 @@ { "name": "ChemGPT", "name_or_path": "ncfrey/ChemGPT-4.7M", - "encoding": "smiles", + "encoding": "selfies", "tokenizer_class": "bpe", "domain": "chemistry", "source": "huggingface", From f03670745ce9b71d09aba6684a331eb941d2e402 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 13:55:13 -0700 Subject: [PATCH 06/54] Summarize results with fixed effects --- opt/TokenizerStats/plots/Project.toml | 1 + .../plots/src/tabulate_results.jl | 180 +++++++++++++++--- .../plots/src/tabulate_tokenizer.jl | 29 +++ 3 files changed, 186 insertions(+), 24 deletions(-) diff --git a/opt/TokenizerStats/plots/Project.toml b/opt/TokenizerStats/plots/Project.toml index 85f022ee..34b502f2 100644 --- a/opt/TokenizerStats/plots/Project.toml +++ b/opt/TokenizerStats/plots/Project.toml @@ -19,6 +19,7 @@ OnlineStats = "a15396b6-48d5-5d58-9928-6d29437db91e" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Preferences = "21216c6a-2e73-6563-6e65-726566657250" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" +RegressionTables = "d519eb52-b820-54da-95a6-98e1306fdade" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" TokenizerStats = "ca7eb560-02c0-4cfb-b1b2-ae9b1b03b10c" diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index dc1eddb4..f5ffe395 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -136,7 +136,7 @@ end function task_metrics(task, metrics; dataset=nothing) if task == "regression" - metric = "r2" + metric = dataset in ["esol", "freesolv", "lipo", "rmse"] ? "rmse" : "mae" elseif task == "binary" metric = dataset == "muv" ? "avg-precision" : "auroc" else @@ -153,7 +153,7 @@ end function get_metric(metrics::Vector, name::String; split, tok_group="all", type="best") for metric in metrics - if metric["metric"] == name && metric["split"] == split && metric["tok_group"] == tok_group && metric["type"] == type + if metric["metric"] == name && metric["split"] == split && coalesce(metric["tok_group"] == tok_group, false) && coalesce(metric["type"] == type, false) return metric["value"] end end @@ -214,31 +214,39 @@ function link_training_runs(runs) return linked end -function df_ngrams_vs_transformer(stats_dir, loss_stats, dfp) +function df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) tokenizers = tokenizers_info(stats_dir) - val_loss = subset(loss_stats, + df_ng = subset(loss_stats, :split => ByRow(==("val")), - :dataset => ByRow(==("realspace")), + :ngram => ByRow(==(5)), :tokenizer => ByRow(x -> haskey(tokenizers, x)), ) - best_models = combine(groupby(val_loss, :tokenizer)) do gdf - sort!(gdf, :avg_model_loss; rev=false) - return gdf[1, :] - end - select!(best_models, :tokenizer, :ngram, :avg_model_loss => :ngram_loss, :avg_model_token_loss => :ngram_token_loss) - - # Merge with pretraining data - dfp = leftjoin(dfp, best_models, on=[:tokenizer]) - sort!(dfp, :val_loss) - dfp.tokenizer = categorical(dfp.tokenizer, levels=unique(dfp.tokenizer)) - replace!(dfp.encoding, - "smiles" => "SMILES", - "smiles-canonical" => "Canonical SMILES", - "selfies" => "SELFIES" + transform!(df_ng, + :tokenizer => ByRow(x -> tokenizers[x]["tokenizer_class"]) => :tokenizer_class, + :tokenizer => ByRow(x -> tokenizers[x]["encoding"]) => :encoding, + ) + select!(df_ng, :tokenizer, :tokenizer_class, :dataset, :encoding, :samples, :loss_per_token_moments) + + # Combine Molecular Foundation Models + dfp = select(dfp, :id => :pretrained_id, :id, :tokenizer, :encoding, :tokenizer_class, :val_loss, :train_loss) + dfp.task .= "mlm" + dfp.metric .= "cross-entropy" + dff = subset(dff, + :frozen => ByRow(!), + [:encoding, :pretrained_encoding] => ByRow(==), + :dataset => ByRow(!=("muv")), ) - dfp.encoding = categorical(dfp.encoding, levels=["SMILES", "Canonical SMILES", "SELFIES"]) + select!(dff, Not([:frozen, :train_oov_loss, :val_oov_loss, :train_loss, :val_loss, :step])) + select!(dff, Not([:pretrained_encoding])) + + # Get preferred MoleculeNet Metrics + dft = subset(dft, :tok_group => ByRow(==("all")), :channel => ByRow(isnothing)) + select!(dft, :ckpt_id, :metric, :mean, :std) + leftjoin!(dff, dft; on=[:id => :ckpt_id, :metric]) + dropmissing!(dff) + - return dfp, best_models + return df_ng, dfp, dff end struct LogLikelihoodRatioTest @@ -260,21 +268,145 @@ function Base.show(io::IO, mime::MIME"text/plain", lrt::LogLikelihoodRatioTest) println(io, "p-value: $(round(pvalue(lrt); sigdigits=3))") end -function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp) - df, best_models = df_ngrams_vs_transformer(stats_dir, loss_stats, dfp) +nobs_nonwts(model) = size(model.model.pp.X, 1) +zscore(x) = (x .- mean(x)) ./ std(x) + +function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) + df_ng, df_p, df_f = df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) contrasts = Dict( :tokenizer_class => EffectsCoding(; base="atomwise"), - :encoding => EffectsCoding(; base="SMILES"), + :encoding => EffectsCoding(; base="smiles"), ) + # Pretraining models + models = [] + df_ng.val_loss = mean.(df_ng.loss_per_token_moments) + df_ng.wts = inv.(var.(df_ng.loss_per_token_moments)) + # subset!(df_ng, :tokenizer_class => ByRow(in(unique(df_f.tokenizer_class)))) + df_ng_pt = subset(df_ng, :dataset => ByRow(==("realspace"))) + model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_ng_pt; + contrasts, + wts=aweights(df_ng_pt.wts), + ) + df_ng_pt.ng_est_loss = predict(model) + df_predict = leftjoin( + select(df_ng_pt, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), + select(df_p, :tokenizer, :val_loss => :fm_loss); + on=:tokenizer, + ) + dropmissing!(df_predict) + rho = corspearman(df_predict.ng_loss, df_predict.fm_loss) + rho_est = corspearman(df_predict.ng_est_loss, df_predict.fm_loss) + + + push!(models, (; model, dataset = "REALSpace", ngram=true, metric="CE", rho, rho_est)) + model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_p; contrasts) + push!(models, (; model, dataset = "REALSpace", ngram=false, metric="CE")) + + ft_ng = Dict{String,Any}() + ft_fm = Dict{String,Any}() + subset!(df_f, :dataset => ByRow(!=("freesolv"))) # fit issue + for dataset in unique(df_f.dataset) + df_f_fm = subset(df_f, :dataset => ByRow(==(dataset))) + df_f_ng = subset(df_ng, :dataset => ByRow(==(dataset))) + task = first(df_f_fm.task) + link = task == "regression" ? LogLink() : LogitLink() + + # Foundation Model + df_f_fm.val_loss = zscore(df_f_fm.mean) + # df_f_fm.wts = inv.(df_f_fm.std .^2) + model = lm( + @formula(val_loss ~ 1 + tokenizer_class + encoding), df_f_fm; + contrasts, + ) + push!(models, (; model, dataset, ngram=false, metric=first(df_f_fm.metric))) + + # NGram + df_f_ng.val_loss = zscore(mean.(df_f_ng.loss_per_token_moments)) + df_f_ng.wts = inv.(var.(df_f_ng.loss_per_token_moments)) + model = lm( + @formula(val_loss ~ 1 + tokenizer_class + encoding), df_f_ng; + contrasts, + wts=aweights(df_f_ng.wts), + ) + + # Spearman's for n-gram fit vs. fm results + df_f_ng.ng_est_loss = predict(model) + df_predict = leftjoin( + select(df_f_ng, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), + select(df_f_fm, :tokenizer, :val_loss => :fm_loss); + on=:tokenizer, + ) + dropmissing!(df_predict) + rho = corspearman(df_predict.ng_loss, df_predict.fm_loss) + rho_est = corspearman(df_predict.ng_est_loss, df_predict.fm_loss) + + push!(models, (; model, dataset, ngram=true, metric="CE", rho, rho_est)) + end + + models = map(models) do m + haskey(m, :rho) ? m : (; m..., rho=missing, rho_est=missing) + end + @show models = DataFrame(models) + + tbl_datasets = ["REALSpace", "tmQM", "qm9", "bbbp", "hiv"] + models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) + models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) + sort!(models_tbl, [:dataset, :ngram]) + transform!(models_tbl, + :metric => ByRow(m -> replace(m, + "auroc" => L"AUROC ($\uparrow$)", + "mae" => L"MAE ($\downarrow$)", + "CE" => L"CE ($\downarrow$)", + )) => :metric + ) + + return regtable( + models_tbl.model...; + groups=string.(models_tbl.dataset), + stat_below=false, + print_depvar = false, + number_regressions = false, + align=:r, + regression_statistics=[ + nobs_nonwts => "N", + (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", + (model -> cor(response(model), predict(model))) => L"R^2", + + ], + extralines=[ + ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], + ["Metric:", models_tbl.metric...], + [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], + ], + labels=Dict( + "tokenizer_class: spe" => "SPE/APE Tokenizers", + "tokenizer_class: bpe" => "BPE Tokenizers", + "tokenizer_class: smirk" => "Smirk", + "tokenizer_class: smirk-gpe" => "Smirk-GPE", + "tokenizer_class: character" => "Character-level", + "tokenizer_class: unigram" => "Unigram Tokenizers", + "encoding: selfies" => "SELFIES", + "encoding: smiles-canonical" => "Canonical SMILES", + ), + # render=LatexTable(), + ) + + + # Test impact of encoding on pretraining null = lm(@formula(val_loss ~ 1), df) @show tok = lm( @formula(val_loss ~ 1 + tokenizer_class + encoding), df; contrasts ) + @show tok_ngram = lm( + @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), + df; contrasts + ) LogLikelihoodRatioTest(tok, null) |> display + LogLikelihoodRatioTest(tok_ngram, null) |> display # Test impact of encoding on pretraining tokenizers = tokenizers_info(stats_dir) diff --git a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl index 2232b497..3eadf63c 100644 --- a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl +++ b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl @@ -6,12 +6,24 @@ function usage_stats(stats_dir) tokenizer = joinpath(splitpath(file)[1:end-2]) dataset = splitpath(file)[end-1] dataset = dataset == "tmqm" ? "tmQM" : dataset + unk_id = data["tokenizer"][:unk_token_id] + for split in ["train", "val", "test"] split ∉ keys(data) && continue Set(keys(data[split])) >= Set(["samples", "out_of_vocab", "fertility"]) || continue samples = data[split]["samples"] fertility = CountMap(data[split]["fertility"], samples) + unigram = data[split]["ngrams"]["1"] + unk_count = get(unigram, (unk_id,), 0) + tokens_seen = sum(values(unigram)) nunique = CountMap(data[split]["fertility"], samples) + + entropy = sum(values(unigram)) do c + p = c / tokens_seen + -p * log(p) + end + normalized_entropy = entropy / log(data["tokenizer"][:vocab_size]) + push!(rows, (; file, tokenizer, @@ -20,7 +32,11 @@ function usage_stats(stats_dir) samples, out_of_vocab=data[split]["out_of_vocab"], fertility, + entropy, + normalized_entropy, nunique, + unk_count, + tokens_seen, avg_fertility=mean(fertility), std_fertility=std(fertility), max_fertility=maximum(keys(data[split]["fertility"])), @@ -139,3 +155,16 @@ function find_oov_samples(results::Dict) sort!(df, :ntokenizers; rev=true) return df end + +function fertility_summary(df_tokenizer, df_usage) + df_usage = transform(df_usage, + :dataset => ByRow(x -> lowercase(x) in ["tmqm", "realspace"] ? x : "MoleculeNet") => :dataset + ) + df_usage = combine(groupby(df_usage, [:tokenizer, :dataset, :encoding])) do gdf + return (; + loss_per_token_moments=reduce(merge, gdf.loss_per_token_moments), + loss_moments=reduce(merge, gdf.loss_per_token_moments), + ) + end + return df_usage +end From d6fbae06b2de854f929aa9d98476d1bfff429d51 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Tue, 6 May 2025 13:55:43 -0700 Subject: [PATCH 07/54] move plot code to a script --- opt/TokenizerStats/plots/plots.jl | 131 ++++++++++++++++++ .../plots/src/SmirkPaperPlots.jl | 52 +------ opt/TokenizerStats/submit_plots.sh | 2 +- 3 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 opt/TokenizerStats/plots/plots.jl diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl new file mode 100644 index 00000000..30c95ac8 --- /dev/null +++ b/opt/TokenizerStats/plots/plots.jl @@ -0,0 +1,131 @@ +using DataFrames +using CairoMakie +using CSV: CSV +using SmirkPaperPlots +using TokenizerStats +using SmirkPaperPlots: savefig +using StatsBase +using Format + +stats_dir=joinpath(pkgdir(TokenizerStats), "stats") + +# Tokenizer Statistics +loss_stats = SmirkPaperPlots.model_loss_stats(stats_dir) +info_loss = SmirkPaperPlots.info_loss_stats(stats_dir) +token_usage = SmirkPaperPlots.usage_stats(stats_dir) + +# Tokenizer Fertility by Dataset +function fertility_summary(token_usage) + tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) + token_usage = transform(token_usage, + :dataset => ByRow(x -> x ∉ ["tmQM", "realspace"] ? "MoleculeNet" : x) => :dataset, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + ) + combine(groupby(df_usage_summary, [:tokenizer_class, :dataset])) do gdf + dataset = first(gdf.dataset) + fertility = reduce(merge, gdf.fertility) + avg_fertility = mean(fertility) + std_fertility = std(fertility) + return (; + dataset = lowercase(dataset) in ("tmqm", "realspace") ? dataset : "MoleculeNet", + fertility, + avg_fertility, + std_fertility, + fmt_fertility = format("{}\\pm{}", round(avg_fertility; sigdigits=3), round(std_fertility; sigdigits=3)), + ) + end +end +fertility_summary(token_usage) |> display + +# Frequency of Unknown Tokens +function unk_freq(token_usage) + tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) + token_usage = transform(token_usage, + :dataset => ByRow(x -> x ∉ ["tmQM", "realspace"] ? "MoleculeNet" : x) => :dataset, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + ) + combine(groupby(token_usage, [:tokenizer, :dataset])) do gdf + unk_count = sum(gdf.unk_count) + tokens_seen = sum(gdf.tokens_seen) + unk_freq = unk_count / tokens_seen + return (; + unk_freq, + fmt_unk_freq = format("{:.2f}%", round(100*unk_freq; sigdigits=3)), + ) + end +end +unk_freq(token_usage) |> display + +# Normalized Entropy +function norm_entropy_summary(token_usage) + token_usage = subset(token_usage, + :dataset => ByRow(==("realspace")), + ) + # Combine splits + df = combine(groupby(token_usage, :tokenizer)) do gdf + return (; normalized_entropy = mean(gdf.normalized_entropy, fweights(gdf.tokens_seen))) + end + df = transform(df, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + :tokenizer => ByRow(x -> tok_info[x]["domain"]) => :domain, + ) + # subset!(df, :domain => ByRow(==("chemistry"))) + df.tokenizer_class .= replace.(df.tokenizer_class, "smirk-gpe" => "smirk") + # Combine over tokenizer classes + df = combine(groupby(df, [:tokenizer_class, :domain])) do gdf + return(; + norm_entropy_avg = mean(gdf.normalized_entropy), + norm_entropy_std = std(gdf.normalized_entropy), + norm_entropy_n = nrow(gdf), + ) + end + sort!(df, [:norm_entropy_avg, :norm_entropy_std, :norm_entropy_n]) + return df +end +norm_entropy_summary(token_usage) |> display + + +# Transformer Models +dfp, dff, dft = SmirkPaperPlots.transformer_models(stats_dir) + +# Tokenizer Summary +df_tok = SmirkPaperPlots.tokenizer_summary(stats_dir; k=5) + +# Tokenizer Summary +SmirkPaperPlots.report_tokenizer_summary_stats(stats_dir, loss_stats, info_loss, token_usage; k=3) + +with_theme(SmirkPaperPlots.theme()) do + # Token Usage + savefig("token_usage", SmirkPaperPlots.figure_token_usage(stats_dir)) + savefig("oov_rate", SmirkPaperPlots.figure_oov_rate(stats_dir)) + savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) + + # Transformers vs. N-Grams + df = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp) + savefig("ngram_vs_transformer", SmirkPaperPlots.figure_ngram_vs_transformer(stats_dir, df)) + + # Transformer Model Summary + f, df = SmirkPaperPlots.figure_tf_finetune(stats_dir, dff, dft) + savefig("tf_finetune", f) + CSV.write(joinpath("stats/tf_model_summary.csv"), df) + + # N-Gram Analysis + savefig("ngram_fits", SmirkPaperPlots.figure_ngram_fits(loss_stats, stats_dir)) + savefig("kl_v_info_loss", SmirkPaperPlots.figure_kl_v_info_loss(stats_dir, loss_stats, info_loss, df)) + + # Example n-gram predictions + compunds = [ + "caffeine" => "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", + "lsd" => "CCN(CC)C(=O)[C@H]1CN([C@@H]2Cc3c[nH]c4c3c(ccc4)C2=C1)C", + "cortisol" => "O=C4\\C=C2/[C@]([C@H]1[C@@H](O)C[C@@]3([C@@](O)(C(=O)CO)CC[C@H]3[C@@H]1CC2)C)(C)CC4", + ] + for (name, smi) in compunds + for direction in [:forward, :bidirectional] + savefig( + "log_prob_$(direction)_$name", + SmirkPaperPlots.figure_ngram_prediction(smi, stats_dir; direction) + ) + end + end +end + diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index 194909f0..e1284549 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -7,9 +7,10 @@ using GLM using DataFrames using CairoMakie: CairoMakie using Colors: distinguishable_colors, weighted_color_mean, RGBA +using CategoricalArrays: categorical using Format: format using PythonCall: Py, pyconvert -using StatsBase: StatsBase, mean, stderr, mean_and_std, quantile, AbstractWeights, Weights +using StatsBase: StatsBase, mean, stderr, mean_and_std, quantile, AbstractWeights, Weights, aweights, corspearman, cor using FreeTypeAbstraction: FreeTypeAbstraction, newface, FTFont using JLD2: jldopen using JSON: JSON @@ -19,6 +20,7 @@ using Distributions: Chisq, FDist, Normal using OrderedCollections: OrderedDict using CSV: CSV using Clustering: hclust +using RegressionTables: regtable, LatexTable using TokenizerStats using TokenizerStats: load_tokenizer, find @@ -73,51 +75,3 @@ function savefig(name::String, f::Figure; dpi=300) return nothing end -function (@main)(stats_dir=joinpath(pkgdir(TokenizerStats), "stats")) - CairoMakie.activate!() - - # Tokenizer Statistics - loss_stats = model_loss_stats(stats_dir) - info_loss = info_loss_stats(stats_dir) - token_usage = usage_stats(stats_dir) - - # Transformer Models - dfp, dff, dft = transformer_models(stats_dir) - - # Tokenizer Summary - report_tokenizer_summary_stats(stats_dir, loss_stats, info_loss, token_usage; k=3) - - with_theme(theme()) do - # Token Usage - savefig("token_usage", figure_token_usage(stats_dir)) - savefig("oov_rate", figure_oov_rate(stats_dir)) - savefig("jaccard", figure_jaccard(stats_dir)) - - # Transformers vs. N-Grams - df = ngram_vs_transformer_fits(stats_dir, loss_stats, dfp) - savefig("ngram_vs_transformer", figure_ngram_vs_transformer(stats_dir, df)) - - # Transformer Model Summary - f, df = figure_tf_finetune(stats_dir, dff, dft) - savefig("tf_finetune", f) - CSV.write(joinpath("stats/tf_model_summary.csv"), df) - - # N-Gram Analysis - savefig("ngram_fits", figure_ngram_fits(loss_stats, stats_dir)) - savefig("kl_v_info_loss", figure_kl_v_info_loss(stats_dir, loss_stats, info_loss, df)) - - # Example n-gram predictions - compunds = [ - "caffeine" => "CN1C=NC2=C1C(=O)N(C(=O)N2C)C", - "lsd" => "CCN(CC)C(=O)[C@H]1CN([C@@H]2Cc3c[nH]c4c3c(ccc4)C2=C1)C", - "cortisol" => "O=C4\\C=C2/[C@]([C@H]1[C@@H](O)C[C@@]3([C@@](O)(C(=O)CO)CC[C@H]3[C@@H]1CC2)C)(C)CC4", - ] - for (name, smi) in compunds - for direction in [:forward, :bidirectional] - savefig("log_prob_$(direction)_$name", figure_ngram_prediction(smi, stats_dir; direction)) - end - end - end -end - -end diff --git a/opt/TokenizerStats/submit_plots.sh b/opt/TokenizerStats/submit_plots.sh index ed25e4be..4f06f367 100755 --- a/opt/TokenizerStats/submit_plots.sh +++ b/opt/TokenizerStats/submit_plots.sh @@ -13,4 +13,4 @@ cd "$(git rev-parse --show-toplevel)/opt/TokenizerStats" source ./activate env -julia --color=no --startup-file=no --project=plots -e 'using SmirkPaperPlots; SmirkPaperPlots.main()' +julia --color=no --startup-file=no --project=plots ./plots/plots.jl From 9481dde0ad1ba498301132c3abc917283a7378c6 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 7 May 2025 09:49:39 -0400 Subject: [PATCH 08/54] add script for finding ambiguous smiles --- opt/TokenizerStats/check_ambi_smiles.py | 296 ++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100755 opt/TokenizerStats/check_ambi_smiles.py diff --git a/opt/TokenizerStats/check_ambi_smiles.py b/opt/TokenizerStats/check_ambi_smiles.py new file mode 100755 index 00000000..92661a36 --- /dev/null +++ b/opt/TokenizerStats/check_ambi_smiles.py @@ -0,0 +1,296 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "pandas", +# "rdkit", +# "typer", +# ] +# /// + +# python +import re +import csv +from pathlib import Path +import pandas as pd +from rdkit import Chem +import typer + +AMBIGUOUS_PAIRS = ["Sn", "Sb", "Co", "Os", "Po", "Sc", "Cs", "Cn"] + +app = typer.Typer() + + +def canonicalize_smiles(smiles: str): + """Return canonical SMILES or None if invalid.""" + mol = Chem.MolFromSmiles(smiles) + if mol: + return Chem.MolToSmiles(mol, canonical=True) + return None + + +def count_ambiguous_substrings(smiles: str): + """Count occurrences of each ambiguous substring in a SMILES string.""" + smiles = re.sub(r"\[[^\]]+]", "", smiles) # Ignore bracketed atoms + return {pair: len(re.findall(pair, smiles)) for pair in AMBIGUOUS_PAIRS} + + +def get_chunks(input_file, chunk_size): + """Read CSV file dynamically handling .csv and .csv.gz extensions in chunks.""" + if input_file.suffix == ".gz": + return pd.read_csv(input_file, compression="gzip", chunksize=chunk_size) + return pd.read_csv(input_file, chunksize=chunk_size) + + +def write_header(output_file, ambiguity_keys): + """Write the header to the output CSV file.""" + header = ["name", "cid", "smiles", "total_ambiguity_count"] + ambiguity_keys + with open(output_file, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(header) + + +def append_to_csv(output_file, row): + """Append a row to the output CSV file.""" + with open(output_file, "a", newline="") as f: + writer = csv.DictWriter(f, fieldnames=row.keys()) + writer.writerow(row) + + +def process_chunk(chunk, output_file): + """Process a single chunk and append results incrementally to the output file.""" + for _, row in chunk.iterrows(): + original = row["SMILES"] + canonical = canonicalize_smiles(original) + if canonical: + ambiguity_counts = count_ambiguous_substrings(canonical) + total_count = sum(ambiguity_counts.values()) + if total_count > 0: + output_row = { + "name": row["Name"], # Include Name column + "cid": row["Compound_CID"], # Include Compound_CID column + "smiles": canonical, # Canonical SMILES column + "total_ambiguity_count": total_count, # Total count column + } + output_row.update(ambiguity_counts) # Add individual counts + append_to_csv(output_file, output_row) + + +@app.command("filter") +def process_smiles( + input_file: str, + chunk_size: int = typer.Option(10000, help="Number of rows to process per chunk."), +): + """ + Process SMILES strings in the input file to identify ambiguities and write results incrementally. + """ + input_path = Path(input_file) + output_file = input_path.with_name(input_path.stem + "_ambiguous.csv") + + # Initialize output file with header + ambiguity_keys = list( + count_ambiguous_substrings("").keys() + ) # Keys for ambiguous pairs + header = [ + "name", + "cid", + "smiles", + "InChI", + "IUPAC_Name", + "total_ambiguity_count", + ] + ambiguity_keys + with open(output_file, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(header) + + total_molecules_checked = 0 # Counter for molecules + chunks = get_chunks(input_path, chunk_size) + for chunk in chunks: + if not {"SMILES", "Name", "Compound_CID", "InChI", "IUPAC_Name"}.issubset( + chunk.columns + ): + typer.echo( + "CSV must contain 'SMILES', 'Name', 'Compound_CID', 'InChI', and 'IUPAC_Name' columns.", + err=True, + ) + raise typer.Exit() + + for _, row in chunk.iterrows(): + total_molecules_checked += 1 # Increment counter + original = row["SMILES"] + canonical = canonicalize_smiles(original) + if canonical: + ambiguity_counts = count_ambiguous_substrings(canonical) + total_count = sum(ambiguity_counts.values()) + if total_count > 0: + output_row = { + "name": row["Name"], # Include Name column + "cid": row["Compound_CID"], # Include Compound_CID column + "smiles": canonical, # Canonical SMILES column + "InChI": row["InChI"], # InChI source column + "IUPAC_Name": row["IUPAC_Name"], # IUPAC Name column + "total_ambiguity_count": total_count, # Total count column + } + output_row.update(ambiguity_counts) # Add individual counts + with open(output_file, "a", newline="") as f: + writer = csv.DictWriter(f, fieldnames=output_row.keys()) + writer.writerow(output_row) + + # Print total molecules checked to stderr + typer.echo(f"Total molecules checked: {total_molecules_checked}", err=True) + typer.echo(f"Results written incrementally to: {output_file}") + + +@app.command("merge") +def merge_ambiguous_files( + output_file: str, + input_files: list[str] = typer.Argument( + ..., help="List of ambiguous.csv files to merge." + ), +): + """ + Merge multiple ambiguous.csv files and deduplicate using InChIKey. + """ + combined_df = pd.DataFrame() + for file in input_files: + input_path = Path(file) + if not input_path.exists(): + typer.echo(f"File {file} does not exist.") + raise typer.Exit() + + typer.echo(f"Reading file: {file}") + df = pd.read_csv(file) + + if "InChI" not in df or "smiles" not in df: + typer.echo(f"File {file} must contain 'InChI' and 'smiles' columns.") + raise typer.Exit() + + combined_df = pd.concat([combined_df, df], ignore_index=True) + + # Deduplicate using InChIKey + if "InChI" in combined_df.columns: + combined_df = combined_df.drop_duplicates(subset=["InChI"]) + + typer.echo(f"Merged and deduplicated {len(combined_df)} rows based on InChIKey.") + + # Write to output file + output_path = Path(output_file) + combined_df.to_csv(output_path, index=False) + typer.echo(f"Final merged file written to: {output_path}") + + +@app.command("stats") +def tabulate_ambiguities( + output_file: str, + remove_nonbond: bool = True, + ignore_cn: bool = False, +): + """ + Tabulate the total counts of each ambiguity type and display shortest 5 examples of each. + Optionally, remove SMILES strings containing a period ("."), and ignore "Cn" in the distinct ambiguities calculation. + """ + output_path = Path(output_file) + if not output_path.exists(): + typer.echo(f"File {output_file} does not exist.") + raise typer.Exit() + + # Read the output file + df = pd.read_csv(output_file) + + # Deduplicate on the `smiles` column + df = df.drop_duplicates(subset=["smiles"]) + + # Optionally remove rows where `smiles` contains a period (".") + if remove_nonbond: + df = df[~df["smiles"].str.contains(r"\.", na=False)] + typer.echo("Removed SMILES containing a nonbond ('.').") + + # Ensure required ambiguity columns exist + ambiguity_keys = [key for key in AMBIGUOUS_PAIRS if key in df.columns] + + if not ambiguity_keys: + typer.echo("No ambiguity type columns found in the file.") + raise typer.Exit() + + if ignore_cn and "Cn" in ambiguity_keys: + ambiguity_keys.remove("Cn") + typer.echo("Ignoring 'Cn' in distinct ambiguities calculation.") + + # Calculate totals for each ambiguity type + ambiguity_totals = df[ambiguity_keys].sum().sort_values(ascending=False) + + # Display results in tabular form + typer.echo("\nTabulated Ambiguities:") + typer.echo("-" * 40) + for ambiguity, count in ambiguity_totals.items(): + typer.echo(f"{ambiguity}: {count}") + + # Extract shortest 5 SMILES examples for this ambiguity type + filtered_df = df[df[ambiguity] > 0] + shortest_examples = filtered_df.assign( + smiles_length=filtered_df["smiles"].str.len() + ).nsmallest(5, "smiles_length") + typer.echo(f"Shortest 5 examples for {ambiguity}:") + for _, row in shortest_examples.iterrows(): + typer.echo(f"\t{row['name']}: {row['smiles']}") + typer.echo("-" * 40) + + # Calculate the number of distinct ambiguity types per molecule + df["distinct_ambiguities"] = df[ambiguity_keys].gt(0).sum(axis=1) + + # Sort by number of distinct ambiguities and then by SMILES length + df = df.assign(smiles_length=df["smiles"].str.len()) + top_distinct = df.sort_values( + by=["distinct_ambiguities", "smiles_length"], ascending=[False, True] + ).head(5) + + # Print top 5 molecules with the greatest number of distinct ambiguities + typer.echo("\nTop 5 molecules with the greatest number of distinct ambiguities:") + typer.echo("-" * 40) + for _, row in top_distinct.iterrows(): + typer.echo( + f"{row['name']} (CID: {row['cid']}): {row['smiles']} - {row['distinct_ambiguities']} distinct ambiguities" + ) + typer.echo("-" * 40) + + +@app.command("merge") +def merge_ambiguous_files( + output_file: str, + input_files: list[str] = typer.Argument( + ..., help="List of ambiguous.csv files to merge." + ), +): + """ + Merge multiple ambiguous.csv files and deduplicate using InChIKey. + """ + combined_df = pd.DataFrame() + for file in input_files: + input_path = Path(file) + if not input_path.exists(): + typer.echo(f"File {file} does not exist.") + raise typer.Exit() + + typer.echo(f"Reading file: {file}") + df = pd.read_csv(file) + + if "InChI" not in df or "smiles" not in df: + typer.echo(f"File {file} must contain 'InChI' and 'smiles' columns.") + raise typer.Exit() + + combined_df = pd.concat([combined_df, df], ignore_index=True) + + # Deduplicate using InChIKey + if "InChI" in combined_df.columns: + combined_df = combined_df.drop_duplicates(subset=["InChI"]) + + typer.echo(f"Merged and deduplicated {len(combined_df)} rows based on InChIKey.") + + # Write to output file + output_path = Path(output_file) + combined_df.to_csv(output_path, index=False) + typer.echo(f"Final merged file written to: {output_path}") + + +if __name__ == "__main__": + app() From 93a5527879bcf6db51c84b3103ce928bba46c4d9 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 7 May 2025 09:43:14 -0400 Subject: [PATCH 09/54] expand fe models to cover finetuned models --- opt/TokenizerStats/plots/plots.jl | 27 ++++-- .../plots/src/SmirkPaperPlots.jl | 1 + .../plots/src/tabulate_results.jl | 96 ++++++++++--------- .../plots/src/tabulate_tokenizer.jl | 4 +- 4 files changed, 74 insertions(+), 54 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 30c95ac8..13f262c5 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -5,9 +5,10 @@ using SmirkPaperPlots using TokenizerStats using SmirkPaperPlots: savefig using StatsBase +using JLD2: JLD2 using Format -stats_dir=joinpath(pkgdir(TokenizerStats), "stats") +stats_dir = joinpath(pkgdir(TokenizerStats), "stats") # Tokenizer Statistics loss_stats = SmirkPaperPlots.model_loss_stats(stats_dir) @@ -21,17 +22,17 @@ function fertility_summary(token_usage) :dataset => ByRow(x -> x ∉ ["tmQM", "realspace"] ? "MoleculeNet" : x) => :dataset, :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, ) - combine(groupby(df_usage_summary, [:tokenizer_class, :dataset])) do gdf + combine(groupby(token_usage, [:tokenizer_class, :dataset])) do gdf dataset = first(gdf.dataset) fertility = reduce(merge, gdf.fertility) avg_fertility = mean(fertility) std_fertility = std(fertility) return (; - dataset = lowercase(dataset) in ("tmqm", "realspace") ? dataset : "MoleculeNet", + dataset=lowercase(dataset) in ("tmqm", "realspace") ? dataset : "MoleculeNet", fertility, avg_fertility, std_fertility, - fmt_fertility = format("{}\\pm{}", round(avg_fertility; sigdigits=3), round(std_fertility; sigdigits=3)), + fmt_fertility=format("{}\\pm{}", round(avg_fertility; sigdigits=3), round(std_fertility; sigdigits=3)), ) end end @@ -50,7 +51,7 @@ function unk_freq(token_usage) unk_freq = unk_count / tokens_seen return (; unk_freq, - fmt_unk_freq = format("{:.2f}%", round(100*unk_freq; sigdigits=3)), + fmt_unk_freq=format("{:.2f}%", round(100 * unk_freq; sigdigits=3)), ) end end @@ -63,8 +64,9 @@ function norm_entropy_summary(token_usage) ) # Combine splits df = combine(groupby(token_usage, :tokenizer)) do gdf - return (; normalized_entropy = mean(gdf.normalized_entropy, fweights(gdf.tokens_seen))) + return (; normalized_entropy=mean(gdf.normalized_entropy, fweights(gdf.tokens_seen))) end + tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) df = transform(df, :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, :tokenizer => ByRow(x -> tok_info[x]["domain"]) => :domain, @@ -73,10 +75,10 @@ function norm_entropy_summary(token_usage) df.tokenizer_class .= replace.(df.tokenizer_class, "smirk-gpe" => "smirk") # Combine over tokenizer classes df = combine(groupby(df, [:tokenizer_class, :domain])) do gdf - return(; - norm_entropy_avg = mean(gdf.normalized_entropy), - norm_entropy_std = std(gdf.normalized_entropy), - norm_entropy_n = nrow(gdf), + return (; + norm_entropy_avg=mean(gdf.normalized_entropy), + norm_entropy_std=std(gdf.normalized_entropy), + norm_entropy_n=nrow(gdf), ) end sort!(df, [:norm_entropy_avg, :norm_entropy_std, :norm_entropy_n]) @@ -88,6 +90,11 @@ norm_entropy_summary(token_usage) |> display # Transformer Models dfp, dff, dft = SmirkPaperPlots.transformer_models(stats_dir) +# Fixed Effects models for results +fe_models = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) +JLD2.jldsave("fe_models.jld2"; fe_models) + + # Tokenizer Summary df_tok = SmirkPaperPlots.tokenizer_summary(stats_dir; k=5) diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index e1284549..e3073898 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -75,3 +75,4 @@ function savefig(name::String, f::Figure; dpi=300) return nothing end +end diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index f5ffe395..29cf7f68 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -225,7 +225,7 @@ function df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) :tokenizer => ByRow(x -> tokenizers[x]["tokenizer_class"]) => :tokenizer_class, :tokenizer => ByRow(x -> tokenizers[x]["encoding"]) => :encoding, ) - select!(df_ng, :tokenizer, :tokenizer_class, :dataset, :encoding, :samples, :loss_per_token_moments) + select!(df_ng, :tokenizer, :tokenizer_class, :dataset, :encoding, :finetuned, :samples, :loss_per_token_moments) # Combine Molecular Foundation Models dfp = select(dfp, :id => :pretrained_id, :id, :tokenizer, :encoding, :tokenizer_class, :val_loss, :train_loss) @@ -283,7 +283,6 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) models = [] df_ng.val_loss = mean.(df_ng.loss_per_token_moments) df_ng.wts = inv.(var.(df_ng.loss_per_token_moments)) - # subset!(df_ng, :tokenizer_class => ByRow(in(unique(df_f.tokenizer_class)))) df_ng_pt = subset(df_ng, :dataset => ByRow(==("realspace"))) model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_ng_pt; contrasts, @@ -300,55 +299,68 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) rho_est = corspearman(df_predict.ng_est_loss, df_predict.fm_loss) - push!(models, (; model, dataset = "REALSpace", ngram=true, metric="CE", rho, rho_est)) + push!(models, (; model, dataset="REALSpace", ngram=true, metric="CE", rho, rho_est)) model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_p; contrasts) - push!(models, (; model, dataset = "REALSpace", ngram=false, metric="CE")) + push!(models, (; model, dataset="REALSpace", ngram=false, metric="CE")) - ft_ng = Dict{String,Any}() - ft_fm = Dict{String,Any}() subset!(df_f, :dataset => ByRow(!=("freesolv"))) # fit issue for dataset in unique(df_f.dataset) df_f_fm = subset(df_f, :dataset => ByRow(==(dataset))) - df_f_ng = subset(df_ng, :dataset => ByRow(==(dataset))) task = first(df_f_fm.task) link = task == "regression" ? LogLink() : LogitLink() # Foundation Model - df_f_fm.val_loss = zscore(df_f_fm.mean) - # df_f_fm.wts = inv.(df_f_fm.std .^2) + df_f_fm.val_loss = df_f_fm.mean + df_f_fm.wts = inv.(df_f_fm.std .^ 2) model = lm( @formula(val_loss ~ 1 + tokenizer_class + encoding), df_f_fm; contrasts, + wts=aweights(df_f_fm.wts), ) push!(models, (; model, dataset, ngram=false, metric=first(df_f_fm.metric))) - # NGram - df_f_ng.val_loss = zscore(mean.(df_f_ng.loss_per_token_moments)) - df_f_ng.wts = inv.(var.(df_f_ng.loss_per_token_moments)) - model = lm( - @formula(val_loss ~ 1 + tokenizer_class + encoding), df_f_ng; - contrasts, - wts=aweights(df_f_ng.wts), - ) - - # Spearman's for n-gram fit vs. fm results - df_f_ng.ng_est_loss = predict(model) - df_predict = leftjoin( - select(df_f_ng, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), - select(df_f_fm, :tokenizer, :val_loss => :fm_loss); - on=:tokenizer, - ) - dropmissing!(df_predict) - rho = corspearman(df_predict.ng_loss, df_predict.fm_loss) - rho_est = corspearman(df_predict.ng_est_loss, df_predict.fm_loss) - - push!(models, (; model, dataset, ngram=true, metric="CE", rho, rho_est)) + for finetuned in [true, false] + # NGram + df_f_ng = subset(df_ng, :dataset => ByRow(==(dataset)), :finetuned => ByRow(==(finetuned))) + nrow(df_f_ng) == 0 && continue + df_f_ng.val_loss = mean.(df_f_ng.loss_per_token_moments) + df_f_ng.wts = inv.(var.(df_f_ng.loss_per_token_moments)) + model = lm( + @formula(val_loss ~ 1 + tokenizer_class + encoding), df_f_ng; + contrasts, + wts=aweights(df_f_ng.wts), + ) + + # Spearman's for n-gram fit vs. fm results + df_f_ng.ng_est_loss = predict(model) + df_predict = leftjoin( + select(df_f_ng, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), + select(df_f_fm, :tokenizer, :val_loss => :fm_loss); + on=:tokenizer, + ) + dropmissing!(df_predict) + fm_loss = first(df_f_fm.metric) in ["auroc"] ? -df_predict.fm_loss : df_predict.fm_loss + rho = corspearman(df_predict.ng_loss, fm_loss) + rho_est = corspearman(df_predict.ng_est_loss, fm_loss) + + push!(models, (; model, dataset, ngram=true, finetuned, metric="CE", rho, rho_est)) + end end models = map(models) do m - haskey(m, :rho) ? m : (; m..., rho=missing, rho_est=missing) + m = haskey(m, :rho) ? m : (; m..., rho=missing, rho_est=missing) + m = haskey(m, :finetuned) ? m : (; m..., finetuned=missing) end - @show models = DataFrame(models) + + df_model = DataFrame(models) + transform!(df_model, + :model => ByRow(m -> cor(response(m), predict(m))) => :r2, + :model => ByRow(m -> corspearman(response(m), predict(m))) => :spearman, + :model => ByRow(nobs_nonwts) => :nobs, + :model => ByRow(confint) => :confint, + :model => ByRow(coef) => :coef, + ) + return df_model tbl_datasets = ["REALSpace", "tmQM", "qm9", "bbbp", "hiv"] models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) @@ -366,27 +378,25 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) models_tbl.model...; groups=string.(models_tbl.dataset), stat_below=false, - print_depvar = false, - number_regressions = false, + print_depvar=false, + number_regressions=false, align=:r, regression_statistics=[ nobs_nonwts => "N", (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", - (model -> cor(response(model), predict(model))) => L"R^2", - - ], + (model -> cor(response(model), predict(model))) => L"R^2",], extralines=[ ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], ["Metric:", models_tbl.metric...], [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], ], labels=Dict( - "tokenizer_class: spe" => "SPE/APE Tokenizers", - "tokenizer_class: bpe" => "BPE Tokenizers", - "tokenizer_class: smirk" => "Smirk", - "tokenizer_class: smirk-gpe" => "Smirk-GPE", - "tokenizer_class: character" => "Character-level", - "tokenizer_class: unigram" => "Unigram Tokenizers", + "tokenizer_class: spe" => "SPE/APE Tokenizers", + "tokenizer_class: bpe" => "BPE Tokenizers", + "tokenizer_class: smirk" => "Smirk", + "tokenizer_class: smirk-gpe" => "Smirk-GPE", + "tokenizer_class: character" => "Character-level", + "tokenizer_class: unigram" => "Unigram Tokenizers", "encoding: selfies" => "SELFIES", "encoding: smiles-canonical" => "Canonical SMILES", ), diff --git a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl index 3eadf63c..7604c201 100644 --- a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl +++ b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl @@ -99,11 +99,12 @@ end function model_loss_stats(stats_dir) rows = [] - for file in find(stats_dir, r"/model_loss\.jld2$") + for file in find(stats_dir, r"/model_loss.*\.jld2$") jldopen(file) do data tokenizer = data["ref_tokenizer"][:name] dataset = basename(dirname(file)) dataset = dataset == "tmqm" ? "tmQM" : dataset + finetuned = occursin(dataset, basename(file)) for split in ["train", "val", "test"] split ∉ keys(data) && continue split_data = data[split] @@ -114,6 +115,7 @@ function model_loss_stats(stats_dir) push!(rows, (; tokenizer, dataset, + finetuned, split, ngram, vocab_size=data["tokenizer"][:vocab_size], From 7876c6057455f76ee623e88f659a97b75e38cf02 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 7 May 2025 10:10:56 -0400 Subject: [PATCH 10/54] add script for compressing tok stats --- opt/TokenizerStats/submit_archive.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 opt/TokenizerStats/submit_archive.sh diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh new file mode 100644 index 00000000..844b9ab3 --- /dev/null +++ b/opt/TokenizerStats/submit_archive.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#SBATCH -p venkvis-cpu +#SBATCH --cpus-per-task 8 +#SBATCH --mem-per-cpu 1800M +#SBATCH --time=1:0:00 +set -x +my_job_header + +# Add tokenizers.json +cp tokenizers.json stats/ + +# Compress stats +export XZ_OPT="-e9 --memlimit=4000000000 --threads=${SLURM_CPUS_PER_TASK}" +tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ + --xz \ + --exclude="*.slurm" \ + --exclude="*.tmp" \ + --exclude=".unmerged/" \ + -C stats \ + . From dc30fcfbfe70d736c565cdc0d9dafaaa169bdf59 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 7 May 2025 20:36:12 -0400 Subject: [PATCH 11/54] feat: add fixed effects models comparing fits --- opt/TokenizerStats/plots/plots.jl | 11 +- .../plots/src/SmirkPaperPlots.jl | 1 + .../plots/src/figures/info_loss.jl | 7 +- opt/TokenizerStats/plots/src/prognostics.jl | 55 +++++++ .../plots/src/tabulate_results.jl | 134 +++--------------- .../plots/src/tabulate_tokenizer.jl | 3 +- opt/TokenizerStats/submit_archive.sh | 7 +- opt/TokenizerStats/submit_jobs.py | 6 +- 8 files changed, 96 insertions(+), 128 deletions(-) create mode 100644 opt/TokenizerStats/plots/src/prognostics.jl diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 13f262c5..b8467695 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -14,6 +14,7 @@ stats_dir = joinpath(pkgdir(TokenizerStats), "stats") loss_stats = SmirkPaperPlots.model_loss_stats(stats_dir) info_loss = SmirkPaperPlots.info_loss_stats(stats_dir) token_usage = SmirkPaperPlots.usage_stats(stats_dir) +tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) # Tokenizer Fertility by Dataset function fertility_summary(token_usage) @@ -90,10 +91,14 @@ norm_entropy_summary(token_usage) |> display # Transformer Models dfp, dff, dft = SmirkPaperPlots.transformer_models(stats_dir) -# Fixed Effects models for results -fe_models = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) -JLD2.jldsave("fe_models.jld2"; fe_models) +# NGrams Stats vs. FM Performance +df_prog = SmirkPaperPlots.df_ngram_stats_v_fm_perf(tok_info, loss_stats, info_loss, df_f) +CSV.write(joinpath("stats", "ngram_stats_vs_fm.csv"), df_prog) +# Predictive and Fixed-Effect Models +fe_models, df_predict = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) +prog_models = SmirkPaperPlots.ngram_prognostic_fits(df_prog) +JLD2.jldsave(joinpath("stats", "quality_models.jld2"); fe_models, df_predict, prog_models) # Tokenizer Summary df_tok = SmirkPaperPlots.tokenizer_summary(stats_dir; k=5) diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index e3073898..e549cbc7 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -29,6 +29,7 @@ using TokenizerStats: load_tokenizer, find include("tabulate_results.jl") include("tabulate_tokenizer.jl") include("tokenizer_summary.jl") +include("prognostics.jl") # Helper for plotting include("plot_utils.jl") diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index 3a253a1a..df920b20 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -221,13 +221,8 @@ function tok_info_loss!(f, cb, tok::String, smi::String; ngram, ref_tok, ref_cod end -function figure_kl_v_info_loss(stats_dir, model_loss, info_loss, df_tf; reference="character") - tokenizers = tokenizers_info(stats_dir) +function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference="character") - model_loss = subset(model_loss, - :dataset => ByRow(∉(["realspace"])), - :split => ByRow(==("val")), - ) model_loss.dataset = map(d -> d == "tmQM" ? d : "MoleculeNet", model_loss.dataset) model_loss = combine(groupby(model_loss, [:tokenizer, :split, :ngram, :dataset])) do gdf loss_per_token_moments = reduce(merge, gdf.loss_per_token_moments) diff --git a/opt/TokenizerStats/plots/src/prognostics.jl b/opt/TokenizerStats/plots/src/prognostics.jl new file mode 100644 index 00000000..752b31d1 --- /dev/null +++ b/opt/TokenizerStats/plots/src/prognostics.jl @@ -0,0 +1,55 @@ +function df_ngram_stats_v_fm_perf(tok_info, model_loss, info_loss, df_f; reference="character") + info_loss = subset(info_loss, + :ngram => ByRow(==(5)), + :ref_tokenizer => ByRow(==(reference)), + ) + model_loss = subset(model_loss, + :dataset => ByRow(!=("realspace")), + :split => ByRow(==("val")), + :ngram => ByRow(==(5)), + ) + df = leftjoin( + select(info_loss, :tokenizer, :dataset, :info_loss_moments), + select(model_loss, :tokenizer, :dataset, :finetuned, :loss_per_token_moments); + on=[:tokenizer, :dataset], + ) + disallowmissing!(df) + transform!(df, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + :tokenizer => ByRow(x -> tok_info[x]["encoding"]) => :encoding, + :info_loss_moments => ByRow(mean) => :info_loss_avg, + :info_loss_moments => ByRow(std) => :info_loss_std, + :loss_per_token_moments => ByRow(mean) => :ng_loss_avg, + :loss_per_token_moments => ByRow(std) => :ng_loss_std, + ) + select!(df, Not([:info_loss_moments, :loss_per_token_moments])) + df_f = select(df_f, :tokenizer, :dataset, :encoding, :metric, :mean => :fm_loss_avg, :std => :fm_loss_std) + return leftjoin!(df, df_f; on=[:tokenizer, :dataset, :encoding]) +end + +function ngram_prognostic_fits(df_prog) + df_prog = select(df_prog, [:finetuned, :dataset, :encoding, :ng_loss_avg, :info_loss_avg, :fm_loss_avg, :fm_loss_std]) + dropmissing!(df_prog) + df = combine(groupby(df_prog, [:dataset, :finetuned])) do gdf + wts = aweights(inv.(gdf.fm_loss_std .^ 2)) + loss_only = lm(@formula(fm_loss_avg ~ 1 + ng_loss_avg), gdf; wts) + loss_and_info = lm(@formula(fm_loss_avg ~ 1 + ng_loss_avg + info_loss_avg), gdf; wts) + return (; loss_only, loss_and_info) + end + df_ft = combine(groupby(df_prog, :dataset)) do gdf + gdf = unstack(gdf, :finetuned, :ng_loss_avg, renamecols=x -> x ? :ng_ft_loss_avg : :ng_loss_avg) + wts = aweights(inv.(gdf.fm_loss_std .^ 2)) + loss_and_info_and_ft = lm(@formula(fm_loss_avg ~ 1 + ng_loss_avg + ng_ft_loss_avg + info_loss_avg), gdf; wts) + return (; loss_and_info_and_ft) + end + leftjoin!(df, df_ft; on=:dataset) + transform!(df, + :loss_only => ByRow(r2), + :loss_and_info => ByRow(r2), + :loss_and_info_and_ft => ByRow(r2), + :loss_only => ByRow(nobs_nonwts) => :nobs, + :loss_only => ByRow(StatsBase.variation ∘ response) => :response_cv, + ) + return df +end + diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index 29cf7f68..6469589f 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -282,6 +282,7 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) # Pretraining models models = [] df_ng.val_loss = mean.(df_ng.loss_per_token_moments) + df_ng.ng_loss_std = std.(df_ng.loss_per_token_moments) df_ng.wts = inv.(var.(df_ng.loss_per_token_moments)) df_ng_pt = subset(df_ng, :dataset => ByRow(==("realspace"))) model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_ng_pt; @@ -289,25 +290,24 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) wts=aweights(df_ng_pt.wts), ) df_ng_pt.ng_est_loss = predict(model) + + # Dataframe for predictions df_predict = leftjoin( - select(df_ng_pt, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), - select(df_p, :tokenizer, :val_loss => :fm_loss); - on=:tokenizer, + select(df_ng_pt, :tokenizer, :val_loss => :ng_loss_avg, :ng_loss_std, :encoding), + select(df_p, :tokenizer, :val_loss => :fm_loss_avg, :encoding); + on=[:tokenizer, :encoding], ) - dropmissing!(df_predict) - rho = corspearman(df_predict.ng_loss, df_predict.fm_loss) - rho_est = corspearman(df_predict.ng_est_loss, df_predict.fm_loss) - + df_predict.fm_loss_std .= missing + df_predict.finetuned .= false + df_predict.dataset .= "realspace" - push!(models, (; model, dataset="REALSpace", ngram=true, metric="CE", rho, rho_est)) + push!(models, (; model, dataset="realspace", ngram=true, metric="CE")) model = lm(@formula(val_loss ~ 1 + tokenizer_class + encoding), df_p; contrasts) - push!(models, (; model, dataset="REALSpace", ngram=false, metric="CE")) + push!(models, (; model, dataset="realspace", ngram=false, metric="CE")) - subset!(df_f, :dataset => ByRow(!=("freesolv"))) # fit issue for dataset in unique(df_f.dataset) df_f_fm = subset(df_f, :dataset => ByRow(==(dataset))) task = first(df_f_fm.task) - link = task == "regression" ? LogLink() : LogitLink() # Foundation Model df_f_fm.val_loss = df_f_fm.mean @@ -319,8 +319,8 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) ) push!(models, (; model, dataset, ngram=false, metric=first(df_f_fm.metric))) + # NGram for finetuned in [true, false] - # NGram df_f_ng = subset(df_ng, :dataset => ByRow(==(dataset)), :finetuned => ByRow(==(finetuned))) nrow(df_f_ng) == 0 && continue df_f_ng.val_loss = mean.(df_f_ng.loss_per_token_moments) @@ -330,26 +330,22 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) contrasts, wts=aweights(df_f_ng.wts), ) + push!(models, (; model, dataset, ngram=true, finetuned, metric="CE")) - # Spearman's for n-gram fit vs. fm results - df_f_ng.ng_est_loss = predict(model) - df_predict = leftjoin( - select(df_f_ng, :tokenizer, :val_loss => :ng_loss, :ng_est_loss), - select(df_f_fm, :tokenizer, :val_loss => :fm_loss); - on=:tokenizer, + ds_predict = leftjoin( + select(df_f_ng, :tokenizer, :val_loss => :ng_loss_avg, :ng_loss_std, :encoding), + select(df_f_fm, :tokenizer, :val_loss => :fm_loss_avg, :std => :fm_loss_std, :encoding); + on=[:tokenizer, :encoding], ) - dropmissing!(df_predict) - fm_loss = first(df_f_fm.metric) in ["auroc"] ? -df_predict.fm_loss : df_predict.fm_loss - rho = corspearman(df_predict.ng_loss, fm_loss) - rho_est = corspearman(df_predict.ng_est_loss, fm_loss) - - push!(models, (; model, dataset, ngram=true, finetuned, metric="CE", rho, rho_est)) + ds_predict.dataset .= dataset + ds_predict.finetuned .= finetuned + df_predict = vcat(df_predict, ds_predict) end + end models = map(models) do m - m = haskey(m, :rho) ? m : (; m..., rho=missing, rho_est=missing) - m = haskey(m, :finetuned) ? m : (; m..., finetuned=missing) + m = haskey(m, :finetuned) ? m : (; m..., finetuned=false) end df_model = DataFrame(models) @@ -357,92 +353,8 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) :model => ByRow(m -> cor(response(m), predict(m))) => :r2, :model => ByRow(m -> corspearman(response(m), predict(m))) => :spearman, :model => ByRow(nobs_nonwts) => :nobs, - :model => ByRow(confint) => :confint, - :model => ByRow(coef) => :coef, - ) - return df_model - - tbl_datasets = ["REALSpace", "tmQM", "qm9", "bbbp", "hiv"] - models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) - models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) - sort!(models_tbl, [:dataset, :ngram]) - transform!(models_tbl, - :metric => ByRow(m -> replace(m, - "auroc" => L"AUROC ($\uparrow$)", - "mae" => L"MAE ($\downarrow$)", - "CE" => L"CE ($\downarrow$)", - )) => :metric - ) - - return regtable( - models_tbl.model...; - groups=string.(models_tbl.dataset), - stat_below=false, - print_depvar=false, - number_regressions=false, - align=:r, - regression_statistics=[ - nobs_nonwts => "N", - (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", - (model -> cor(response(model), predict(model))) => L"R^2",], - extralines=[ - ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], - ["Metric:", models_tbl.metric...], - [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], - ], - labels=Dict( - "tokenizer_class: spe" => "SPE/APE Tokenizers", - "tokenizer_class: bpe" => "BPE Tokenizers", - "tokenizer_class: smirk" => "Smirk", - "tokenizer_class: smirk-gpe" => "Smirk-GPE", - "tokenizer_class: character" => "Character-level", - "tokenizer_class: unigram" => "Unigram Tokenizers", - "encoding: selfies" => "SELFIES", - "encoding: smiles-canonical" => "Canonical SMILES", - ), - # render=LatexTable(), - ) - - - - # Test impact of encoding on pretraining - null = lm(@formula(val_loss ~ 1), df) - @show tok = lm( - @formula(val_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - @show tok_ngram = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - LogLikelihoodRatioTest(tok_ngram, null) |> display - - # Test impact of encoding on pretraining - tokenizers = tokenizers_info(stats_dir) - best_models.tokenizer_class = map(tok -> tokenizers[tok]["tokenizer_class"], best_models.tokenizer) - best_models.encoding = map(tok -> tokenizers[tok]["encoding"], best_models.tokenizer) - replace!(best_models.encoding, - "smiles" => "SMILES", - "smiles-canonical" => "Canonical SMILES", - "selfies" => "SELFIES" ) - - null = lm(@formula(ngram_token_loss ~ 1), best_models) - @show tok = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - best_models; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - - - # Predict pretraining loss using n-gram model - null = glm(@formula(val_loss ~ 1), df, Normal(), LogLink()) - @show ngram = glm(@formula(val_loss ~ 1 + log(ngram_token_loss)), df, Normal(), LogLink(); contrasts) - LogLikelihoodRatioTest(ngram, null) |> display - r2(ngram.model, :devianceratio) |> display - - return df + return df_model, df_predict end function tmqm_finetune(stats_dir, dff, dft) diff --git a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl index 7604c201..8e3014cf 100644 --- a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl +++ b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl @@ -102,9 +102,10 @@ function model_loss_stats(stats_dir) for file in find(stats_dir, r"/model_loss.*\.jld2$") jldopen(file) do data tokenizer = data["ref_tokenizer"][:name] + tokenizer = dirname(tokenizer) == "." ? basename(tokenizer) : tokenizer dataset = basename(dirname(file)) - dataset = dataset == "tmqm" ? "tmQM" : dataset finetuned = occursin(dataset, basename(file)) + dataset = lowercase(dataset) == "tmqm" ? "tmQM" : dataset for split in ["train", "val", "test"] split ∉ keys(data) && continue split_data = data[split] diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index 844b9ab3..420a471b 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -1,8 +1,8 @@ #!/bin/bash #SBATCH -p venkvis-cpu -#SBATCH --cpus-per-task 8 +#SBATCH --cpus-per-task 3 #SBATCH --mem-per-cpu 1800M -#SBATCH --time=1:0:00 +#SBATCH --time=8:0:00 set -x my_job_header @@ -10,11 +10,10 @@ my_job_header cp tokenizers.json stats/ # Compress stats -export XZ_OPT="-e9 --memlimit=4000000000 --threads=${SLURM_CPUS_PER_TASK}" +export XZ_OPT="-9 -v --extreme --memlimit=4000000000 --threads=${SLURM_CPUS_PER_TASK}" tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ --xz \ --exclude="*.slurm" \ --exclude="*.tmp" \ - --exclude=".unmerged/" \ -C stats \ . diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 185a4c84..46ee7bf2 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -231,9 +231,9 @@ def show(self): REF_INFO_LOSS = [ "character", - # "meta-llama/Meta-Llama-3.1-8B", - # "google/gemma-7b", - # "Xenova/gpt-4o", + "meta-llama/Meta-Llama-3.1-8B", + "google/gemma-7b", + "Xenova/gpt-4o", ] From 8f2a1e100607538eea01d9fc642297b95d5e7908 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 7 May 2025 20:37:29 -0400 Subject: [PATCH 12/54] add fixed effects figure --- opt/TokenizerStats/activate | 2 +- opt/TokenizerStats/fe_models.jld2 | Bin 0 -> 884366 bytes opt/TokenizerStats/plots/Project.toml | 1 + opt/TokenizerStats/plots/plots.jl | 3 + .../plots/src/SmirkPaperPlots.jl | 2 + opt/TokenizerStats/plots/src/coeff_plot.jl | 122 +++++++++++++ .../plots/src/figures/fe_models.jl | 156 ++++++++++++++++ opt/TokenizerStats/plots/src/figures/ngram.jl | 1 - opt/TokenizerStats/plots/src/plot_utils.jl | 9 + .../plots/src/tabulate_results.jl | 170 ++++++++++++++++++ 10 files changed, 464 insertions(+), 2 deletions(-) create mode 100644 opt/TokenizerStats/fe_models.jld2 create mode 100644 opt/TokenizerStats/plots/src/coeff_plot.jl create mode 100644 opt/TokenizerStats/plots/src/figures/fe_models.jl diff --git a/opt/TokenizerStats/activate b/opt/TokenizerStats/activate index 0bcdd953..6c072d92 100755 --- a/opt/TokenizerStats/activate +++ b/opt/TokenizerStats/activate @@ -8,6 +8,7 @@ if command -v module > /dev/null; then module --ignore_cache load gcc python/3.11.5 openmpi/4.1.6 export SSL_CERT_DIR=/etc/pki/tls/certs export SSL_CERT_FILE=/etc/pki/tls/cert.pem + export JULIA_CPU_TARGET="generic;znver4,clone_all;znver3,clone_all;haswell" fi # Activate virtual environment @@ -18,6 +19,5 @@ export HF_HOME="$(git rev-parse --show-toplevel)/.cache/huggingface" export TOKENIZERS_PARALLELISM=false # Configure julia -export JULIA_CPU_TARGET="generic;znver4,clone_all;znver3,clone_all;haswell" export JULIA_CONDAPKG_BACKEND=Null export JULIA_PYTHONCALL_EXE="$DIR/.venv/bin/python" diff --git a/opt/TokenizerStats/fe_models.jld2 b/opt/TokenizerStats/fe_models.jld2 new file mode 100644 index 0000000000000000000000000000000000000000..d5fc8e24facf6436ff9a11471976e3ad487f0c2b GIT binary patch literal 884366 zcmeEv2YePq^ZzBG1PBm%htNY0AwVed96eG)uR=lyB#=fDI%wz}f`S4npd#WcU_?+v z@p zbXx4#pdOPG;-iDQM5jjwbxTf7j81PHG$l4QEj~FZD5Pm<(-0*nSg=PLAM&F1o{e6{1 zejOCG6=M%#3`x<6v5L~Bl#dd~un_j&hy7O+eq|6Ku4y} zrrGQHJ=??mv|i^lvH%SeEjG1gZ>3k4{hPojf)+A+71a=rIYg{bR?c#-{OJ zQKOb4ezZ&XUcH<4icgA-PSvuN@|0hcs4V1OBv_g9;jdamuR?VAo7>eTHYqJOGBtK==QxTAMX5@BMTA&!9d(Wq zLBVL%D8Cxzi@JAA7#}+(HM%Jm6q`12dSv_*7OZ0#Pj%wAs!t8dhreWh1W`Wf`z$Gr zt1b+v>85-wv-}5>$eW5%TasU~hLAgxU>(Y@NcDyOf~0>B!MenUeqkI7!(`SE)+0Ku zqsb`=$y`)@qWh8Dl=vyyuV9HkH9Bb`&uAd=r&EhHl=$P5#%b~!5gmR*oLsLW6gHOf z?Ue+*CFM7ve5-b8O8Hjp+Dww)C`ia(Niamp?@9U(kl&kAKC!#KE<=gm%H9@~Z)I6w?@od(<$SXDVr{|Kit-BzR7vDr2>acd=;$vK#|egTnNA&E8=|8ga{tj*%9pQ~ zc9d^b&-Rp$xae0;*t4791C)>cI4zHh>`M8#E@P+*>_&9-e^f59IoZ+O zB0rS!BPhQl;adCjVe^c!1KFh?t2{{gsE42`N)Mu=pNXJ}hzxrYAN&KmdP#KH(_5mq zAiMey9ql|NI$?A|a#~tnqQn05>Yr==;8Qn}cqnB=kXN#pww9dWDG z8@7t4#t#yD$4mJ^Jp}#LNCD$0A9hjqt;7?(kYH2N zV&Y;GqZMTW(b2E)xjtu^NOWBH7#|XdPSZ4}c<7v*lpdcnIeBv0z}Qq89}q9;lGQ)mX5D$x_;HFhOUrbkcHEKDal z4VO;!W7DwM)R@?m^xWzxlZhYppBg4AI)&&oJ#muLIXXRdd~#}hOmu=SX{rmoUB``! zjY&`ItaV&MyfTgW5x1IO4RJr6@~!+ngYpq){v(Cpi3Dd-KKhSNBSil51osl(EaH>f zX*SU@F2%6eNR3WQPb2#urhHHR^9b>qwTqd5GKk;GKXWMG%0F`{AO5*GN*LaAw1D#{ zAO5LD`3nfHBR;Aw+iY-7VxJ<#ufEu);A#?*lP6A2$=!kAOV~8U->VG?F6RJg!W7P^ z`1P7w%WJBAUiI-?$#k>pn{SU{TR`9bztK+j9~U>VNig@CH6WeI@~>1h8EIU!@Sc(hDfDi7DyRowk{|!+eIme^M3d!<}mAD=ytWR0DtJ zeck|Cm&>6cJ$mf?6E2(-LacC-Kf_@(bqoe!?59ahmWL#elT6z|R>5cKP{Ds2Lqoez zO;qf7{riEU7M+|Nq4=>q+z@6ow_8zE#h(RV0fvdufsssv^++6iUI?2?!dM~RvkD=s zdFykliZL-x+xWQXxWT_sm%^gL2Xuc{n*u)DsQ*+MmzGQ{_`K@187%L+Ai=TivgjEy za|En-g?ftX0xl*Pazo%peizV-cIBGjbbehJovt;IiIuh5?c60)#QO#1cIK(6xzml5 z6fb*noE=d!d4Nn6MgiLq^I?CMv2;J5^AqxUW})M^~`1XXA5mqe~ibt4-{E?fZbz!rp%9 zNqLv$QBTT;eE6p-moKF$-M@ZblkM}Yk6$c{R5sP(!G!&3efp$rO#Aa*oa^S>(E+%4 zwLR#v#;%0#K37Gyw&h&MnC+3}d?O#0^WS_(173&y4p_sz=lQ%7D{UDsL>`kl_JCg@V-OENYgTgW)Mq!~Be#NP8ytP@jj#UmBQUb?B| z4GlF>QK;X}_q}q7XHg=}Tlf{tlLzybi!{FEowr2sNMcDvtC&=0^p+}o(Qu1*T`V2g zBVh0^0{S-=Wv3+znDvf;nRf-;)>c&S^l|~S_6c~TAWiin1U$V!z&{)UhLqv`6H6Th z3|=l^)JXwD0>pZJ=nw%j)(aT*t$@=?i@qvrtbiGh5`IF!i_Lg{swjIO67cjk0sVgx z@M5sPmamKzF#I(E{m%&)(o8H$hfWeO{AB@CYVojSE{zm0_7KDa6TyB>*9}l8FE@la`^yi)DzD2sGJtS z=GWG{925}>?wPV;PHE&zbfkyij56A6husN{ESyN9rVm z{U7uXrv2IGTEY%2^?d3J{Djs&D#}-b1%B}*YkQmf)jlZfUEtTA1uwh)f*5z=A z7Bk>C+m8gNKHU8||F5pt^5Jup&+>U|y72iEMQ$DxT>t)c@<2ZPzLzd1SAOqzw|hBF zU)#wcAz_=ks=KalU%q`oKQ(dQ&l3W!_SNCXS8twBUh+{NyFO^sXRozWs}|YVu+6nd zH8N~{boii1_2WkEZ-3i0Qk}Hm>06)N?xx=PDl7CxT6;C1b+;d?m5o#zeUMn~W^6xo z$AoH+eRis^dZyLdu)B@Bse{J;;QxH9F6xU9?++Z_rn8#X_QBxtyB^2`m%e%W&F60w zQ%An{!k*)^nyd4hzx(a-@IP(OD-EKqbS$DCYE-)I!Ec7BpV#VgE%HiVwb}X13Vj%? zxwiD|`H%Nd%g1&Zz2~hSYJsD@Kizn?i+bX+fBeZh{n+)}^-S?_o4O+A^tb7mHnqfO zx6VJlBbv58(T#c4$AmN%E$GVcYugrHpmAM|tv{-3##P(E=lwffzk1DfWW~WU^JiYs zVbWLqzv;TpSB(oev%L2s*KH-JpXt2+;~fvAT(M0#-C_U6Nmp!xO4pftbH`O%@9q=Y zpF4BS78;k4(Q?fd+k@LH|NO#l*KFAx%8!P*oZ7#Z}wcaZ`L!8eOy9?)s!} zPUJP)svrE9I>RL|hKMUO*jrG~;kof{$8v-i*d%ktxb(y!t8!{MEfZm{&e`|YfCR(vGq#qzPs^_E4Ei!jR}0N%r)D!uRe`0S^kQx zc9|0ue@ObxR;um!hw^Y-8*$ z6P{v|nQC8}<>%Z}GAp^m#LbEj;>X0pI8n06vN&!sEBJiV(G5by8tFEGb}LimD}R>! zqqv7gDzivu9G^O}6v*d%EbS#2l{-Njl+CA;Ecpwdr10i8qz?$s;_l?h#Jn-FKaLdU zMUi=ItKHW(_r`)}^P`Job_Q%QQMI}EF41=>N)CDNUnm+Tk00zrCn&I7zv;Nd=T1(J zg}glFY|eJ+`iL&$dU?qn@BxAkxTg?y1Ipzf7x{pB_$2$R_~df;ibFo&y~>fxK^~x- z4;=Y`paX&~L*(CY=&2sGr>x(SF14#%4!Ar0az1dBzuyq@0o}<*{Q<4`Ay0?Wb&GO( zKKT!HKsg^c@;!yJy)q6tkPirYej(%mTFFB`pv(t)9%$iTxxBZyl^!eqS;^PskexbC z?VfKuzj9F?P`1m8F7vs=&G#cPX>q*=luc7I6<#)(0g<}7~C{zC1z zNyi6#m>)Z*(NxiuowH^RJB@1O3p&r9X}#;2KZ9s0>&nhUPnM&ZWxlLb6qj>c&+N^{ z)SE95crt1KqVH$)*7Ck?Ty4X-x?R)*%ZJPlZPnFrC*iYpcP=b()KdTIQl{uk$B6oi zyY=v$>uB@(`_(?~mf`qt%Xh;n44>+_TeEG6t<^Ie%T_mS+u=x#ebI={HNSpWRbP4H zV!LO*Oi^onzoJF)2j;qk-JeL_^X2L=$C+K7zHuz9rgmBV+#gdetLpNHy1!O_NUB=$ z*NC+X->>N)d0#JFTdt-$=l8FIyL?bp z-R=1B@X5wOdE|7ceR%2MWwjh3Z!BDxQN6i3`}1CXC%jNWZT;~dvr6`^r#8FhJHDo) zp1SVhAC0ecIBbt9I3>DEQaxQx{Es2?YHn!Ycr@^KL`~o3>XKE>j~1)=t^K=!@jne| zRa1Q?w8PTXPgYaA{jM%)@e^@Ke6JXj?SRu4r zQ?&pxkFQ7C*8g71#-1o*5h08fvA9bpR_VL|Uw!k0dsO0j=pijht@gm#b!5vDU7n}=u+WEnSl|}>unyXX)%AcM`>?=0h42HQTu$1T*8L&#(Y~~$KV<&= z;5qGr4?h4}$&uqj=KH7m1N-!{z1XLh>F@)foDclJ)gQ25zHb4#4B-br+{cmm z{w{yuJ`VKbJ`Qle|JLgT_q9-u`vqlx$oI88)kEWpL%PqWk1rG-4jNw^)So)!_@ME{ zLHGF_QvZSbd=B!5gT@yJ-RE=AeZD*p@qzJ$;=@kki$l84r}wAi4+q`nbI|zWp#H-_ z*NcPh^EuFexSZ->rsR4(85BXz#~jXNR#m^yr5`k|66Xil%8g#HuP3lhz`6nJ2&^lx z&cM0@>yWqVP24o%vDNxK*WG#o>kjAxf441XhNh30?^X`h6R;EI0nc7*vaiI!#g=lg zEaxPr{=D&uug(MWxZB-*w&}s5BzPRzTQGR zz@P97>IXZa59>6vyNI`yeqF`mcNJMs;Anu{yPWc&0Q&t-oCc8lRFg-y5{+*@@L=VE zdO!4xc;oTF&GCj+^l|&ZQHfR3l(gctGjdn7bz!`9{TOM z&-N55Jbb7gD|a({-8=}#uPvT%KicS$L5yy6$q>$HV($;xJwv^4iSaHzJ#5JZ=Oz;s z%i1>YcK==Cv((3LHj7afB-{_+L$JkS%*esezH6WDKK-#C%s0TDv4s|PBei?KT; z9KQi#_**MZi6-{w?PZ+qQIDvtAK zd|XsK`C93~d#w0FtimIxi{6N?+Qhd$^Bb(VZx#1wm||PG`c*iJw~L%itQ@u{F5878 zLP!L)uc-^L3hiQ)Hp)QfS(oy_DIX9y__H|9^0@Kgy;W4Jd*ylDYs<1C7`;CcqY~4BreX6or>5aS>%oZ_n>yE|YoVRftV25pB8+76 z^_oA6SbmPN=`l6yH@zul2|lHK{K8mxv&)cAZ!N<+_arL)DQAy@u?iE48c$)rjX^S# z;Q?$!_h$Oay9=I-a`-b6#^--$Gdv*N{qz*F!^0t z3l+wt*)%5J*lEt->P%F$7~J<`FH{&;tJCEgSg7dscHHAag@@Asc-+k1+*iOo6o#A9 zX2SgHA5GCL`#aFCTtdP2=cwf;caq zuTOZ=${Wu0#d-NbTCSpe%BLDEHKp<$`Jl-XAkNQ+f5&KFk5CkxpRZph>_~ie#Ct3? zmws+O;vp?DK9z5V;kq6hEGQu0jct+BuqiDq(7T zT5L@zE?{>pqC-$mihW&L+;mhy1kI1a>HDnu2rhWx7iK!<3ZOlYJqB^*2P9`8!$gcP4siiZ^3F zJ%G-h=2=~c5B`y_3!Hn8xHih+0~5}_hd(e+z`6GrpN-}4*%e+sg7icFqGiJ1Y0Cw~ z`S<>%lqs?)2c?e7WAQtP*-~Za(_iaq+2X zyszp<_UZ8v%VuC~gAM29BmNP8I457G4m;aklMa2~!@KF-5OoZz~_ z`S&s%=ib8(T+cY?UZ&%md&t4Kh;!~ihhCg>k9L&%1)Og$>xq*52D_q(F6$X%L65PJ zGuDERbL(Y$a9%z71&sSRr(U)v&cYs?PcQRNu;9n}^fG^f#E*8sIrK6=&YuSz{POwp zXg8cc4}Q=mS@;d-&&z(p`SY^h(n*d?$2s$|UYs+J>lEV^&XY%c$>SBykGIOl`SG$` zoExuS|1>_}ym;7+aR%qZ%ltSWUZ&$*c$tp#-(`9R`AeqHv7pbjpwF|Q&$pm2u%Is_ zy6lHV7W|7X=u0fa!T^OMGVu?@vnMlgxrKrx!6^%|I3a9xx}9=}dU0^gr4{~kVm3s`(G z)jU4f>Xz?m@W%d!h6~@lfzNp87=6Z9Gyz?_yNAHb^ThoPxqJm0!0PYrR6dl&4$;vz zVPYfS=T9HMYChVHlqe=k+sXN7+%)8yMJDyeabI}Q`lWxjs4_mS?hib{6P5BEbQQE_ls8T7G^J?9(p<_1uVXIxP z{P8*a?&!x5;kYAiF8KM3)ml?czvW4IwOFNzkSg!O5D#5W-RQ3 zz-$*Z_vyEoW3AJbFOFIC7O^@{PgC1J}&+?Hdee?nNzlPXs4m z-y;^b+6|u=yGiM4aY)FU>#*c~m;I4}e;iqWo`3^&%R7Q>D8sx9T$ zk;>O)xUpXK7%u9S>{m}LJGg25>+`w83XLK9L)9J)55~E2eI`;)g+JJNTWB_YsxO5+ z=Fcv8{?VfcW(xrYX_McqV~PtnK)po=X(0Dkd|I%vntK+XSivrmnV>r#%-%0-^ZqFz z;|RdVXgPWCl+gQa|3gn^gge!Po=sjzu5k6;oWL(n(MiWI_*D?g1z!1yQ>Mhml9q8C9JHizVzSo?0( zY3od25m zY2dEtu3o4x21A&$aDkbqcsla2XrTfJt~g!eInq`TX-SbI78rZ=|hsuI=vx2{n~gT+GuqfCf3h|=t)mi zrUOGg^0jf^(UytDt#WrSFOovv3#>qxa; z8T|*TX5h8JnAN!$))P5)p&o-1&Fi7w^}IfiYN%_|f8;6(FO(LiVLVRIxEn+mT$wiB%uKv2b5ZPXu zI?(Ku>-T4UVgY6eJ)>Y^zJK|LcL*`}Ve;tFBqrNMrG8m8jvjcI#%8ovzQ*REOPEVo z(%qo{5On_c&*fWa@)k$j?-3MU@~D3?hC)Xs6j7e5IalGHcOH)OLbNfeIx#75jG5?w zrTbnNqNDvP=FaNrrC0q$ZI7n)d@B;s+&R~J{nv~dO(>2`gP{6S!HJpdcgv}yNfZ41 z;}V-N`@Swlx-gC0Tc||@`+X0Kkq60eBUt*ui~WVg7yN1pNAr4FzF^^z$GHU9+SRCy ze&6ez5aS5djmh%nP6*zfC_bf-H!A@?M@3b-n^eUtcy~10Krfz}2Gn6Gpnj&Of6PQx zm^1LQXda9{PUCC{S%igL5refo*l%Od^y0iGF{=(7=gn~eMu+s|BIRVF;#lA?n&U`) zBX3ZiJm?$ik(ctWTT)gEA>1-8nOKnc_^nqJrEI2PXi1~I-=hN8JnU1=$4||(FyS|( zxID`X0;?20@%3l_J-R@Q?bMqUGPYA+E@U95dH%LMx>DmLKa$BYcDenyTsDVxck2+c z@UR>!rlrvKtG)SzlQB;EK!y>Tw9>HpJ1dH4Hyx`MQ$u4(!`+Q*vp@TtOwDT+<=e*} zq`MoYqVYt(ndkvb^d~Mv7l|%ktW6qMRp`|ik7{GQy*QYu97R{Jndn3%j6T$9DjH8mpt+5r zhcMA^xDdT_VBslVH23|vDTLmi)*iK(jXb$t@+^K#aJl-njxBrje&yfq>X>|2M}IZf z#OK!QE@iFoc@Vl$lS}Pn@e4;ZF5`S=C~r>oA065-_S<-ov28aee1yb5Qon zC6ym7;ZY1X>W`N2F%o}_gpZZ*ScV%H6XO_els{g=<0L#@!Y4@hL!;STu%y8py%qxVZMM<8bj4tUODu z;_#E%4~1(?#%aO~Mlsft-oZq4`;(XuVi1Ry^Mm$5p=ScWm37;q7^Cf%% z!;SVXl=2rz_+klPBH?^;ZnSrqgfEwHJ}ous=W`SzzDnZf6A7byJ|uJgZR^<|me_n# z%LmT;5zY^s$Cwdka}6h)$DA>r|2E=Wml5YNZ^U_w8to~{aI5z>IUi4W9+*9@py6B| zPk6t?&w^gSf?m*qUdV!8*n-ZslQcVIdx}`lixOR~S23bj(4;xPm)JX*eOHa%Nem!9 z?lYdW_csjh7Z#^{Hlk=r%kP$u9G%`Oj1x*wKE{mjmjvIF1WQsrZ!?|_|3|<;qVs$I zJQ?4I=^ULNJ3cuzJ|;SWzY?S6mm>a38s^Mz_TD7k+q9||-rKaQ7klzS(}a4p`b;QZ zPY~~ARwR2;J{3Q{B6xxL@O~!026)2zKzJ{c$1YE~kA4Xp?`3-GAH1jOsekaErj>v2 zzNVFbs!IMT|Anx7Izha*3I9ZXF7jU`_#yFCCpofz@cyQie}br-ll=oHrzG$}oTZwS zZ{`oP_o{0VKjQTAmqJc$qN5&5i9X;Hk>BE=fOUvZM2+|cHqWUm(L)8hQct3{AbNeG zmnYnKT^Qf14kkX>TP9l+Xh3w{r}9M4AnWgQn)L_9_o^FFdBi;yQ7sM$d5tL_?S=QM zn@DuLSKXB8uoLf9H6waSAvVu@)gh8Ryw}p4=%^pwFOlEVl;1DGdz!Et@707!_TkAB zyq5`n=+E!_n2-nkcpp=)58lH>{LA&h`QhrX0Aw{N5cy`Mx!oXGD@LOWTL!Wqa}dB<#iYh4&`qxQ`-#S;c*{oBT0u z@?$7pz7EF{UA8CIfE8fGjidVdk33<3K@IEH&!*z%EFu{*FcK80Jm43X3 z33dZ-wl1s4mE#koj1nY72*3Y9tu75DWu9n*LjMR9?6X}4V%7`%rJ zHL^NJ9@C!(|qf#v#1 z$0fczew6bq?qZrhsqn7Jp3JA~EG{^qRmAydmJ}j|Dt=Q9!dPZ?C$K&2gb(R9#UN zDprz;#jgu`#$Evr6Ess1_S2M-&ML^urO7PUPul!JDl>`OOvSd5v!+n?iUp0k^m}iM zt?m9F%auOzwr$#1Px>CO{-zFDd%W;rx8pYOebTA?f7V63VFNsQee2enWlq{A&iJZI zm$NV1I+x$ovr5)+oByx_kB?ck$ANgkQn12)$EspO&Q;Wkw%NA!#I)2mLtc}0?sGRgo6R*N3oGuDi z&P*r~xs)l78nj>s?_Z(@9o=K&*KfwAeH`EXpNLmER5E;z3HaW#f^U#R!Dyj)JkWjR*mz$fPe zm-B(kP}VQwdHCUHYFF?B=HZw4kZ;AW=gWG4OHk*R^gurRp+lJu@<0bJLF|W0<0m>8 z%W;JDJOzA0NO(A`HR)JpSKKWgl(^cWN7T4u@7k*E%1qq1bBDcW@{I3FjoxQpfAV^_ zKJB;K_y5>?=)}jrvOPN`s_E?wi|h+a+rEFL;A->n-81{LJ+N=Z-nKa~|DK@f%*!+T zQXE2~EB{rPxS`WM*@Z$b2N2KftoV))wwRCAJejofA0;mHr$`m$Xu_$_ru-48YDn%M zNA6GKud<-49vQcBb0Nx?%jM00v}5v*wo4lZ_3}&fRf(HmjxA%P z{Xb2=yJ}CKyYvry#6?pcqvX^YeI&t5g4qNE8&f{POoG`21DlBa6oOd< za|nhsrF?=}1ak<6G^2ciSp;(khJ;W)!7PF~1VfrrKEW)4IRryODW6~#!5o4iEhwL0 z7Qq~XAz_qHFpFRg!H|}ePcVyM4#CJ)B0rO0Ho=hAL?@U{Ffd%uBMD{_%qAGvhVlt! z63iwT*p~7MW)jRM7}$>T31$+^CK%YB@(E@V%qAH40Ob?RB$!Pwumj~2%p{miFwjQ% z1TzU{6AW>@rp03l!7PF~1Vi4Te1cg7a|njKOZfz|2<8wBd5`i5W)aLG81g>l6U-u* zLonn6$|smbFo$5shm=n+i(n4HkdG*zU>3m~f*}VepI{cj9D*SqQ$E2gf`Ok1dL+S2 zg4qNEKNb1+Vf-9=K%H1`-UZBXIR#t!al@ehTuz zPx)DdXM;{Ku%0L%LNJnGCgq2;68Vt?QwU}f%p#~L9X{svWj7Q2DW5q*|I-4np9Q%E*ukRY=lxK4Ib32gBA3gBq^q|ZhX864xh{HDuAh!XR6XHG`czf}Lmz?TmJUT#Yn}0tIz`Vj7s6{k?NHq5~Lhn8% z&8KQ;m4@uMvEy&V@cS`*HfC~+oesav=FRXazlyePewQc36}rysHr|r2n0%Iup9Vk` zn~JP~=Wo1kvv>LJp{K4rrR}_Bh&zwkokad{EclzT-=W`#LjDGN2lo4;?*!ef6Peci z)klPh*ralrUdrdJJWVRLeJeyp9uW|yvHKsjR@HMu<1)^w*o2fnVT$P)cIYfO-PnY- zfR8_^-b|XzYIWo7OCv z7kF^YRr z=s?;q>D=X7GO=R&tCdo{5N&LowoE1h+w5v>osoRX3(>|vX~#rk>@^d;VZ%pa99BGX zh%x5)X{~5uGtq4x4gb&!tBs-`V4_{k*-Q9M{!WPYs8bF-&g8L_O>pQ5fp1$RU=HDC zweSo0Vx_Yx>Xcz0V#0N&yqQ#o2#u!m)s0C3KOj5zIe)uOa{7V()1sf$t#jE=hEv6Z zAlf9ZH(lSgQp@J|i*PaUp_cOkSJ`jjC8-}YddIpSbdVjcyt8{!qPQbvw4)=_%c-!V z6Z>tntFvyGNBvy8lZ>Ay6F;>7rdu6RMX8@d74y~4(V4jH$ZSd`78E}8^D>rugp6!) zO0cdZD2(&yGB&!X3zLE>nAN*h(tyQYh&HxPS0>t3=hNrbh4o&DHa2uOCK@qrW_70# z=jen{(}tIiR}DMO9?9-;+ZE{4ohzI`mB zvWqs6Yhzqb^<*k1Ae2n5nvMHzJnTX=wu8{zM$x^P=nq_oKHl>A!a}r1&3%8CWYW0C zZyoUDdemQcPH<%kVA?07A1?bjvj2h4*Se^;^?06nUeqYahiYaLAT4j*_(97xUg^CV z#W*wW!*D^STpj3X4RDQ6OKSaWZO878_^Eol>9WLQ~ z(QUM6B*TsNKE!ZieMd1|g!(Oh zc9E6iiPud^-+i#Hx)&n7d;i?jO8Kql=06IPGB1;9CRSd3e(+}JzLu6h+&esZ))8(d zRj!`V@>PcL%as!Li~cdt4$B0T?LscrWx!=9=R=OnC*vpwe&8~njJxA=n{N{mp%?Py za=>Li@B_+xGC%Uc?+&88Tu#PiIacMsC+7o~^MT7y)-U6E_~B=YGw=iE;g|T3Z^f_Y z%X)xIQ0JHQKtBATLzxfqKnE^C^sAUo)BY*C7xVWFbEwA(hm|Hh*RzW@Y|yeX2f=dD z3i5H6CqUnLnAdV1`Hyog$h)3(+CxcICTTEJaV#*Z3}rV=dbah_3rB5B ze%tbS=f6(dT1D@hHz4Pv4oB`PbGLS{tt$D$)XZbH!-p4_TXo`l8{|8-EPpj+^;z2wf$#WF9&*+uZH^H~= zv;Jx7_v2nK)S%Q1b=-_wO;#?Lsa`MCr~cipscOk9@r}1lo2&-?960 i3<{cm5J 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�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 /(x...), Iterators.product(x.credible_interval, y.credible_interval)) + return CoeffInternal(μ, extrema(limits)) +end + +function coefint(model::M; level=0.95) where {M<:StatsBase.StatisticalModel} + μ = coef(model) + ci = map(x -> tuple(x...), eachrow(confint(model; level))) + return CoeffInternal.(μ, ci) +end + +function coefint(model, names::Vector{String}; kwargs...) + ci = Dict(zip(coefnames(model), coefint(model))) + return [get(ci, name, missing) for name in names] +end + +@recipe(EffectBars, val, effect) do scene + Theme( + color = Makie.inherit(scene, :linecolor, :black), + colormap = Makie.inherit(scene, :colormap, :tab10), + direction = :x, + whiskerwidth = Makie.inherit(scene, (:RangeBars, :whiskerwidth), 10), + gap = 0.2, + dodge = Makie.automatic, + n_dodge = Makie.automatic, + dodge_gap = 0.03, + noeffect_visible = true, + noeffect_color = :black, + noeffect_linewidth = Makie.inherit(scene, :linewidth, 1), + noeffect_linestyle = :dash, + ) +end + +function Makie.plot!(plt::EffectBars) + effect = plt.effect + highs = @lift(map(upper, $effect)) + lows = @lift(map(lower, $effect)) + + val = first(Makie.compute_x_and_width(plt.val[], 1, plt.gap[], plt.dodge[], plt.n_dodge[], plt.dodge_gap[])) + + if plt[:noeffect_visible][] + vlines!(plt, 0; + color=plt[:noeffect_color], + linewidth=plt[:noeffect_linewidth], + linestyle=plt[:noeffect_linestyle], + ) + end + + rangebars!(plt, val, lows, highs; + color = plt[:color], + colormap=plt[:colormap], + direction = plt[:direction], + whiskerwidth = plt[:whiskerwidth], + Makie.shared_attributes(plt, Makie.Rangebars)... + ) + expecation = lift(plt[:direction], effect, val) do dir, effect, val + if dir == :x + return Point2.(mean.(effect), val) + else + return Point2.(val, mean.(effect)) + end + end + scatter!(plt, expecation; + color=plt[:color], + colormap=plt[:colormap], + Makie.shared_attributes(plt, Scatter)... + ) + + + + return plt +end + + +effect_size_plot(models; kwargs...) = effect_size_plot!(Figure(), models; kwargs...) +function effect_size_plot!(f::Figure, models; relative=true) + @show names = intersect(coefnames.(models)...) + ci = map(models) do model + cdx = map(in(names), coefnames(model)) + return coefint(model)[cdx] + end + ci = hcat(ci...) + if relative + ci = (ci .- ci[[1], :]) ./ ci[[1], :] + ci = ci[2:end, :] + names = names[2:end] + end + + ax = Axis(f[1, 1]; + yticks=(eachindex(names), names), + ) + ci = vec(ci) + val = vec(repeat(1:length(names), 1, length(models))) + dodge = vec(repeat((1:length(models))', length(names))) + + effectbars!(ax, val, vec(ci); dodge, color=dodge) + + return f +end diff --git a/opt/TokenizerStats/plots/src/figures/fe_models.jl b/opt/TokenizerStats/plots/src/figures/fe_models.jl new file mode 100644 index 00000000..5550a3c1 --- /dev/null +++ b/opt/TokenizerStats/plots/src/figures/fe_models.jl @@ -0,0 +1,156 @@ +function figure_fe_models(models, prog_models; relative=true, colormap=:tab10) + f = Figure(size=(7inch, 2.5inch)) + + datasets = OrderedDict( + "realspace" => "REALSpace", + "tmQM" => "tmQM", + "qm9" => "QM9", + # "qm8" => "QM8", + "lipo" => "Lipo.", + "hiv" => "HIV", + "toxcast" => "ToxCast", + "clintox" => "ClinTox", + # "freesolv" => "FreeSolv", + # "esol" => "ESOL", + ) + coefnames = OrderedDict( + "(Intercept)" => missing, + "tokenizer_class: bpe" => "BPE", + "tokenizer_class: smirk" => "Smirk", + "tokenizer_class: smirk-gpe" => "Smirk-GPE", + "tokenizer_class: spe" => "SPE/APE", + "encoding: selfies" => "SELFIES", + ) + @show ds_metric = Dict(eachrow(subset(models, :ngram => ByRow(!))[!, [:dataset, :metric]])) + + function label_model(ngram, finetuned) + if !ngram + return "Transformer" + elseif ismissing(finetuned) || !finetuned + return "N-Gram" + else + return "N-Gram, Finetuned" + end + end + models = transform(models, + [:ngram, :finetuned] => ByRow(label_model) => :label + ) + models.label = categorical(models.label, + levels=["Transformer", "N-Gram", "N-Gram, Finetuned"], + ordered=true, + ) + n_levels = length(unique(models.label)) + colorrange = (1, n_levels) + gl_coef = GridLayout(f[1, 1]) + gl_parity = GridLayout(f[2, 1]) + + for (adx, ds) in enumerate(keys(datasets)) + plt_ds = datasets[ds] + ds_models = subset(models, :dataset => ByRow(==(ds))) + metric = uppercase(ds_metric[ds]) + ci = hcat(map(m -> coefint(m, collect(keys(coefnames))), ds_models.model)...) + yticklabels = collect(values(coefnames)) + baseline = ci[1] + if relative + ci = ci ./ ci[[1], :] + end + + # Drop intercept + ci = ci[2:end, :] + yticklabels = yticklabels[2:end] + + # # Flip so positive is better + # ci_sign = map(ds_models.metric) do metric + # metric in ["auroc"] ? 1 : -1 + # end + # ci = ci .* ci_sign' + + # Plot Effect sizes + lb, ub = round.(baseline.credible_interval; sigdigits=3) + if lb != ub + title = "$plt_ds\n$metric: $lb - $ub" + else + title = "$plt_ds\n$metric: $lb" + end + ax = Axis(gl_coef[1, adx]; + title, + yticks=(eachindex(yticklabels), yticklabels), + yticklabelsvisible = adx == 1, + yticksvisible=adx == 1, + xlabel="Relative Effect Size", + xticks=WilkinsonTicks(3), + xminorticksvisible=true, + xtickformat="{:.0%}", + ) + val = vec(repeat(1:size(ci, 1), 1, size(ci, 2))) + dodge = levelcode.(ds_models.label) + dodge = vec(repeat(dodge', size(ci, 1), 1)) + ci = vec(ci) + val = val[(!ismissing).(ci)] + dodge = dodge[(!ismissing).(ci)] + ci = ci[(!ismissing).(ci)] + effectbars!(ax, val, ci; dodge, color=dodge, colormap, colorrange) + + if adx == 1 + h = map(enumerate(levels(models.label))) do (color, label) + PolyElement(; label, color, colormap, colorrange) + end + Legend(gl_coef[1, 1], h, labels(h); + tellheight=false, + tellwidth=false, + valign=:bottom, + halign=:right, + margin=(2, 2, 2, 2), + alignmode=Outside(), + labelsize=6pt, + patchsize=(6pt, 6pt), + ) + end + + # Plot Predictions + if adx != 1 + qm = subset(prog_models, + :dataset => ByRow(==(ds)), + :finetuned => ByRow(==(true)), + ) |> only + qm = qm.loss_and_info_and_ft + rho = round(corspearman(predict(qm), response(qm)); sigdigits=3) + qm_r2 = round(r2(qm); sigdigits=3) + ax = Axis(gl_parity[1, adx-1]; + title = L"%$plt_ds, $R^2: %$qm_r2$ $\rho: %$rho$", + ylabel="Transformer - $metric", + xlabel="N-Gram Based Estimate $metric", + ) + ablines!(ax, 0, 1; color=:black, linestyle=:dash, label="Parity") + scatter!(ax, predict(qm), response(qm)) + end + + end + + # Add Labels + label_kwargs = (; + fontsize=12pt, font=:bold, + halign=:right, + tellheight=false, + ) + Label(f[1, 1, TopLeft()], "A)"; padding=(0, 15, -5, 0), label_kwargs...) + Label(f[2, 1, TopLeft()], "B)"; padding=(0, 15, -5, 0), label_kwargs...) + # Label(f[1, 2, TopLeft()], "C)"; padding=(0, 5, -5, 0), label_kwargs...) + + # Label(f[2, :], "Effect Size"; + # tellwidth=false, tellheight=true, + # font=:bold, + # fontsize=10pt, + # ) + + # h = map(enumerate(levels(models.label))) do (color, label) + # PolyElement(; label, color, colormap, colorrange) + # end + # Legend(f[end, 1], h, labels(h); + # # orientation=:horizontal, + # tellheight=false, + # tellwidth=false, + # ) + + return f +end diff --git a/opt/TokenizerStats/plots/src/figures/ngram.jl b/opt/TokenizerStats/plots/src/figures/ngram.jl index 5c390a74..6ab317ea 100644 --- a/opt/TokenizerStats/plots/src/figures/ngram.jl +++ b/opt/TokenizerStats/plots/src/figures/ngram.jl @@ -351,7 +351,6 @@ function figure_ngram_fits(df, stats_dir; colormap=:Set2_5) colorrange=h.colorrange ) end - labels(x) = map(e -> e.label[], x) Legend(f[2, 1:2], [domains, classes], diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 5da3131b..73809004 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -61,6 +61,14 @@ function theme() ErrrorBar=(; whiskerwidth=2, linewidth=0.5, + ), + EffectBars=(; + linewidth=1pt, + marker=:circle, + markersize=3pt, + colormap=[:red, :blue], + whiskerwidth=8pt, + noeffect_linewidth=1pt, ) ) end @@ -79,6 +87,7 @@ Makie.inverse_transform(::typeof(asinh)) = sinh Makie.defined_interval(::typeof(asinh)) = Makie.defined_interval(identity) Makie.defaultlimits(::typeof(asinh)) = (0.0, 10.0) +labels(x) = map(e -> e.label[], x) # Estimate Number of Histogram Bins from data hist_nbins(x::AbstractVector, w::AbstractWeights) = hist_nbins(:scott, x) diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index 6469589f..a378ed2b 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -354,7 +354,177 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) :model => ByRow(m -> corspearman(response(m), predict(m))) => :spearman, :model => ByRow(nobs_nonwts) => :nobs, ) +<<<<<<< Updated upstream return df_model, df_predict +||||||| Stash base + return df_model + + tbl_datasets = ["REALSpace", "tmQM", "qm9", "bbbp", "hiv"] + models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) + models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) + sort!(models_tbl, [:dataset, :ngram]) + transform!(models_tbl, + :metric => ByRow(m -> replace(m, + "auroc" => L"AUROC ($\uparrow$)", + "mae" => L"MAE ($\downarrow$)", + "CE" => L"CE ($\downarrow$)", + )) => :metric + ) + + return regtable( + models_tbl.model...; + groups=string.(models_tbl.dataset), + stat_below=false, + print_depvar=false, + number_regressions=false, + align=:r, + regression_statistics=[ + nobs_nonwts => "N", + (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", + (model -> cor(response(model), predict(model))) => L"R^2",], + extralines=[ + ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], + ["Metric:", models_tbl.metric...], + [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], + ], + labels=Dict( + "tokenizer_class: spe" => "SPE/APE Tokenizers", + "tokenizer_class: bpe" => "BPE Tokenizers", + "tokenizer_class: smirk" => "Smirk", + "tokenizer_class: smirk-gpe" => "Smirk-GPE", + "tokenizer_class: character" => "Character-level", + "tokenizer_class: unigram" => "Unigram Tokenizers", + "encoding: selfies" => "SELFIES", + "encoding: smiles-canonical" => "Canonical SMILES", + ), + # render=LatexTable(), + ) + + + + # Test impact of encoding on pretraining + null = lm(@formula(val_loss ~ 1), df) + @show tok = lm( + @formula(val_loss ~ 1 + tokenizer_class + encoding), + df; contrasts + ) + @show tok_ngram = lm( + @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), + df; contrasts + ) + LogLikelihoodRatioTest(tok, null) |> display + LogLikelihoodRatioTest(tok_ngram, null) |> display + + # Test impact of encoding on pretraining + tokenizers = tokenizers_info(stats_dir) + best_models.tokenizer_class = map(tok -> tokenizers[tok]["tokenizer_class"], best_models.tokenizer) + best_models.encoding = map(tok -> tokenizers[tok]["encoding"], best_models.tokenizer) + replace!(best_models.encoding, + "smiles" => "SMILES", + "smiles-canonical" => "Canonical SMILES", + "selfies" => "SELFIES" + ) + + null = lm(@formula(ngram_token_loss ~ 1), best_models) + @show tok = lm( + @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), + best_models; contrasts + ) + LogLikelihoodRatioTest(tok, null) |> display + + + # Predict pretraining loss using n-gram model + null = glm(@formula(val_loss ~ 1), df, Normal(), LogLink()) + @show ngram = glm(@formula(val_loss ~ 1 + log(ngram_token_loss)), df, Normal(), LogLink(); contrasts) + LogLikelihoodRatioTest(ngram, null) |> display + r2(ngram.model, :devianceratio) |> display + + return df +======= + return df_model + + tbl_datasets = ["realspace", "tmQM", "qm9", "bbbp", "hiv"] + models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) + models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) + sort!(models_tbl, [:dataset, :ngram]) + transform!(models_tbl, + :metric => ByRow(m -> replace(m, + "auroc" => L"AUROC ($\uparrow$)", + "mae" => L"MAE ($\downarrow$)", + "CE" => L"CE ($\downarrow$)", + )) => :metric + ) + + return regtable( + models_tbl.model...; + groups=string.(models_tbl.dataset), + stat_below=false, + print_depvar=false, + number_regressions=false, + align=:r, + regression_statistics=[ + nobs_nonwts => "N", + (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", + (model -> cor(response(model), predict(model))) => L"R^2",], + extralines=[ + ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], + ["Metric:", models_tbl.metric...], + [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], + ], + labels=Dict( + "tokenizer_class: spe" => "SPE/APE Tokenizers", + "tokenizer_class: bpe" => "BPE Tokenizers", + "tokenizer_class: smirk" => "Smirk", + "tokenizer_class: smirk-gpe" => "Smirk-GPE", + "tokenizer_class: character" => "Character-level", + "tokenizer_class: unigram" => "Unigram Tokenizers", + "encoding: selfies" => "SELFIES", + "encoding: smiles-canonical" => "Canonical SMILES", + ), + # render=LatexTable(), + ) + + + + # Test impact of encoding on pretraining + null = lm(@formula(val_loss ~ 1), df) + @show tok = lm( + @formula(val_loss ~ 1 + tokenizer_class + encoding), + df; contrasts + ) + @show tok_ngram = lm( + @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), + df; contrasts + ) + LogLikelihoodRatioTest(tok, null) |> display + LogLikelihoodRatioTest(tok_ngram, null) |> display + + # Test impact of encoding on pretraining + tokenizers = tokenizers_info(stats_dir) + best_models.tokenizer_class = map(tok -> tokenizers[tok]["tokenizer_class"], best_models.tokenizer) + best_models.encoding = map(tok -> tokenizers[tok]["encoding"], best_models.tokenizer) + replace!(best_models.encoding, + "smiles" => "SMILES", + "smiles-canonical" => "Canonical SMILES", + "selfies" => "SELFIES" + ) + + null = lm(@formula(ngram_token_loss ~ 1), best_models) + @show tok = lm( + @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), + best_models; contrasts + ) + LogLikelihoodRatioTest(tok, null) |> display + + + # Predict pretraining loss using n-gram model + null = glm(@formula(val_loss ~ 1), df, Normal(), LogLink()) + @show ngram = glm(@formula(val_loss ~ 1 + log(ngram_token_loss)), df, Normal(), LogLink(); contrasts) + LogLikelihoodRatioTest(ngram, null) |> display + r2(ngram.model, :devianceratio) |> display + + return df +>>>>>>> Stashed changes end function tmqm_finetune(stats_dir, dff, dft) From 724887515c10a5fe42cf7b47866d80399d65e736 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 8 May 2025 10:41:31 -0400 Subject: [PATCH 13/54] refactor fe figure, add more scale options --- opt/TokenizerStats/fe_models.jld2 | Bin 884366 -> 0 bytes opt/TokenizerStats/plots/plots.jl | 3 +- opt/TokenizerStats/plots/src/coeff_plot.jl | 1 + .../plots/src/figures/fe_models.jl | 156 ++++++++++------ .../plots/src/tabulate_results.jl | 170 ------------------ 5 files changed, 105 insertions(+), 225 deletions(-) delete mode 100644 opt/TokenizerStats/fe_models.jld2 diff --git a/opt/TokenizerStats/fe_models.jld2 b/opt/TokenizerStats/fe_models.jld2 deleted file mode 100644 index d5fc8e24facf6436ff9a11471976e3ad487f0c2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 884366 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 i3<{cm5J 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�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 "SPE/APE", "encoding: selfies" => "SELFIES", ) - @show ds_metric = Dict(eachrow(subset(models, :ngram => ByRow(!))[!, [:dataset, :metric]])) + ds_metric = Dict(eachrow(subset(models, :ngram => ByRow(!))[!, [:dataset, :metric]])) function label_model(ngram, finetuned) if !ngram @@ -39,57 +39,57 @@ function figure_fe_models(models, prog_models; relative=true, colormap=:tab10) levels=["Transformer", "N-Gram", "N-Gram, Finetuned"], ordered=true, ) + + # Block out figure n_levels = length(unique(models.label)) + n_datasets = length(datasets) colorrange = (1, n_levels) - gl_coef = GridLayout(f[1, 1]) - gl_parity = GridLayout(f[2, 1]) + gl_coef = GridLayout(f[1, 1]) #range(1; length=n_datasets)]) + gl_prog_effect = GridLayout(f[2, 1]) + gl_parity = GridLayout(f[2, 1]) #range(1; length=n_datasets-1)]) + label_kwargs = (; + fontsize=10pt, font=:bold, + halign=:right, + tellheight=false, + ) + Label(f[1, 1, TopLeft()], "a)"; padding=(0, 15, 3, 0), label_kwargs...) + Label(f[2, 1, TopLeft()], "b)"; padding=(0, 15, 3, 0), label_kwargs...) + # Label(f[2, 2, TopLeft()], "c)"; padding=(0, 15, 5, 0), label_kwargs...) for (adx, ds) in enumerate(keys(datasets)) plt_ds = datasets[ds] ds_models = subset(models, :dataset => ByRow(==(ds))) metric = uppercase(ds_metric[ds]) - ci = hcat(map(m -> coefint(m, collect(keys(coefnames))), ds_models.model)...) - yticklabels = collect(values(coefnames)) - baseline = ci[1] - if relative - ci = ci ./ ci[[1], :] + better_sign = map(ds_models.metric) do metric + metric in ["auroc"] ? 1 : -1 end - # Drop intercept - ci = ci[2:end, :] - yticklabels = yticklabels[2:end] - - # # Flip so positive is better - # ci_sign = map(ds_models.metric) do metric - # metric in ["auroc"] ? 1 : -1 - # end - # ci = ci .* ci_sign' + val, ci, kwargs = val_ci_dodge(ds_models.model; + scale, + better_sign = positive_is_better ? better_sign : nothing, + dodge=levelcode.(ds_models.label), + coefs=keys(coefnames), + ) # Plot Effect sizes - lb, ub = round.(baseline.credible_interval; sigdigits=3) + lb, ub = round.(kwargs.baseline.credible_interval; sigdigits=3) if lb != ub title = "$plt_ds\n$metric: $lb - $ub" else title = "$plt_ds\n$metric: $lb" end + yticks = (first(kwargs.yticks), map(k -> get(coefnames, k, k), last(kwargs.yticks))) ax = Axis(gl_coef[1, adx]; title, - yticks=(eachindex(yticklabels), yticklabels), + yticks=yticks, yticklabelsvisible = adx == 1, yticksvisible=adx == 1, - xlabel="Relative Effect Size", + xlabel=kwargs.effectlabel, xticks=WilkinsonTicks(3), xminorticksvisible=true, - xtickformat="{:.0%}", + xtickformat=kwargs.effectformat, ) - val = vec(repeat(1:size(ci, 1), 1, size(ci, 2))) - dodge = levelcode.(ds_models.label) - dodge = vec(repeat(dodge', size(ci, 1), 1)) - ci = vec(ci) - val = val[(!ismissing).(ci)] - dodge = dodge[(!ismissing).(ci)] - ci = ci[(!ismissing).(ci)] - effectbars!(ax, val, ci; dodge, color=dodge, colormap, colorrange) + effectbars!(ax, val, ci; dodge=kwargs.dodge, color=kwargs.dodge, colormap, colorrange) if adx == 1 h = map(enumerate(levels(models.label))) do (color, label) @@ -99,7 +99,7 @@ function figure_fe_models(models, prog_models; relative=true, colormap=:tab10) tellheight=false, tellwidth=false, valign=:bottom, - halign=:right, + halign=positive_is_better ? :left : :right, margin=(2, 2, 2, 2), alignmode=Outside(), labelsize=6pt, @@ -124,33 +124,81 @@ function figure_fe_models(models, prog_models; relative=true, colormap=:tab10) ablines!(ax, 0, 1; color=:black, linestyle=:dash, label="Parity") scatter!(ax, predict(qm), response(qm)) end - end - # Add Labels - label_kwargs = (; - fontsize=12pt, font=:bold, - halign=:right, - tellheight=false, - ) - Label(f[1, 1, TopLeft()], "A)"; padding=(0, 15, -5, 0), label_kwargs...) - Label(f[2, 1, TopLeft()], "B)"; padding=(0, 15, -5, 0), label_kwargs...) - # Label(f[1, 2, TopLeft()], "C)"; padding=(0, 5, -5, 0), label_kwargs...) - - # Label(f[2, :], "Effect Size"; - # tellwidth=false, tellheight=true, - # font=:bold, - # fontsize=10pt, + # # Plot Prog Effect Size + # prog_effect = stack(coefint, prog_models.loss_and_info_and_ft) + # val, ci, kwargs = val_ci_dodge(prog_models.loss_and_info_and_ft; + # scale=:std2, + # better_sign=map(ds -> ds in ["tmQM", "sider", "QM9", "lipo"] ? -1 : 1, prog_models.dataset) # ) - - # h = map(enumerate(levels(models.label))) do (color, label) - # PolyElement(; label, color, colormap, colorrange) - # end - # Legend(f[end, 1], h, labels(h); - # # orientation=:horizontal, - # tellheight=false, - # tellwidth=false, + # ax = Axis(gl_prog_effect[1,1]; + # yticks=kwargs.yticks, + # limits=(nothing, (0, 4)), + # ) + # effectbars!(ax, val, ci; + # dodge=kwargs.dodge, + # color=kwargs.dodge, + # colormap, + # colorrange=(1, size(prog_effect, 2)) # ) return f end + +function val_ci_dodge(models; dodge::Union{AbstractVector,Nothing}=nothing, scale=:absolute, coefs=Colon(), better_sign=nothing) + names = coefs isa Colon ? union(coefnames.(models)...) : collect(coefs) + ci = stack(m -> coefint(m, names), models) + baseline = ci[1] + + # Rescale coefficients + effectlabel = "Effect Size" + effectformat = "{:.2f}" + if scale == :relative + ci = ci ./ ci[[1], :] + effectlabel = "Relative Effect Size" + effectformat = "{:.0%}" + elseif scale == :std + ci = ci ./ map(std∘response, models)' + effectlabel = "Effect Size (Std. Dev.)" + elseif scale == :std2 + response_std = map(std∘response, models)' + coef_std = stack(m -> vec(std(modelmatrix(m); dims=1)), models) + ci = ci .* (coef_std ./ response_std) + elseif scale == :coef_scale + ci = ci ./ span.(ci[1, :])' + elseif scale != :absolute + error("unknown scale $scale") + end + + # Drop intercept + ci = ci[2:end, :] + names = names[2:end] + + # Flip so positive is better + if !isnothing(better_sign) + ci = ci .* vec(better_sign)' + effectlabel = "Pos. " * effectlabel + end + + # Pack results + val = vec(repeat(1:size(ci, 1), 1, size(ci, 2))) + if isnothing(dodge) + dodge = 1:size(ci, 2) + end + dodge = vec(repeat(dodge', size(ci, 1), 1)) + ci = vec(ci) + val = val[(!ismissing).(ci)] + dodge = dodge[(!ismissing).(ci)] + ci = ci[(!ismissing).(ci)] + + kwargs = (; + dodge, + effectlabel, + effectformat, + yticks = (1:length(names), names), + baseline, + ) + return val, ci, kwargs +end + diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index a378ed2b..6469589f 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -354,177 +354,7 @@ function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) :model => ByRow(m -> corspearman(response(m), predict(m))) => :spearman, :model => ByRow(nobs_nonwts) => :nobs, ) -<<<<<<< Updated upstream return df_model, df_predict -||||||| Stash base - return df_model - - tbl_datasets = ["REALSpace", "tmQM", "qm9", "bbbp", "hiv"] - models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) - models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) - sort!(models_tbl, [:dataset, :ngram]) - transform!(models_tbl, - :metric => ByRow(m -> replace(m, - "auroc" => L"AUROC ($\uparrow$)", - "mae" => L"MAE ($\downarrow$)", - "CE" => L"CE ($\downarrow$)", - )) => :metric - ) - - return regtable( - models_tbl.model...; - groups=string.(models_tbl.dataset), - stat_below=false, - print_depvar=false, - number_regressions=false, - align=:r, - regression_statistics=[ - nobs_nonwts => "N", - (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", - (model -> cor(response(model), predict(model))) => L"R^2",], - extralines=[ - ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], - ["Metric:", models_tbl.metric...], - [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], - ], - labels=Dict( - "tokenizer_class: spe" => "SPE/APE Tokenizers", - "tokenizer_class: bpe" => "BPE Tokenizers", - "tokenizer_class: smirk" => "Smirk", - "tokenizer_class: smirk-gpe" => "Smirk-GPE", - "tokenizer_class: character" => "Character-level", - "tokenizer_class: unigram" => "Unigram Tokenizers", - "encoding: selfies" => "SELFIES", - "encoding: smiles-canonical" => "Canonical SMILES", - ), - # render=LatexTable(), - ) - - - - # Test impact of encoding on pretraining - null = lm(@formula(val_loss ~ 1), df) - @show tok = lm( - @formula(val_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - @show tok_ngram = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - LogLikelihoodRatioTest(tok_ngram, null) |> display - - # Test impact of encoding on pretraining - tokenizers = tokenizers_info(stats_dir) - best_models.tokenizer_class = map(tok -> tokenizers[tok]["tokenizer_class"], best_models.tokenizer) - best_models.encoding = map(tok -> tokenizers[tok]["encoding"], best_models.tokenizer) - replace!(best_models.encoding, - "smiles" => "SMILES", - "smiles-canonical" => "Canonical SMILES", - "selfies" => "SELFIES" - ) - - null = lm(@formula(ngram_token_loss ~ 1), best_models) - @show tok = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - best_models; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - - - # Predict pretraining loss using n-gram model - null = glm(@formula(val_loss ~ 1), df, Normal(), LogLink()) - @show ngram = glm(@formula(val_loss ~ 1 + log(ngram_token_loss)), df, Normal(), LogLink(); contrasts) - LogLikelihoodRatioTest(ngram, null) |> display - r2(ngram.model, :devianceratio) |> display - - return df -======= - return df_model - - tbl_datasets = ["realspace", "tmQM", "qm9", "bbbp", "hiv"] - models_tbl = subset(models, :dataset => ByRow(in(tbl_datasets))) - models_tbl.dataset .= categorical(models_tbl.dataset, levels=tbl_datasets, ordered=true) - sort!(models_tbl, [:dataset, :ngram]) - transform!(models_tbl, - :metric => ByRow(m -> replace(m, - "auroc" => L"AUROC ($\uparrow$)", - "mae" => L"MAE ($\downarrow$)", - "CE" => L"CE ($\downarrow$)", - )) => :metric - ) - - return regtable( - models_tbl.model...; - groups=string.(models_tbl.dataset), - stat_below=false, - print_depvar=false, - number_regressions=false, - align=:r, - regression_statistics=[ - nobs_nonwts => "N", - (model -> corspearman(response(model), predict(model))) => L"Spearman's $\rho$", - (model -> cor(response(model), predict(model))) => L"R^2",], - extralines=[ - ["N-Gram:", map(x -> x ? "Yes" : "No", models_tbl.ngram)...], - ["Metric:", models_tbl.metric...], - [L"Spearman's $\rho$, N-Gram vs. FM:", models_tbl.rho...], - ], - labels=Dict( - "tokenizer_class: spe" => "SPE/APE Tokenizers", - "tokenizer_class: bpe" => "BPE Tokenizers", - "tokenizer_class: smirk" => "Smirk", - "tokenizer_class: smirk-gpe" => "Smirk-GPE", - "tokenizer_class: character" => "Character-level", - "tokenizer_class: unigram" => "Unigram Tokenizers", - "encoding: selfies" => "SELFIES", - "encoding: smiles-canonical" => "Canonical SMILES", - ), - # render=LatexTable(), - ) - - - - # Test impact of encoding on pretraining - null = lm(@formula(val_loss ~ 1), df) - @show tok = lm( - @formula(val_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - @show tok_ngram = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - df; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - LogLikelihoodRatioTest(tok_ngram, null) |> display - - # Test impact of encoding on pretraining - tokenizers = tokenizers_info(stats_dir) - best_models.tokenizer_class = map(tok -> tokenizers[tok]["tokenizer_class"], best_models.tokenizer) - best_models.encoding = map(tok -> tokenizers[tok]["encoding"], best_models.tokenizer) - replace!(best_models.encoding, - "smiles" => "SMILES", - "smiles-canonical" => "Canonical SMILES", - "selfies" => "SELFIES" - ) - - null = lm(@formula(ngram_token_loss ~ 1), best_models) - @show tok = lm( - @formula(ngram_token_loss ~ 1 + tokenizer_class + encoding), - best_models; contrasts - ) - LogLikelihoodRatioTest(tok, null) |> display - - - # Predict pretraining loss using n-gram model - null = glm(@formula(val_loss ~ 1), df, Normal(), LogLink()) - @show ngram = glm(@formula(val_loss ~ 1 + log(ngram_token_loss)), df, Normal(), LogLink(); contrasts) - LogLikelihoodRatioTest(ngram, null) |> display - r2(ngram.model, :devianceratio) |> display - - return df ->>>>>>> Stashed changes end function tmqm_finetune(stats_dir, dff, dft) From d9177eab7ce115080d24502cf14b44005b97c32a Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 8 May 2025 15:52:46 -0400 Subject: [PATCH 14/54] add precompile flag to submit jobs --- opt/TokenizerStats/submit_jobs.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 46ee7bf2..6992f41d 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -155,7 +155,7 @@ def active_jobs(self, file: Path) -> Optional[int]: job_file.unlink() return None - def run(self, dry_run=False, rate_limit=100): + def run(self, dry_run=False, rate_limit=100, preflight=list[Process]): outputs = { process.output: process for process in self.processes @@ -166,6 +166,11 @@ def run(self, dry_run=False, rate_limit=100): last_launch = time.time() jobs_launched = 0 tasks_launched = defaultdict(int) + + preflight_ids = [] + for process in preflight: + preflight_ids.append(process.launch([], dry_run)) + for file in topological_sort(self.work): # Skip input files if file not in outputs: @@ -183,6 +188,8 @@ def run(self, dry_run=False, rate_limit=100): # Get the process that writes to this file process = outputs[file] deps = self._get_deps(process, jobs) + deps.extend(preflight_ids) + deps = list(set(deps)) # Launch the process job_id = process.launch(deps, dry_run) @@ -361,6 +368,7 @@ def set_logging_level(verbosity): if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--dry-run", "-n", action="store_true") + parser.add_argument("--precompile", action="store_true") parser.add_argument("-v", "--verbose", action="count", default=0) parser.add_argument( "--realspace", type=str, default="/nfs/turbo/coe-venkvis/mist/realspace_v4_dev2" @@ -447,4 +455,8 @@ def set_logging_level(verbosity): ) wk.show() - wk.run(dry_run=args.dry_run) + preflight = [] + if args.precompile: + preflight.append(Process(["submit_precompile.sh"], [])) + + wk.run(dry_run=args.dry_run, preflight=preflight) From 6d5bd934ffc008c8632ef6bd62342c2d47c1d96d Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 8 May 2025 17:07:42 -0400 Subject: [PATCH 15/54] update figure --- opt/TokenizerStats/plots/plots.jl | 38 +++++++++++-- .../plots/src/SmirkPaperPlots.jl | 4 +- .../plots/src/figures/info_loss.jl | 56 +++++++++++-------- opt/TokenizerStats/plots/src/figures/ngram.jl | 17 ++++-- opt/TokenizerStats/plots/src/plot_utils.jl | 4 +- .../plots/src/tabulate_results.jl | 5 +- .../plots/src/tabulate_tokenizer.jl | 24 +++++--- opt/TokenizerStats/submit_precompile.sh | 1 + 8 files changed, 100 insertions(+), 49 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 624df7ee..cc788aeb 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -16,6 +16,30 @@ info_loss = SmirkPaperPlots.info_loss_stats(stats_dir) token_usage = SmirkPaperPlots.usage_stats(stats_dir) tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) +function intrinsic_metrics(token_usage) + tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) + token_usage = transform(token_usage, + [:unk_count, :tokens_seen] => ByRow(/) => :oov_rate, + ) + wmean = (x, s) -> mean(x, fweights(s)) + df = combine(groupby(token_usage, [:tokenizer, :dataset]), + :fertility => (x -> mean(reduce(merge, x))) => :fertility, + [:divergence, :samples] => wmean => :divergence, + [:f95_all, :samples] => wmean => :f95, + [:oov_rate, :tokens_seen] => wmean => :oov_rate, + [:normalized_entropy, :samples] => wmean => :normalized_entropy, + [:entropy, :samples] => wmean => :entropy, + ) + transform!(df, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + :tokenizer => ByRow(x -> tok_info[x]["domain"]) => :tokenizer_domain, + :tokenizer => ByRow(x -> tok_info[x]["encoding"]) => :encoding, + ) + return df +end +df_intrinsic = intrinsic_metrics(token_usage) +CSV.write(joinpath("stats", "intrinsic_metrics.csv"), df_intrinsic) + # Tokenizer Fertility by Dataset function fertility_summary(token_usage) tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) @@ -90,15 +114,17 @@ norm_entropy_summary(token_usage) |> display # Transformer Models dfp, dff, dft = SmirkPaperPlots.transformer_models(stats_dir) +df_ng, df_p, df_f = SmirkPaperPlots.df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) # NGrams Stats vs. FM Performance df_prog = SmirkPaperPlots.df_ngram_stats_v_fm_perf(tok_info, loss_stats, info_loss, df_f) CSV.write(joinpath("stats", "ngram_stats_vs_fm.csv"), df_prog) # Predictive and Fixed-Effect Models -fe_models, df_predict = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) +fe_models, df_predict = SmirkPaperPlots.ngram_vs_transformer_fits(df_ng, df_p, df_f) prog_models = SmirkPaperPlots.ngram_prognostic_fits(df_prog) JLD2.jldsave(joinpath("stats", "quality_models.jld2"); fe_models, df_predict, prog_models) +CSV.write(joinpath("stats", "ngram_vs_transformer.csv"), df_predict) # Tokenizer Summary df_tok = SmirkPaperPlots.tokenizer_summary(stats_dir; k=5) @@ -110,12 +136,12 @@ with_theme(SmirkPaperPlots.theme()) do # Token Usage savefig("token_usage", SmirkPaperPlots.figure_token_usage(stats_dir)) savefig("oov_rate", SmirkPaperPlots.figure_oov_rate(stats_dir)) + savefig("oov_rate_no_transcode", + SmirkPaperPlots.figure_oov_rate(stats_dir; include_transcode_errors=false) + ) savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) # Transformers vs. N-Grams - df = SmirkPaperPlots.ngram_vs_transformer_fits(stats_dir, loss_stats, dfp) - savefig("ngram_vs_transformer", SmirkPaperPlots.figure_ngram_vs_transformer(stats_dir, df)) - savefig("prognostic", SmirkPaperPlots.figure_fe_models(fe_models, prog_models)) savefig("prognostic_abs", SmirkPaperPlots.figure_fe_models(fe_models, prog_models; scale=:absolute)) savefig("prognostic_std", SmirkPaperPlots.figure_fe_models(fe_models, prog_models; scale=:std)) @@ -123,11 +149,11 @@ with_theme(SmirkPaperPlots.theme()) do # Transformer Model Summary f, df = SmirkPaperPlots.figure_tf_finetune(stats_dir, dff, dft) savefig("tf_finetune", f) - CSV.write(joinpath("stats/tf_model_summary.csv"), df) + CSV.write(joinpath("stats", "tf_model_summary.csv"), df) # N-Gram Analysis savefig("ngram_fits", SmirkPaperPlots.figure_ngram_fits(loss_stats, stats_dir)) - savefig("kl_v_info_loss", SmirkPaperPlots.figure_kl_v_info_loss(stats_dir, loss_stats, info_loss, df)) + savefig("kl_v_info_loss", SmirkPaperPlots.figure_kl_v_info_loss(tok_info, loss_stats, info_loss, df_f)) # Example n-gram predictions compunds = [ diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index 93cf21e3..50e284bd 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -2,6 +2,7 @@ module SmirkPaperPlots using Makie using OnlineStats +using StatsBase using OnlineStats: Moments using GLM using DataFrames @@ -9,9 +10,8 @@ using CairoMakie: CairoMakie using Colors: distinguishable_colors, weighted_color_mean, RGBA using CategoricalArrays: categorical using Format: format -using PythonCall: Py, pyconvert -using StatsBase: StatsBase, mean, stderr, mean_and_std, quantile, AbstractWeights, Weights, aweights, corspearman, cor using FreeTypeAbstraction: FreeTypeAbstraction, newface, FTFont +using PythonCall: Py, pyconvert using JLD2: jldopen using JSON: JSON using CategoricalArrays: categorical, levelcode diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index df920b20..533780ff 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -211,7 +211,6 @@ function tok_info_loss!(f, cb, tok::String, smi::String; ngram, ref_tok, ref_cod ) # Highlight the correct token - @info token_color for (code_pos, token_id) in enumerate(ref_code) color = ismissing(token_color) ? :black : token_color[code_pos] box_token!(ax, token_id, code_pos; linewidth=0.5, color) @@ -249,14 +248,13 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference stderr_info_loss=std(info_loss_moments) / sqrt(nobs(info_loss_moments)), ) end - @info info_loss # Summarize Transformer Model df_tf = deepcopy(df_tf) df_tf.dataset = map(d -> d == "tmQM" ? d : "MoleculeNet", string.(df_tf.dataset)) df_tf = combine(groupby(df_tf, [:tokenizer, :dataset])) do gdf - i = argmin(gdf.test_loss) - return (; test_loss=gdf.test_loss[i], test_loss_std=gdf.test_loss_std[i]) + i = argmin(gdf.mean) + return (; test_loss=gdf.mean[i], test_loss_std=gdf.std[i]) end df = innerjoin(model_loss, info_loss, on=[:tokenizer, :ngram, :dataset]) @@ -291,12 +289,13 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference display(df) - f = Figure(; size=72 .* (4.5, 3), figure_padding=(1, 1, 1, 4)) + f = Figure(; size=(3.42inch, 2.1inch), figure_padding=(1, 2, 1, 4)) + gl = GridLayout(f[1, 2]) ax = Axis(f[1, 1]; - limits=(nothing, (-0.1, nothing)), + limits=(nothing, (-0.1, 2048)), xlabel="Cross Entropy Loss [nats/token]", ylabel="Information Loss [nats/molecule]", - yticks=[0, 0.5, 2, 4, 16, 64, 256, 512], + yticks=[0, 0.5, 2, 4, 16, 64, 256, 512, 1024], yscale=Asinh(1), yminorticksvisible=true, yminorticks=IntervalsBetween(4), @@ -313,12 +312,10 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference marker=df.marker ) - ax_info = Axis(f[1, 2]; - limits=(nothing, (0, 1)), - ylabel="tmQM Test R2", + ax_info = Axis(gl[1, 1]; + ylabel="tmQM Avg. MAE", xlabel="Information Loss [nats/molecule]", yticks=LinearTicks(5), - ytickformat="{:.0%}", ) df = subset(df, :dataset => ByRow(==("tmQM"))) @@ -330,9 +327,12 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference ) # Check correlation + @show rho = corspearman(df.avg_info_loss, df.test_loss) + @info length(df.avg_info_loss) factorial(length(df.avg_info_loss)) + @show rho_p_value = mean(HypothesisTests.permutations(df.avg_info_loss)) do x + rho <= corspearman(x, df.test_loss) + end @show t = HypothesisTests.CorrelationTest(df.avg_info_loss, df.test_loss) - @show pvalue(t) - Label(f[1, 1, TopLeft()], "a)"; font=:bold, @@ -343,7 +343,7 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference Label(f[1, 2, TopLeft()], "b)"; font=:bold, halign=:right, - padding=(0, 25, -3, 0), + padding=(0, 10, -3, 0), tellwidth=false, ) @@ -354,26 +354,36 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference ngram_elements = map(enumerate(levels(df.dataset))) do (gdx, label) PolyElement(; label, color=gdx, colorrange=h.colorrange, colormap=h.colormap) end - ngram_labels = [x.label[] for x in ngram_elements] tokenizer_elements = map(levels(df.tokenizer)) do name MarkerElement(; marker=Dict(plt_tokenizers)[name], color=:black) end tokenizer_labels = map(levels(df.tokenizer)) do name_or_path - return tokenizers[name_or_path]["name"] + return tok_info[name_or_path]["name"] end - Legend(f[1, 2], - [ngram_elements, tokenizer_elements], - [ngram_labels, tokenizer_labels], - ["Dataset", "Tokenizer"]; + Legend(gl[2, 1], + [tokenizer_elements], + [tokenizer_labels], + ["Tokenizer"]; + nbanks=2, + tellheight=true, + tellwidth=false, + halign=:center, + valign=:center, + margin=(0, 0, -15, 0), + ) + Legend(f[1, 1], + [ngram_elements], + [labels(ngram_elements)], + ["Datset"]; nbanks=2, tellheight=false, tellwidth=false, - halign=:right, - valign=:bottom, + halign=:left, + valign=:top, margin=(2, 2, 2, 2), ) - + resize_to_layout!(f) return f end diff --git a/opt/TokenizerStats/plots/src/figures/ngram.jl b/opt/TokenizerStats/plots/src/figures/ngram.jl index 6ab317ea..cd379869 100644 --- a/opt/TokenizerStats/plots/src/figures/ngram.jl +++ b/opt/TokenizerStats/plots/src/figures/ngram.jl @@ -105,7 +105,7 @@ function collate_atomic_oov(key::Regex, results::Dict) return (covered / nobs), (failed_encode / nobs) end -function figure_oov_rate(stats_dir) +function figure_oov_rate(stats_dir; include_transcode_errors=true) datasets = OrderedDict( "Elements" => "elements", "Bonds" => "bonds", @@ -156,10 +156,17 @@ function figure_oov_rate(stats_dir) tok_info["oov_rate"] = Dict{String,Float64}() for (gdx, group_key) in enumerate(collect(values(datasets))) group_oov_rate, group_encode_rate = collate_atomic_oov(group_key, oov_stats) - append!(tok_id, (idx, idx)) - append!(oov_rate, (group_oov_rate, group_encode_rate)) - append!(ds_grp, (gdx, gdx)) - append!(enc_grp, (0, 1)) + if include_transcode_errors + append!(tok_id, (idx, idx)) + append!(oov_rate, (group_oov_rate, group_encode_rate)) + append!(ds_grp, (gdx, gdx)) + append!(enc_grp, (0, 1)) + else + push!(tok_id, idx) + push!(oov_rate, group_oov_rate) + push!(ds_grp, gdx) + push!(enc_grp, 0) + end end end diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 73809004..a2e1ffc8 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -127,8 +127,8 @@ function collate_token_usage(ids::AbstractVector{<:Integer}, counts::Dict{<:Abst counts = Dict((parse(Int, k) => v for (k, v) in pairs(counts))) return collate_token_usage(ids, counts; smoothing) end -collate_token_usage(counts::Dict, vocab_size::Integer; kwargs...) = collate_token_usage(0:vocab_size-1, counts; kwargs...) -function collate_token_usage(ids::AbstractVector{T}, counts::Dict{T,<:Integer}; smoothing) where {T} +collate_token_usage(counts::Dict, vocab_size::Integer; kwargs...) = collate_token_usage(collect(0:vocab_size-1), counts; kwargs...) +function collate_token_usage(ids::AbstractVector{T}, counts::Dict; smoothing) where {T} usage = Vector{Int}(undef, length(ids)) for (idx, token_id) in enumerate(ids) usage[idx] = get(counts, token_id, 0) + smoothing diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index 6469589f..0fb51625 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -270,10 +270,7 @@ end nobs_nonwts(model) = size(model.model.pp.X, 1) -zscore(x) = (x .- mean(x)) ./ std(x) - -function ngram_vs_transformer_fits(stats_dir, loss_stats, dfp, dff, dft) - df_ng, df_p, df_f = df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) +function ngram_vs_transformer_fits(df_ng, df_p, df_f) contrasts = Dict( :tokenizer_class => EffectsCoding(; base="atomwise"), :encoding => EffectsCoding(; base="smiles"), diff --git a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl index 8e3014cf..45ed2af9 100644 --- a/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl +++ b/opt/TokenizerStats/plots/src/tabulate_tokenizer.jl @@ -7,6 +7,7 @@ function usage_stats(stats_dir) dataset = splitpath(file)[end-1] dataset = dataset == "tmqm" ? "tmQM" : dataset unk_id = data["tokenizer"][:unk_token_id] + vocab_size = data["tokenizer"][:vocab_size] for split in ["train", "val", "test"] split ∉ keys(data) && continue @@ -18,12 +19,22 @@ function usage_stats(stats_dir) tokens_seen = sum(values(unigram)) nunique = CountMap(data[split]["fertility"], samples) - entropy = sum(values(unigram)) do c - p = c / tokens_seen + token_prob = values(unigram) ./ tokens_seen + entropy = sum(token_prob) do p -p * log(p) end normalized_entropy = entropy / log(data["tokenizer"][:vocab_size]) + divergence = 0.5 * sum(token_prob) do p + abs(p - (1 / vocab_size)) + end + + c_all = collate_token_usage(unigram, vocab_size; smoothing=0) + f95_all = tokens_seen .* percentile(c_all ./ tokens_seen, 5) + p_smooth = @. (c_all + 1) / (tokens_seen + length(c_all)) + f95_smooth = tokens_seen .* percentile(p_smooth, 5) + f95 = tokens_seen .* percentile(token_prob, 5) + push!(rows, (; file, tokenizer, @@ -33,15 +44,14 @@ function usage_stats(stats_dir) out_of_vocab=data[split]["out_of_vocab"], fertility, entropy, + divergence, + f95, + f95_all, + f95_smooth, normalized_entropy, nunique, unk_count, tokens_seen, - avg_fertility=mean(fertility), - std_fertility=std(fertility), - max_fertility=maximum(keys(data[split]["fertility"])), - avg_nunique=mean(nunique), - std_nunique=std(nunique), )) end end diff --git a/opt/TokenizerStats/submit_precompile.sh b/opt/TokenizerStats/submit_precompile.sh index 5db9a3a9..64f7ac77 100755 --- a/opt/TokenizerStats/submit_precompile.sh +++ b/opt/TokenizerStats/submit_precompile.sh @@ -18,3 +18,4 @@ env julia --color=no --startup-file=no --project -e 'using MPIPreferences; MPIPreferences.use_system_binary()' julia --color=no --startup-file=no --project -e 'using Pkg; Pkg.resolve(); Pkg.instantiate(); Pkg.precompile(timing=true)' julia --color=no --startup-file=no --project=plots -e 'using Pkg; Pkg.resolve(); Pkg.instantiate(); Pkg.precompile(timing=true)' +exit 0 From 934043124a0ed46a6250508034fca9d30a2f3484 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 8 May 2025 17:08:21 -0400 Subject: [PATCH 16/54] update deps and add intrinsic plots --- opt/TokenizerStats/plots/Project.toml | 6 +- opt/TokenizerStats/plots/plots.jl | 1 + .../plots/src/SmirkPaperPlots.jl | 1 + .../plots/src/figures/intrinsic.jl | 99 +++++++++++++++++++ opt/TokenizerStats/plots/src/plot_utils.jl | 11 ++- 5 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 opt/TokenizerStats/plots/src/figures/intrinsic.jl diff --git a/opt/TokenizerStats/plots/Project.toml b/opt/TokenizerStats/plots/Project.toml index 4274e2e0..e80338d2 100644 --- a/opt/TokenizerStats/plots/Project.toml +++ b/opt/TokenizerStats/plots/Project.toml @@ -28,9 +28,9 @@ TokenizerStats = "ca7eb560-02c0-4cfb-b1b2-ae9b1b03b10c" TokenizerStats = {path = "../"} [compat] -CairoMakie = "0.12" +Makie = "0.22" +GLM = "1.9" Clustering = "0.15" -Colors = "0.12.11" -DataFrames = "1.6" +DataFrames = "1.7" JSON = "0.21" julia = "1.11" diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index cc788aeb..bce6a26f 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -140,6 +140,7 @@ with_theme(SmirkPaperPlots.theme()) do SmirkPaperPlots.figure_oov_rate(stats_dir; include_transcode_errors=false) ) savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) + savefig("intrinsic_metrics", SmirkPaperPlots.figure_intrinsic(df_intrinsic)) # Transformers vs. N-Grams savefig("prognostic", SmirkPaperPlots.figure_fe_models(fe_models, prog_models)) diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index 50e284bd..f747f6eb 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -41,6 +41,7 @@ include("figures/ngram.jl") include("figures/transfromer.jl") include("figures/jaccard.jl") include("figures/fe_models.jl") +include("figures/intrinsic.jl") const CLASS_MARKER = Dict( diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl new file mode 100644 index 00000000..2cad2a83 --- /dev/null +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -0,0 +1,99 @@ +function figure_intrinsic(df) + f = Figure(; size=(3.42inch, 1.5inch)) + colgap!(f.layout, 3pt) + tokenizer_classes = OrderedDict( + "bpe, nlp" => "BPE, NLP", + "bpe" => "BPE, Chemistry", + "atomwise" => "Atom-wise", + "spe" => "SPE/APE", + "character" => "Character", + "unigram" => "Unigram", + "smirk-gpe" => "Smirk-GPE", + "smirk" => "Smirk", + ) + df = subset(df, :tokenizer_domain => ByRow(in(["nlp", "chemistry"]))) + df.tokenizer_class = map(df.tokenizer_domain, df.tokenizer_class) do domain, tokenizer_class + if domain == "chemistry" + return tokenizer_class + else + return "$tokenizer_class, $domain" + end + end + df.tokenizer_class = categorical(df.tokenizer_class, levels=(collect∘keys)(tokenizer_classes), ordered=true) + + df.dataset = map(ds -> ds in ["realspace", "tmQM"] ? ds : "MoleculeNet", df.dataset) + df.dataset = map(ds -> ds == "realspace" ? "REALSpace" : ds, df.dataset) + df.dataset = categorical(df.dataset, levels=["REALSpace", "tmQM", "MoleculeNet"], ordered=true) + colormap=:Set1_3 + colorrange=(1, 3) + + metrics = [:fertility, :divergence, :normalized_entropy, :oov_rate] + ax_kwargs = Dict( + :fertility => (; + title="Fertility", + limits=((0, nothing), nothing), + ), + :divergence => (; + title="Imbalance", + limits=((0.4, nothing), nothing), + xtickformat="{:.0%}", + ), + :oov_rate => (; + title="UNK Freq.", + limits=((0, 1), nothing), + xtickformat="{:.0%}", + xscale=my_sqrt, + ), + :normalized_entropy => (; + title=L"\eta", + limits=((0, 1), nothing), + xticks=[0.25, 0.75], + xtickformat="{:.0%}", + ), + ) + # ax_kwargs = Dict() + + for (mdx, metric) in enumerate(metrics) + kwargs = get(ax_kwargs, metric, (;)) + ax = Axis(f[1, mdx]; + yticks=(1:length(tokenizer_classes), collect(values(tokenizer_classes))), + yticksvisible=false, + yticklabelsvisible=mdx == 1, + xticklabelrotation=-pi/4, + xminorticks=IntervalsBetween(4), + xminorticksvisible=true, + kwargs... + ) + for (gdx, gdf) in enumerate(keys(tokenizer_classes)) + gdf = subset(df, :tokenizer_class => ByRow(==(gdf))) + x = gdx * ones(nrow(gdf)) + y = collect(gdf[!, metric]) + h = boxplot!(ax, x, y; + orientation=:horizontal, + dodge=levelcode.(gdf.dataset), + color=levelcode.(gdf.dataset), + show_outliers=false, + whiskerwidth=1pt, + colormap, + colorrange, + ) + end + end + ds_elements = map(enumerate(levels(df.dataset))) do (color, label) + PolyElement(; label, color, colorrange, colormap) + end + Legend(f[1, end], ds_elements, labels(ds_elements); + tellheight=false, tellwidth=false, orientation=:vertical, + framevisible=true, + margin=(2, 2, 2, 2), + fontsize=6pt, + patchsize=(6pt, 6pt), + padding=2pt, + rowgap=1pt, + valign=:top, + halign=:right, + ) + resize_to_layout!(f) + + return f +end diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index a2e1ffc8..5fb9b045 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -22,6 +22,8 @@ function theme() Lines=(; cycle=Cycle([:color, :linestyle], covary=true), ), + markersize=4pt, + linewidth=1pt, Axis=(; spinewidth=0.5, ylabelpadding=3pt, @@ -32,7 +34,7 @@ function theme() xtickwidth=0.5, xticksize=3, xminortickwidth=0.5, - xminorticksize=2, + xminorticksize=1.5, xgridwidth=0.5, ygridwidth=0.5, xminorgridwidth=0.5, @@ -69,7 +71,7 @@ function theme() colormap=[:red, :blue], whiskerwidth=8pt, noeffect_linewidth=1pt, - ) + ), ) end @@ -83,6 +85,11 @@ Makie.inverse_transform(m::Asinh) = x -> m.a * sinh(x / m.a) Makie.defined_interval(::Asinh) = Makie.defined_interval(identity) Makie.defaultlimits(m::Asinh) = (0.0, 10 * m.a) +my_sqrt(x) = sqrt(x) +Makie.inverse_transform(::typeof(my_sqrt)) = x -> x^2 +Makie.defaultlimits(::typeof(my_sqrt)) = (0.0, 10.0) +Makie.defined_interval(::typeof(my_sqrt)) = Makie.defined_interval(sqrt) + Makie.inverse_transform(::typeof(asinh)) = sinh Makie.defined_interval(::typeof(asinh)) = Makie.defined_interval(identity) Makie.defaultlimits(::typeof(asinh)) = (0.0, 10.0) From 1132d6085c4c6422b1de81761faf83b534fb8511 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 11:04:19 -0400 Subject: [PATCH 17/54] Add 90% percentile span to intrinsic metric plots --- opt/TokenizerStats/plots/plots.jl | 1 + .../plots/src/figures/intrinsic.jl | 66 ++++++++++++++++--- opt/TokenizerStats/plots/src/plot_utils.jl | 1 + opt/TokenizerStats/submit_jobs.py | 6 +- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index bce6a26f..16556666 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -141,6 +141,7 @@ with_theme(SmirkPaperPlots.theme()) do ) savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) savefig("intrinsic_metrics", SmirkPaperPlots.figure_intrinsic(df_intrinsic)) + savefig("intrinsic_metrics_95", SmirkPaperPlots.figure_intrinsic(df_intrinsic; p=95)) # Transformers vs. N-Grams savefig("prognostic", SmirkPaperPlots.figure_fe_models(fe_models, prog_models)) diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl index 2cad2a83..41a1abbe 100644 --- a/opt/TokenizerStats/plots/src/figures/intrinsic.jl +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -1,25 +1,38 @@ -function figure_intrinsic(df) +function figure_intrinsic(df; p=90) f = Figure(; size=(3.42inch, 1.5inch)) colgap!(f.layout, 3pt) tokenizer_classes = OrderedDict( - "bpe, nlp" => "BPE, NLP", - "bpe" => "BPE, Chemistry", - "atomwise" => "Atom-wise", - "spe" => "SPE/APE", + "nlp" => "NLP", "character" => "Character", "unigram" => "Unigram", + "bpe" => "BPE", + "atomwise" => "Atom-wise", + "spe" => "SPE/APE", "smirk-gpe" => "Smirk-GPE", "smirk" => "Smirk", ) - df = subset(df, :tokenizer_domain => ByRow(in(["nlp", "chemistry"]))) + df = deepcopy(df) df.tokenizer_class = map(df.tokenizer_domain, df.tokenizer_class) do domain, tokenizer_class if domain == "chemistry" return tokenizer_class + elseif domain in ["nlp", "nlp-science"] + return "nlp" else return "$tokenizer_class, $domain" end end - df.tokenizer_class = categorical(df.tokenizer_class, levels=(collect∘keys)(tokenizer_classes), ordered=true) + ckeys = collect∘keys + @info unique(df.tokenizer_class) + subset!(df, :tokenizer_class => ByRow(in(ckeys(tokenizer_classes)))) + df.tokenizer_class = categorical(df.tokenizer_class, levels=ckeys(tokenizer_classes), ordered=true) + + # Label classes with the number of examples + foreach(eachrow(combine(groupby(df, :tokenizer_class), :tokenizer => length∘unique => :nclass))) do r + (; tokenizer_class, nclass) = r + plt_class = tokenizer_classes[tokenizer_class] + tokenizer_classes[tokenizer_class] = "$plt_class (n=$nclass)" + end + @info tokenizer_classes df.dataset = map(ds -> ds in ["realspace", "tmQM"] ? ds : "MoleculeNet", df.dataset) df.dataset = map(ds -> ds == "realspace" ? "REALSpace" : ds, df.dataset) @@ -34,7 +47,7 @@ function figure_intrinsic(df) limits=((0, nothing), nothing), ), :divergence => (; - title="Imbalance", + title=L"Imbalance ($D$)", limits=((0.4, nothing), nothing), xtickformat="{:.0%}", ), @@ -51,11 +64,11 @@ function figure_intrinsic(df) xtickformat="{:.0%}", ), ) - # ax_kwargs = Dict() for (mdx, metric) in enumerate(metrics) kwargs = get(ax_kwargs, metric, (;)) ax = Axis(f[1, mdx]; + titlefont=:regular, yticks=(1:length(tokenizer_classes), collect(values(tokenizer_classes))), yticksvisible=false, yticklabelsvisible=mdx == 1, @@ -64,6 +77,16 @@ function figure_intrinsic(df) xminorticksvisible=true, kwargs... ) + vspan!(ax, + percentile(df[!, metric], (100 - p) / 2), + percentile(df[!, metric], (100 + p) / 2), + color=:black, + alpha=0.2, + ) + vlines!(ax, mean(df[!, metric]); + color=:black, + linestyle=:dash, + ) for (gdx, gdf) in enumerate(keys(tokenizer_classes)) gdf = subset(df, :tokenizer_class => ByRow(==(gdf))) x = gdx * ones(nrow(gdf)) @@ -93,7 +116,30 @@ function figure_intrinsic(df) valign=:top, halign=:right, ) - resize_to_layout!(f) return f end + +function vspan!(ax, lb, ub, n; kwargs...) + points = Point2f[(lb, 0), (lb, n), (ub, n), (ub, 0)] + poly!(ax, points; kwargs...) +end + +@recipe(VSpan, lb, ub) do scene + Attributes(; + ) +end + +function Makie.plot!(plt::VSpan) + ax= Makie.current_axis() + limits = ax.finallimits + p = lift(limits, plt[:lb], plt[:ub]) do limits, lb, ub + ly = limits.origin[2] + uy = limits.origin[2] + limits.widths[2] + ly, uy = ly < uy ? (ly, uy) : (uy, ly) + Point2f[(lb, ly), (lb, uy), (ub, uy), (ub, ly)] + end + poly!(plt, p; Makie.shared_attributes(plt, Poly)...) + return plt +end + diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 5fb9b045..e555405c 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -25,6 +25,7 @@ function theme() markersize=4pt, linewidth=1pt, Axis=(; + titlegap=2pt, spinewidth=0.5, ylabelpadding=3pt, yticksize=3, diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 6992f41d..16b9fb41 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -238,9 +238,9 @@ def show(self): REF_INFO_LOSS = [ "character", - "meta-llama/Meta-Llama-3.1-8B", - "google/gemma-7b", - "Xenova/gpt-4o", + # "meta-llama/Meta-Llama-3.1-8B", + # "google/gemma-7b", + # "Xenova/gpt-4o", ] From 519bb60a95ad1cd05098d0d173cf497d1d5660e1 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 13:46:08 -0400 Subject: [PATCH 18/54] update plots --- opt/TokenizerStats/plots/plots.jl | 1 + .../plots/src/SmirkPaperPlots.jl | 2 +- .../plots/src/figures/fe_models.jl | 9 +++-- .../plots/src/figures/info_loss.jl | 6 +--- .../plots/src/figures/intrinsic.jl | 5 +-- opt/TokenizerStats/plots/src/plot_utils.jl | 5 +++ opt/TokenizerStats/plots/src/prognostics.jl | 34 ++++++++++++++++++- .../plots/src/tabulate_results.jl | 3 +- .../plots/src/tokenizer_summary.jl | 2 +- 9 files changed, 53 insertions(+), 14 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 16556666..20059477 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -123,6 +123,7 @@ CSV.write(joinpath("stats", "ngram_stats_vs_fm.csv"), df_prog) # Predictive and Fixed-Effect Models fe_models, df_predict = SmirkPaperPlots.ngram_vs_transformer_fits(df_ng, df_p, df_f) prog_models = SmirkPaperPlots.ngram_prognostic_fits(df_prog) +display(prog_models[!, [:dataset, :finetuned, :all_rho, :all_rho_p]]) JLD2.jldsave(joinpath("stats", "quality_models.jld2"); fe_models, df_predict, prog_models) CSV.write(joinpath("stats", "ngram_vs_transformer.csv"), df_predict) diff --git a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl index f747f6eb..351aa5f0 100644 --- a/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl +++ b/opt/TokenizerStats/plots/src/SmirkPaperPlots.jl @@ -16,7 +16,7 @@ using JLD2: jldopen using JSON: JSON using CategoricalArrays: categorical, levelcode using HypothesisTests: HypothesisTests, HypothesisTest, VarianceEqualityTest, pvalue -using Distributions: Chisq, FDist, Normal +using Distributions: Chisq, FDist, Normal, TDist using OrderedCollections: OrderedDict using CSV: CSV using Clustering: hclust diff --git a/opt/TokenizerStats/plots/src/figures/fe_models.jl b/opt/TokenizerStats/plots/src/figures/fe_models.jl index 80f042f1..76ab19ae 100644 --- a/opt/TokenizerStats/plots/src/figures/fe_models.jl +++ b/opt/TokenizerStats/plots/src/figures/fe_models.jl @@ -109,17 +109,20 @@ function figure_fe_models(models, prog_models; scale=:relative, colormap=:tab10, # Plot Predictions if adx != 1 - qm = subset(prog_models, + row = subset(prog_models, :dataset => ByRow(==(ds)), :finetuned => ByRow(==(true)), ) |> only - qm = qm.loss_and_info_and_ft - rho = round(corspearman(predict(qm), response(qm)); sigdigits=3) + qm = row.loss_and_info_and_ft + spearman = SpearmanTTest(qm) + rho = round(spearman.rho; sigdigits=3) qm_r2 = round(r2(qm); sigdigits=3) ax = Axis(gl_parity[1, adx-1]; title = L"%$plt_ds, $R^2: %$qm_r2$ $\rho: %$rho$", ylabel="Transformer - $metric", xlabel="N-Gram Based Estimate $metric", + xticks=WilkinsonTicks(3), + yticks=WilkinsonTicks(3), ) ablines!(ax, 0, 1; color=:black, linestyle=:dash, label="Parity") scatter!(ax, predict(qm), response(qm)) diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index 533780ff..5b253686 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -327,11 +327,7 @@ function figure_kl_v_info_loss(tok_info, model_loss, info_loss, df_tf; reference ) # Check correlation - @show rho = corspearman(df.avg_info_loss, df.test_loss) - @info length(df.avg_info_loss) factorial(length(df.avg_info_loss)) - @show rho_p_value = mean(HypothesisTests.permutations(df.avg_info_loss)) do x - rho <= corspearman(x, df.test_loss) - end + @info "info loss vs. test loss spearman" SpearmanTTest(df.avg_info_loss, df.test_loss) @show t = HypothesisTests.CorrelationTest(df.avg_info_loss, df.test_loss) Label(f[1, 1, TopLeft()], "a)"; diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl index 41a1abbe..340679a0 100644 --- a/opt/TokenizerStats/plots/src/figures/intrinsic.jl +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -55,10 +55,11 @@ function figure_intrinsic(df; p=90) title="UNK Freq.", limits=((0, 1), nothing), xtickformat="{:.0%}", - xscale=my_sqrt, + xscale=sqrt, + xticks=[0.25, 0.5, 1], ), :normalized_entropy => (; - title=L"\eta", + title=L"Normalized Entropy ($\eta$)", limits=((0, 1), nothing), xticks=[0.25, 0.75], xtickformat="{:.0%}", diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index e555405c..fca3f51e 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -232,3 +232,8 @@ function Makie.plot!(plt::Powerlaw) lines!(plt, points; Makie.shared_attributes(plt, Lines)...) return plt end + +function siglevel(p::Real; cutoff=[0.05, 0.01, 0.001], symbol="*") + l = findlast(sort(cutoff; rev=true) .>= p) + return isnothing(l) ? "" : symbol ^ l +end diff --git a/opt/TokenizerStats/plots/src/prognostics.jl b/opt/TokenizerStats/plots/src/prognostics.jl index 752b31d1..ebe95e92 100644 --- a/opt/TokenizerStats/plots/src/prognostics.jl +++ b/opt/TokenizerStats/plots/src/prognostics.jl @@ -40,16 +40,48 @@ function ngram_prognostic_fits(df_prog) gdf = unstack(gdf, :finetuned, :ng_loss_avg, renamecols=x -> x ? :ng_ft_loss_avg : :ng_loss_avg) wts = aweights(inv.(gdf.fm_loss_std .^ 2)) loss_and_info_and_ft = lm(@formula(fm_loss_avg ~ 1 + ng_loss_avg + ng_ft_loss_avg + info_loss_avg), gdf; wts) - return (; loss_and_info_and_ft) + loss_and_ft = lm(@formula(fm_loss_avg ~ 1 + ng_loss_avg + ng_ft_loss_avg), gdf; wts) + return (; loss_and_info_and_ft, loss_and_ft) end leftjoin!(df, df_ft; on=:dataset) transform!(df, :loss_only => ByRow(r2), :loss_and_info => ByRow(r2), :loss_and_info_and_ft => ByRow(r2), + :loss_and_ft => ByRow(r2), + # [:loss_only, :loss_and_info_and_ft] => ByRow(ftests) => :ftests, + :loss_and_info_and_ft => ByRow(m -> SpearmanTTest(m).rho) => :all_rho, + :loss_and_info_and_ft => ByRow(pvalue∘SpearmanTTest) => :all_rho_p, :loss_only => ByRow(nobs_nonwts) => :nobs, :loss_only => ByRow(StatsBase.variation ∘ response) => :response_cv, ) return df end +function ftests(models...) + ftest(getfield.(models, :model)...) +end + +struct SpearmanTTest + rho::Float64 + t::Float64 + nobs::Int +end + +SpearmanTTest(model::StatsBase.RegressionModel) = SpearmanTTest(response(model), predict(model)) +function SpearmanTTest(x::AbstractVector, y::AbstractVector) + rho = corspearman(x, y) + nobs = length(x) + t = rho * sqrt((nobs - 2) / (1 - rho ^ 2)) + return SpearmanTTest(rho, t, nobs) +end + +HypothesisTests.pvalue(stt::SpearmanTTest) = pvalue(TDist(stt.nobs-2), stt.t) + +function Base.show(io::IO, mime::MIME"text/plain", stt::SpearmanTTest) + println(io, "ρ: $(round(stt.rho; sigdigits=3))") + println(io, "n: $(stt.nobs)") + println(io, "t: $(round(stt.t; sigdigits=3))") + println(io, "p-value: $(round(pvalue(stt); sigdigits=3))") +end + diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index 0fb51625..aeafe837 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -270,7 +270,8 @@ end nobs_nonwts(model) = size(model.model.pp.X, 1) -function ngram_vs_transformer_fits(df_ng, df_p, df_f) +function ngram_vs_transformer_fits(df_ng, df_p,) + df_ng, df_p, df_f = df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) contrasts = Dict( :tokenizer_class => EffectsCoding(; base="atomwise"), :encoding => EffectsCoding(; base="smiles"), diff --git a/opt/TokenizerStats/plots/src/tokenizer_summary.jl b/opt/TokenizerStats/plots/src/tokenizer_summary.jl index 55e4f95e..bb068e93 100644 --- a/opt/TokenizerStats/plots/src/tokenizer_summary.jl +++ b/opt/TokenizerStats/plots/src/tokenizer_summary.jl @@ -158,7 +158,7 @@ function report_tokenizer_summary_stats(stats_dir, model_loss, info_loss, usage_ \\begin{landscape} \\begin{table} \\resizebox{\\linewidth}{!}{% - \begin{tabular}{llllc|cc|cc|cc} + \\begin{tabular}{llllc|cc|cc|cc} & & & From b83b137c630e3c43e5916ab1b844628942037a21 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 17:28:18 -0400 Subject: [PATCH 19/54] add ngram statistics plot --- opt/TokenizerStats/plots/plots.jl | 3 +- opt/TokenizerStats/plots/src/coeff_plot.jl | 5 + .../plots/src/figures/info_loss.jl | 94 +++++++++++++++++++ .../plots/src/figures/intrinsic.jl | 2 +- .../plots/src/figures/transfromer.jl | 32 +------ opt/TokenizerStats/plots/src/plot_utils.jl | 74 ++++++++++++++- 6 files changed, 177 insertions(+), 33 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 20059477..324d85ad 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -141,8 +141,9 @@ with_theme(SmirkPaperPlots.theme()) do SmirkPaperPlots.figure_oov_rate(stats_dir; include_transcode_errors=false) ) savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) - savefig("intrinsic_metrics", SmirkPaperPlots.figure_intrinsic(df_intrinsic)) + savefig("intrinsic_metrics", SmirkPaperPlots.figure_intrinsic(df_intrinsic; p=90)) savefig("intrinsic_metrics_95", SmirkPaperPlots.figure_intrinsic(df_intrinsic; p=95)) + savefig("ngram_metrics", SmirkPaperPlots.figure_ngram_metrics(tok_info, loss_stats, info_loss) # Transformers vs. N-Grams savefig("prognostic", SmirkPaperPlots.figure_fe_models(fe_models, prog_models)) diff --git a/opt/TokenizerStats/plots/src/coeff_plot.jl b/opt/TokenizerStats/plots/src/coeff_plot.jl index 716a7479..81ff2a8f 100644 --- a/opt/TokenizerStats/plots/src/coeff_plot.jl +++ b/opt/TokenizerStats/plots/src/coeff_plot.jl @@ -7,6 +7,11 @@ lower(x::CoeffInternal) = first(x.credible_interval) upper(x::CoeffInternal) = last(x.credible_interval) StatsBase.mean(x::CoeffInternal) = x.expectation +function CoeffInternal(μ::Real, σ²::Real; p=0.95) + ci = quantile(Normal(μ, σ²), [(1 - p) / 2, (1 + p) / 2]) + return CoeffInternal(μ, tuple(ci...)) +end + function Base.:-(x::CoeffInternal, y::CoeffInternal) μ = mean(x) - mean(y) lb = lower(x) - upper(y) diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index 5b253686..b8c5ae30 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -474,3 +474,97 @@ function figure_info_loss_ref_tokenizer(model_loss) return f end + +function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) + mergereduce(x) = reduce(merge, x) + df_loss = subset(df_loss, :finetuned => ByRow(==(false)), :split=>ByRow(==("val"))) + df = leftjoin(df_loss, select(df_info, Not(:vocab_size)); + on=[:tokenizer, :dataset, :ngram], + makeunique=true, + ) + transform!(df, + :tokenizer => ByRow(x -> tok_info[x]["tokenizer_class"]) => :tokenizer_class, + :tokenizer => ByRow(x -> tok_info[x]["domain"]) => :tokenizer_domain, + :dataset => ByRow(x -> x ∈ ["realspace", "tmQM"] ? x : "MoleculeNet") => :dataset, + ) + label_tokenizers!(df) + label_datasets!(df) + + f = Figure(; size=(3.42inch, 2.1inch), figure_padding=(1, 8, 1, 4)) + ax = Axis(f[1, 1]; + xlabel="N-Gram Cross Entropy [nats/token]", + limits=((0, nothing), nothing), + yticks=categorical_ticks(df.tokenizer_class) + ) + colormap=:Set1_3 + colorrange=(1, 3) + + df_loss = combine(groupby(df, [:tokenizer_class, :dataset, :ngram]), + :loss_per_token_moments => mean∘mergereduce => :ng_loss, + :samples => sum => :samples, + ) + barplot!(ax, levelcode.(df_loss.tokenizer_class), df_loss.ng_loss; + dodge=levelcode.(df_loss.dataset), + color=levelcode.(df_loss.dataset), + stack=df_loss.ngram, + direction=:x, + colormap, + colorrange, + strokewidth=0.5pt, + strokecolor=:white, + bar_labels=format.("{:d}", df_loss.ngram), + label_position=:center, + label_color=:white, + label_size=6pt, + label_font=:bold, + ) + power2ticks(r) = (2.0 .^r, [L"2^{%$p}" for p in r]) + xticks = power2ticks(-1:2:12) + xticks = ([0, xticks[1]...], [L"0", xticks[2]...]) + ax = Axis(f[1, 2]; + xscale=Asinh(1), + limits=((0, 1024), nothing), + xticks=[0, 1, 4, 16, 64, 256, 1024], + xlabel="Information Loss [nats/molecule]", + xminorticksvisible=true, + xminorticks=IntervalsBetween(4), + ylabelvisible=false, + yticksvisible=false, + yticklabelsvisible=false, + ) + df_info = dropmissing(df, :info_loss_moments) + df_info = combine(groupby(df_info, [:tokenizer_class, :dataset, :ngram]), + :info_loss_moments => mean∘mergereduce => :info_loss, + :samples => sum => :samples, + ) + df_info = subset(df_info, :ngram => ByRow(==(ngram))) + barplot!(ax, levelcode.(df_info.tokenizer_class), df_info.info_loss; + dodge=levelcode.(df_info.dataset), + color=levelcode.(df_info.dataset), + # stack=df_info.ngram, + direction=:x, + colormap, + colorrange, + ) + + + ds_elements = map(enumerate(levels(df.dataset))) do (color, label) + PolyElement(; label, color, colorrange, colormap) + end + Legend(f[1, end], ds_elements, labels(ds_elements); + tellheight=false, tellwidth=false, orientation=:vertical, + framevisible=true, + margin=(2, 2, 2, 2), + fontsize=6pt, + patchsize=(6pt, 6pt), + padding=2pt, + rowgap=1pt, + valign=:top, + halign=:right, + ) + + resize_to_layout!(f) + + return f +end + diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl index 340679a0..8649c15f 100644 --- a/opt/TokenizerStats/plots/src/figures/intrinsic.jl +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -96,7 +96,7 @@ function figure_intrinsic(df; p=90) orientation=:horizontal, dodge=levelcode.(gdf.dataset), color=levelcode.(gdf.dataset), - show_outliers=false, + outliercolor=:black, whiskerwidth=1pt, colormap, colorrange, diff --git a/opt/TokenizerStats/plots/src/figures/transfromer.jl b/opt/TokenizerStats/plots/src/figures/transfromer.jl index 7260b2c2..3a1bbc86 100644 --- a/opt/TokenizerStats/plots/src/figures/transfromer.jl +++ b/opt/TokenizerStats/plots/src/figures/transfromer.jl @@ -113,7 +113,8 @@ function figure_tf_finetune(stats_dir, dff, dft) ytickformat="{:.0%}", xgridvisible=false, ) - h = _finetune_results!(ax, dfr.dataset, dfr.test_loss, dfr.tokenizer; + h = barploterrors!(ax, dfr.dataset, dfr.test_loss; + dodge=dfr.tokenizer, std=dfr.test_loss_std, colormap, colorrange=(1, length(colormap)), @@ -151,35 +152,6 @@ function figure_tf_finetune(stats_dir, dff, dft) return f, dff_all end -function _finetune_results!(ax, x, y, dodge; std=nothing, colormap, colorrange=nothing) - if isnothing(colorrange) - colorrange = extrema(levelcode.(dodge)) - end - - h = barplot!(ax, levelcode.(x), y; - dodge=levelcode.(dodge), - colormap, - colorrange, - color=levelcode.(dodge), - ) - - if !isnothing(std) - SmirkPaperPlots.dodgederrorbars!(ax, levelcode.(x), y, std; - dodge=h.dodge, - width=h.width, - n_dodge=h.n_dodge, - gap=h.gap, - dodge_gap=h.dodge_gap, - linewidth=1, - color=:black, - ) - end - - return h -end - -categorical_ticks(x) = (1:length(levels(x)), levels(x)) - function table_finetuned_models(stats_dir, dff, dft) dff = select(dff, [:id, :pretrained_id, :tokenizer, :task, :dataset, :encoding]) dff.benchmark = map(dff.dataset, dff.task) do dataset, task diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index fca3f51e..67efb21c 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -163,6 +163,7 @@ Makie.@recipe(DodgedErrorBars, x, y, error) do scene n_dodge=Makie.inherit(scene, :BarPlot, :n_dodge), gap=Makie.inherit(scene, :BarPlot, :gap), dodge_gap=Makie.inherit(scene, :BarPlot, :dodge_gap), + direction=:y, ) end @@ -171,7 +172,11 @@ function Makie.plot!(plt::DodgedErrorBars) first(Makie.compute_x_and_width(x, width, gap, dodge, n_dodge, dodge_gap)) end attr = Makie.shared_attributes(plt, Errorbars) - errorbars!(plt, x, plt[:y], plt[:error]; attr...) + if plt[:direction][] == :y + errorbars!(plt, x, plt[:y], plt[:error]; direction=:y, attr...) + else + errorbars!(plt, plt[:y], plt[:x], plt[:error]; direction=:x, attr...) + end return plt end @@ -237,3 +242,70 @@ function siglevel(p::Real; cutoff=[0.05, 0.01, 0.001], symbol="*") l = findlast(sort(cutoff; rev=true) .>= p) return isnothing(l) ? "" : symbol ^ l end + +function label_tokenizers!(df) + tokenizer_classes = OrderedDict( + "nlp" => "NLP", + "character" => "Character", + "unigram" => "Unigram", + "bpe" => "BPE", + "atomwise" => "Atom-wise", + "spe" => "SPE/APE", + "smirk-gpe" => "Smirk-GPE", + "smirk" => "Smirk", + ) + df.tokenizer_class = map(df.tokenizer_domain, df.tokenizer_class) do domain, tokenizer_class + if domain == "chemistry" + return tokenizer_class + elseif domain in ["nlp", "nlp-science"] + return "nlp" + else + return "$tokenizer_class, $domain" + end + end + ckeys = collect∘keys + subset!(df, :tokenizer_class => ByRow(in(ckeys(tokenizer_classes)))) + transform!(df, :tokenizer_class => ByRow(x -> tokenizer_classes[x]) => :tokenizer_class) + df.tokenizer_class = categorical(df.tokenizer_class, levels=(collect∘values)(tokenizer_classes), ordered=true) + return df +end + +function label_datasets!(df) + df.dataset = map(ds -> ds in ["realspace", "tmQM"] ? ds : "MoleculeNet", df.dataset) + df.dataset = map(ds -> ds == "realspace" ? "REALSpace" : ds, df.dataset) + df.dataset = categorical(df.dataset, levels=["REALSpace", "tmQM", "MoleculeNet"], ordered=true) + return df +end + +categorical_ticks(x) = (1:length(levels(x)), levels(x)) + +function barploterrors!(ax, x, y; dodge=Makie.automatic(), std=nothing, colormap, colorrange=nothing, direction=:y) + if isnothing(colorrange) + colorrange = extrema(levelcode.(dodge)) + end + + h = barplot!(ax, levelcode.(x), y; + dodge=levelcode.(dodge), + colormap, + colorrange, + color=levelcode.(dodge), + direction, + ) + + if !isnothing(std) + SmirkPaperPlots.dodgederrorbars!(ax, levelcode.(x), y, std; + dodge=h.dodge, + width=h.width, + n_dodge=h.n_dodge, + gap=h.gap, + dodge_gap=h.dodge_gap, + linewidth=1, + color=:black, + direction, + ) + end + + return h +end + + From c8e97ec1f9d8c906c6d101304edb6d130ce8d1ec Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 17:28:56 -0400 Subject: [PATCH 20/54] fixup and save tokenizer stats --- opt/TokenizerStats/plots/plots.jl | 1 + opt/TokenizerStats/plots/src/tabulate_results.jl | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 324d85ad..1ab2a644 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -15,6 +15,7 @@ loss_stats = SmirkPaperPlots.model_loss_stats(stats_dir) info_loss = SmirkPaperPlots.info_loss_stats(stats_dir) token_usage = SmirkPaperPlots.usage_stats(stats_dir) tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) +JLD2.jldsave(joinpath(stats_dir, "tokenizer_stats.jld2"); loss_stats, info_loss, token_usage, tok_info) function intrinsic_metrics(token_usage) tok_info = SmirkPaperPlots.tokenizers_info(stats_dir) diff --git a/opt/TokenizerStats/plots/src/tabulate_results.jl b/opt/TokenizerStats/plots/src/tabulate_results.jl index aeafe837..0fb51625 100644 --- a/opt/TokenizerStats/plots/src/tabulate_results.jl +++ b/opt/TokenizerStats/plots/src/tabulate_results.jl @@ -270,8 +270,7 @@ end nobs_nonwts(model) = size(model.model.pp.X, 1) -function ngram_vs_transformer_fits(df_ng, df_p,) - df_ng, df_p, df_f = df_ngrams_vs_transformer(stats_dir, loss_stats, dfp, dff, dft) +function ngram_vs_transformer_fits(df_ng, df_p, df_f) contrasts = Dict( :tokenizer_class => EffectsCoding(; base="atomwise"), :encoding => EffectsCoding(; base="smiles"), From 839fb3edf2f95b904f4480673d431de9baafdfd5 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 17:35:16 -0400 Subject: [PATCH 21/54] fix barploterrors --- opt/TokenizerStats/plots/src/figures/info_loss.jl | 5 ++++- opt/TokenizerStats/plots/src/figures/transfromer.jl | 5 ++--- opt/TokenizerStats/plots/src/plot_utils.jl | 4 +--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index b8c5ae30..a95965e9 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -494,7 +494,10 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) ax = Axis(f[1, 1]; xlabel="N-Gram Cross Entropy [nats/token]", limits=((0, nothing), nothing), - yticks=categorical_ticks(df.tokenizer_class) + yticks=categorical_ticks(df.tokenizer_class), + xminorticks=IntervalsBetween(4), + xminorticksvisible=true, + yticksvisible=false, ) colormap=:Set1_3 colorrange=(1, 3) diff --git a/opt/TokenizerStats/plots/src/figures/transfromer.jl b/opt/TokenizerStats/plots/src/figures/transfromer.jl index 3a1bbc86..6846cd1d 100644 --- a/opt/TokenizerStats/plots/src/figures/transfromer.jl +++ b/opt/TokenizerStats/plots/src/figures/transfromer.jl @@ -113,8 +113,7 @@ function figure_tf_finetune(stats_dir, dff, dft) ytickformat="{:.0%}", xgridvisible=false, ) - h = barploterrors!(ax, dfr.dataset, dfr.test_loss; - dodge=dfr.tokenizer, + h = barploterrors!(ax, dfr.dataset, dfr.test_loss, dfr.tokenizer; std=dfr.test_loss_std, colormap, colorrange=(1, length(colormap)), @@ -131,7 +130,7 @@ function figure_tf_finetune(stats_dir, dff, dft) yticks=LinearTicks(5), ytickformat="{:.0%}", ) - _finetune_results!(ax, dfc.dataset, dfc.test_loss, dfc.tokenizer; + barploterrors!(ax, dfc.dataset, dfc.test_loss, dfc.tokenizer; std=dfc.test_loss_std, colormap=h.colormap, colorrange=h.colorrange diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 67efb21c..15713efb 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -279,7 +279,7 @@ end categorical_ticks(x) = (1:length(levels(x)), levels(x)) -function barploterrors!(ax, x, y; dodge=Makie.automatic(), std=nothing, colormap, colorrange=nothing, direction=:y) +function barploterrors!(ax, x, y, dodge; std=nothing, colormap, colorrange=nothing) if isnothing(colorrange) colorrange = extrema(levelcode.(dodge)) end @@ -289,7 +289,6 @@ function barploterrors!(ax, x, y; dodge=Makie.automatic(), std=nothing, colormap colormap, colorrange, color=levelcode.(dodge), - direction, ) if !isnothing(std) @@ -301,7 +300,6 @@ function barploterrors!(ax, x, y; dodge=Makie.automatic(), std=nothing, colormap dodge_gap=h.dodge_gap, linewidth=1, color=:black, - direction, ) end From 687dae5089b648df066b2b699c7c2deeb9f0f767 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 17:31:15 -0400 Subject: [PATCH 22/54] fix typo --- opt/TokenizerStats/plots/plots.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index 1ab2a644..cdaa84ff 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -144,7 +144,7 @@ with_theme(SmirkPaperPlots.theme()) do savefig("jaccard", SmirkPaperPlots.figure_jaccard(stats_dir)) savefig("intrinsic_metrics", SmirkPaperPlots.figure_intrinsic(df_intrinsic; p=90)) savefig("intrinsic_metrics_95", SmirkPaperPlots.figure_intrinsic(df_intrinsic; p=95)) - savefig("ngram_metrics", SmirkPaperPlots.figure_ngram_metrics(tok_info, loss_stats, info_loss) + savefig("ngram_metrics", SmirkPaperPlots.figure_ngram_metrics(tok_info, loss_stats, info_loss)) # Transformers vs. N-Grams savefig("prognostic", SmirkPaperPlots.figure_fe_models(fe_models, prog_models)) From bc4f55d785f1463c22ebab62b758d596910a9bb0 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 9 May 2025 23:15:48 -0400 Subject: [PATCH 23/54] plot tweaks --- .../plots/src/figures/info_loss.jl | 20 +++++++++---- .../plots/src/figures/intrinsic.jl | 29 ++++++++++--------- opt/TokenizerStats/plots/src/plot_utils.jl | 16 +++++++++- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index a95965e9..725d0494 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -490,11 +490,22 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) label_tokenizers!(df) label_datasets!(df) + df_loss = combine(groupby(df, [:tokenizer_class, :dataset, :ngram]), + :loss_per_token_moments => mean∘mergereduce => :ng_loss, + :samples => sum => :samples, + ) + + plt_label = map(eachrow(combine(groupby(df, :tokenizer_class), :tokenizer => length∘unique => :nclass))) do r + (; tokenizer_class, nclass) = r + tokenizer_class => "$tokenizer_class (n=$nclass)" + end |> Dict + f = Figure(; size=(3.42inch, 2.1inch), figure_padding=(1, 8, 1, 4)) + yticks = (1:length(plt_label), [plt_label[k] for k in levels(df.tokenizer_class)]) ax = Axis(f[1, 1]; xlabel="N-Gram Cross Entropy [nats/token]", limits=((0, nothing), nothing), - yticks=categorical_ticks(df.tokenizer_class), + yticks, xminorticks=IntervalsBetween(4), xminorticksvisible=true, yticksvisible=false, @@ -502,10 +513,6 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) colormap=:Set1_3 colorrange=(1, 3) - df_loss = combine(groupby(df, [:tokenizer_class, :dataset, :ngram]), - :loss_per_token_moments => mean∘mergereduce => :ng_loss, - :samples => sum => :samples, - ) barplot!(ax, levelcode.(df_loss.tokenizer_class), df_loss.ng_loss; dodge=levelcode.(df_loss.dataset), color=levelcode.(df_loss.dataset), @@ -517,6 +524,7 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) strokecolor=:white, bar_labels=format.("{:d}", df_loss.ngram), label_position=:center, + label_align=(:center, :center), label_color=:white, label_size=6pt, label_font=:bold, @@ -554,7 +562,7 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) ds_elements = map(enumerate(levels(df.dataset))) do (color, label) PolyElement(; label, color, colorrange, colormap) end - Legend(f[1, end], ds_elements, labels(ds_elements); + Legend(f[1, 1], ds_elements, labels(ds_elements); tellheight=false, tellwidth=false, orientation=:vertical, framevisible=true, margin=(2, 2, 2, 2), diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl index 8649c15f..192d9c35 100644 --- a/opt/TokenizerStats/plots/src/figures/intrinsic.jl +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -22,7 +22,6 @@ function figure_intrinsic(df; p=90) end end ckeys = collect∘keys - @info unique(df.tokenizer_class) subset!(df, :tokenizer_class => ByRow(in(ckeys(tokenizer_classes)))) df.tokenizer_class = categorical(df.tokenizer_class, levels=ckeys(tokenizer_classes), ordered=true) @@ -32,7 +31,6 @@ function figure_intrinsic(df; p=90) plt_class = tokenizer_classes[tokenizer_class] tokenizer_classes[tokenizer_class] = "$plt_class (n=$nclass)" end - @info tokenizer_classes df.dataset = map(ds -> ds in ["realspace", "tmQM"] ? ds : "MoleculeNet", df.dataset) df.dataset = map(ds -> ds == "realspace" ? "REALSpace" : ds, df.dataset) @@ -41,27 +39,31 @@ function figure_intrinsic(df; p=90) colorrange=(1, 3) metrics = [:fertility, :divergence, :normalized_entropy, :oov_rate] + ylims = (0.5, length(tokenizer_classes) + 0.5) ax_kwargs = Dict( :fertility => (; - title="Fertility", - limits=((0, nothing), nothing), + xlabel="Fertility", + limits=((0, nothing), ylims), + xticks=WilkinsonTicks(3), ), :divergence => (; - title=L"Imbalance ($D$)", - limits=((0.4, nothing), nothing), + xlabel=L"Imbalance ($D$)", + limits=((0, nothing), ylims), xtickformat="{:.0%}", + xticks=WilkinsonTicks(3), ), :oov_rate => (; - title="UNK Freq.", - limits=((0, 1), nothing), + xlabel="UNK Freq.", + limits=((0, 1), ylims), xtickformat="{:.0%}", xscale=sqrt, xticks=[0.25, 0.5, 1], ), :normalized_entropy => (; - title=L"Normalized Entropy ($\eta$)", - limits=((0, 1), nothing), - xticks=[0.25, 0.75], + xlabel=L"Normalized Entropy ($\eta$)", + limits=((0, 1), ylims), + # xticks=[0.25, 0.75], + xticks=WilkinsonTicks(3), xtickformat="{:.0%}", ), ) @@ -69,11 +71,11 @@ function figure_intrinsic(df; p=90) for (mdx, metric) in enumerate(metrics) kwargs = get(ax_kwargs, metric, (;)) ax = Axis(f[1, mdx]; - titlefont=:regular, + xlabelfont=:regular, yticks=(1:length(tokenizer_classes), collect(values(tokenizer_classes))), yticksvisible=false, yticklabelsvisible=mdx == 1, - xticklabelrotation=-pi/4, + # xticklabelrotation=-pi/4, xminorticks=IntervalsBetween(4), xminorticksvisible=true, kwargs... @@ -97,7 +99,6 @@ function figure_intrinsic(df; p=90) dodge=levelcode.(gdf.dataset), color=levelcode.(gdf.dataset), outliercolor=:black, - whiskerwidth=1pt, colormap, colorrange, ) diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 15713efb..46fd4a25 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -61,6 +61,11 @@ function theme() markersize=8pt, marker=:x, ), + BarPlot=(; + whiskerwidth=1pt, + markersize=2pt, + medianlinewidth=0.5pt, + ), ErrrorBar=(; whiskerwidth=2, linewidth=0.5, @@ -243,7 +248,7 @@ function siglevel(p::Real; cutoff=[0.05, 0.01, 0.001], symbol="*") return isnothing(l) ? "" : symbol ^ l end -function label_tokenizers!(df) +function label_tokenizers!(df; include_count=true) tokenizer_classes = OrderedDict( "nlp" => "NLP", "character" => "Character", @@ -267,6 +272,15 @@ function label_tokenizers!(df) subset!(df, :tokenizer_class => ByRow(in(ckeys(tokenizer_classes)))) transform!(df, :tokenizer_class => ByRow(x -> tokenizer_classes[x]) => :tokenizer_class) df.tokenizer_class = categorical(df.tokenizer_class, levels=(collect∘values)(tokenizer_classes), ordered=true) + + # if include_count + # map(eachrow(combine(groupby(df, :tokenizer_class), :tokenizer => length∘unique => :nclass))) do r + # (; tokenizer_class, nclass) = r + # plt_class = tokenizer_classes[tokenizer_class] + # tokenizer_class => "$plt_class (n=$nclass)" + # end + # end + return df end From cda20bcdefc6e0a429d6306e65aa0cc9a8dc826e Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Sat, 10 May 2025 12:44:10 -0400 Subject: [PATCH 24/54] refactor: dedicated tokenizer dataset loading Instead of hooking into the training code, just do it manually - Way faster loading - Delay loading till mpi init - More smaller functions instead of repeat code --- electrolyte_fm/data_modules/molnet_dataset.py | 71 +- electrolyte_fm/data_modules/utils.py | 66 ++ opt/TokenizerStats/pyproject.toml | 20 +- opt/TokenizerStats/python/helper/__init__.py | 0 opt/TokenizerStats/python/helper/cache.py | 1 + opt/TokenizerStats/python/helper/loader.py | 112 ++ opt/TokenizerStats/python/helper/tokenizer.py | 1 + opt/TokenizerStats/python/helper/utils.py | 1 + opt/TokenizerStats/src/TokenizerStats.jl | 44 +- opt/TokenizerStats/src/atomic_oov.py | 4 +- opt/TokenizerStats/src/cli.jl | 46 +- opt/TokenizerStats/src/collect.jl | 159 +-- opt/TokenizerStats/uv.lock | 1030 ++--------------- 13 files changed, 404 insertions(+), 1151 deletions(-) create mode 100644 opt/TokenizerStats/python/helper/__init__.py create mode 120000 opt/TokenizerStats/python/helper/cache.py create mode 100644 opt/TokenizerStats/python/helper/loader.py create mode 120000 opt/TokenizerStats/python/helper/tokenizer.py create mode 120000 opt/TokenizerStats/python/helper/utils.py diff --git a/electrolyte_fm/data_modules/molnet_dataset.py b/electrolyte_fm/data_modules/molnet_dataset.py index f60d029e..dd3e86f7 100644 --- a/electrolyte_fm/data_modules/molnet_dataset.py +++ b/electrolyte_fm/data_modules/molnet_dataset.py @@ -1,10 +1,7 @@ -import logging - -from datasets import Dataset, DatasetDict, load_dataset -from rdkit.Chem.Scaffolds.MurckoScaffold import MurckoScaffoldSmiles -from sklearn.model_selection import GroupShuffleSplit +from datasets import Dataset, load_dataset from .property_prediction_dataset import PropertyPredictionDataModule +from .utils import scaffold_split, train_val_test_split _URLS = { "qm8": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/qm8.csv", @@ -85,67 +82,3 @@ def _get_dataset(self): return train_val_test_split(ds) else: raise ValueError(f"Unknown split {self.split}") - - -def train_val_test_split(ds, **kwargs): - ds_train_other = ds.train_test_split(test_size=0.2, seed=42, **kwargs) - ds_val_test = ds_train_other["test"].train_test_split( - test_size=0.5, seed=42, **kwargs - ) - return DatasetDict( - { - "train": ds_train_other["train"], - "validation": ds_val_test["train"], - "test": ds_val_test["test"], - } - ) - - -def scaffold_hash(smi: str) -> str: - try: - scaffold = MurckoScaffoldSmiles(smi) - except ValueError: - logging.warn("No scaffold for %s, using input smiles string", smi) - scaffold = smi - return scaffold - - -def scaffold_split(ds: Dataset, smi_column): - # Hash scaffolds and then bin into groups, maintains the scaffold split - # but reduces the compute - df = ds.map( - lambda x: {"scaffold": scaffold_hash(x)}, - input_columns=smi_column, - batched=False, - ).to_pandas(batched=False) - - # Split - train, other = next( - GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42).split( - df.index, groups=df["scaffold"].values - ) - ) - df_other = df.iloc[other] - val, test = next( - GroupShuffleSplit(n_splits=1, test_size=0.5, random_state=42).split( - df_other, groups=df.iloc[other]["scaffold"] - ) - ) - return DatasetDict( - { - "train": Dataset.from_pandas(df.iloc[train], preserve_index=False), - "validation": Dataset.from_pandas(df_other.iloc[val], preserve_index=False), - "test": Dataset.from_pandas(df_other.iloc[test], preserve_index=False), - } - ) - - -def strip_unk_tokens(encoding: dict, unk_token_id: int) -> dict: - """Remove unknown tokens from input""" - is_oov = [id == unk_token_id for id in encoding["input_ids"]] - out = {} - for k, v in encoding.items(): - assert len(v) == len(is_oov) - out[k] = [x for x, oov in zip(v, is_oov) if not oov] - out["is_oov"] = any(is_oov) - return out diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index 5a05ae6a..08139259 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -2,6 +2,8 @@ import random from typing import Optional, TypeVar from rdkit import Chem +from rdkit.Chem.Scaffolds.MurckoScaffold import MurckoScaffoldSmiles +from sklearn.model_selection import GroupShuffleSplit from datasets import Dataset, DatasetDict, IterableDatasetDict from datasets.distributed import split_dataset_by_node @@ -104,3 +106,67 @@ def encode_molecules( **kwargs, ) return ds.filter(lambda x: x[output_column] is not None, batched=False, **kwargs) + + +def train_val_test_split(ds, **kwargs): + ds_train_other = ds.train_test_split(test_size=0.2, seed=42, **kwargs) + ds_val_test = ds_train_other["test"].train_test_split( + test_size=0.5, seed=42, **kwargs + ) + return DatasetDict( + { + "train": ds_train_other["train"], + "validation": ds_val_test["train"], + "test": ds_val_test["test"], + } + ) + + +def scaffold_hash(smi: str) -> str: + try: + scaffold = MurckoScaffoldSmiles(smi) + except ValueError: + logging.warn("No scaffold for %s, using input smiles string", smi) + scaffold = smi + return scaffold + + +def scaffold_split(ds: Dataset, smi_column): + # Hash scaffolds and then bin into groups, maintains the scaffold split + # but reduces the compute + df = ds.map( + lambda x: {"scaffold": scaffold_hash(x)}, + input_columns=smi_column, + batched=False, + ).to_pandas(batched=False) + + # Split + train, other = next( + GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42).split( + df.index, groups=df["scaffold"].values + ) + ) + df_other = df.iloc[other] + val, test = next( + GroupShuffleSplit(n_splits=1, test_size=0.5, random_state=42).split( + df_other, groups=df.iloc[other]["scaffold"] + ) + ) + return DatasetDict( + { + "train": Dataset.from_pandas(df.iloc[train], preserve_index=False), + "validation": Dataset.from_pandas(df_other.iloc[val], preserve_index=False), + "test": Dataset.from_pandas(df_other.iloc[test], preserve_index=False), + } + ) + + +def strip_unk_tokens(encoding: dict, unk_token_id: int) -> dict: + """Remove unknown tokens from input""" + is_oov = [id == unk_token_id for id in encoding["input_ids"]] + out = {} + for k, v in encoding.items(): + assert len(v) == len(is_oov) + out[k] = [x for x, oov in zip(v, is_oov) if not oov] + out["is_oov"] = any(is_oov) + return out diff --git a/opt/TokenizerStats/pyproject.toml b/opt/TokenizerStats/pyproject.toml index d2cc6c99..792f6f77 100644 --- a/opt/TokenizerStats/pyproject.toml +++ b/opt/TokenizerStats/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "TokenizerStats" +name = "tokenizer_stats" version = "0.1.0" description = "Add your description here" readme = "README.md" @@ -8,13 +8,27 @@ authors = [ ] requires-python = ">=3.10, <3.14" dependencies = [ + "smirk", "mendeleev >= 0.16", "selfies == 2.1.2", "jsonnet ~= 0.20.0", - "electrolyte-fm", "smilespe==0.0.3", + "datasets~=3.5", "apetokenizer @ git+https://github.com/mikemayuare/apetokenizer.git@8b070d6278503bcf8ad2eedc1756ac34f4bbd981", + "transformers>=4.51.3", + "rdkit>=2025.3.1", + "scikit-learn>=1.6.1", ] +[build-system] +requires = ["setuptools>=42"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-dir = {"" = "python"} + +[tool.setuptools.packages.find] +where = ["python"] + [tool.uv.sources] -electrolyte-fm = { path = "../../", editable = true } +smirk = { git = "ssh://git@github.com/BattModels/smirk.git", rev = "dfdd9dc27c066f956b69d1f55c2d4717beb7838d" } diff --git a/opt/TokenizerStats/python/helper/__init__.py b/opt/TokenizerStats/python/helper/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/opt/TokenizerStats/python/helper/cache.py b/opt/TokenizerStats/python/helper/cache.py new file mode 120000 index 00000000..3f2ac92b --- /dev/null +++ b/opt/TokenizerStats/python/helper/cache.py @@ -0,0 +1 @@ +../../../../electrolyte_fm/utils/cache.py \ No newline at end of file diff --git a/opt/TokenizerStats/python/helper/loader.py b/opt/TokenizerStats/python/helper/loader.py new file mode 100644 index 00000000..93cac039 --- /dev/null +++ b/opt/TokenizerStats/python/helper/loader.py @@ -0,0 +1,112 @@ +from pathlib import Path +from dataclasses import dataclass +from datasets import load_dataset +from .tokenizer import load_tokenizer + +from .utils import ( + MolEncoding, + AbstractDataset, + maybe_shard_dataset, + encode_molecules, + is_fast, +) + + +@dataclass +class GlobalComm: + global_rank: int = 0 + world_size: int = 1 + + +MOLNET_URLS = { + "qm8": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/qm8.csv", + "qm9": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/qm9.csv", + "esol": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/delaney-processed.csv", + "freesolv": "https://deepchemdata.s3.us-west-1.amazonaws.com/datasets/freesolv.csv.gz", + "lipo": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/Lipophilicity.csv", + "muv": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/muv.csv.gz", + "hiv": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/HIV.csv", + "bace": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/bace.csv", + "bbbp": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/BBBP.csv", + "tox21": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/tox21.csv.gz", + "toxcast": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/toxcast_data.csv.gz", + "sider": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/sider.csv.gz", + "clintox": "https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/clintox.csv.gz", +} + + +def tokenizer_dataset( + tokenizer: str, + name_or_path: str, + encoding: str, + world_size: int = 1, + global_rank: int = 0, +) -> AbstractDataset: + tokenizer = load_tokenizer(tokenizer) + encoding = MolEncoding(encoding) + ds = get_dataset(name_or_path) + ds = maybe_shard_dataset(GlobalComm(global_rank, world_size), ds) + ds = encode_molecules(ds, "smi", encoding=encoding) + return ds.map( + tokenizer, + batched=is_fast(tokenizer), + input_columns="smi", + ) + + +def get_dataset(name_or_path: str): + if Path(name_or_path).is_dir(): + if "tmQM" in Path(name_or_path).parts: + return tmqm(name_or_path) + else: + return smiles_dataset(name_or_path) + else: + assert name_or_path in MOLNET_URLS.keys() + return molnet(name_or_path) + + +def molnet(name: str): + ds = load_dataset( + "csv", + name=name, + data_files=[MOLNET_URLS[name]], + split="train", + streaming=True, + save_infos=False, + ) + ds = ds.rename_column("smiles" if name != "bace" else "mol", "smi") + return ds.select_columns("smi") + + +def tmqm(name_or_path: str): + path = Path(name_or_path) + ds = load_dataset( + "arrow", + name=self.path.name, + data_files={ + "train": str(path.joinpath("train/*.arrow")), + "validation": str(path.joinpath("validation/*.arrow")), + "test": str(path.joinpath("test/*.arrow")), + }, + keep_in_memory=False, + streaming=True, + save_infos=False, + ) + return ds.select_columns("smiles").rename_column("smiles", "smi") + + +def smiles_dataset(name_or_path: str): + path = Path(name_or_path) + ds = load_dataset( + "text", + name=str(path.name), + data_files={ + "train": str(path.joinpath("data/train/*.txt")), + "validation": str(path.joinpath("data/val/*.txt")), + "test": str(path.joinpath("data/test/*.txt")), + }, + keep_in_memory=False, + streaming=True, + save_infos=False, + ) + return ds.rename_column("text", "smi") diff --git a/opt/TokenizerStats/python/helper/tokenizer.py b/opt/TokenizerStats/python/helper/tokenizer.py new file mode 120000 index 00000000..b053eac1 --- /dev/null +++ b/opt/TokenizerStats/python/helper/tokenizer.py @@ -0,0 +1 @@ +../../../../electrolyte_fm/utils/tokenizer.py \ No newline at end of file diff --git a/opt/TokenizerStats/python/helper/utils.py b/opt/TokenizerStats/python/helper/utils.py new file mode 120000 index 00000000..85c980d8 --- /dev/null +++ b/opt/TokenizerStats/python/helper/utils.py @@ -0,0 +1 @@ +../../../../electrolyte_fm/data_modules/utils.py \ No newline at end of file diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index b4bc9d37..06ecfb52 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -28,7 +28,49 @@ function find(dir, pattern) return found end -@annotate load_tokenizer(args...; kwargs...) = @pyconst(pyimport("electrolyte_fm.utils.tokenizer")).load_tokenizer(args...; kwargs...) + +const __loader = Ref{Py}() +function __init__() + __loader[] = pyimport("helper.loader") + __tokenizer[] = pyimport("helper.tokenizer") + return nothing +end + +tokenizer_dataset(args...; kwargs...) = __loader[].tokenizer_dataset(args...; kwargs...) +load_tokenizer(args...; kwargs...) = __tokenizer[].load_tokenizer(args...; kwargs...) + +struct DatasetConfig + name_or_path::String + tokenizer::String + encoding::String +end + +function dataset_split(dc::DatasetConfig, split::String; kwargs...) + return tokenizer_dataset(dc.tokenizer, dc.name_or_path, dc.encoding; kwargs...)[split] +end + +tokenizer_name(dc::DatasetConfig) = isdir(dc.tokenizer) ? basename(dc.tokenizer) : dc.tokenizer +function tokenizer(dc::DatasetConfig) + tok = load_tokenizer(dc.tokenizer) + info = (; + tokenizer_name = isdir(dc.tokenizer) ? basename(dc.tokenizer) : dc.tokenizer, + vocab_size=pyconvert(Int, length(tokenizer)), + unk_token_id=pyconvert(Union{Int,Nothing}, tokenizer.unk_token_id), + ) + return tok, info +end + +function dataset_name(dc::DatasetConfig) + if isdir(dc.name_or_path): + if "tmQM" in splitpath(dc.name_or_path) + return "tmQM" + else + return basename(dc.name_or_path) + end + end + return dc.name_or_path +end + function split_dataset_by_node(args...; kwargs...) m = @pyconst(pyimport("datasets.distributed")) return m.split_dataset_by_node(args...; kwargs...) diff --git a/opt/TokenizerStats/src/atomic_oov.py b/opt/TokenizerStats/src/atomic_oov.py index e916fc5b..1ae9d90a 100644 --- a/opt/TokenizerStats/src/atomic_oov.py +++ b/opt/TokenizerStats/src/atomic_oov.py @@ -10,7 +10,7 @@ from typing import Callable, Iterable, Optional import selfies -from build_vocab import ( +from .build_vocab import ( ALIPHATIC_ORGANIC, AROMATIC_ORGANIC, AROMATIC_SYMBOLS, @@ -21,7 +21,7 @@ from datasets import load_dataset from mendeleev import element -from electrolyte_fm.utils.tokenizer import PreTrainedTokenizerBase, load_tokenizer +from .tokenizer import PreTrainedTokenizerBase, load_tokenizer logging.basicConfig( level=logging.INFO, diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index 42633a4f..283ffca4 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -19,23 +19,16 @@ function common_args!(s) end -@annotate function get_dataset(name_or_path, tokenizer, encoding) - start = time() - tokenizer = isdir(tokenizer) ? realpath(tokenizer) : tokenizer - if isdir(name_or_path) +@annotate function get_dataset(name_or_path::String, tokenizer::String, encoding::String) + dataset_name = name_or_path + if isdir(name_or_path): if "tmQM" in splitpath(name_or_path) - dm = TokenizerStats.tmqm(name_or_path; tokenizer, encoding) dataset_name = "tmQM" else - dm = TokenizerStats.pretrain(name_or_path; tokenizer, encoding) dataset_name = basename(name_or_path) end - else - dm = TokenizerStats.molnet(name_or_path; tokenizer, encoding) - dataset_name = name_or_path end - @info "loaded $dataset_name in $(time() - start) s" - return dm, dataset_name + return DatasetConfig(name_or_path, tokenizer, encoding), dataset_name end function maybe_parse_env(T::Type, x::String) @@ -109,17 +102,13 @@ end # Run command if args["%COMMAND%"] == "distortion" - tokenizer = args_cmd["tokenizer"] - tokenizer_name = isdir(tokenizer) ? basename(tokenizer) : tokenizer - dm, dataset = get_dataset(args_cmd["dataset"], tokenizer, args_cmd["encoding"]) - avg_information_loss(dm, args_cmd["reference"], args_cmd["output"]) + ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + avg_information_loss(ds, args_cmd["reference"], args_cmd["output"]) elseif args["%COMMAND%"] == "loss" # Load the tokenizer - tokenizer = args_cmd["tokenizer"] - tokenizer_name = isdir(tokenizer) ? basename(tokenizer) : tokenizer - dm = () -> first(get_dataset(args_cmd["dataset"], tokenizer, args_cmd["encoding"])) - model_loss(dm, args_cmd["model"], args_cmd["output"]) + ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + model_loss(ds, args_cmd["model"], args_cmd["output"]) elseif args["%COMMAND%"] == "merge" directory = args_cmd["directory"] @@ -130,19 +119,17 @@ end merge_usage_stats(files; output) elseif args["%COMMAND%"] == "usage" - tokenizer = args_cmd["tokenizer"] - tokenizer_name = isdir(tokenizer) ? basename(tokenizer) : tokenizer - dm, dataset = get_dataset(args_cmd["dataset"], tokenizer, args_cmd["encoding"]) - splits = split(args_cmd["splits"], ",") + ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + splits = parse_splits(args_cmd["splits"]) # Distribute computation if args_cmd["mode"] == "mpi" - tabulate_dataset(dm, args_cmd["output"]; tokenizer_name, splits) + tabulate_dataset(ds, args_cmd["output"], splits) elseif args_cmd["mode"] == "batch" size = maybe_parse_env(Int, args_cmd["size"]) rank = maybe_parse_env(Int, args_cmd["rank"]) @info "Using batch mode: $rank of $size (0-indexed)" - job_array_usage_stats(dm, args_cmd["output"]; tokenizer_name, splits, size, rank) + job_array_usage_stats(ds, args_cmd["output"]; splits, size, rank) else error("Unknown mode $(args_cmd["mode"])") end @@ -151,6 +138,15 @@ end return 0 end +parse_splits(x::String) = parse_splits(split(x, ",")) +function parse_splits(x::Vector{String}) + if length(x) == 1 && first(x) + return ["val", "train", "test"] + else + return x + end +end + function merge_usage_stats(files::Vector{String}; output::String="merged.jld2", splits::Vector{String}=["train", "val", "test"]) merged = jldopen(output, "w") for file in files diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 2249fd16..428067fc 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -1,7 +1,3 @@ -# Maximum number of oov samples to track -const MAX_OOV_SAMPLES = 100 - - function tracked_stats() return (; fertility=CountMap(Int), @@ -40,40 +36,25 @@ function leader_reduce(f, x; comm=MPI.COMM_WORLD) return nothing end -function setup_dm_mpi(dm::Py, split::AbstractString; rank::Int=0, size::Int=1) - dm.trainer = (; global_rank=rank, world_size=size) - dm.setup("fit") - if split == "train" - ds = dm.train_dataset - elseif split == "val" - ds = dm.val_dataset - elseif split == "test" - ds = dm.test_dataset - else - throw(ArgumentError(lazy"Invalid split: $split")) - end - return ds -end - -function rank_usage_stats(datamodule, split; rank, size) - ds = setup_dm_mpi(datamodule, split; rank, size) - tokenizer = datamodule.tokenizer - unk_token_id = pyconvert(Int, tokenizer.unk_token_id) +function rank_usage_stats(dataset::DatasetConfig, split::String; global_rank::Int, world_size::Int) + ds = dataset_split(dataset, split; global_rank, world_size) + tokenizer, tok_info = tokenizer(dataset) + unk_token_id = tok_info.unk_token_id local_stats = tracked_stats() start_time = time() - @info "rank $rank: started processing $split" now() + @info "rank $global_rank: started processing $split" now() for (idx, example) in enumerate(ds) input_ids = pyconvert(Vector{Int}, example["input_ids"]) is_oov = unk_token_id in input_ids usage_stats!(local_stats, input_ids, is_oov) if idx % 1_000_000 == 0 elapsed = time() - start_time - @info "rank $rank on molecule $idx" idx elapsed idx / elapsed now() + @info "rank $global_rank on molecule $idx" idx elapsed idx / elapsed now() end end n_obs = nobs(local_stats[:fertility]) elapsed = time() - start_time - @info "Rank $rank has finished tokenizer stats" n_obs elapsed n_obs / elapsed now() + @info "Rank $global_rank has finished tokenizer stats" n_obs elapsed n_obs / elapsed now() return OnlineStats.Series(; fertility=local_stats.fertility, @@ -83,25 +64,19 @@ function rank_usage_stats(datamodule, split; rank, size) ) end -function job_array_usage_stats(datamodule::Py, out_file::AbstractString; tokenizer_name::AbstractString="", splits=["train"], size::Int=1, rank::Int=0) +function job_array_usage_stats(dataset::DatasetConfig, out_file::AbstractString; splits=["train"], world_size::Int=1, global_rank::Int=0) # Load Dataset and Tokenizer - tokenizer = datamodule.tokenizer - tokenizer_info = (; - name=tokenizer_name, - vocab_size=pyconvert(Int, length(tokenizer)), - unk_token_id=pyconvert(Union{Int,Nothing}, tokenizer.unk_token_id), - ) - - splits = (length(splits) == 1 && first(splits) == "all") ? ["val", "train", "test"] : splits - out_file = out_file * "_split_$(join(splits, "_"))_rank_$rank.jld2" + tokenizer, tokenizer_info = tokenizer(dataset) + out_file = out_file * "_split_$(join(splits, "_"))_rank_$global_rank.jld2" mkpath(dirname(out_file)) jldopen(out_file * ".tmp", "w+") do f f["tokenizer"] = tokenizer_info + f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) end for split in splits - tokenizer_stats = rank_usage_stats(datamodule, split; rank, size) - @info "Saving results for $split on rank $rank" now() + tokenizer_stats = rank_usage_stats(dataset, split; global_rank, world_size) + @info "Saving results for $split on rank $global_rank" now() jldopen(out_file * ".tmp", "a+") do f serialize_usage!(f, split, tokenizer_stats) end @@ -114,87 +89,80 @@ function job_array_usage_stats(datamodule::Py, out_file::AbstractString; tokeniz return 0 end -function tabulate_dataset(datamodule::Py, out_file::AbstractString; tokenizer_name::AbstractString="", splits=["train"]) +function tabulate_dataset(dataset::DatasetConfig, out_file::AbstractString, splits::Vector{String}=["train"]) # Setup mpi MPI.Init() comm = MPI.COMM_WORLD - rank = MPI.Comm_rank(comm) - size = MPI.Comm_size(comm) - @info "Rank $rank of $size is starting" now() + global_rank = MPI.Comm_rank(comm) + world_size = MPI.Comm_size(comm) + @info "Rank $global_rank of $world_size is starting" now() # Load Dataset and Tokenizer - tokenizer = datamodule.tokenizer - tokenizer_info = (; - name=tokenizer_name, - vocab_size=pyconvert(Int, length(tokenizer)), - unk_token_id=pyconvert(Union{Int,Nothing}, tokenizer.unk_token_id), - ) - splits = (length(splits) == 1 && first(splits) == "all") ? ["val", "train", "test"] : splits - if rank == 0 - @debug "rank $rank: created $out_file" now() + tokenizer, tokenizer_info = tokenizer(dataset) + if global_rank == 0 + @debug "rank $global_rank: created $out_file" now() mkpath(dirname(out_file)) jldopen(out_file * ".tmp", "w+") do f f["tokenizer"] = tokenizer_info + f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) end end MPI.Barrier(comm) for split in splits - rank_stats = rank_usage_stats(datamodule, split; rank, size) + rank_stats = rank_usage_stats(dataset, split; global_rank, world_size) tokenizer_stats = leader_reduce(merge!, rank_stats; comm) - if rank == 0 + if global_rank == 0 jldopen(out_file * ".tmp", "a+") do f serialize_usage!(f, split, tokenizer_stats) end - @info "rank $rank: saved results for $split" now() + @info "rank $global_rank: saved results for $split" now() end end - if rank == 0 + if global_rank == 0 mv(out_file * ".tmp", out_file; force=true) chmod(out_file, 0o444) - @info "Saved stats on rank $rank to $out_file" now() + @info "Saved stats on rank $global_rank to $out_file" now() end MPI.Barrier(comm) MPI.Finalize() - @debug "rank $rank: finished" now() + @debug "rank $global_rank: finished" now() return 0 end -function model_loss(constructor, ref_file::String, output::String) +function model_loss(dataset::DatasetConfig, ref_file::String, output::String) # Init MPI MPI.Init() comm = MPI.COMM_WORLD - rank = MPI.Comm_rank(comm) - size = MPI.Comm_size(comm) - @info "Rank $rank of $size is ready" now() + global_rank = MPI.Comm_rank(comm) + world_size = MPI.Comm_size(comm) + @info "Rank $global_rank of $world_size is ready" now() # Create datamodule - datamodule = constructor() + ds = tokenizer_dataset(dataset) + tok, tok_info = tokenizer(dataset) MPI.Barrier(comm) - @info "Rank $rank of $size is starting" now() + @info "Rank $global_rank of $world_size is starting" now() # Load Reference Tokenizer / n-gram model ngram, _, ref_info = load_ngram_model(ref_file) - rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" + global_rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" # Init Fit Stats tok = datamodule.tokenizer - if rank == 0 + if global_rank == 0 mkpath(dirname(output)) jldopen(output * ".tmp", "w+") do f - f["tokenizer"] = (; - vocab_size=pyconvert(Int, length(tok)), - unk_token_id=pyconvert(Union{Int,Nothing}, tok.unk_token_id), - ) + f["tokenizer"] = tok_info f["ref_tokenizer"] = ref_info + f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) end end MPI.Barrier(comm) for split in ["val", "train", "test"] - ds = setup_dm_mpi(datamodule, split; rank, size) stats = map(1:length(ngram)) do _ OnlineStats.Series(; moments=OnlineStats.Moments(), @@ -204,7 +172,7 @@ function model_loss(constructor, ref_file::String, output::String) end |> OnlineStats.Group stats = (; cross_entropy=deepcopy(stats), cross_entropy_per_token=deepcopy(stats)) - @info "rank $rank: started processing $split" now() + @info "rank $global_rank: started processing $split" now() loss = zeros(length(ngram)) start_time = time() for (idx, encoding) in enumerate(ds) @@ -214,29 +182,32 @@ function model_loss(constructor, ref_file::String, output::String) end fit!(stats.cross_entropy, tuple(loss)) fit!(stats.cross_entropy_per_token, tuple(loss ./ length(code))) - if idx % 1_000_000 == 0 && rank == 0 + if idx % 1_000_000 == 0 && global_rank == 0 elapsed = time() - start_time - @info "rank $rank on molecule $idx" idx elapsed idx / elapsed + @info "rank $global_rank on molecule $idx" idx elapsed idx / elapsed end end # Reduce stats over ranks stats = leader_reduce(merge!, OnlineStats.Group(; stats...); comm) - if rank == 0 + split_wall_time = time() - start_time + if global_rank == 0 jldopen(output * ".tmp", "a+") do f f[split] = (; samples=nobs(stats), cross_entropy=map(value, stats[:cross_entropy]), cross_entropy_per_token=map(value, stats[:cross_entropy_per_token]), + walltime=split_wall_time, + world_size, ) end end MPI.Barrier(comm) end - if rank == 0 + if global_rank == 0 mv(output * ".tmp", output; force=true) chmod(output, 0o444) - @info "rank $rank: saved stats to $output" now() + @info "rank $global_rank: saved stats to $output" now() end MPI.Barrier(comm) @@ -245,28 +216,25 @@ function model_loss(constructor, ref_file::String, output::String) end -@annotate function avg_information_loss(datamodule::Py, ref_file::String, output::String) +@annotate function avg_information_loss(dataset::DatasetConfig, ref_file::String, output::String; split="val") # Init MPI MPI.Init() comm = MPI.COMM_WORLD - rank = MPI.Comm_rank(comm) - size = MPI.Comm_size(comm) - @info "Rank $rank of $size is starting" now() + global_rank = MPI.Comm_rank(comm) + world_size = MPI.Comm_size(comm) + @info "Rank $global_rank of $world_size is starting" now() # Load Reference Tokenizer / n-gram model - tokenizer = datamodule.tokenizer + tokenizer, tok_info = tokenizer(dataset) ngram, ref_tok, ref_info = load_ngram_model(ref_file) - rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" + global_rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" - if rank == 0 + if global_rank == 0 mkpath(dirname(output)) jldopen(output * ".tmp", "w+") do f - tok = datamodule.tokenizer - f["tokenizer"] = (; - vocab_size=pyconvert(Int, length(tok)), - unk_token_id=pyconvert(Union{Int,Nothing}, tok.unk_token_id), - ) + f["tokenizer"] = tokenizer_info f["ref_tokenizer"] = ref_info + f["system"] = (; world_size) end end @@ -280,25 +248,26 @@ end end |> OnlineStats.Group info_loss = zeros(length(ngram)) - @info "rank $rank: started processing" now() - ds = setup_dm_mpi(datamodule, "val"; rank, size) + @info "rank $global_rank: started processing" now() + ds = dataset_split(dataset, split; global_rank, world_size) MPI.Barrier(comm) - smi_column = pyconvert(String, datamodule.smi_column) start_time = time() for (idx, encoding) in enumerate(ds) - info_loss = unk_information_loss(ngram, ref_tok, tokenizer, encoding; smi_column) + info_loss = unk_information_loss(ngram, ref_tok, tokenizer, encoding; smi_column="smi") fit!(stats, tuple(info_loss)) - if idx % 100 == 0 && rank == 0 + if idx % 100 == 0 && global_rank == 0 elapsed = time() - start_time - @info "rank $rank on molecule $idx" idx elapsed idx / elapsed now() + @info "rank $global_rank on molecule $idx" idx elapsed idx / elapsed now() end end stats = leader_reduce(merge!, stats; comm) - if rank == 0 + walltime = time() - start_time + if global_rank == 0 jldopen(output * ".tmp", "a+") do f f["samples"] = nobs(stats) f["info_loss"] = map(value, stats) + f["walltime"] = walltime end mv(output * ".tmp", output; force=true) @info "saved results to $output" now() diff --git a/opt/TokenizerStats/uv.lock b/opt/TokenizerStats/uv.lock index 36781d93..06c86c4f 100644 --- a/opt/TokenizerStats/uv.lock +++ b/opt/TokenizerStats/uv.lock @@ -1,5 +1,4 @@ version = 1 -revision = 1 requires-python = ">=3.10, <3.14" resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'linux'", @@ -217,18 +216,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, ] -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -240,10 +227,9 @@ wheels = [ [[package]] name = "datasets" -version = "3.5.1" +version = "3.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp" }, { name = "dill" }, { name = "filelock" }, { name = "fsspec", extra = ["http"] }, @@ -259,28 +245,9 @@ dependencies = [ { name = "tqdm" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/af/8c1d10daf37383c32ab0a7461eaa4d5c7a3c47808fe5a8563744de002df7/datasets-3.5.1.tar.gz", hash = "sha256:f835b45dbbd7065c1191734b6f7c8d96fdf8c5751feaa5aa52b2a0dc43eea58f", size = 568915 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/89/d3d6fef58a488f8569c82fd293ab7cbd4250244d67f425dcae64c63800ea/datasets-3.6.0.tar.gz", hash = "sha256:1b2bf43b19776e2787e181cfd329cb0ca1a358ea014780c3581e0f276375e041", size = 569336 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/f5/668b3444a2f487b0052b908af631fe39eeb2bdb2359d9bbc2c3b80b71119/datasets-3.5.1-py3-none-any.whl", hash = "sha256:4074dda8dd6e9ece242b1580a8ef3928777d59ae1db144d911229e443a093cbb", size = 491436 }, -] - -[[package]] -name = "deepspeed" -version = "0.16.4+e2dc3eeb" -source = { git = "https://github.com/microsoft/DeepSpeed.git?rev=v0.16.4#e2dc3eeb1923073e32739596a4fd051417d4ff92" } -dependencies = [ - { name = "einops" }, - { name = "hjson" }, - { name = "msgpack" }, - { name = "ninja" }, - { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "py-cpuinfo" }, - { name = "pydantic" }, - { name = "torch" }, - { name = "tqdm" }, + { url = "https://files.pythonhosted.org/packages/20/34/a08b0ee99715eaba118cbe19a71f7b5e2425c2718ef96007c325944a1152/datasets-3.6.0-py3-none-any.whl", hash = "sha256:25000c4a2c0873a710df127d08a202a06eab7bf42441a6bc278b499c2f72cd1b", size = 491546 }, ] [[package]] @@ -304,92 +271,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 }, ] -[[package]] -name = "docker-pycreds" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/e6/d1f6c00b7221e2d7c4b470132c931325c8b22c51ca62417e300f5ce16009/docker-pycreds-0.4.0.tar.gz", hash = "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", size = 8754 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl", hash = "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49", size = 8982 }, -] - -[[package]] -name = "docstring-parser" -version = "0.16" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, -] - -[[package]] -name = "einops" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/81/df4fbe24dff8ba3934af99044188e20a98ed441ad17a274539b74e82e126/einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84", size = 54805 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/62/9773de14fe6c45c23649e98b83231fffd7b9892b6cf863251dc2afa73643/einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737", size = 64359 }, -] - -[[package]] -name = "electrolyte-fm" -version = "0.1.0" -source = { editable = "../../" } -dependencies = [ - { name = "datasets" }, - { name = "deepspeed" }, - { name = "jsonargparse", extra = ["jsonnet", "signatures"] }, - { name = "jsonnet" }, - { name = "lightning" }, - { name = "pydoe2" }, - { name = "pytorch-lightning" }, - { name = "rdkit" }, - { name = "rich" }, - { name = "safetensors" }, - { name = "scikit-learn" }, - { name = "selfies" }, - { name = "smirk" }, - { name = "torch" }, - { name = "torchmetrics" }, - { name = "transformers" }, - { name = "typer" }, - { name = "wandb" }, -] - -[package.metadata] -requires-dist = [ - { name = "datasets", specifier = ">=2.20" }, - { name = "deepspeed", git = "https://github.com/microsoft/DeepSpeed.git?rev=v0.16.4" }, - { name = "jsonargparse", extras = ["signatures", "jsonnet"], specifier = ">=4.27" }, - { name = "jsonnet", specifier = ">=0.20.0" }, - { name = "lightning", specifier = "~=2.5" }, - { name = "pydoe2", specifier = ">=1.3" }, - { name = "pytorch-lightning", specifier = "~=2.5" }, - { name = "rdkit", specifier = ">=2024.3.5" }, - { name = "rich", specifier = ">=13" }, - { name = "safetensors", specifier = ">=0.5.1" }, - { name = "scikit-learn", specifier = ">=1.5.1" }, - { name = "selfies", specifier = ">=2.1.0" }, - { name = "smirk", git = "ssh://git@github.com/BattModels/smirk.git?rev=dfdd9dc27c066f956b69d1f55c2d4717beb7838d" }, - { name = "torch", specifier = "~=2.6" }, - { name = "torchmetrics", specifier = "~=1.7" }, - { name = "transformers", specifier = ">=4.40" }, - { name = "typer", specifier = ">=0.9" }, - { name = "wandb", specifier = ">=0.16" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "pre-commit", specifier = ">=3.7" }, - { name = "pytest", specifier = ">=8" }, - { name = "ruff", specifier = ">=0.5.4" }, -] -plotting = [{ name = "matplotlib", specifier = "~=3.9" }] -tokenizers = [{ name = "smilespe", specifier = "==0.0.3" }] - [[package]] name = "fastprogress" version = "1.0.3" @@ -540,30 +421,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/97/b8253236dfedb9094f4273393a3fd03997da81f27f15822e56128da894ae/gensim-4.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5a52001226f9e89f7833503f99c9b4fd028fdf837002f24cdc1bc3cf901a4003", size = 24010152 }, ] -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, -] - -[[package]] -name = "gitpython" -version = "3.1.44" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, -] - [[package]] name = "greenlet" version = "3.2.1" @@ -616,30 +473,37 @@ wheels = [ ] [[package]] -name = "hjson" -version = "3.1.0" +name = "hf-xet" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2c/70009910fcbd204bde75842b60c1e47fe72edb0e978954cb8001735885c7/hf_xet-1.1.0.tar.gz", hash = "sha256:a7c2a4c2b6eee9ce0a1a367a82b60d95ba634420ef1c250addad7aa4af419cf4", size = 263996 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018 }, + { url = "https://files.pythonhosted.org/packages/dc/fd/0db331297e331f0f02005fd7ea666439bf15efd74f0dd62af02a43236a1b/hf_xet-1.1.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0322c42551e275fcb7949c083a54a81b2898e50787c9aa74284fcb8d2c58c12c", size = 5069444 }, + { url = "https://files.pythonhosted.org/packages/b9/7d/4d7ae44219d3744ad55669cb90ef3d4ed9f5f8a4729fa635a6499491cb78/hf_xet-1.1.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:667153a0304ac2debf2af95a8ff7687186f885b493f4cd16344869af270cd110", size = 4881465 }, + { url = "https://files.pythonhosted.org/packages/83/9a/d40d2a57b132d609d8a4ccc29e59ed69749021610616749cabcda2532158/hf_xet-1.1.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995eeffb119636ea617b96c7d7bf3c3f5ea8727fa57974574e25d700b8532d48", size = 53584225 }, + { url = "https://files.pythonhosted.org/packages/2e/01/d94553f91d85746e0862f24d239da88d10f5ce252b028565744e982432f4/hf_xet-1.1.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3aee847da362393331f515c4010d0aaa1c2669acfcca1f4b28946d6949cc0086", size = 52043680 }, + { url = "https://files.pythonhosted.org/packages/29/89/1f31853bf378f0ceb3363c07fd8a12af9b904b1f8c21e65eb5c19397bc98/hf_xet-1.1.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68c5813a6074aa36e12ef5983230e3b03148cce61e0fcdd294096493795565b4", size = 53072672 }, + { url = "https://files.pythonhosted.org/packages/b5/9f/5ecb92b18a4b2135a72a95dc08bcbeda9176f46642c745ee052420d2aea8/hf_xet-1.1.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ee9222bf9274b1c198b88a929de0b5a49349c4962d89c5b3b2f0f7f47d9761c", size = 53521053 }, + { url = "https://files.pythonhosted.org/packages/53/d6/cb32842cbf1cf5a154b41fa918a2fd86003af9bca227a2397cd7f312a8a6/hf_xet-1.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:73153eab9abf3d6973b21e94a67ccba5d595c3e12feb8c0bf50be02964e7f126", size = 4204376 }, ] [[package]] name = "huggingface-hub" -version = "0.30.2" +version = "0.31.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "packaging" }, { name = "pyyaml" }, { name = "requests" }, { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/22/8eb91736b1dcb83d879bd49050a09df29a57cc5cd9f38e48a4b1c45ee890/huggingface_hub-0.30.2.tar.gz", hash = "sha256:9a7897c5b6fd9dad3168a794a8998d6378210f5b9688d0dfc180b1a228dc2466", size = 400868 } +sdist = { url = "https://files.pythonhosted.org/packages/25/eb/9268c1205d19388659d5dc664f012177b752c0eef194a9159acc7227780f/huggingface_hub-0.31.1.tar.gz", hash = "sha256:492bb5f545337aa9e2f59b75ef4c5f535a371e8958a6ce90af056387e67f1180", size = 403036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/27/1fb384a841e9661faad1c31cbfa62864f59632e876df5d795234da51c395/huggingface_hub-0.30.2-py3-none-any.whl", hash = "sha256:68ff05969927058cfa41df4f2155d4bb48f5f54f719dd0390103eefa9b191e28", size = 481433 }, + { url = "https://files.pythonhosted.org/packages/3a/bf/6002da17ec1c7a47bedeb216812929665927c70b6e7500b3c7bf36f01bdd/huggingface_hub-0.31.1-py3-none-any.whl", hash = "sha256:43f73124819b48b42d140cbc0d7a2e6bd15b2853b1b9d728d4d55ad1750cac5b", size = 484265 }, ] [[package]] @@ -651,57 +515,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, -] - [[package]] name = "joblib" -version = "1.4.2" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 }, -] - -[[package]] -name = "jsonargparse" -version = "4.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/87/10/c77a54b0d7f3f7a3263b0f2d5e5697bce417dd08bc483a34369420456d10/jsonargparse-4.39.0.tar.gz", hash = "sha256:d72f9e84c251aeac6cd3cd5d91212f4a7c47be9a58e642dd3bce4b990543b360", size = 201775 } +sdist = { url = "https://files.pythonhosted.org/packages/30/08/8bd4a0250247861420a040b33ccf42f43c426ac91d99405374ef117e5872/joblib-1.5.0.tar.gz", hash = "sha256:d8757f955389a3dd7a23152e43bc297c2e0c2d3060056dad0feefc88a06939b5", size = 330234 } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/68/fe1c9ce7acb2155e2d94777dd906a08ba2cc60df2568cdb18c0fed94847c/jsonargparse-4.39.0-py3-none-any.whl", hash = "sha256:62ba3d5a66518b865252cfa285988e2ef8d0339de3cd2ad25f98465dc45d41f1", size = 223002 }, -] - -[package.optional-dependencies] -jsonnet = [ - { name = "jsonnet", marker = "python_full_version < '3.13' and os_name == 'posix'" }, - { name = "jsonnet-binary", marker = "python_full_version < '3.13' and os_name != 'posix'" }, - { name = "jsonnet-sdist", marker = "python_full_version >= '3.13'" }, -] -signatures = [ - { name = "docstring-parser" }, - { name = "typeshed-client" }, + { url = "https://files.pythonhosted.org/packages/da/d3/13ee227a148af1c693654932b8b0b02ed64af5e1f7406d56b088b57574cd/joblib-1.5.0-py3-none-any.whl", hash = "sha256:206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491", size = 307682 }, ] [[package]] @@ -710,131 +530,6 @@ version = "0.20.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a5/0e/76e5029265bc4bc4d99ad521fcb32a83070af29a0babc331ad181c299ee0/jsonnet-0.20.0.tar.gz", hash = "sha256:7e770c7bf3a366b97b650a39430450f77612e74406731eb75c5bd59f3f104d4f", size = 594173 } -[[package]] -name = "jsonnet-binary" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/1c/9ceeb537e74c6687a3ee3b96f651502061f6b9bd939034cd57038ea15333/jsonnet-binary-0.17.0.tar.gz", hash = "sha256:fbadf25f28161b0ccf29e0b72ef689790d14a9b23a681ab6846bd7cb12e17f1d", size = 294155 } - -[[package]] -name = "jsonnet-sdist" -version = "2024.6.23" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/58/28693aa624874f260e143fbd3e9c8fa9c9c6c043ecd09cbdb7282fc774b8/jsonnet-sdist-2024.6.23.tar.gz", hash = "sha256:37c88078ab3f3cec8aa2969e88fd2de278d4801abaa52e0a573458ae205370d9", size = 458479 } - -[[package]] -name = "lightning" -version = "2.5.1.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fsspec", extra = ["http"] }, - { name = "lightning-utilities" }, - { name = "packaging" }, - { name = "pytorch-lightning" }, - { name = "pyyaml" }, - { name = "torch" }, - { name = "torchmetrics" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/d0/fb3c5077efdd74c28ea3a277fd80bbf03738d866013a8637691138bfebca/lightning-2.5.1.post0.tar.gz", hash = "sha256:fda1ac63c283b3b08a54be8d905dd88469cf09e9845d36dd28b699e78911cbc8", size = 631113 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/1b/67201d693a575e8a086831710f33e697fab66166223f792e459ef2b84934/lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:a228a52ca52f0c5006ff327c92b8942f09e1aea3f2d9b0d7c8a209edd5b9e71d", size = 819001 }, -] - -[[package]] -name = "lightning-utilities" -version = "0.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "setuptools" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/bb/63a6a8c9e7a96b6ba92647fa5b1595c2dbee29f8178705adb4704d82ecba/lightning_utilities-0.14.3.tar.gz", hash = "sha256:37e2f83f273890052955a44054382c211a303012ee577619efbaa5df9e65e9f5", size = 30346 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/c1/31b3184cba7b257a4a3b5ca5b88b9204ccb7aa02fe3c992280899293ed54/lightning_utilities-0.14.3-py3-none-any.whl", hash = "sha256:4ab9066aa36cd7b93a05713808901909e96cc3f187ea6fd3052b2fd91313b468", size = 28894 }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - [[package]] name = "mendeleev" version = "1.0.0" @@ -855,67 +550,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/2c/5ca2d5c503b788b638c79308f5dfb1f4cb9fad95d070fc3c2e0b81ce8542/mendeleev-1.0.0-py3-none-any.whl", hash = "sha256:8474658ffa7025ac0367c791e715870a6db2756a47698f032178f978b2f1b2f1", size = 1377517 }, ] -[[package]] -name = "mpmath" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, -] - -[[package]] -name = "msgpack" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/f9/a892a6038c861fa849b11a2bb0502c07bc698ab6ea53359e5771397d883b/msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", size = 150428 }, - { url = "https://files.pythonhosted.org/packages/df/7a/d174cc6a3b6bb85556e6a046d3193294a92f9a8e583cdbd46dc8a1d7e7f4/msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", size = 84131 }, - { url = "https://files.pythonhosted.org/packages/08/52/bf4fbf72f897a23a56b822997a72c16de07d8d56d7bf273242f884055682/msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", size = 81215 }, - { url = "https://files.pythonhosted.org/packages/02/95/dc0044b439b518236aaf012da4677c1b8183ce388411ad1b1e63c32d8979/msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", size = 371229 }, - { url = "https://files.pythonhosted.org/packages/ff/75/09081792db60470bef19d9c2be89f024d366b1e1973c197bb59e6aabc647/msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", size = 378034 }, - { url = "https://files.pythonhosted.org/packages/32/d3/c152e0c55fead87dd948d4b29879b0f14feeeec92ef1fd2ec21b107c3f49/msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", size = 363070 }, - { url = "https://files.pythonhosted.org/packages/d9/2c/82e73506dd55f9e43ac8aa007c9dd088c6f0de2aa19e8f7330e6a65879fc/msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", size = 359863 }, - { url = "https://files.pythonhosted.org/packages/cb/a0/3d093b248837094220e1edc9ec4337de3443b1cfeeb6e0896af8ccc4cc7a/msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", size = 368166 }, - { url = "https://files.pythonhosted.org/packages/e4/13/7646f14f06838b406cf5a6ddbb7e8dc78b4996d891ab3b93c33d1ccc8678/msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", size = 370105 }, - { url = "https://files.pythonhosted.org/packages/67/fa/dbbd2443e4578e165192dabbc6a22c0812cda2649261b1264ff515f19f15/msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", size = 68513 }, - { url = "https://files.pythonhosted.org/packages/24/ce/c2c8fbf0ded750cb63cbcbb61bc1f2dfd69e16dca30a8af8ba80ec182dcd/msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", size = 74687 }, - { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, - { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, - { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, - { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 }, - { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 }, - { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 }, - { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 }, - { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 }, - { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, - { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, - { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, - { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, - { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, - { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, - { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, - { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, - { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, - { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, - { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, - { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, - { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, - { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, - { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, - { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, - { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, - { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, - { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, - { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, - { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, - { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, - { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, - { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, - { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, -] - [[package]] name = "multidict" version = "6.4.3" @@ -1031,39 +665,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, ] -[[package]] -name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, -] - -[[package]] -name = "ninja" -version = "1.11.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/d4/6b0324541018561c5e73e617bd16f20a4fc17d1179bb3b3520b6ca8beb7b/ninja-1.11.1.4.tar.gz", hash = "sha256:6aa39f6e894e0452e5b297327db00019383ae55d5d9c57c73b04f13bf79d438a", size = 201256 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/b1/3a61b348936b62a386465b1937cd778fa3a5748582e26d832dbab844ff27/ninja-1.11.1.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:b33923c8da88e8da20b6053e38deb433f53656441614207e01d283ad02c5e8e7", size = 279071 }, - { url = "https://files.pythonhosted.org/packages/12/42/4c94fdad51fcf1f039a156e97de9e4d564c2a8cc0303782d36f9bd893a4b/ninja-1.11.1.4-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cede0af00b58e27b31f2482ba83292a8e9171cdb9acc2c867a3b6e40b3353e43", size = 472026 }, - { url = "https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0", size = 422814 }, - { url = "https://files.pythonhosted.org/packages/e3/ad/fb6cca942528e25e8e0ab0f0cf98fe007319bf05cf69d726c564b815c4af/ninja-1.11.1.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3090d4488fadf6047d0d7a1db0c9643a8d391f0d94729554dbb89b5bdc769d7", size = 156965 }, - { url = "https://files.pythonhosted.org/packages/a8/e7/d94a1b60031b115dd88526834b3da69eaacdc3c1a6769773ca8e2b1386b5/ninja-1.11.1.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecce44a00325a93631792974659cf253a815cc6da4ec96f89742925dfc295a0d", size = 179937 }, - { url = "https://files.pythonhosted.org/packages/08/cc/e9316a28235409e9363794fc3d0b3083e48dd80d441006de66421e55f364/ninja-1.11.1.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c29bb66d2aa46a2409ab369ea804c730faec7652e8c22c1e428cc09216543e5", size = 157020 }, - { url = "https://files.pythonhosted.org/packages/e3/30/389b22300541aa5f2e9dad322c4de2f84be4e32aa4e8babd9160d620b5f1/ninja-1.11.1.4-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:055f386fb550c2c9d6157e45e20a84d29c47968876b9c5794ae2aec46f952306", size = 130389 }, - { url = "https://files.pythonhosted.org/packages/a9/10/e27f35cb92813aabbb7ae771b1685b45be1cc8a0798ce7d4bfd08d142b93/ninja-1.11.1.4-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:f6186d7607bb090c3be1e10c8a56b690be238f953616626f5032238c66e56867", size = 372435 }, - { url = "https://files.pythonhosted.org/packages/c2/26/e3559619756739aae124c6abf7fe41f7e546ab1209cfbffb13137bff2d2e/ninja-1.11.1.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:cf4453679d15babc04ba023d68d091bb613091b67101c88f85d2171c6621c6eb", size = 419300 }, - { url = "https://files.pythonhosted.org/packages/35/46/809e4e9572570991b8e6f88f3583807d017371ab4cb09171cbc72a7eb3e4/ninja-1.11.1.4-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:d4a6f159b08b0ac4aca5ee1572e3e402f969139e71d85d37c0e2872129098749", size = 420239 }, - { url = "https://files.pythonhosted.org/packages/e6/64/5cb5710d15f844edf02ada577f8eddfdcd116f47eec15850f3371a3a4b33/ninja-1.11.1.4-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:c3b96bd875f3ef1db782470e9e41d7508905a0986571f219d20ffed238befa15", size = 415986 }, - { url = "https://files.pythonhosted.org/packages/95/b2/0e9ab1d926f423b12b09925f78afcc5e48b3c22e7121be3ddf6c35bf06a3/ninja-1.11.1.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:cf554e73f72c04deb04d0cf51f5fdb1903d9c9ca3d2344249c8ce3bd616ebc02", size = 379657 }, - { url = "https://files.pythonhosted.org/packages/c8/3e/fd6d330d0434168e7fe070d414b57dd99c4c133faa69c05b42a3cbdc6c13/ninja-1.11.1.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:cfdd09776436a1ff3c4a2558d3fc50a689fb9d7f1bdbc3e6f7b8c2991341ddb3", size = 454466 }, - { url = "https://files.pythonhosted.org/packages/e6/df/a25f3ad0b1c59d1b90564096e4fd89a6ca30d562b1e942f23880c3000b89/ninja-1.11.1.4-py3-none-win32.whl", hash = "sha256:2ab67a41c90bea5ec4b795bab084bc0b3b3bb69d3cd21ca0294fc0fc15a111eb", size = 255931 }, - { url = "https://files.pythonhosted.org/packages/5b/10/9b8fe9ac004847490cc7b54896124c01ce2d87d95dc60aabd0b8591addff/ninja-1.11.1.4-py3-none-win_amd64.whl", hash = "sha256:4617b3c12ff64b611a7d93fd9e378275512bb36eff8babff7c83f5116b4f8d66", size = 296461 }, - { url = "https://files.pythonhosted.org/packages/b9/58/612a17593c2d117f96c7f6b7f1e6570246bddc4b1e808519403a1417f217/ninja-1.11.1.4-py3-none-win_arm64.whl", hash = "sha256:5713cf50c5be50084a8693308a63ecf9e55c3132a78a41ab1363a28b6caaaee1", size = 271441 }, -] - [[package]] name = "numpy" version = "1.26.4" @@ -1170,146 +771,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/67/1175790323026d3337cc285cc9c50eca637d70472b5e622529df74bb8f37/numpy-2.2.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169", size = 12859001 }, ] -[[package]] -name = "nvidia-cublas-cu12" -version = "12.6.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322 }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.6.80" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980 }, - { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972 }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380 }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690 }, - { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678 }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.5.1.17" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386 }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632 }, - { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622 }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.11.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103 }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.7.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010 }, - { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000 }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790 }, - { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780 }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367 }, - { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357 }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796 }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.26.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755 }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.6.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971 }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276 }, - { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265 }, -] - [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] @@ -1438,15 +906,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, ] -[[package]] -name = "platformdirs" -version = "4.3.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, -] - [[package]] name = "propcache" version = "0.3.1" @@ -1536,44 +995,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, ] -[[package]] -name = "protobuf" -version = "6.30.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148 }, - { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003 }, - { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579 }, - { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319 }, - { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212 }, - { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062 }, -] - -[[package]] -name = "psutil" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, -] - -[[package]] -name = "py-cpuinfo" -version = "9.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335 }, -] - [[package]] name = "pyarrow" version = "20.0.0" @@ -1729,17 +1150,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, ] -[[package]] -name = "pydoe2" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/1f/8a2e1b57f6dd22cd2002e4332e229e87a3858d560c516b50ab7fe5bb075c/pyDOE2-1.3.0.tar.gz", hash = "sha256:5492b0f984af52da3af20b1cd61deb21b067c858e65243ec3ba573375f0d6720", size = 19967 } - [[package]] name = "pyfiglet" version = "0.8.post1" @@ -1770,25 +1180,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] -[[package]] -name = "pytorch-lightning" -version = "2.5.1.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fsspec", extra = ["http"] }, - { name = "lightning-utilities" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "torch" }, - { name = "torchmetrics" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fe/0c/cfa6223c525f67ea3a7a2907e36e9e9a9653300f82cfd9af88f8136514ab/pytorch_lightning-2.5.1.post0.tar.gz", hash = "sha256:abc3d5a804d41f941b14e3fd7db5572a1270cd1e9889b50e962984c87d498d94", size = 634368 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/a9/e14821cfaf08e8d78185cca0477c9d3a62bafe1b4b530100f7b66bb1f7bb/pytorch_lightning-2.5.1.post0-py3-none-any.whl", hash = "sha256:873fb21392c8b79908218f5ca8f65bd835439216e52550c36ff55d849e99c93e", size = 823084 }, -] - [[package]] name = "pytz" version = "2025.2" @@ -1844,7 +1235,7 @@ wheels = [ [[package]] name = "rdkit" -version = "2024.9.6" +version = "2025.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, @@ -1852,26 +1243,26 @@ dependencies = [ { name = "pillow" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/44/8908dc5e26248a1e27d5a0f149e17af2b5e85585a227e57d2d4f53f68d07/rdkit-2024.9.6-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:015c7200fffefdae53b59a4658e0323e2d16ac16b2b00174188f769a0cabe52a", size = 29960044 }, - { url = "https://files.pythonhosted.org/packages/8f/2e/36a2cee64073df724c7fc635c969de604440d754c65ac1dd2627e5aacc16/rdkit-2024.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffe05be4354178b48bfd60ea40c847e6364e75e71893bd2c659da8819c8afab6", size = 27718856 }, - { url = "https://files.pythonhosted.org/packages/13/28/c9abf09ade43a8ed6165bc1e7660cbefdf449f89cb400eb20377e84ceb2c/rdkit-2024.9.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6e6bc9b6edc3e5efa88f796d8a86192b72b5d54ce9488f8ccb043e6b74308b79", size = 33528869 }, - { url = "https://files.pythonhosted.org/packages/a3/f6/3a2278acc1d3831fd1d5b81ae355297be1c2ea8d8d0e200291aa2edfbdeb/rdkit-2024.9.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2b5573055c8defbad7ce25db10786a56e0699faf3282daea21100c59f7af7298", size = 34344527 }, - { url = "https://files.pythonhosted.org/packages/cb/61/18b1fec82a2c4ab60234eab6673174c7dbcfd82490fc834a4d2b10d42d8b/rdkit-2024.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:9d64c865de57c15bbe7726f20a967016f2ac04f738889c83210091be944b512b", size = 22472377 }, - { url = "https://files.pythonhosted.org/packages/cc/c1/b21d793ab572545917af8d00c7519ea1e0fc10d3e74ac936de4a06fc3649/rdkit-2024.9.6-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:bbb3fe860e5ccb8debad592ded991b2aca611c997640fc6e68b0f41beb648639", size = 29960558 }, - { url = "https://files.pythonhosted.org/packages/72/64/f96f4a72052be7580ab825279cc71653a476c023c53828765be520db47e6/rdkit-2024.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a28971024ba0300f7c566874212b8c2c8f7db984e85418eaceec5cb739bcab1d", size = 27719061 }, - { url = "https://files.pythonhosted.org/packages/1d/2f/3befa6b3a061665351d5a58aba66e318175777c40cbfbfc511bd57d2aa7b/rdkit-2024.9.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6e236da74308b19f695ed259b12b75e100e56593333f2b47cff2420e9746e571", size = 33524980 }, - { url = "https://files.pythonhosted.org/packages/cc/3f/472c33312ca8a55242fc2cf6179809f4a967185e9dc6e76ea28ddd37a097/rdkit-2024.9.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7b473a715711e7f1f747173fc18a16b02d5952eadbeedb09d05d1f0a6879c282", size = 34343341 }, - { url = "https://files.pythonhosted.org/packages/94/85/d8d7da7ba8d00ad8977193f30d0062324aa9279e8af67e413674d3f60483/rdkit-2024.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:f302e8a347debe24ffeeb303b8b031ef2e61265872a6450f46aa819b25f5c684", size = 22472536 }, - { url = "https://files.pythonhosted.org/packages/b0/b4/54a543114b58bfbb0f659d30c7902d10e7fa908544691455b36e0783fa77/rdkit-2024.9.6-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:f57a2ad6ceff54917586f47a01ecbe43bbdf4a6f00defd746b500228752ccf84", size = 30035515 }, - { url = "https://files.pythonhosted.org/packages/d1/5a/83deae41ff455e417f67e91393d3fae9f604e15fc633544412fa16d9bcc6/rdkit-2024.9.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f3931ff192aeee29ce41cf7c30650883fc10f1504af2a9dcc67d8554a0779d3", size = 27760806 }, - { url = "https://files.pythonhosted.org/packages/95/41/c52930491f52d7bdb50e297d5064fdc959a82adea9f735a9ab7c412ac874/rdkit-2024.9.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:97661c84a9dc10797d197db1024757676e0ca29a306d5eec289bbfdf4c53c80c", size = 33414832 }, - { url = "https://files.pythonhosted.org/packages/e9/f1/2f978f26b2c58716e70ca1e442e51c260b43e55c2eb64bd2ad76584b60da/rdkit-2024.9.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:82e91020e700979f6fc94a4722b47d770912d54cd9accb1c85961c57550cb945", size = 34276560 }, - { url = "https://files.pythonhosted.org/packages/36/a7/738b3eb302b95ece0efea4d9b9c90d8dd7bb9ef529a56ee3bbbcfd239cde/rdkit-2024.9.6-cp312-cp312-win_amd64.whl", hash = "sha256:995df4e0b09a866fc628b6c9f1c10dca32726964ff1c7506cbf7975af0a9ba60", size = 22487552 }, - { url = "https://files.pythonhosted.org/packages/07/be/e0fb0bf1301ce3add94a248a24b29c7784b7d504de4776554fafe3372c54/rdkit-2024.9.6-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:5001786e1f06749fa4cf7d6a9d23b36afc6d9b028d500777a3861c8a4d91238b", size = 30034429 }, - { url = "https://files.pythonhosted.org/packages/d4/0e/918410c8975cb365ad51d3e487965c3c5ccfc3dea5715c81f0e89339cec0/rdkit-2024.9.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b2578458414600beb3a5691fc900e0640da3c571b30ed3dc76381cfe5caadc9e", size = 27759699 }, - { url = "https://files.pythonhosted.org/packages/41/ee/9758cbcde26bacb820579ecbfbd3069ab8bd10b55e08f6731c208ce2eb3f/rdkit-2024.9.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:4fbf4d680a1070a15f6d9c7cf93218bed87c7110e4e7a467d1b449e430e20ff5", size = 33413200 }, - { url = "https://files.pythonhosted.org/packages/d9/f9/6e5b8f6445450a3b01e28915f749f9d644a8b855f6e7b26c09f193b1978d/rdkit-2024.9.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4b0efe61d9eee664dc19203478e3312e596be0d200c3bbd4ef1b584aef7146d3", size = 34275798 }, - { url = "https://files.pythonhosted.org/packages/3f/9b/3c32fd6dbb1210a8e9dc83feee82778c8bf2ad483c9e8394138c25789110/rdkit-2024.9.6-cp313-cp313-win_amd64.whl", hash = "sha256:6c740c543b55f99d8d3bdea92c68617ef5669bcf2cc1652752a65f156f26dbc0", size = 22486705 }, + { url = "https://files.pythonhosted.org/packages/33/97/887498e1b77cb557c358e397ebd588016909965886effc3e1078ad71044d/rdkit-2025.3.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:81650b6500a92048ef30f03f3df0a0a4d1c657a2b05b52b0d6dd3a610b02cdf5", size = 30157023 }, + { url = "https://files.pythonhosted.org/packages/14/18/dc351d2584d83300174e9052871466811f9c28f060891dc26b7c852894ff/rdkit-2025.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:304f4956d871e10144430db10b894ebdb609325cf04c3c07c72d3f77431b2759", size = 27883635 }, + { url = "https://files.pythonhosted.org/packages/ab/6e/e4c2cb7566a53b106f5e699be56295ef4a5c6a64590da1f596f1c574db94/rdkit-2025.3.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:527cf1d8f7dd8de292ae7304456e7a23e6a170eadcc99421921d8ae743650f01", size = 33723830 }, + { url = "https://files.pythonhosted.org/packages/7f/42/b9e6bcabb31d236e9cb02780650966cb0812c28c7c51ef7c40dc4b681cc2/rdkit-2025.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8428acc9c02a56c55a94d577bf4f9f631e25c0ecb271d872e391c399f900d948", size = 34553394 }, + { url = "https://files.pythonhosted.org/packages/2d/37/6cd6dd7cb8179955759724e237df2d9e5ea5b6b22e92851c9d6a3a779ce3/rdkit-2025.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:95ef6c770a135ec9f8f93f4707ca4ed4b0d340274e91da40b9821a654c26cd53", size = 22700094 }, + { url = "https://files.pythonhosted.org/packages/7a/7c/81c0395c2bcf0b719e5b9de4b2bf5008ac8a73306e15528a17b6a5b43260/rdkit-2025.3.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:fc4073bf5d5d66698da1b389b6bad8cb7fed9e907424f7cc68f1bb83f7cf8393", size = 30157520 }, + { url = "https://files.pythonhosted.org/packages/88/ee/842f82c436092d76bddac74b519596a9b2504981bcce87f262b65627e761/rdkit-2025.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:432b37ff1df875ab41fc191f18ec25bb84a6ca4501873430ab5212393a0da78d", size = 27883867 }, + { url = "https://files.pythonhosted.org/packages/8a/86/4e653e13f156f70baf139eb1b4fa74a2e32bacfec65fcfe6b2a9e0f8db88/rdkit-2025.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1f66dc6b41e0445fedef72ede0a53a4fe98502cd7e9252c3aeb78fe6be82b453", size = 33719525 }, + { url = "https://files.pythonhosted.org/packages/fc/e2/52be0e4b44af0be8f6adbac6df127bc3efecfa0e4b43d9a441dc0db680f1/rdkit-2025.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:291cb7b9f25a8f2e3d9318a550fb9f5ded4ebfc53c3975b3d87cf452f124591a", size = 34552096 }, + { url = "https://files.pythonhosted.org/packages/3d/15/06e0f8b065c1b406d8d3659dd899bae62c5b09f2ab73ab4af021e9d227a3/rdkit-2025.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdd9288513741fa0301e0c637bd6a79b4d1e9d488af6f2c8e326b7e8e6f8844d", size = 22700572 }, + { url = "https://files.pythonhosted.org/packages/89/85/1fb4b2921b9439b3aed626407364a7107655dfd26dab9e93c86b180586df/rdkit-2025.3.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:e9fb18d9139da91567518157a77375cc7c9989225968c1f8d5229b6219c8d9d0", size = 30232215 }, + { url = "https://files.pythonhosted.org/packages/40/2d/0caaff0fa6ea54988c1f589210ba12b3a00973d859fa38f94c5574674f7d/rdkit-2025.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:16fe7be14065a302d1899e06acf014965973977aaa823783ad04a49d13cac4f6", size = 27926515 }, + { url = "https://files.pythonhosted.org/packages/d7/51/85a8658ad598d9d1a5477ade22ec0ceae2a1eebae5cd28a5b9fb8ae01547/rdkit-2025.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:6c856fff67c19a68c810ed69733085e51e82d28d60f463f509854034ad442fc0", size = 33608590 }, + { url = "https://files.pythonhosted.org/packages/6c/95/66a3e17008d9c9c95c94c2eadb71483ae576d1f196700c86f30c15b368e8/rdkit-2025.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ad5e2b3a541d8986b5f4fdc7767b81a45d7067d1d2a49ce3bf67f90265779f11", size = 34487841 }, + { url = "https://files.pythonhosted.org/packages/33/65/5f734d74fdaf8cfdfc972bc4c72209650f1b4475952419b78f66dd6b3b90/rdkit-2025.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:903de3a0d28c9c872aba4c9c4a9ccb9fe6035e813d04a777e00e592067b28323", size = 22718644 }, + { url = "https://files.pythonhosted.org/packages/6a/97/7ec81c0ab4586a634e1c149969439baa9c1c6462d04ec128f8f59f075f44/rdkit-2025.3.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:b74f1f611e8d8398e8c54beb7a393d39bf0bb876f4c6b9d58126c76bdcca9dc1", size = 30231154 }, + { url = "https://files.pythonhosted.org/packages/b3/bc/bb189d44f36c2b1bf131a4854d8c7c8e0886e52d8796e6c0daef9f84f1fc/rdkit-2025.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28aafd75ed630f1ea5e9b3c6f9fcb41dd3fbe4e9e2f5217c340d4c7e816bf5f3", size = 27925399 }, + { url = "https://files.pythonhosted.org/packages/07/e1/5090aa7d040fb0b89a51309c7b9f077df18dad29140ac50d7130fa3cb728/rdkit-2025.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:3d99cf40b2f0464d643a4d1dab7d120c07d1baa155a8d9a8d9bdc6e126753875", size = 33607065 }, + { url = "https://files.pythonhosted.org/packages/c4/af/809e82bc5b3e04f0110a2ca1198272259013039e4b4af5d62cdee62d3b7c/rdkit-2025.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:2204e76c40113705237deb72fbbfbefe9e208f3c51e21333f1c895481b1d848f", size = 34486625 }, + { url = "https://files.pythonhosted.org/packages/72/63/20b763a263c7533bf69725aa47d4eb12ff01ee166ee4f5aec9a53422eadc/rdkit-2025.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:f0b4ab9aaebd9500318a882f7b7d6fe410f0c7c480c6a416063ed0ca4582ce23", size = 22717258 }, ] [[package]] @@ -1958,20 +1349,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] -[[package]] -name = "rich" -version = "14.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, -] - [[package]] name = "safetensors" version = "0.5.3" @@ -2099,109 +1476,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/ea/ddf401b64a42bebb28bf83319740725e4de418760bb4a23757e9f89393a6/selfies-2.1.2-py3-none-any.whl", hash = "sha256:30f675b9b0e9ac71d8489b36cc19e82389dbf319260743d90c9de5d19587b98f", size = 35860 }, ] -[[package]] -name = "sentry-sdk" -version = "2.27.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786 }, -] - -[[package]] -name = "setproctitle" -version = "1.3.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/db/8214810cae49e2e474ea741aaa7d6111486f27377e864f0eb6d297c9be56/setproctitle-1.3.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ebcf34b69df4ca0eabaaaf4a3d890f637f355fed00ba806f7ebdd2d040658c26", size = 17412 }, - { url = "https://files.pythonhosted.org/packages/a4/45/909b0dcd68b16d2e58de0e861c0c0b67748ccc87ff9b59136e9710b528b1/setproctitle-1.3.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1aa1935aa2195b76f377e5cb018290376b7bf085f0b53f5a95c0c21011b74367", size = 11966 }, - { url = "https://files.pythonhosted.org/packages/8a/f4/f1cd54fedae1cdacf1d1db833dc096bfb1f029451f60e68563e4c26ed2f7/setproctitle-1.3.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13624d9925bb481bc0ccfbc7f533da38bfbfe6e80652314f789abc78c2e513bd", size = 31350 }, - { url = "https://files.pythonhosted.org/packages/5a/5f/f159b22d286a349633d4090090b8e6632fb84575a64f189b68e70a613c65/setproctitle-1.3.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97a138fa875c6f281df7720dac742259e85518135cd0e3551aba1c628103d853", size = 32704 }, - { url = "https://files.pythonhosted.org/packages/9c/25/e5ea2673d951dafc04b6544d7b33dd9283733d715c8f40e93d39ae35d6a0/setproctitle-1.3.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c86e9e82bfab579327dbe9b82c71475165fbc8b2134d24f9a3b2edaf200a5c3d", size = 29833 }, - { url = "https://files.pythonhosted.org/packages/67/2b/c3cbd4a4462c1143465d8c151f1d51bbfb418e60a96a754329d28d416575/setproctitle-1.3.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6af330ddc2ec05a99c3933ab3cba9365357c0b8470a7f2fa054ee4b0984f57d1", size = 30884 }, - { url = "https://files.pythonhosted.org/packages/27/04/b43a622a9fbf0f216a50b523067d3b07739ede2106a7226223e33abf6659/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:109fc07b1cd6cef9c245b2028e3e98e038283342b220def311d0239179810dbe", size = 30798 }, - { url = "https://files.pythonhosted.org/packages/41/60/8eb197b56b0a3110eef2a1d2ddb61b3f5809dbab9d975aa40c86e5d4b312/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7df5fcc48588f82b6cc8073db069609ddd48a49b1e9734a20d0efb32464753c4", size = 29758 }, - { url = "https://files.pythonhosted.org/packages/db/1d/c394322a5425c12f4ada0696eb6d194768752d4e3acaca0c9d593025feb4/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2407955dc359d735a20ac6e797ad160feb33d529a2ac50695c11a1ec680eafab", size = 32157 }, - { url = "https://files.pythonhosted.org/packages/e7/24/ce080682b144f057814efbe95daac09149e90f7689e2515897817a941686/setproctitle-1.3.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:38ca045626af693da042ac35d7332e7b9dbd52e6351d6973b310612e3acee6d6", size = 30291 }, - { url = "https://files.pythonhosted.org/packages/a7/4f/4db265493567865428dcec376f8142a9c65d27c10c3ac915d173b4053afb/setproctitle-1.3.6-cp310-cp310-win32.whl", hash = "sha256:9483aa336687463f5497dd37a070094f3dff55e2c888994f8440fcf426a1a844", size = 11492 }, - { url = "https://files.pythonhosted.org/packages/38/b0/64c3948f7957db44b4c5edfe9c197de493144dc915ddf95cf36aeca0dc52/setproctitle-1.3.6-cp310-cp310-win_amd64.whl", hash = "sha256:4efc91b437f6ff2578e89e3f17d010c0a0ff01736606473d082913ecaf7859ba", size = 12204 }, - { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412 }, - { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963 }, - { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718 }, - { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027 }, - { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223 }, - { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204 }, - { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181 }, - { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101 }, - { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438 }, - { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625 }, - { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488 }, - { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201 }, - { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399 }, - { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966 }, - { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017 }, - { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419 }, - { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711 }, - { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742 }, - { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925 }, - { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981 }, - { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209 }, - { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587 }, - { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487 }, - { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208 }, - { url = "https://files.pythonhosted.org/packages/89/76/f1a2fdbf9b9602945a7489ba5c52e9863de37381ef1a85a2b9ed0ff8bc79/setproctitle-1.3.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e2a9e62647dc040a76d55563580bf3bb8fe1f5b6ead08447c2ed0d7786e5e794", size = 17392 }, - { url = "https://files.pythonhosted.org/packages/5c/5b/4e0db8b10b4543afcb3dbc0827793d46e43ec1de6b377e313af3703d08e0/setproctitle-1.3.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:751ba352ed922e0af60458e961167fa7b732ac31c0ddd1476a2dfd30ab5958c5", size = 11951 }, - { url = "https://files.pythonhosted.org/packages/dc/fe/d5d00aaa700fe1f6160b6e95c225b29c01f4d9292176d48fd968815163ea/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7890e291bf4708e3b61db9069ea39b3ab0651e42923a5e1f4d78a7b9e4b18301", size = 32087 }, - { url = "https://files.pythonhosted.org/packages/9f/b3/894b827b93ef813c082479bebf88185860f01ac243df737823dd705e7fff/setproctitle-1.3.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2b17855ed7f994f3f259cf2dfbfad78814538536fa1a91b50253d84d87fd88d", size = 33502 }, - { url = "https://files.pythonhosted.org/packages/b2/cd/5330734cca1a4cfcb721432c22cb7899ff15a4101ba868b2ef452ffafea1/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e51ec673513465663008ce402171192a053564865c2fc6dc840620871a9bd7c", size = 30713 }, - { url = "https://files.pythonhosted.org/packages/fa/d3/c2590c5daa2e9a008d3f2b16c0f4a351826193be55f147cb32af49c6d814/setproctitle-1.3.6-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63cc10352dc6cf35a33951656aa660d99f25f574eb78132ce41a85001a638aa7", size = 31792 }, - { url = "https://files.pythonhosted.org/packages/e6/b1/c553ed5af8cfcecd5ae7737e63af58a17a03d26f3d61868c7eb20bf7e3cf/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0dba8faee2e4a96e934797c9f0f2d093f8239bf210406a99060b3eabe549628e", size = 31927 }, - { url = "https://files.pythonhosted.org/packages/70/78/2d5385206540127a3dca0ff83225b1ac66873f5cc89d4a6d3806c92f5ae2/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e3e44d08b61de0dd6f205528498f834a51a5c06689f8fb182fe26f3a3ce7dca9", size = 30981 }, - { url = "https://files.pythonhosted.org/packages/31/62/e3e4a4e006d0e549748e53cded4ff3b667be0602860fc61b7de8b412b667/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:de004939fc3fd0c1200d26ea9264350bfe501ffbf46c8cf5dc7f345f2d87a7f1", size = 33244 }, - { url = "https://files.pythonhosted.org/packages/aa/05/4b223fd4ef94e105dc7aff27fa502fb7200cf52be2bb0c064bd2406b5611/setproctitle-1.3.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3f8194b4d631b003a1176a75d1acd545e04b1f54b821638e098a93e6e62830ef", size = 31630 }, - { url = "https://files.pythonhosted.org/packages/1b/ba/5f68eb969f7336f54b54a599fd3ffbd7662f9733b080bc8598705971b3dd/setproctitle-1.3.6-cp313-cp313-win32.whl", hash = "sha256:d714e002dd3638170fe7376dc1b686dbac9cb712cde3f7224440af722cc9866a", size = 11480 }, - { url = "https://files.pythonhosted.org/packages/ba/f5/7f47f0ca35c9c357f16187cee9229f3eda0237bc6fdd3061441336f361c0/setproctitle-1.3.6-cp313-cp313-win_amd64.whl", hash = "sha256:b70c07409d465f3a8b34d52f863871fb8a00755370791d2bd1d4f82b3cdaf3d5", size = 12198 }, - { url = "https://files.pythonhosted.org/packages/39/ad/c3941b8fc6b32a976c9e2d9615a90ae793b69cd010ca8c3575dbc822104f/setproctitle-1.3.6-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:23a57d3b8f1549515c2dbe4a2880ebc1f27780dc126c5e064167563e015817f5", size = 17401 }, - { url = "https://files.pythonhosted.org/packages/04/38/a184f857b988d3a9c401e470a4e38182a5c99ee77bf90432d7665e9d35a3/setproctitle-1.3.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:81c443310831e29fabbd07b75ebbfa29d0740b56f5907c6af218482d51260431", size = 11959 }, - { url = "https://files.pythonhosted.org/packages/b7/b9/4878ef9d8483adfd1edf6bf95151362aaec0d05aac306a97ff0383f491b5/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d88c63bd395c787b0aa81d8bbc22c1809f311032ce3e823a6517b711129818e4", size = 33463 }, - { url = "https://files.pythonhosted.org/packages/cc/60/3ef49d1931aff2a36a7324a49cca10d77ef03e0278452fd468c33a52d7e3/setproctitle-1.3.6-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73f14b86d0e2858ece6bf5807c9889670e392c001d414b4293d0d9b291942c3", size = 34959 }, - { url = "https://files.pythonhosted.org/packages/81/c6/dee0a973acecefb0db6c9c2e0ea7f18b7e4db773a72e534741ebdee8bbb8/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3393859eb8f19f5804049a685bf286cb08d447e28ba5c6d8543c7bf5500d5970", size = 32055 }, - { url = "https://files.pythonhosted.org/packages/ea/a5/5dd5c4192cf18d16349a32a07f728a9a48a2a05178e16966cabd6645903e/setproctitle-1.3.6-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:785cd210c0311d9be28a70e281a914486d62bfd44ac926fcd70cf0b4d65dff1c", size = 32986 }, - { url = "https://files.pythonhosted.org/packages/df/a6/1508d37eb8008670d33f13fcdb91cbd8ef54697276469abbfdd3d4428c59/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c051f46ed1e13ba8214b334cbf21902102807582fbfaf0fef341b9e52f0fafbf", size = 32736 }, - { url = "https://files.pythonhosted.org/packages/1a/73/c84ec8880d543766a12fcd6b65dbd013770974a40577889f357409b0441e/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:49498ebf68ca3e75321ffe634fcea5cc720502bfaa79bd6b03ded92ce0dc3c24", size = 31945 }, - { url = "https://files.pythonhosted.org/packages/95/0a/126b9ff7a406a69a62825fe5bd6d1ba8671919a7018c4f9e2c63f49bfcb6/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4431629c178193f23c538cb1de3da285a99ccc86b20ee91d81eb5f1a80e0d2ba", size = 34333 }, - { url = "https://files.pythonhosted.org/packages/9a/fd/5474b04f1c013ff460129d2bc774557dd6e186da4667865efef9a83bf378/setproctitle-1.3.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d136fbf8ad4321716e44d6d6b3d8dffb4872626010884e07a1db54b7450836cf", size = 32508 }, - { url = "https://files.pythonhosted.org/packages/32/21/2503e38520cb076a7ecaef6a35d6a6fa89cf02af3541c84c811fd7500d20/setproctitle-1.3.6-cp313-cp313t-win32.whl", hash = "sha256:d483cc23cc56ab32911ea0baa0d2d9ea7aa065987f47de847a0a93a58bf57905", size = 11482 }, - { url = "https://files.pythonhosted.org/packages/65/23/7833d75a27fba25ddc5cd3b54cd03c4bf8e18b8e2dbec622eb6326278ce8/setproctitle-1.3.6-cp313-cp313t-win_amd64.whl", hash = "sha256:74973aebea3543ad033b9103db30579ec2b950a466e09f9c2180089e8346e0ec", size = 12209 }, - { url = "https://files.pythonhosted.org/packages/d0/2b/f19977b646b64c1440dade2c385c89c39f74ce5254defa102dfd9c163e0b/setproctitle-1.3.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3cde5b83ec4915cd5e6ae271937fd60d14113c8f7769b4a20d51769fe70d8717", size = 11471 }, - { url = "https://files.pythonhosted.org/packages/78/46/db58cf700f1408cf0f63d3f939f7b077bd450da8e037310f70e74c41262f/setproctitle-1.3.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:797f2846b546a8741413c57d9fb930ad5aa939d925c9c0fa6186d77580035af7", size = 13520 }, - { url = "https://files.pythonhosted.org/packages/5c/46/0b89e7ebe77543e721c67077ad93fc8c7c3c31a8db3b12e00d02950f6adc/setproctitle-1.3.6-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac3eb04bcf0119aadc6235a2c162bae5ed5f740e3d42273a7228b915722de20", size = 13094 }, - { url = "https://files.pythonhosted.org/packages/f7/78/03f2e42eb83bce6f853d7751ae95f8a3ae7408145a0b6cdd717be01497d7/setproctitle-1.3.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e6b5633c94c5111f7137f875e8f1ff48f53b991d5d5b90932f27dc8c1fa9ae4", size = 12241 }, -] - -[[package]] -name = "setuptools" -version = "80.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b2/bd26ed086b842b68c8fe9aac380ad7e5118cf84fa7abd45bb059a88368a8/setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd", size = 1354038 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/f6/126c9309c8fe93e5d6bb850593cd58d591daf2da45cc78b61e48d8d95879/setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4", size = 1240689 }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, -] - [[package]] name = "six" version = "1.17.0" @@ -2244,15 +1518,6 @@ dependencies = [ { name = "transformers" }, ] -[[package]] -name = "smmap" -version = "5.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, -] - [[package]] name = "sqlalchemy" version = "2.0.40" @@ -2298,18 +1563,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894 }, ] -[[package]] -name = "sympy" -version = "1.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mpmath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, -] - [[package]] name = "threadpoolctl" version = "3.6.0" @@ -2320,118 +1573,59 @@ wheels = [ ] [[package]] -name = "tokenizers" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, - { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, - { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, - { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, - { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, - { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, - { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, - { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, - { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, - { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, - { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, -] - -[[package]] -name = "tokenizerstats" +name = "tokenizer-stats" version = "0.1.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "apetokenizer" }, - { name = "electrolyte-fm" }, + { name = "datasets" }, { name = "jsonnet" }, { name = "mendeleev" }, + { name = "rdkit" }, + { name = "scikit-learn" }, { name = "selfies" }, { name = "smilespe" }, + { name = "smirk" }, + { name = "transformers" }, ] [package.metadata] requires-dist = [ { name = "apetokenizer", git = "https://github.com/mikemayuare/apetokenizer.git?rev=8b070d6278503bcf8ad2eedc1756ac34f4bbd981" }, - { name = "electrolyte-fm", editable = "../../" }, + { name = "datasets", specifier = "~=3.5" }, { name = "jsonnet", specifier = "~=0.20.0" }, { name = "mendeleev", specifier = ">=0.16" }, + { name = "rdkit", specifier = ">=2025.3.1" }, + { name = "scikit-learn", specifier = ">=1.6.1" }, { name = "selfies", specifier = "==2.1.2" }, { name = "smilespe", specifier = "==0.0.3" }, + { name = "smirk", git = "ssh://git@github.com/BattModels/smirk.git?rev=dfdd9dc27c066f956b69d1f55c2d4717beb7838d" }, + { name = "transformers", specifier = ">=4.51.3" }, ] [[package]] -name = "torch" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/c2/3fb87940fa160d956ee94d644d37b99a24b9c05a4222bf34f94c71880e28/torch-2.7.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c9afea41b11e1a1ab1b258a5c31afbd646d6319042bfe4f231b408034b51128b", size = 99158447 }, - { url = "https://files.pythonhosted.org/packages/cc/2c/91d1de65573fce563f5284e69d9c56b57289625cffbbb6d533d5d56c36a5/torch-2.7.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b9960183b6e5b71239a3e6c883d8852c304e691c0b2955f7045e8a6d05b9183", size = 865164221 }, - { url = "https://files.pythonhosted.org/packages/7f/7e/1b1cc4e0e7cc2666cceb3d250eef47a205f0821c330392cf45eb08156ce5/torch-2.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:2ad79d0d8c2a20a37c5df6052ec67c2078a2c4e9a96dd3a8b55daaff6d28ea29", size = 212521189 }, - { url = "https://files.pythonhosted.org/packages/dc/0b/b2b83f30b8e84a51bf4f96aa3f5f65fdf7c31c591cc519310942339977e2/torch-2.7.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:34e0168ed6de99121612d72224e59b2a58a83dae64999990eada7260c5dd582d", size = 68559462 }, - { url = "https://files.pythonhosted.org/packages/40/da/7378d16cc636697f2a94f791cb496939b60fb8580ddbbef22367db2c2274/torch-2.7.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2b7813e904757b125faf1a9a3154e1d50381d539ced34da1992f52440567c156", size = 99159397 }, - { url = "https://files.pythonhosted.org/packages/0e/6b/87fcddd34df9f53880fa1f0c23af7b6b96c935856473faf3914323588c40/torch-2.7.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd5cfbb4c3bbadd57ad1b27d56a28008f8d8753733411a140fcfb84d7f933a25", size = 865183681 }, - { url = "https://files.pythonhosted.org/packages/13/85/6c1092d4b06c3db1ed23d4106488750917156af0b24ab0a2d9951830b0e9/torch-2.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:58df8d5c2eeb81305760282b5069ea4442791a6bbf0c74d9069b7b3304ff8a37", size = 212520100 }, - { url = "https://files.pythonhosted.org/packages/aa/3f/85b56f7e2abcfa558c5fbf7b11eb02d78a4a63e6aeee2bbae3bb552abea5/torch-2.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:0a8d43caa342b9986101ec5feb5bbf1d86570b5caa01e9cb426378311258fdde", size = 68569377 }, - { url = "https://files.pythonhosted.org/packages/aa/5e/ac759f4c0ab7c01feffa777bd68b43d2ac61560a9770eeac074b450f81d4/torch-2.7.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:36a6368c7ace41ad1c0f69f18056020b6a5ca47bedaca9a2f3b578f5a104c26c", size = 99013250 }, - { url = "https://files.pythonhosted.org/packages/9c/58/2d245b6f1ef61cf11dfc4aceeaacbb40fea706ccebac3f863890c720ab73/torch-2.7.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:15aab3e31c16feb12ae0a88dba3434a458874636f360c567caa6a91f6bfba481", size = 865042157 }, - { url = "https://files.pythonhosted.org/packages/44/80/b353c024e6b624cd9ce1d66dcb9d24e0294680f95b369f19280e241a0159/torch-2.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:f56d4b2510934e072bab3ab8987e00e60e1262fb238176168f5e0c43a1320c6d", size = 212482262 }, - { url = "https://files.pythonhosted.org/packages/ee/8d/b2939e5254be932db1a34b2bd099070c509e8887e0c5a90c498a917e4032/torch-2.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:30b7688a87239a7de83f269333651d8e582afffce6f591fff08c046f7787296e", size = 68574294 }, - { url = "https://files.pythonhosted.org/packages/14/24/720ea9a66c29151b315ea6ba6f404650834af57a26b2a04af23ec246b2d5/torch-2.7.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:868ccdc11798535b5727509480cd1d86d74220cfdc42842c4617338c1109a205", size = 99015553 }, - { url = "https://files.pythonhosted.org/packages/4b/27/285a8cf12bd7cd71f9f211a968516b07dcffed3ef0be585c6e823675ab91/torch-2.7.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b52347118116cf3dff2ab5a3c3dd97c719eb924ac658ca2a7335652076df708", size = 865046389 }, - { url = "https://files.pythonhosted.org/packages/74/c8/2ab2b6eadc45554af8768ae99668c5a8a8552e2012c7238ded7e9e4395e1/torch-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:434cf3b378340efc87c758f250e884f34460624c0523fe5c9b518d205c91dd1b", size = 212490304 }, - { url = "https://files.pythonhosted.org/packages/28/fd/74ba6fde80e2b9eef4237fe668ffae302c76f0e4221759949a632ca13afa/torch-2.7.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:edad98dddd82220465b106506bb91ee5ce32bd075cddbcf2b443dfaa2cbd83bf", size = 68856166 }, - { url = "https://files.pythonhosted.org/packages/cb/b4/8df3f9fe6bdf59e56a0e538592c308d18638eb5f5dc4b08d02abb173c9f0/torch-2.7.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a885fc25afefb6e6eb18a7d1e8bfa01cc153e92271d980a49243b250d5ab6d9", size = 99091348 }, - { url = "https://files.pythonhosted.org/packages/9d/f5/0bd30e9da04c3036614aa1b935a9f7e505a9e4f1f731b15e165faf8a4c74/torch-2.7.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:176300ff5bc11a5f5b0784e40bde9e10a35c4ae9609beed96b4aeb46a27f5fae", size = 865104023 }, - { url = "https://files.pythonhosted.org/packages/d1/b7/2235d0c3012c596df1c8d39a3f4afc1ee1b6e318d469eda4c8bb68566448/torch-2.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d0ca446a93f474985d81dc866fcc8dccefb9460a29a456f79d99c29a78a66993", size = 212750916 }, - { url = "https://files.pythonhosted.org/packages/90/48/7e6477cf40d48cc0a61fa0d41ee9582b9a316b12772fcac17bc1a40178e7/torch-2.7.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:27f5007bdf45f7bb7af7f11d1828d5c2487e030690afb3d89a651fd7036a390e", size = 68575074 }, -] - -[[package]] -name = "torchmetrics" -version = "1.7.1" +name = "tokenizers" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "lightning-utilities" }, - { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, - { name = "numpy", version = "2.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "packaging" }, - { name = "torch" }, + { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/c4/2d921ccf7433ee6d8d6edeaca238674cbbc74528120631c662014795c645/torchmetrics-1.7.1.tar.gz", hash = "sha256:0ac1a0e90d2375866ceb5d3868720c6df7d7d0c5729b7ad36e92c897c6af70c2", size = 565045 } +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/ee/4d0a7213a6f412afb3483031009a3b970dd7bed3be24de95ab04fba1c05a/torchmetrics-1.7.1-py3-none-any.whl", hash = "sha256:9a4c45edbd0a1844cc1540c9e71bfbe0ee783a2ed997344d947fefecac90dbb9", size = 961489 }, + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767 }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555 }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541 }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058 }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278 }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253 }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225 }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874 }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877 }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645 }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, ] [[package]] @@ -2468,49 +1662,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/b6/5257d04ae327b44db31f15cce39e6020cc986333c715660b1315a9724d82/transformers-4.51.3-py3-none-any.whl", hash = "sha256:fd3279633ceb2b777013234bbf0b4f5c2d23c4626b05497691f00cfda55e8a83", size = 10383940 }, ] -[[package]] -name = "triton" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools", marker = "sys_platform == 'linux'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/04/d54d3a6d077c646624dc9461b0059e23fd5d30e0dbe67471e3654aec81f9/triton-3.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fad99beafc860501d7fcc1fb7045d9496cbe2c882b1674640304949165a916e7", size = 156441993 }, - { url = "https://files.pythonhosted.org/packages/3c/c5/4874a81131cc9e934d88377fbc9d24319ae1fb540f3333b4e9c696ebc607/triton-3.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3161a2bf073d6b22c4e2f33f951f3e5e3001462b2570e6df9cd57565bdec2984", size = 156528461 }, - { url = "https://files.pythonhosted.org/packages/11/53/ce18470914ab6cfbec9384ee565d23c4d1c55f0548160b1c7b33000b11fd/triton-3.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b68c778f6c4218403a6bd01be7484f6dc9e20fe2083d22dd8aef33e3b87a10a3", size = 156504509 }, - { url = "https://files.pythonhosted.org/packages/7d/74/4bf2702b65e93accaa20397b74da46fb7a0356452c1bb94dbabaf0582930/triton-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47bc87ad66fa4ef17968299acacecaab71ce40a238890acc6ad197c3abe2b8f1", size = 156516468 }, - { url = "https://files.pythonhosted.org/packages/0a/93/f28a696fa750b9b608baa236f8225dd3290e5aff27433b06143adc025961/triton-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce4700fc14032af1e049005ae94ba908e71cd6c2df682239aed08e49bc71b742", size = 156580729 }, -] - -[[package]] -name = "typer" -version = "0.15.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/1a/5f36851f439884bcfe8539f6a20ff7516e7b60f319bbaf69a90dc35cc2eb/typer-0.15.3.tar.gz", hash = "sha256:818873625d0569653438316567861899f7e9972f2e6e0c16dab608345ced713c", size = 101641 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/20/9d953de6f4367163d23ec823200eb3ecb0050a2609691e512c8b95827a9b/typer-0.15.3-py3-none-any.whl", hash = "sha256:c86a65ad77ca531f03de08d1b9cb67cd09ad02ddddf4b34745b5008f43b239bd", size = 45253 }, -] - -[[package]] -name = "typeshed-client" -version = "2.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "importlib-resources" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/1e/f20e33447be772486acf028295cdd21437454a051adb602d52ddb5334f9e/typeshed_client-2.7.0.tar.gz", hash = "sha256:e63df1e738588ad39f1226de042f4407ab6a99c456f0837063afd83b1415447c", size = 433569 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/39/4702c2901899c018189b9aa7eb75aa8eb54527aed71c3f285895190dc664/typeshed_client-2.7.0-py3-none-any.whl", hash = "sha256:97084e5abc58a76ace2c4618ecaebd625f2d19bbd85aa1b3fb86216bf174bbea", size = 624417 }, -] - [[package]] name = "typing-extensions" version = "4.13.2" @@ -2550,39 +1701,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, ] -[[package]] -name = "wandb" -version = "0.19.10" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "docker-pycreds" }, - { name = "gitpython" }, - { name = "platformdirs" }, - { name = "protobuf" }, - { name = "psutil" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sentry-sdk" }, - { name = "setproctitle" }, - { name = "setuptools" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/1b/39261dbbfa35b2d4f21656dca87b7b43eea7a8e0cfec6ad1514faed6e8d3/wandb-0.19.10.tar.gz", hash = "sha256:010772d26cd259eee2b7e0c8a8d59ad53de050ba96b0bc56e3ee23ff4f1436bb", size = 39467171 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/9a/0a3f323b4dbf5527ce19203a2adc86afc0db546256cc3dd4c20cf45ef182/wandb-0.19.10-py3-none-any.whl", hash = "sha256:c0ed28c56005fa899eed2f03db50b3c579682797afd0baf925c73e9e5d38e2f6", size = 6411518 }, - { url = "https://files.pythonhosted.org/packages/03/a8/7b38239f352c8b6cf47084d125d0bb9d76e99301748b82cf6856883676b9/wandb-0.19.10-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1dbe1aa6f321a761a6be35f55dedb120c50a37fb807f6cb72714c51a6e987444", size = 20941581 }, - { url = "https://files.pythonhosted.org/packages/5b/dc/01aa44bf5b9831f02643859eff4125a05aac726290a171e9e05e144cac09/wandb-0.19.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5eeaeabcf6e90cc96292ee47d08b2e180e81a560773e3127bafa8c02c96c0029", size = 20341851 }, - { url = "https://files.pythonhosted.org/packages/6d/02/7d29203f0e522fbaaaa52ca0a77c8120fa5f971c6cf521fb5fb887fb9db2/wandb-0.19.10-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:cb3b88eb69d4e17fd7cdfb89970eff06b7328862ec6dd739ae524b7b17cd8a9f", size = 20941301 }, - { url = "https://files.pythonhosted.org/packages/f0/fe/51f9450a449394368018af8b56108ec67c8b7ad32a463daa6957eee9e3b4/wandb-0.19.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c290791a58a2afdf0ec76d02e9f5d750387d52797bc46b12e889cf9baeec2880", size = 19921945 }, - { url = "https://files.pythonhosted.org/packages/21/ef/657260437386bb55b17f93751f8c62e39b8c786da943da8c61cda155e070/wandb-0.19.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a518b94b0b481230e62c069d341a1c35f5bf0ac0011bc06554b33b213ea6d40", size = 21346350 }, - { url = "https://files.pythonhosted.org/packages/be/62/1a6c27f11cea57423ba0466c1b8f2e4183a4a5631a9d2b1064e6f9c14065/wandb-0.19.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3dfabb4fa64b84663d34fd11c53bd6e4a3a42026e7776519966cd45a4c39f93a", size = 19928921 }, - { url = "https://files.pythonhosted.org/packages/7e/97/54f211f880f6f9179819c7ee84172516d6f56dd7ef8c358a8ed71e844969/wandb-0.19.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:92d6889f6f291f954ac27019ea6c4b5115d1670846f2474c875c98baf93f8f51", size = 21426985 }, - { url = "https://files.pythonhosted.org/packages/bb/a5/ff6c1986a0ad0e9b768bb6369a8b461a8707c37685acc2d3f4cd0a880cd4/wandb-0.19.10-py3-none-win32.whl", hash = "sha256:1df6a5ad4f4eea1732453598360e99350b6a14e5d911d305d595bd1c412ff0e8", size = 20732820 }, - { url = "https://files.pythonhosted.org/packages/00/46/46a2e7cf5113671eb7323dc1ae7a33b7e6ecbc1042b078f34f28a8968fa1/wandb-0.19.10-py3-none-win_amd64.whl", hash = "sha256:ae6b09efaa3c5b70146710fc153889f4faf059ea2e0bd2c1fb821dc59a77f50a", size = 20732825 }, -] - [[package]] name = "wrapt" version = "1.17.2" From eedcad854f2f8ab809ec5dad925b43323327caa1 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Sat, 10 May 2025 13:23:51 -0400 Subject: [PATCH 25/54] fixup typos --- opt/TokenizerStats/pyproject.toml | 1 + opt/TokenizerStats/src/TokenizerStats.jl | 2 +- opt/TokenizerStats/src/cli.jl | 19 +++------------- opt/TokenizerStats/submit_jobs.py | 28 +++++++++++++++++------- opt/TokenizerStats/uv.lock | 12 ++++++++++ 5 files changed, 37 insertions(+), 25 deletions(-) diff --git a/opt/TokenizerStats/pyproject.toml b/opt/TokenizerStats/pyproject.toml index 792f6f77..afcbb620 100644 --- a/opt/TokenizerStats/pyproject.toml +++ b/opt/TokenizerStats/pyproject.toml @@ -18,6 +18,7 @@ dependencies = [ "transformers>=4.51.3", "rdkit>=2025.3.1", "scikit-learn>=1.6.1", + "networkx>=3.4.2", ] [build-system] diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index 06ecfb52..3ec41fce 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -61,7 +61,7 @@ function tokenizer(dc::DatasetConfig) end function dataset_name(dc::DatasetConfig) - if isdir(dc.name_or_path): + if isdir(dc.name_or_path) if "tmQM" in splitpath(dc.name_or_path) return "tmQM" else diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index 283ffca4..fba94d90 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -18,19 +18,6 @@ function common_args!(s) end end - -@annotate function get_dataset(name_or_path::String, tokenizer::String, encoding::String) - dataset_name = name_or_path - if isdir(name_or_path): - if "tmQM" in splitpath(name_or_path) - dataset_name = "tmQM" - else - dataset_name = basename(name_or_path) - end - end - return DatasetConfig(name_or_path, tokenizer, encoding), dataset_name -end - function maybe_parse_env(T::Type, x::String) env = get(ENV, x, nothing) if !isnothing(env) @@ -102,12 +89,12 @@ end # Run command if args["%COMMAND%"] == "distortion" - ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + ds = DatasetConfig(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) avg_information_loss(ds, args_cmd["reference"], args_cmd["output"]) elseif args["%COMMAND%"] == "loss" # Load the tokenizer - ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + ds = DatasetConfig(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) model_loss(ds, args_cmd["model"], args_cmd["output"]) elseif args["%COMMAND%"] == "merge" @@ -119,7 +106,7 @@ end merge_usage_stats(files; output) elseif args["%COMMAND%"] == "usage" - ds, _ = get_dataset(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) + ds = DatasetConfig(args_cmd["dataset"], args_cmd["tokenizer"], args_cmd["encoding"]) splits = parse_splits(args_cmd["splits"]) # Distribute computation diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 16b9fb41..f3f3919b 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -22,7 +22,7 @@ @dataclass class Process: cmd: list[str] - inputs: list[str] = field(default_factory=list) + inputs: list[Path] = field(default_factory=list) output: Optional[Path] = None slurm: Optional[dict] = None meta: dict[str, Any] = field(default_factory=dict) @@ -285,7 +285,9 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles"): ) -def ngram_loss(dataset, tokenizer, slurm=None, encoding="smiles", ngram="realspace", ds_name=None): +def ngram_loss( + dataset, tokenizer, slurm=None, encoding="smiles", ngram="realspace", ds_name=None +): input = STATS_DIR.joinpath(tokenizer, ngram, "usage.jld2") ds_name = ds_name or str(dataset) outfile = "model_loss.jld2" if ngram == "realspace" else f"model_loss_{ngram}.jld2" @@ -309,13 +311,13 @@ def ngram_loss(dataset, tokenizer, slurm=None, encoding="smiles", ngram="realspa "--encoding", encoding, "--output", - output, + str(output), "--model", - input, + str(input), dataset, tokenizer, ], - inputs=input, + inputs=[input], output=output, slurm=slurm, meta={"dataset": dataset, "tokenizer": tokenizer, "task": "model_loss"}, @@ -345,9 +347,9 @@ def ngram_info_loss( "--encoding", encoding, "--output", - output, + str(output), "--reference", - ref_usage, + str(ref_usage), dataset, tokenizer, ], @@ -412,6 +414,16 @@ def set_logging_level(verbosity): slurm={"ntasks": 32, "time": "8:0:0"}, ) ) + wk.add_process( + ngram_info_loss( + args.realspace, + tok_name, + ref="character", + ds_name="realspace", + encoding=tok["encoding"], + slurm={"ntasks": 48, "time": "1-0:0:0"}, + ) + ) # Tokenize MoleculeNet for ds in [*MOLNET_DATASETS, "tmqm"]: @@ -439,7 +451,7 @@ def set_logging_level(verbosity): for ref in REF_INFO_LOSS: if ds == "tmqm" and tok["encoding"] == "selfies": - continue # Majority of selfies fail + continue # Majority of selfies fail if tok["name_or_path"] == "ncfrey/ChemGPT-4.7M": continue # Token Alignment will fail diff --git a/opt/TokenizerStats/uv.lock b/opt/TokenizerStats/uv.lock index 06c86c4f..a00fbd5b 100644 --- a/opt/TokenizerStats/uv.lock +++ b/opt/TokenizerStats/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.10, <3.14" resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'linux'", @@ -665,6 +666,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, ] +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + [[package]] name = "numpy" version = "1.26.4" @@ -1581,6 +1591,7 @@ dependencies = [ { name = "datasets" }, { name = "jsonnet" }, { name = "mendeleev" }, + { name = "networkx" }, { name = "rdkit" }, { name = "scikit-learn" }, { name = "selfies" }, @@ -1595,6 +1606,7 @@ requires-dist = [ { name = "datasets", specifier = "~=3.5" }, { name = "jsonnet", specifier = "~=0.20.0" }, { name = "mendeleev", specifier = ">=0.16" }, + { name = "networkx", specifier = ">=3.4.2" }, { name = "rdkit", specifier = ">=2025.3.1" }, { name = "scikit-learn", specifier = ">=1.6.1" }, { name = "selfies", specifier = "==2.1.2" }, From 039d8e0d746663b0289052c5b785ee970110cbbf Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 12 May 2025 12:00:35 -0400 Subject: [PATCH 26/54] fix spe and expand tokenizer summary table --- electrolyte_fm/{tokenize => utils}/spe.py | 2 +- electrolyte_fm/utils/tokenizer.py | 2 +- .../plots/src/tokenizer_summary.jl | 57 +++++++------------ opt/TokenizerStats/python/helper/spe.py | 1 + opt/TokenizerStats/src/TokenizerStats.jl | 33 ++--------- opt/TokenizerStats/src/collect.jl | 19 +++---- opt/TokenizerStats/submit_archive.sh | 13 +++++ 7 files changed, 51 insertions(+), 76 deletions(-) rename electrolyte_fm/{tokenize => utils}/spe.py (99%) create mode 120000 opt/TokenizerStats/python/helper/spe.py diff --git a/electrolyte_fm/tokenize/spe.py b/electrolyte_fm/utils/spe.py similarity index 99% rename from electrolyte_fm/tokenize/spe.py rename to electrolyte_fm/utils/spe.py index 2191e489..38a9828a 100644 --- a/electrolyte_fm/tokenize/spe.py +++ b/electrolyte_fm/utils/spe.py @@ -10,7 +10,7 @@ from transformers import PreTrainedTokenizerBase from transformers.tokenization_utils_base import BatchEncoding -from ..utils.cache import cached_download +from .cache import cached_download class PreTrainedSPETokenizer(PreTrainedTokenizerBase): diff --git a/electrolyte_fm/utils/tokenizer.py b/electrolyte_fm/utils/tokenizer.py index aa6721b3..529d7e06 100644 --- a/electrolyte_fm/utils/tokenizer.py +++ b/electrolyte_fm/utils/tokenizer.py @@ -24,7 +24,7 @@ def load_tokenizer(name: str, **kwargs) -> PreTrainedTokenizerBase: raise unk_name elif name == "SmilesPE/SPE_ChEMBL": - from ..tokenize.spe import pretrained_spe_tokenizer + from .spe import pretrained_spe_tokenizer return pretrained_spe_tokenizer(cache_generated=True) diff --git a/opt/TokenizerStats/plots/src/tokenizer_summary.jl b/opt/TokenizerStats/plots/src/tokenizer_summary.jl index bb068e93..4ab840ab 100644 --- a/opt/TokenizerStats/plots/src/tokenizer_summary.jl +++ b/opt/TokenizerStats/plots/src/tokenizer_summary.jl @@ -121,34 +121,14 @@ function report_tokenizer_summary_stats(stats_dir, model_loss, info_loss, usage_ replace.(top_k_tokens, "#" => "\\#", "" => "[UNK]") end - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("realspace"))), :tokenizer, :cross_entropy => :cross_entropy_realspace); - on="name_or_path" => "tokenizer" - ) - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("realspace"))), :tokenizer, :fertility => :fertility_realspace); - on="name_or_path" => "tokenizer" - ) - - # MoleculeNet - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("MoleculeNet"))), :tokenizer, :cross_entropy => :cross_entropy_molnet); - on="name_or_path" => "tokenizer" - ) - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("MoleculeNet"))), :tokenizer, :info_loss => :info_loss_molnet); - on="name_or_path" => "tokenizer" - ) - - # tmQM - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("tmQM"))), :tokenizer, :cross_entropy => :cross_entropy_tmqm); - on="name_or_path" => "tokenizer" - ) - leftjoin!(df_out, - select(subset(df, :dataset => ByRow(==("tmQM"))), :tokenizer, :info_loss => :info_loss_tmqm); - on="name_or_path" => "tokenizer" - ) + # Stack dataset specific metrics + @info describe(df) + rename_cols(col, x) = Symbol(col, "_", lowercase(x)) + for col in [:info_loss, :cross_entropy, :fertility] + df_col = select(df, :dataset, :tokenizer, col) + df_col = unstack(df_col, :dataset, col; renamecols=Base.Fix1(rename_cols, col)) + leftjoin!(df_out, df_col; on="name_or_path" => "tokenizer") + end select!(df_out, Not("name_or_path")) fig_dir = joinpath(pkgdir(TokenizerStats), "fig") @@ -158,24 +138,28 @@ function report_tokenizer_summary_stats(stats_dir, model_loss, info_loss, usage_ \\begin{landscape} \\begin{table} \\resizebox{\\linewidth}{!}{% - \\begin{tabular}{llllc|cc|cc|cc} + \\begin{tabular}{llllrc|cc|ccc|ccc} & & & & & - \\multicolumn{2}{c|}{REAL Space} & - \\multicolumn{2}{c|}{MoleculeNet} & - \\multicolumn{2}{c}{tmQM} \\\\ + & + \\multicolumn{2}{c|}{REALSpace} & + \\multicolumn{3}{c|}{MoleculeNet} & + \\multicolumn{3}{c}{tmQM} \\\\ Tokenizer & Domain & Encoding & Class & + Vocab. Size & Top-$k & + Fertility & \\(H\\) & Fertility & \\(H\\) & \\(D_{KL}\\) & + Fertility & \\(H\\) & \\(D_{KL}\\) \\\\ \\hline """ @@ -192,11 +176,14 @@ function report_tokenizer_summary_stats(stats_dir, model_loss, info_loss, usage_ $(row["Domain"]) & $(row["Encoding"]) & $(row["Class"]) & + $(format("{:,d}", row["Vocab. Size"])) & $top_k_tokens & - $(row["cross_entropy_realspace"]) & $(row["fertility_realspace"]) & - $(row["cross_entropy_molnet"]) & - $(coalesce(row["info_loss_molnet"], "---")) & + $(row["cross_entropy_realspace"]) & + $(row["fertility_moleculenet"]) & + $(row["cross_entropy_moleculenet"]) & + $(coalesce(row["info_loss_moleculenet"], "---")) & + $(row["fertility_tmqm"]) & $(row["cross_entropy_tmqm"]) & $(coalesce(row["info_loss_tmqm"], "---")) \\\\ """ diff --git a/opt/TokenizerStats/python/helper/spe.py b/opt/TokenizerStats/python/helper/spe.py new file mode 120000 index 00000000..71ff814d --- /dev/null +++ b/opt/TokenizerStats/python/helper/spe.py @@ -0,0 +1 @@ +../../../../electrolyte_fm/utils/spe.py \ No newline at end of file diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index 3ec41fce..d0117319 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -30,6 +30,7 @@ end const __loader = Ref{Py}() +const __tokenizer = Ref{Py}() function __init__() __loader[] = pyimport("helper.loader") __tokenizer[] = pyimport("helper.tokenizer") @@ -46,6 +47,7 @@ struct DatasetConfig end function dataset_split(dc::DatasetConfig, split::String; kwargs...) + split = split == "val" ? "validation" : split return tokenizer_dataset(dc.tokenizer, dc.name_or_path, dc.encoding; kwargs...)[split] end @@ -54,8 +56,8 @@ function tokenizer(dc::DatasetConfig) tok = load_tokenizer(dc.tokenizer) info = (; tokenizer_name = isdir(dc.tokenizer) ? basename(dc.tokenizer) : dc.tokenizer, - vocab_size=pyconvert(Int, length(tokenizer)), - unk_token_id=pyconvert(Union{Int,Nothing}, tokenizer.unk_token_id), + vocab_size=pyconvert(Int, length(tok)), + unk_token_id=pyconvert(Union{Int,Nothing}, tok.unk_token_id), ) return tok, info end @@ -71,33 +73,6 @@ function dataset_name(dc::DatasetConfig) return dc.name_or_path end -function split_dataset_by_node(args...; kwargs...) - m = @pyconst(pyimport("datasets.distributed")) - return m.split_dataset_by_node(args...; kwargs...) -end - -function molnet(name::AbstractString; tokenizer="smirk", encoding::String="smiles") - data_modules = @pyconst(pyimport("electrolyte_fm.data_modules")) - target_columns = String[] - dm = data_modules.MolNetDataModule(name; tokenizer, encoding, target_columns, include_encoding=true) - dm.prepare_data() - return dm -end - -function tmqm(path::AbstractString; tokenizer="smirk", encoding::String="smiles") - data_modules = @pyconst(pyimport("electrolyte_fm.data_modules")) - dm = data_modules.tmQMDataModule(path; tokenizer, encoding, include_encoding=true) - dm.prepare_data() - return dm -end - -function pretrain(path::AbstractString; tokenizer="smirk", encoding::String="smiles") - data_modules = @pyconst(pyimport("electrolyte_fm.data_modules")) - dm = data_modules.RobertaDataSet(path; tokenizer, encoding) - dm.prepare_data() - return dm -end - include("ngrams.jl") include("serialize.jl") include("collect.jl") diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 428067fc..cc4429b4 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -38,7 +38,7 @@ end function rank_usage_stats(dataset::DatasetConfig, split::String; global_rank::Int, world_size::Int) ds = dataset_split(dataset, split; global_rank, world_size) - tokenizer, tok_info = tokenizer(dataset) + _, tok_info = tokenizer(dataset) unk_token_id = tok_info.unk_token_id local_stats = tracked_stats() start_time = time() @@ -66,11 +66,11 @@ end function job_array_usage_stats(dataset::DatasetConfig, out_file::AbstractString; splits=["train"], world_size::Int=1, global_rank::Int=0) # Load Dataset and Tokenizer - tokenizer, tokenizer_info = tokenizer(dataset) + _, tok_info = tokenizer(dataset) out_file = out_file * "_split_$(join(splits, "_"))_rank_$global_rank.jld2" mkpath(dirname(out_file)) jldopen(out_file * ".tmp", "w+") do f - f["tokenizer"] = tokenizer_info + f["tokenizer"] = tok_info f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) end @@ -98,12 +98,12 @@ function tabulate_dataset(dataset::DatasetConfig, out_file::AbstractString, spli @info "Rank $global_rank of $world_size is starting" now() # Load Dataset and Tokenizer - tokenizer, tokenizer_info = tokenizer(dataset) + _, tok_info = tokenizer(dataset) if global_rank == 0 @debug "rank $global_rank: created $out_file" now() mkpath(dirname(out_file)) jldopen(out_file * ".tmp", "w+") do f - f["tokenizer"] = tokenizer_info + f["tokenizer"] = tok_info f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) end end @@ -141,7 +141,6 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) @info "Rank $global_rank of $world_size is ready" now() # Create datamodule - ds = tokenizer_dataset(dataset) tok, tok_info = tokenizer(dataset) MPI.Barrier(comm) @info "Rank $global_rank of $world_size is starting" now() @@ -151,7 +150,6 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) global_rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" # Init Fit Stats - tok = datamodule.tokenizer if global_rank == 0 mkpath(dirname(output)) jldopen(output * ".tmp", "w+") do f @@ -163,6 +161,7 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) MPI.Barrier(comm) for split in ["val", "train", "test"] + ds = dataset_split(dataset, split; global_rank, world_size) stats = map(1:length(ngram)) do _ OnlineStats.Series(; moments=OnlineStats.Moments(), @@ -225,14 +224,14 @@ end @info "Rank $global_rank of $world_size is starting" now() # Load Reference Tokenizer / n-gram model - tokenizer, tok_info = tokenizer(dataset) + tok, tok_info = tokenizer(dataset) ngram, ref_tok, ref_info = load_ngram_model(ref_file) global_rank == 0 && @info "Loaded n-gram model for $(ref_info.name) from $ref_file ($(ref_info.sha256[1:8]))" if global_rank == 0 mkpath(dirname(output)) jldopen(output * ".tmp", "w+") do f - f["tokenizer"] = tokenizer_info + f["tokenizer"] = tok_info f["ref_tokenizer"] = ref_info f["system"] = (; world_size) end @@ -254,7 +253,7 @@ end MPI.Barrier(comm) start_time = time() for (idx, encoding) in enumerate(ds) - info_loss = unk_information_loss(ngram, ref_tok, tokenizer, encoding; smi_column="smi") + info_loss = unk_information_loss(ngram, ref_tok, tok, encoding; smi_column="smi") fit!(stats, tuple(info_loss)) if idx % 100 == 0 && global_rank == 0 elapsed = time() - start_time diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index 420a471b..33bb34d7 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -15,5 +15,18 @@ tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ --xz \ --exclude="*.slurm" \ --exclude="*.tmp" \ + --exclude=".unmerged" \ -C stats \ . + +# Compress Code (TokenizerStats) +{ git ls-files; find fig/ -type f; find -name Manifest.toml; } | \ + tar -cav \ + -f "$(realpath ~/scratch)/TokenizerStats.jl_$(date +"%d%m%Y").tar.xz" \ + --dereference \ + --files-from - + +# Compress Code (Smirk) +SMIRK_VERSION="v0.1.1" +curl -L https://github.com/BattModels/smirk/archive/refs/tags/$(SMIRK_VERSION).tar.gz \ + --output "$(realpath ~/scratch)/smrik_${SMIRK_VERSION}.tar.xz" From f08c03e04e13451cd02d0d62d2911b6544d25404 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 12 May 2025 12:06:22 -0400 Subject: [PATCH 27/54] don't archive unmerged files --- opt/TokenizerStats/submit_archive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index 33bb34d7..da28cd6b 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -15,7 +15,7 @@ tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ --xz \ --exclude="*.slurm" \ --exclude="*.tmp" \ - --exclude=".unmerged" \ + --exclude='*/.unmerged/*' -C stats \ . From 2e88bef95c6d0a0b8580db5567b88c267f44ece2 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 12 May 2025 12:10:40 -0400 Subject: [PATCH 28/54] fix: replace paths with real ones when loading --- opt/TokenizerStats/src/TokenizerStats.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index d0117319..d4a0764e 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -38,7 +38,12 @@ function __init__() end tokenizer_dataset(args...; kwargs...) = __loader[].tokenizer_dataset(args...; kwargs...) -load_tokenizer(args...; kwargs...) = __tokenizer[].load_tokenizer(args...; kwargs...) +function load_tokenizer(name_or_path) + if isdir(name_or_path) + name_or_path = realpath(name_or_path) + end + return __tokenizer[].load_tokenizer(name_or_path) +end struct DatasetConfig name_or_path::String From a94f0a282792e5e4e329868a4074eabfc052948e Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 12 May 2025 12:21:07 -0400 Subject: [PATCH 29/54] fix paths in archive --- opt/TokenizerStats/submit_archive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index da28cd6b..53a68b37 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -20,7 +20,7 @@ tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ . # Compress Code (TokenizerStats) -{ git ls-files; find fig/ -type f; find -name Manifest.toml; } | \ +{ git ls-files; find fig/ -type f | sed 's|^\./||'; find -name Manifest.toml | sed 's|^\./||'; } | \ tar -cav \ -f "$(realpath ~/scratch)/TokenizerStats.jl_$(date +"%d%m%Y").tar.xz" \ --dereference \ From e79a73bbf16cd116e925e8f866b54371087e3e05 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 12 May 2025 12:23:25 -0400 Subject: [PATCH 30/54] fix typo in archive --- opt/TokenizerStats/submit_archive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index 53a68b37..9f0d8453 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -29,4 +29,4 @@ tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ # Compress Code (Smirk) SMIRK_VERSION="v0.1.1" curl -L https://github.com/BattModels/smirk/archive/refs/tags/$(SMIRK_VERSION).tar.gz \ - --output "$(realpath ~/scratch)/smrik_${SMIRK_VERSION}.tar.xz" + --output "$(realpath ~/scratch)/smirk_${SMIRK_VERSION}.tar.gz" From ce866f8a4c55a8d10b84637987179ee25bbcd165 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Sat, 17 May 2025 20:02:48 -0400 Subject: [PATCH 31/54] misc fixes --- electrolyte_fm/utils/tokenizer.py | 5 +- opt/TokenizerStats/.gitignore | 3 + opt/TokenizerStats/python/helper/loader.py | 15 ++- opt/TokenizerStats/src/TokenizerStats.jl | 7 +- opt/TokenizerStats/src/collect.jl | 2 +- opt/TokenizerStats/submit_archive.sh | 12 ++- opt/TokenizerStats/submit_jobs.py | 116 +++++++++++++-------- 7 files changed, 103 insertions(+), 57 deletions(-) diff --git a/electrolyte_fm/utils/tokenizer.py b/electrolyte_fm/utils/tokenizer.py index 529d7e06..a27fdc2c 100644 --- a/electrolyte_fm/utils/tokenizer.py +++ b/electrolyte_fm/utils/tokenizer.py @@ -8,7 +8,7 @@ def load_tokenizer(name: str, **kwargs) -> PreTrainedTokenizerBase: # Locate Tokeniser and dataset unk_name = RuntimeError(f"Unknown tokenizer: {name}") - if name.startswith("smirk"): + if name in ["smirk", "smirk-selfies", "smirk-cls"]: from smirk import SmirkTokenizerFast if name == "smirk": @@ -319,6 +319,9 @@ def all_special_ids(self) -> list[int]: "SmirkTokenizer", fast_tokenizer_class=SmirkTokenizerFast ) + if Path(name).exists(): + name = str(Path(name).resolve()) + tok_tf = AutoTokenizer.from_pretrained( name, trust_remote_code=True, diff --git a/opt/TokenizerStats/.gitignore b/opt/TokenizerStats/.gitignore index d6d99908..60128efc 100644 --- a/opt/TokenizerStats/.gitignore +++ b/opt/TokenizerStats/.gitignore @@ -9,3 +9,6 @@ models smirk-gpe-* smirk-gpe-*/ fig/ +*.tar.* +archive/ +*.slurm diff --git a/opt/TokenizerStats/python/helper/loader.py b/opt/TokenizerStats/python/helper/loader.py index 93cac039..7ccc17fe 100644 --- a/opt/TokenizerStats/python/helper/loader.py +++ b/opt/TokenizerStats/python/helper/loader.py @@ -41,11 +41,16 @@ def tokenizer_dataset( encoding: str, world_size: int = 1, global_rank: int = 0, + limit: int | None = None, ) -> AbstractDataset: tokenizer = load_tokenizer(tokenizer) encoding = MolEncoding(encoding) ds = get_dataset(name_or_path) ds = maybe_shard_dataset(GlobalComm(global_rank, world_size), ds) + + if limit is not None: + ds = ds.shuffle(seed=42).take(limit) + ds = encode_molecules(ds, "smi", encoding=encoding) return ds.map( tokenizer, @@ -75,7 +80,7 @@ def molnet(name: str): save_infos=False, ) ds = ds.rename_column("smiles" if name != "bace" else "mol", "smi") - return ds.select_columns("smi") + ds = ds.select_columns("smi") def tmqm(name_or_path: str): @@ -92,7 +97,13 @@ def tmqm(name_or_path: str): streaming=True, save_infos=False, ) - return ds.select_columns("smiles").rename_column("smiles", "smi") + ds = ds.select_columns("smiles").rename_column("smiles", "smi") + + # Split dataset + if name_or_path in ["bace", "bbbp", "hiv"]: + return scaffold_split(ds, "smi") + else: + return train_val_test_split(ds) def smiles_dataset(name_or_path: str): diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index d4a0764e..a73b25b8 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -38,12 +38,7 @@ function __init__() end tokenizer_dataset(args...; kwargs...) = __loader[].tokenizer_dataset(args...; kwargs...) -function load_tokenizer(name_or_path) - if isdir(name_or_path) - name_or_path = realpath(name_or_path) - end - return __tokenizer[].load_tokenizer(name_or_path) -end +load_tokenizer(name_or_path) = __tokenizer[].load_tokenizer(name_or_path) struct DatasetConfig name_or_path::String diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index cc4429b4..e8be4167 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -248,7 +248,7 @@ end info_loss = zeros(length(ngram)) @info "rank $global_rank: started processing" now() - ds = dataset_split(dataset, split; global_rank, world_size) + ds = dataset_split(dataset, split; global_rank, world_size, limit=1000) MPI.Barrier(comm) start_time = time() diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index 9f0d8453..a2c36cec 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -11,22 +11,26 @@ cp tokenizers.json stats/ # Compress stats export XZ_OPT="-9 -v --extreme --memlimit=4000000000 --threads=${SLURM_CPUS_PER_TASK}" -tar -cavf "$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" \ +stats_archive="$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" +tar -cavf $stats_archive \ --xz \ --exclude="*.slurm" \ --exclude="*.tmp" \ --exclude='*/.unmerged/*' -C stats \ . +mkdir -p ./archive +ln -sf $stats_archive ./archive/ngram_tokenizer_stats.tar.xz # Compress Code (TokenizerStats) +code_archive="$(realpath ~/scratch)/TokenizerStats.jl_$(date +"%d%m%Y").tar.xz" { git ls-files; find fig/ -type f | sed 's|^\./||'; find -name Manifest.toml | sed 's|^\./||'; } | \ - tar -cav \ - -f "$(realpath ~/scratch)/TokenizerStats.jl_$(date +"%d%m%Y").tar.xz" \ + tar -cavf $code_archive \ --dereference \ --files-from - +ln -sf $code_archive ./archive/TokenizerStats.tar.xz # Compress Code (Smirk) SMIRK_VERSION="v0.1.1" curl -L https://github.com/BattModels/smirk/archive/refs/tags/$(SMIRK_VERSION).tar.gz \ - --output "$(realpath ~/scratch)/smirk_${SMIRK_VERSION}.tar.gz" + --output "./archive/smirk_${SMIRK_VERSION}.tar.gz" diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index f3f3919b..02a2bfdf 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -1,4 +1,5 @@ #!/usr/bin/env -S uv run python +from os import posix_spawn import time import logging import subprocess @@ -55,9 +56,51 @@ def launch(self, deps: list[int], dry_run: bool = False) -> int: print(p.stdout) print(p.stderr) assert p.returncode == 0, f"Non-zero return code: {p.returncode}" - return int(p.stdout.split(" ")[-1]) + + # Add a marker file with the job id + job_id = int(p.stdout.split(" ")[-1]) + if output := self.output: + output.parent.mkdir(exist_ok=True, parents=True) + output.with_suffix(output.suffix + ".slurm").write_text(str(job_id)) + + return job_id + return randint(1, 967_296) + def active_job(self) -> int | None: + if self.output is None: + return None + + job_file = self.output.with_suffix(self.output.suffix + ".slurm") + if not job_file.exists(): + return None + + # Get the job id + job_id = job_file.read_text() + if not job_id: + job_file.unlink(missing_ok=True) + return None + job_id = int(job_id) + + # Get the job state + p = subprocess.run( + ["scontrol", "show", "--json", "job", str(job_id)], capture_output=True + ) + job_info = json.loads(p.stdout) + try: + state = job_info["jobs"][0]["job_state"][0] + if state in ["RUNNING", "PENDING"]: + return job_id + except KeyError: + pass + + except IndexError: + pass + + # Job is no longer active + job_file.unlink() + return None + class Workflow: def __init__(self): @@ -124,38 +167,20 @@ def _get_deps(self, process: Process, jobs: dict[Path, int]) -> list[int]: return deps - def active_jobs(self, file: Path) -> Optional[int]: - job_file = file.with_suffix(file.suffix + ".slurm") - if not job_file.exists(): - return None + def launch(self, process: Process, deps, dry_run: bool = False): + # Launch the process + job_id = process.launch(deps, dry_run) + assert job_id is not None and isinstance(job_id, int) - # Get the job id - job_id = job_file.read_text() - if not job_id: - job_file.unlink(missing_ok=True) - return None - job_id = int(job_id) + return job_id - # Get the job state - p = subprocess.run( - ["scontrol", "show", "--json", "job", str(job_id)], capture_output=True - ) - job_info = json.loads(p.stdout) - try: - state = job_info["jobs"][0]["job_state"][0] - if state in ["RUNNING", "PENDING"]: - return job_id - except KeyError: - pass - - except IndexError: - pass - - # Job is no longer active - job_file.unlink() - return None - - def run(self, dry_run=False, rate_limit=100, preflight=list[Process]): + def run( + self, + dry_run=False, + rate_limit=100, + preflight=list[Process], + postflight=list[Process], + ): outputs = { process.output: process for process in self.processes @@ -175,9 +200,10 @@ def run(self, dry_run=False, rate_limit=100, preflight=list[Process]): # Skip input files if file not in outputs: continue + process = outputs[file] # Check for an active job and clean up marker files - if job_id := self.active_jobs(file): + if job_id := process.active_job(): logging.info("found active job for %s", file) jobs[file] = job_id continue @@ -185,24 +211,16 @@ def run(self, dry_run=False, rate_limit=100, preflight=list[Process]): if file.exists(): continue - # Get the process that writes to this file - process = outputs[file] + # Get the dependencies for this process deps = self._get_deps(process, jobs) deps.extend(preflight_ids) deps = list(set(deps)) # Launch the process - job_id = process.launch(deps, dry_run) - assert job_id is not None and isinstance(job_id, int) - jobs[file] = job_id + jobs[file] = self.launch(process, deps, dry_run) jobs_launched += 1 tasks_launched[process.meta.get("task", "misc")] += 1 - # Add a marker file with the job id - if not dry_run: - file.parent.mkdir(exist_ok=True, parents=True) - file.with_suffix(file.suffix + ".slurm").write_text(str(job_id)) - # Sleep to avoid hitting the rate limit sleep_time = max(1 / rate_limit - (time.time() - last_launch), 0) logging.debug(f"ratelimiter: sleeping for {sleep_time} seconds") @@ -210,6 +228,14 @@ def run(self, dry_run=False, rate_limit=100, preflight=list[Process]): time.sleep(sleep_time) last_launch = time.time() + # Launch postflight jobs + for process in postflight: + if process.active_job() is not None: + logging.info("found active job for %s", process.output) + continue + + process.launch(jobs.values(), dry_run) + print(f"Launched {jobs_launched} jobs") for task, count in tasks_launched.items(): print(f" {task}: {count}") @@ -471,4 +497,8 @@ def set_logging_level(verbosity): if args.precompile: preflight.append(Process(["submit_precompile.sh"], [])) - wk.run(dry_run=args.dry_run, preflight=preflight) + postflight = [ + Process(["submit_archive.sh"], output=Path("archive")), + ] + + wk.run(dry_run=args.dry_run, preflight=preflight, postflight=postflight) From 00de458a50e2892bec06831ea039a14eb492cd98 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 21 May 2025 11:10:27 -0400 Subject: [PATCH 32/54] more tests and benchmark loader --- electrolyte_fm/data_modules/utils.py | 19 +++++++--- .../benchmark/bench_tokenizer.jl | 13 +++++-- opt/TokenizerStats/python/helper/loader.py | 31 +++++++++------- opt/TokenizerStats/test/cli_tests.jl | 8 +++++ opt/TokenizerStats/test/python_tests.jl | 36 +++++++++++++++++++ 5 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 opt/TokenizerStats/test/python_tests.jl diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index 08139259..c00bdf09 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -1,11 +1,13 @@ -from enum import Enum import random +from asyncio import Semaphore +from enum import Enum from typing import Optional, TypeVar + +from datasets import Dataset, DatasetDict, IterableDatasetDict +from datasets.distributed import split_dataset_by_node from rdkit import Chem from rdkit.Chem.Scaffolds.MurckoScaffold import MurckoScaffoldSmiles from sklearn.model_selection import GroupShuffleSplit -from datasets import Dataset, DatasetDict, IterableDatasetDict -from datasets.distributed import split_dataset_by_node def is_fast(tokenizer): @@ -89,6 +91,7 @@ def encode_molecules( output_column: Optional[str] = None, encoding: MolEncoding = MolEncoding.SMILES, random: bool = False, + max_workers: int = 8, **kwargs, ) -> AbstractDataset: """Convert SMILES encoding in `input_column` to desired `encoding` and save to `output_column`. @@ -97,10 +100,16 @@ def encode_molecules( """ assert isinstance(input_column, str) output_column = output_column or input_column - encode = encoding if not random else encoding.random + + tasks = Semaphore(max_workers) + + async def async_encode(smi): + async with tasks: + return {output_column: encode(smi)} + ds = ds.map( - lambda smi: {output_column: encode(smi)}, + async_encode, input_columns=input_column, batched=False, **kwargs, diff --git a/opt/TokenizerStats/benchmark/bench_tokenizer.jl b/opt/TokenizerStats/benchmark/bench_tokenizer.jl index 6a8e49ef..9e909a53 100644 --- a/opt/TokenizerStats/benchmark/bench_tokenizer.jl +++ b/opt/TokenizerStats/benchmark/bench_tokenizer.jl @@ -1,5 +1,5 @@ using BenchmarkTools -using TokenizerStats +using TokenizerStats: DatasetConfig, dataset_split using PythonCall: pyconvert const suite = BenchmarkGroup() @@ -28,7 +28,14 @@ TOKENIZERS = [ "meta-llama/Meta-Llama-3-8B", ] -setup_dataset(tokenizer) = Iterators.take(TokenizerStats.molnet("freesolv"; tokenizer).train_dataset, 10) +function setup_dataset(tokenizer, encoding) + dc = DatasetConfig("qm9", tokenizer, encoding) + ds = dataset_split(dc, "val") + return Iterators.take(ds, 1000) +end for name in TOKENIZERS - suite[name] = @benchmarkable map(obs -> pyconvert(Vector{Int}, obs["input_ids"]), ds) setup=(ds = setup_dataset($name)) + suite[name] = tok_suite = BenchmarkGroup() + for encoding in ["smiles", "smiles-canonical", "smiles-kekule"] + tok_suite[encoding] = @benchmarkable foreach(identity, ds) setup=(ds = setup_dataset($name, $encoding)) + end end diff --git a/opt/TokenizerStats/python/helper/loader.py b/opt/TokenizerStats/python/helper/loader.py index 7ccc17fe..97de06ab 100644 --- a/opt/TokenizerStats/python/helper/loader.py +++ b/opt/TokenizerStats/python/helper/loader.py @@ -1,3 +1,4 @@ +from itertools import islice from pathlib import Path from dataclasses import dataclass from datasets import load_dataset @@ -9,6 +10,8 @@ maybe_shard_dataset, encode_molecules, is_fast, + scaffold_split, + train_val_test_split, ) @@ -42,22 +45,25 @@ def tokenizer_dataset( world_size: int = 1, global_rank: int = 0, limit: int | None = None, + max_workers: int = 8, ) -> AbstractDataset: tokenizer = load_tokenizer(tokenizer) encoding = MolEncoding(encoding) ds = get_dataset(name_or_path) ds = maybe_shard_dataset(GlobalComm(global_rank, world_size), ds) - if limit is not None: - ds = ds.shuffle(seed=42).take(limit) - - ds = encode_molecules(ds, "smi", encoding=encoding) - return ds.map( + ds = encode_molecules(ds, "smi", encoding=encoding, max_workers=max_workers) + ds = ds.map( tokenizer, batched=is_fast(tokenizer), input_columns="smi", ) + if limit is not None: + return ds.shuffle(seed=42).take(limit) + else: + return ds + def get_dataset(name_or_path: str): if Path(name_or_path).is_dir(): @@ -76,12 +82,17 @@ def molnet(name: str): name=name, data_files=[MOLNET_URLS[name]], split="train", - streaming=True, + streaming=False, save_infos=False, ) ds = ds.rename_column("smiles" if name != "bace" else "mol", "smi") ds = ds.select_columns("smi") + if name in ["hiv", "bace", "bbbp"]: + return scaffold_split(ds, "smi") + else: + return train_val_test_split(ds) + def tmqm(name_or_path: str): path = Path(name_or_path) @@ -97,13 +108,7 @@ def tmqm(name_or_path: str): streaming=True, save_infos=False, ) - ds = ds.select_columns("smiles").rename_column("smiles", "smi") - - # Split dataset - if name_or_path in ["bace", "bbbp", "hiv"]: - return scaffold_split(ds, "smi") - else: - return train_val_test_split(ds) + return ds.select_columns("smiles").rename_column("smiles", "smi") def smiles_dataset(name_or_path: str): diff --git a/opt/TokenizerStats/test/cli_tests.jl b/opt/TokenizerStats/test/cli_tests.jl index c5b1b54b..13be4421 100644 --- a/opt/TokenizerStats/test/cli_tests.jl +++ b/opt/TokenizerStats/test/cli_tests.jl @@ -4,3 +4,11 @@ @test main(["--help"]) == 0 end end + +@testitem "parse_splits" begin + using TokenizerStats: parse_splits + @test parse_splits("val") == ["val"] + @test parse_splits("val,train") == ["val", "train"] + @test parse_splits("all") == ["train", "val", "test"] +end + diff --git a/opt/TokenizerStats/test/python_tests.jl b/opt/TokenizerStats/test/python_tests.jl new file mode 100644 index 00000000..950be7d8 --- /dev/null +++ b/opt/TokenizerStats/test/python_tests.jl @@ -0,0 +1,36 @@ +@testitem "smirk" begin + using TokenizerStats: load_tokenizer, pyconvert + using PythonCall: Py + tok = load_tokenizer("smirk") + @test tok isa Py + @test pyconvert(Vector{String}, tok.tokenize("CO")) == ["C", "O"] +end + +@testitem "dataset loader" begin + using TokenizerStats: DatasetConfig, dataset_split, dataset_name, tokenizer + + @testset "tokenizer" begin + dc = DatasetConfig("qm9", "smirk", "smiles") + @test dataset_name(dc) == "qm9" + tok, info = tokenizer(dc) + @test info.tokenizer_name == "smirk" + @test info.vocab_size == pyconvert(Int, length(tok)) && info.vocab_size > 0 + @test info.unk_token_id == pyconvert(Union{Int}, tok.unk_token_id) # Smirk has an unk_token_id + end + + @testset "dataset" begin + dc = DatasetConfig("qm9", "smirk", "smiles") + @test dataset_name(dc) == "qm9" + ds = dataset_split(dc, "train") + item = first(ds) + @test item isa Py + @test item.haskey("input_ids") + @test pyconvert(Vector{Int}, item["input_ids"]) isa Vector{Int} + @test item.haskey("smi") + @test pyconvert(String, item["smi"]) isa String + + # Check val => validation remapping + @test dataset_split(dc, "val") == dataset_split(dc, "validation") + @test dataset_split(dc, "train") != dataset_split(dc, "validation") + end +end From 5f4805b2e9744bef4628fa9e9514810ae5dc61fd Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 21 May 2025 12:14:51 -0400 Subject: [PATCH 33/54] manage batch jobs in submit_jobs --- opt/TokenizerStats/src/cli.jl | 4 +- opt/TokenizerStats/src/collect.jl | 11 +- opt/TokenizerStats/submit_jobs.py | 256 ++++++++++++++++++----------- opt/TokenizerStats/tokenizers.json | 16 +- 4 files changed, 179 insertions(+), 108 deletions(-) diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index fba94d90..e02fac2c 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -126,8 +126,8 @@ end end parse_splits(x::String) = parse_splits(split(x, ",")) -function parse_splits(x::Vector{String}) - if length(x) == 1 && first(x) +function parse_splits(x::Vector{<:AbstractString}) + if length(x) == 1 && first(x) == "all" return ["val", "train", "test"] else return x diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index e8be4167..1b2ffcf0 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -109,6 +109,7 @@ function tabulate_dataset(dataset::DatasetConfig, out_file::AbstractString, spli end MPI.Barrier(comm) + start_time = time() for split in splits rank_stats = rank_usage_stats(dataset, split; global_rank, world_size) tokenizer_stats = leader_reduce(merge!, rank_stats; comm) @@ -121,6 +122,10 @@ function tabulate_dataset(dataset::DatasetConfig, out_file::AbstractString, spli end if global_rank == 0 + jldopen(out_file * ".tmp", "a+") do f + out_file["walltime"] = time() - start_time + out_file["world_size"] = world_size + end mv(out_file * ".tmp", out_file; force=true) chmod(out_file, 0o444) @info "Saved stats on rank $global_rank to $out_file" now() @@ -160,6 +165,7 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) end MPI.Barrier(comm) + start_time = time() for split in ["val", "train", "test"] ds = dataset_split(dataset, split; global_rank, world_size) stats = map(1:length(ngram)) do _ @@ -204,6 +210,9 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) end if global_rank == 0 + jldopen(output * ".tmp", "a+") do f + f["walltime"] = time() - start_time + end mv(output * ".tmp", output; force=true) chmod(output, 0o444) @info "rank $global_rank: saved stats to $output" now() @@ -248,7 +257,7 @@ end info_loss = zeros(length(ngram)) @info "rank $global_rank: started processing" now() - ds = dataset_split(dataset, split; global_rank, world_size, limit=1000) + ds = dataset_split(dataset, split; global_rank, world_size) MPI.Barrier(comm) start_time = time() diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 02a2bfdf..1e73ad46 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -1,15 +1,15 @@ #!/usr/bin/env -S uv run python -from os import posix_spawn -import time +import argparse +import json import logging import subprocess -import json -import argparse +import time +from collections import defaultdict +from dataclasses import dataclass, field from pathlib import Path -from typing import Optional, Any from random import randint -from dataclasses import dataclass, field -from collections import defaultdict +from typing import Any, Optional + from networkx import DiGraph, topological_sort logging.basicConfig(level=logging.INFO) @@ -33,6 +33,8 @@ def __post_init__(self): self.inputs = [self.inputs] self.inputs = [Path(x) for x in self.inputs] self.output = Path(self.output) if self.output is not None else None + if self.output is not None: + self.output = self.output.resolve() self.slurm = self.slurm or {} def launch(self, deps: list[int], dry_run: bool = False) -> int: @@ -269,35 +271,42 @@ def show(self): # "Xenova/gpt-4o", ] +LARGE_MEM_TOKENIZERS = [ + "mikemayuare/SMILYAPE", + "mikemayuare/SELFYAPE", + "mikemayuare/SMILYBPE", + "mikemayuare/SELFYBPE", + "SmilesPE/SPE_ChEMBL", +] + def large_mem(tokenizer: str, slurm: dict) -> dict: - if tokenizer in [ - "mikemayuare/SMILYAPE", - "mikemayuare/SELFYAPE", - "mikemayuare/SMILYBPE", - "mikemayuare/SELFYBPE", - "SmilesPE/SPE_ChEMBL", - ]: + if tokenizer in LARGE_MEM_TOKENIZERS: slurm["mem-per-cpu"] = "32G" slurm["partition"] = "venkvis-largemem,venkvis-cpu" return slurm -def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles"): +def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles", mode="mpi"): ds_name = ds_name or str(dataset) - output = STATS_DIR.joinpath(tokenizer, ds_name, "usage.jld2") + output = STATS_DIR.joinpath(tokenizer, ds_name, f"usage_{encoding}.jld2") slurm = slurm or {} slurm["job-name"] = slurm.get("job-name", f"usage-{ds_name}") slurm.setdefault("job-name", f"usage-{ds_name}") slurm.setdefault("ntasks", 4) slurm.setdefault("time", "1-0:0:0") - slurm.setdefault("mem-per-cpu", "4G") + slurm.setdefault("mem-per-cpu", "3600M") + slurm.setdefault("partition", "venkvis-cpu,venkvis-largemem") + + if mode == "batch": + output = Path(output.parent, ".unmerged", f"usage_{encoding}", output.name) return Process( [ "submit_tok_stats.sh", "usage", + f"--mode={mode}", "--splits=all", "--encoding", encoding, @@ -305,18 +314,48 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles"): str(dataset), tokenizer, ], - output=output, + output=output.parent if mode == "batch" else output, slurm=slurm, meta={"dataset": dataset, "tokenizer": tokenizer, "task": "usage"}, ) +def usage_array( + wk: Workflow, dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles" +): + p = usage( + dataset, + tokenizer, + ds_name=ds_name, + slurm=slurm, + encoding=encoding, + mode="batch", + ) + output = p.output.parent.parent.joinpath(f"usage_{encoding}.jld2") + slurm = { + "job-name": "merge-usage", + "partition": "venkvis-cpu,venkvis-largemem", + "mem-per-cpu": "64G", + "cpus-per-task": 1, + "ntasks": 1, + "time": "2:0:0", + } + wk._add_process(p) + wk.add_process( + ["submit_tok_stats.sh", "merge", p.output, output], + inputs=[p.output], + output=output, + slurm=slurm, + meta=p.meta, + ) + + def ngram_loss( dataset, tokenizer, slurm=None, encoding="smiles", ngram="realspace", ds_name=None ): - input = STATS_DIR.joinpath(tokenizer, ngram, "usage.jld2") + input = STATS_DIR.joinpath(tokenizer, ngram, f"usage_{encoding}.jld2") ds_name = ds_name or str(dataset) - outfile = "model_loss.jld2" if ngram == "realspace" else f"model_loss_{ngram}.jld2" + outfile = f"model_loss_{ngram}_{encoding}.jld2" output = STATS_DIR.joinpath(tokenizer, ds_name, outfile) slurm = slurm or {} if ngram == "realspace": @@ -324,7 +363,7 @@ def ngram_loss( else: slurm.setdefault("job-name", f"loss-{ds_name}-{ngram}") - slurm.setdefault("mem-per-cpu", "2G") + slurm.setdefault("mem-per-cpu", "1800M") slurm.setdefault("partition", "venkvis-cpu,venkvis-largemem") slurm.setdefault("ntasks", 4) slurm.setdefault("time", "2:0:0") @@ -354,13 +393,15 @@ def ngram_info_loss( dataset, tokenizer, ref, slurm=None, encoding="smiles", ds_name=None ): ref_name = ref.replace("/", "--") - ref_usage = STATS_DIR.joinpath(ref, "realspace", "usage.jld2") + ref_usage = STATS_DIR.joinpath(ref, "realspace", f"usage_{encoding}.jld2") ds_name = ds_name or str(dataset) - output = STATS_DIR.joinpath(tokenizer, ds_name, f"{ref_name}_info_loss.jld2") + output = STATS_DIR.joinpath( + tokenizer, ds_name, f"{ref_name}_info_loss_{encoding}.jld2" + ) slurm = slurm or {} slurm.setdefault("job-name", f"dist-{ds_name}") - slurm.setdefault("mem-per-cpu", "4G") - slurm.setdefault("partition", "venkvis-cpu") + slurm.setdefault("mem-per-cpu", "3600M") + slurm.setdefault("partition", "venkvis-cpu,venkvis-largemem") slurm.setdefault("ntasks", 4) slurm.setdefault("time", "1-0:0:0") @@ -393,104 +434,125 @@ def set_logging_level(verbosity): logging.basicConfig(level=level) -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--dry-run", "-n", action="store_true") - parser.add_argument("--precompile", action="store_true") - parser.add_argument("-v", "--verbose", action="count", default=0) - parser.add_argument( - "--realspace", type=str, default="/nfs/turbo/coe-venkvis/mist/realspace_v4_dev2" - ) - parser.add_argument( - "--tmqm", - type=str, - default=Path(__file__).parent.parent.joinpath("tmQM", "data"), - ) - args = parser.parse_args() - set_logging_level(args.verbose) - - tokenizers = json.loads(Path("tokenizers.json").read_text()) - wk = Workflow() - for tok in tokenizers: - tok_name = tok["name_or_path"] +def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): + tok_name = tok["name_or_path"] - # Tabulate OOVs - output = STATS_DIR.joinpath(tok_name, "oov.json") - p = wk.add_process( - ["submit_oov.sh", "--output", output, tok_name], - output=output, + # Tokenize RealSpace + if tok_name in LARGE_MEM_TOKENIZERS: + usage_array( + wk, + realspace_path, + tok_name, + ds_name="realspace", + encoding=tok["encoding"], + slurm={"ntasks": 128, "time": "1-0:0:0"}, ) - - # Tokenize RealSpace + else: wk.add_process( usage( - args.realspace, + realspace_path, tok_name, ds_name="realspace", encoding=tok["encoding"], - slurm={"ntasks": 32, "time": "1-0:0:0"}, + slurm={"ntasks": 128, "time": "1-0:0:0"}, ) ) - wk.add_process( - ngram_loss( - args.realspace, - tok_name, - ds_name="realspace", - encoding=tok["encoding"], - slurm={"ntasks": 32, "time": "8:0:0"}, - ) + wk.add_process( + ngram_loss( + realspace_path, + tok_name, + ds_name="realspace", + encoding=tok["encoding"], + slurm={"ntasks": 128, "time": "8:0:0"}, ) + ) + wk.add_process( + ngram_info_loss( + realspace_path, + tok_name, + ref="character", + ds_name="realspace", + encoding=tok["encoding"], + slurm={"ntasks": 128, "time": "1-0:0:0"}, + ) + ) + return + + # Tokenize MoleculeNet + for ds in [*MOLNET_DATASETS, "tmqm"]: + dataset = ds if ds != "tmqm" else tmqm_path + ds_name = None if ds != "tmqm" else "tmqm" wk.add_process( - ngram_info_loss( - args.realspace, + usage( + dataset, tok_name, - ref="character", - ds_name="realspace", + ds_name=ds_name, encoding=tok["encoding"], - slurm={"ntasks": 48, "time": "1-0:0:0"}, ) ) + for ngram in ["realspace", ds]: + wk.add_process( + ngram_loss( + dataset, + tok_name, + encoding=tok["encoding"], + ngram=ngram, + ds_name=ds_name, + slurm={"time": "8:0:0" if ds == "tmqm" else "4:0:0"}, + ) + ) + + for ref in REF_INFO_LOSS: + if ds == "tmqm" and tok["encoding"] == "selfies": + continue # Majority of selfies fail - # Tokenize MoleculeNet - for ds in [*MOLNET_DATASETS, "tmqm"]: - dataset = ds if ds != "tmqm" else args.tmqm - ds_name = None if ds != "tmqm" else "tmqm" wk.add_process( - usage( + ngram_info_loss( dataset, tok_name, + ref, ds_name=ds_name, encoding=tok["encoding"], ) ) - for ngram in ["realspace", ds]: - wk.add_process( - ngram_loss( - dataset, - tok_name, - encoding=tok["encoding"], - ngram=ngram, - ds_name=ds_name, - slurm={"time": "8:0:0" if ds == "tmqm" else "4:0:0"}, - ) - ) - for ref in REF_INFO_LOSS: - if ds == "tmqm" and tok["encoding"] == "selfies": - continue # Majority of selfies fail - if tok["name_or_path"] == "ncfrey/ChemGPT-4.7M": - continue # Token Alignment will fail +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dry-run", "-n", action="store_true") + parser.add_argument("--precompile", action="store_true") + parser.add_argument("-v", "--verbose", action="count", default=0) + parser.add_argument( + "--realspace", type=str, default="/nfs/turbo/coe-venkvis/mist/realspace_v4_dev2" + ) + parser.add_argument( + "--tmqm", + type=str, + default=Path(__file__).parent.parent.joinpath("tmQM", "data"), + ) + args = parser.parse_args() + set_logging_level(args.verbose) + + tokenizers = json.loads(Path("tokenizers.json").read_text()) + wk = Workflow() + for tok in tokenizers: + # Tabulate OOVs + output = STATS_DIR.joinpath(tok["name_or_path"], "oov.json") + wk.add_process( + ["submit_oov.sh", "--output", output, tok["name_or_path"]], + output=output, + ) + + if tok["name_or_path"] == "character": + encodings = ["smiles", "selfies", "smiles-canonical", "smiles-kekule"] + elif tok["encoding"] == "selfies": + encodings = ["selfies"] + else: + encodings = ["smiles", "smiles-canonical", "smiles-kekule"] - wk.add_process( - ngram_info_loss( - dataset, - tok_name, - ref, - ds_name=ds_name, - encoding=tok["encoding"], - ) - ) + for encoding in encodings: + tok["encoding"] = encoding + tokenizer_jobs(wk, tok, args.realspace, args.tmqm) wk.show() preflight = [] diff --git a/opt/TokenizerStats/tokenizers.json b/opt/TokenizerStats/tokenizers.json index f358ae19..07c0a65b 100644 --- a/opt/TokenizerStats/tokenizers.json +++ b/opt/TokenizerStats/tokenizers.json @@ -1,4 +1,12 @@ [ + { + "name": "Character", + "name_or_path": "character", + "encoding": "smiles", + "tokenizer_class": "character", + "domain": "chemistry", + "source": "ours" + }, { "name": "Smirk", "name_or_path": "smirk", @@ -31,14 +39,6 @@ "domain": "chemistry", "source": "ours" }, - { - "name": "Character", - "name_or_path": "character", - "encoding": "smiles", - "tokenizer_class": "character", - "domain": "chemistry", - "source": "ours" - }, { "name": "ChemBERTa v1", "name_or_path": "seyonec/ChemBERTa-zinc-base-v1", From d266329fb4e0e58e01b7c0993c8c8cf0202d8e05 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Wed, 21 May 2025 20:47:54 -0400 Subject: [PATCH 34/54] switch to UInt32 for token ids and squash bug --- electrolyte_fm/data_modules/utils.py | 3 ++- .../plots/src/figures/info_loss.jl | 6 ++--- .../plots/src/tokenizer_summary.jl | 2 +- opt/TokenizerStats/python/helper/loader.py | 2 +- opt/TokenizerStats/src/TokenizerStats.jl | 3 ++- opt/TokenizerStats/src/cli.jl | 8 +++---- opt/TokenizerStats/src/collect.jl | 14 +++++------ opt/TokenizerStats/src/ngrams.jl | 24 +++++++++---------- opt/TokenizerStats/submit_jobs.py | 11 +++++---- 9 files changed, 38 insertions(+), 35 deletions(-) diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index c00bdf09..53fab1e4 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -1,3 +1,4 @@ +import logging import random from asyncio import Semaphore from enum import Enum @@ -135,7 +136,7 @@ def scaffold_hash(smi: str) -> str: try: scaffold = MurckoScaffoldSmiles(smi) except ValueError: - logging.warn("No scaffold for %s, using input smiles string", smi) + logging.warning("No scaffold for %s, using input smiles string", smi) scaffold = smi return scaffold diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index 725d0494..4ae27369 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -31,7 +31,7 @@ end function tok_log_prob!(f, cb, file, name, smi, max_vocab=50; direction=:forward) ngram, tok, info = TokenizerStats.load_ngram_model(file) - code = pyconvert(Vector{Int}, tok(smi)["input_ids"]) + code = pyconvert(Vector{UInt32}, tok(smi)["input_ids"]) if direction == :forward P = TokenizerStats.log_probability(ngram, code) @@ -86,7 +86,7 @@ function figure_ngram_info_loss(; # Load model ref_file = joinpath(@__DIR__, "stats", "character", "realspace/usage.jld2") ngram, ref_tok, ref_info = TokenizerStats.load_ngram_model(ref_file) - ref_code = pyconvert(Vector{Int}, ref_tok(smi)["input_ids"]) + ref_code = pyconvert(Vector{UInt32}, ref_tok(smi)["input_ids"]) kwargs = (; ngram, ref_tok, ref_code, token_color) tok_info_loss!(f[1, 1], cb, "smirk", smi; kwargs...) @@ -165,7 +165,7 @@ function tok_info_loss!(f, cb, tok::String, smi::String; ngram, ref_tok, ref_cod # Load model name = tokenizers_info()[tok]["name"] tok = TokenizerStats.load_tokenizer(tok) - code = pyconvert(Vector{Int}, tok(smi)["input_ids"]) + code = pyconvert(Vector{UInt32}, tok(smi)["input_ids"]) # Align both tokenizations smi_tokens = pyconvert(Vector{String}, tok.tokenize(smi)) diff --git a/opt/TokenizerStats/plots/src/tokenizer_summary.jl b/opt/TokenizerStats/plots/src/tokenizer_summary.jl index 4ab840ab..0d24b4b9 100644 --- a/opt/TokenizerStats/plots/src/tokenizer_summary.jl +++ b/opt/TokenizerStats/plots/src/tokenizer_summary.jl @@ -2,7 +2,7 @@ function top_k_tokens(ngram_file; k=5) # Load unigram statistics unigram, tok = jldopen(ngram_file, "r") do data # Load tokenizer - name = data["tokenizer"][:name] + name = data["tokenizer"][:tokenizer_name] name = startswith(name, "smirk-gpe") ? "./" * name : name tok = load_tokenizer(name) diff --git a/opt/TokenizerStats/python/helper/loader.py b/opt/TokenizerStats/python/helper/loader.py index 97de06ab..3fffcbf4 100644 --- a/opt/TokenizerStats/python/helper/loader.py +++ b/opt/TokenizerStats/python/helper/loader.py @@ -98,7 +98,7 @@ def tmqm(name_or_path: str): path = Path(name_or_path) ds = load_dataset( "arrow", - name=self.path.name, + name=path.name, data_files={ "train": str(path.joinpath("train/*.arrow")), "validation": str(path.joinpath("validation/*.arrow")), diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index a73b25b8..0b65bf8c 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -48,7 +48,8 @@ end function dataset_split(dc::DatasetConfig, split::String; kwargs...) split = split == "val" ? "validation" : split - return tokenizer_dataset(dc.tokenizer, dc.name_or_path, dc.encoding; kwargs...)[split] + ds = tokenizer_dataset(dc.tokenizer, dc.name_or_path, dc.encoding; kwargs...)[split] + return ds end tokenizer_name(dc::DatasetConfig) = isdir(dc.tokenizer) ? basename(dc.tokenizer) : dc.tokenizer diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index e02fac2c..a34aa93b 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -113,10 +113,10 @@ end if args_cmd["mode"] == "mpi" tabulate_dataset(ds, args_cmd["output"], splits) elseif args_cmd["mode"] == "batch" - size = maybe_parse_env(Int, args_cmd["size"]) - rank = maybe_parse_env(Int, args_cmd["rank"]) - @info "Using batch mode: $rank of $size (0-indexed)" - job_array_usage_stats(ds, args_cmd["output"]; splits, size, rank) + world_size = maybe_parse_env(Int, args_cmd["size"]) + global_rank = maybe_parse_env(Int, args_cmd["rank"]) + @info "Using batch mode: $global_rank of $world_size (0-indexed)" + job_array_usage_stats(ds, args_cmd["output"]; splits, world_size, global_rank) else error("Unknown mode $(args_cmd["mode"])") end diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 1b2ffcf0..de1339e5 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -3,12 +3,12 @@ function tracked_stats() fertility=CountMap(Int), nunique=CountMap(Int), out_of_vocab=Counter(Int), - ngrams=ntuple(i -> CountMap(NTuple{i,Int}), 5), + ngrams=ntuple(i -> CountMap(NTuple{i,UInt32}), 5), ) end usage_stats(example, is_oov) = usage_stats!(tracked_stats(), example, is_oov) -function usage_stats!(stats, code::Vector{Int}, is_oov::Bool) +function usage_stats!(stats, code::Vector{UInt32}, is_oov::Bool) # Track usage stats fit!(stats.fertility, length(code)) @@ -44,10 +44,10 @@ function rank_usage_stats(dataset::DatasetConfig, split::String; global_rank::In start_time = time() @info "rank $global_rank: started processing $split" now() for (idx, example) in enumerate(ds) - input_ids = pyconvert(Vector{Int}, example["input_ids"]) + input_ids = pyconvert(Vector{UInt32}, example["input_ids"]) is_oov = unk_token_id in input_ids usage_stats!(local_stats, input_ids, is_oov) - if idx % 1_000_000 == 0 + if idx % 10 == 0 elapsed = time() - start_time @info "rank $global_rank on molecule $idx" idx elapsed idx / elapsed now() end @@ -123,8 +123,8 @@ function tabulate_dataset(dataset::DatasetConfig, out_file::AbstractString, spli if global_rank == 0 jldopen(out_file * ".tmp", "a+") do f - out_file["walltime"] = time() - start_time - out_file["world_size"] = world_size + f["walltime"] = time() - start_time + f["world_size"] = world_size end mv(out_file * ".tmp", out_file; force=true) chmod(out_file, 0o444) @@ -181,7 +181,7 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) loss = zeros(length(ngram)) start_time = time() for (idx, encoding) in enumerate(ds) - code = pyconvert(Vector{Int}, encoding["input_ids"]) + code = pyconvert(Vector{UInt32}, encoding["input_ids"]) for N in 1:length(ngram) loss[N] = cross_entropy(ngram, code; N) end diff --git a/opt/TokenizerStats/src/ngrams.jl b/opt/TokenizerStats/src/ngrams.jl index 319850dd..37b2a6f8 100644 --- a/opt/TokenizerStats/src/ngrams.jl +++ b/opt/TokenizerStats/src/ngrams.jl @@ -38,14 +38,14 @@ function NGramModel(tokenizer::Py, ngrams::Union{Tuple,Vector}) # Don't remove in-use special tokens unk_token_id = pyconvert(Union{Nothing,Int}, tokenizer.unk_token_id) - used_special = pyconvert(Vector{Int}, tokenizer("")["input_ids"]) + used_special = pyconvert(Vector{UInt32}, tokenizer("")["input_ids"]) !isnothing(unk_token_id) && setdiff!(special_tokens, unk_token_id) setdiff!(special_tokens, used_special) return NGramModel(ngrams, vocab_size; special_tokens) end -function NGramModel(ngrams::Union{Tuple,Vector}, vocab_size::Int; special_tokens::Vector{Int}=Int[]) +function NGramModel(ngrams::Union{Tuple,Vector}, vocab_size::Int; special_tokens::AbstractVector{<:Integer}=Int[]) total = sum(values(first(ngrams)); init=0) ngrams = ntuple(i -> ngram_counts(ngrams[i], vocab_size), length(ngrams)) N = length(ngrams) @@ -59,7 +59,7 @@ function load_ngram_model(file::String, split="train") jldopen(file, "r") do data # Extract tokenizer info sha256 = bytes2hex(open(SHA.sha256, file)) - name = data["tokenizer"][:name] + name = data["tokenizer"][:tokenizer_name] name = startswith(name, "smirk-gpe") ? "./" * name : name tok = load_tokenizer(name) vocab_size = pyconvert(Int, length(tok)) @@ -90,11 +90,11 @@ end """ Return the conditional n-gram `(..., x_i-1)` for the given n-gram `(..., x_i)`""" -condgram(gram::NTuple{N,Int}) where {N} = reverse(Base.tail(reverse(gram))) +condgram(gram::NTuple{N,<:Integer}) where {N} = reverse(Base.tail(reverse(gram))) condgram(code::AbstractVector{<:Integer}, edx::Int, length::Int) = ngram(code, edx - 1, length - 1) """ Return the backward conditional n-gram `(x_i+1, ...)` for the given n-gram `(x_i, ...)`""" -condgram_backward(gram::NTuple{N,Int}) where {N} = Base.tail(gram) +condgram_backward(gram::NTuple{N,<:Integer}) where {N} = Base.tail(gram) condgram_backward(code::AbstractVector{<:Integer}, edx::Int, length::Int) = ngram(code, range(; start=edx + 1, length=length - 1)) """ Return the n-gram starting at `edx` of at most `length` """ @@ -109,7 +109,7 @@ struct MaskedCode{T,C<:AbstractVector{T},M<:AbstractVector{Bool}} <: AbstractVec mask::M mask_value::T end -MaskedCode(code::Vector{T}, mask::Union{BitVector,Vector{Bool}}, mask_value::T) where {T} = MaskedCode{T}(code, BitVector(mask), mask_value) +MaskedCode(code::Vector{T}, mask::Union{BitVector,Vector{Bool}}, mask_value::Integer) where {T} = MaskedCode{T}(code, BitVector(mask), T(mask_value)) MaskedCode{T}(code::C, mask::M, mask_value) where {T,C<:AbstractVector{T},M<:AbstractVector{Bool}} = MaskedCode{T,C,M}(code, mask, mask_value) Base.getindex(mc::MaskedCode, i::Int) = mc.mask[i] ? mc.mask_value : mc.code[i] @@ -320,7 +320,7 @@ function forward_odds(m::NGramModel, code::AbstractVector; mask::Integer=-100, N return counts, marginal, n_masked end -function forward_counts!(P::Vector{T}, m::NGramModel, cgram::NTuple{N,Int}; mask::Integer=-100) where {T,N} +function forward_counts!(P::Vector{T}, m::NGramModel, cgram::NTuple{N,<:Integer}; mask::Integer=-100) where {T,N} if isempty(cgram) fill_unigram_dist!(P, m) return P @@ -361,7 +361,7 @@ function backward_odds(m::NGramModel, code::AbstractVector; mask::Integer=-100, return counts, marginal, n_masked end -function backward_counts!(P::Vector{T}, m::NGramModel, cgram::NTuple{N,Int}; mask::Integer=-100) where {T,N} +function backward_counts!(P::Vector{T}, m::NGramModel, cgram::NTuple{N,<:Integer}; mask::Integer=-100) where {T,N} if isempty(cgram) fill_unigram_dist!(P, m) return P @@ -503,7 +503,7 @@ Compute the information_loss from unknown tokens using a character-tokenizer as """ @annotate function unk_information_loss(ngram::NGramModel, ref_tok::Py, tok::Py, encoding::Py; N=1:length(ngram), smi_column="smiles") unk_token_id = pyconvert(Int, tok.unk_token_id) - code = pyconvert(Vector{Int}, encoding["input_ids"]) + code = pyconvert(Vector{UInt32}, encoding["input_ids"]) (unk_token_id ∉ code) && return zeros(length(N)) # Mask out unknown tokens @@ -511,7 +511,7 @@ Compute the information_loss from unknown tokens using a character-tokenizer as !(any(masked)) && return zeros(length(N)) # Unexpected, but possible if unk is from whitespace # Compute information_loss from unknown tokens - ref_code = pyconvert(Vector{Int}, ref_tok(encoding[smi_column])["input_ids"]) + ref_code = pyconvert(Vector{UInt32}, ref_tok(encoding[smi_column])["input_ids"]) @assert length(masked) == length(ref_code) return map(n -> information_loss(ngram, ref_code, masked; N=n), N) end @@ -577,11 +577,11 @@ function _maybe_tokenize_offset(tok, smi) end if haskey(out, "offset_mapping") offsets = pyconvert(Vector{Tuple{Int,Int}}, out["offset_mapping"]) - ids = pyconvert(Vector{Int}, out["input_ids"]) + ids = pyconvert(Vector{UInt32}, out["input_ids"]) return (; ids, offsets) else @assert haskey(out, "input_ids") - ids = pyconvert(Vector{Int}, out["input_ids"]) + ids = pyconvert(Vector{UInt32}, out["input_ids"]) return (; ids, offsets=nothing) end end diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 1e73ad46..39fb09a8 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -14,7 +14,7 @@ logging.basicConfig(level=logging.INFO) -STATS_DIR = Path(__file__).joinpath("..", "stats").resolve() +STATS_DIR = Path(__file__).joinpath("..", "stats-encoding").resolve() LOG_FILE = Path(__file__).parent.joinpath("logs", "slurm-%x-%j.log") LOG_FILE.parent.mkdir(exist_ok=True, parents=True) @@ -296,11 +296,13 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles", mode= slurm.setdefault("job-name", f"usage-{ds_name}") slurm.setdefault("ntasks", 4) slurm.setdefault("time", "1-0:0:0") - slurm.setdefault("mem-per-cpu", "3600M") + slurm.setdefault("mem-per-cpu", "1800M") slurm.setdefault("partition", "venkvis-cpu,venkvis-largemem") if mode == "batch": output = Path(output.parent, ".unmerged", f"usage_{encoding}", output.name) + slurm["array"] = f"0-{slurm['ntasks'] - 1}" + slurm["ntasks"] = 1 return Process( [ @@ -445,7 +447,7 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): tok_name, ds_name="realspace", encoding=tok["encoding"], - slurm={"ntasks": 128, "time": "1-0:0:0"}, + slurm={"ntasks": 128, "time": "1-0:0:0", "mem-per-cpu": "6G"}, ) else: wk.add_process( @@ -454,7 +456,7 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): tok_name, ds_name="realspace", encoding=tok["encoding"], - slurm={"ntasks": 128, "time": "1-0:0:0"}, + slurm={"ntasks": 32, "time": "1-0:0:0"}, ) ) wk.add_process( @@ -476,7 +478,6 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): slurm={"ntasks": 128, "time": "1-0:0:0"}, ) ) - return # Tokenize MoleculeNet for ds in [*MOLNET_DATASETS, "tmqm"]: From ec2f8e113564bd9b85df30f9e42ed7e74b295fd5 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 10:10:05 -0400 Subject: [PATCH 35/54] fix oov launcher --- opt/TokenizerStats/{src => python/helper}/atomic_oov.py | 7 ++++++- opt/TokenizerStats/{src => python/helper}/build_vocab.py | 0 opt/TokenizerStats/submit_jobs.py | 1 + opt/TokenizerStats/submit_oov.sh | 4 +++- 4 files changed, 10 insertions(+), 2 deletions(-) rename opt/TokenizerStats/{src => python/helper}/atomic_oov.py (98%) rename opt/TokenizerStats/{src => python/helper}/build_vocab.py (100%) diff --git a/opt/TokenizerStats/src/atomic_oov.py b/opt/TokenizerStats/python/helper/atomic_oov.py similarity index 98% rename from opt/TokenizerStats/src/atomic_oov.py rename to opt/TokenizerStats/python/helper/atomic_oov.py index 1ae9d90a..ece78d5c 100644 --- a/opt/TokenizerStats/src/atomic_oov.py +++ b/opt/TokenizerStats/python/helper/atomic_oov.py @@ -347,6 +347,11 @@ def process_tokenizer(dataset_name: str, tokenizer: dict[str, str]) -> dict: parser.add_argument("--workers", type=int, default=None) parser.add_argument("-d", "--dataset", type=str, default=None, action="append") parser.add_argument("--output", type=argparse.FileType("w"), default="-") + parser.add_argument( + "--tokenizers", + type=Path, + default=Path(__file__).parent.parent.parent.joinpath("tokenizers.json"), + ) args = parser.parse_args() # Lookup tokenizer information @@ -355,7 +360,7 @@ def process_tokenizer(dataset_name: str, tokenizer: dict[str, str]) -> dict: "name_or_path": args.name_or_path, "encoding": args.encoding, } - tokenizers = Path(__file__).parent.parent.joinpath("tokenizers.json").read_text() + tokenizers = args.tokenizers.read_text() for tok in json.loads(tokenizers): if tok["name_or_path"] == args.name_or_path: tokenizer["name"] = args.name or tok["name"] diff --git a/opt/TokenizerStats/src/build_vocab.py b/opt/TokenizerStats/python/helper/build_vocab.py similarity index 100% rename from opt/TokenizerStats/src/build_vocab.py rename to opt/TokenizerStats/python/helper/build_vocab.py diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 39fb09a8..6e6f2bf8 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -542,6 +542,7 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): wk.add_process( ["submit_oov.sh", "--output", output, tok["name_or_path"]], output=output, + meta={"task": "oov"}, ) if tok["name_or_path"] == "character": diff --git a/opt/TokenizerStats/submit_oov.sh b/opt/TokenizerStats/submit_oov.sh index 6dfb85af..918a5599 100755 --- a/opt/TokenizerStats/submit_oov.sh +++ b/opt/TokenizerStats/submit_oov.sh @@ -15,7 +15,9 @@ cd "$(git rev-parse --show-toplevel)/opt/TokenizerStats" source ./activate env -python src/atomic_oov.py $@ +python -m python.helper.atomic_oov \ + --tokenizers $(realpath ./tokenizers.json) \ + $@ exit_code=$? echo "`date`: done" From cff438b3ec210dbeb5489c59e9d1c15ff638e317 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 10:10:38 -0400 Subject: [PATCH 36/54] feat: resume partially completed job arrays --- opt/TokenizerStats/src/collect.jl | 7 +++++++ opt/TokenizerStats/src/ngrams.jl | 3 ++- opt/TokenizerStats/submit_jobs.py | 13 ++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index de1339e5..bfa71ff7 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -69,6 +69,13 @@ function job_array_usage_stats(dataset::DatasetConfig, out_file::AbstractString; _, tok_info = tokenizer(dataset) out_file = out_file * "_split_$(join(splits, "_"))_rank_$global_rank.jld2" mkpath(dirname(out_file)) + + # Early exit if file already exists + if isfile(out_file) + @info "Found existing file $out_file, skipping" + return 0 + end + jldopen(out_file * ".tmp", "w+") do f f["tokenizer"] = tok_info f["dataset"] = (; dataset_name=dataset_name(dataset), encoding=dataset.encoding) diff --git a/opt/TokenizerStats/src/ngrams.jl b/opt/TokenizerStats/src/ngrams.jl index 37b2a6f8..322ec1f6 100644 --- a/opt/TokenizerStats/src/ngrams.jl +++ b/opt/TokenizerStats/src/ngrams.jl @@ -59,7 +59,8 @@ function load_ngram_model(file::String, split="train") jldopen(file, "r") do data # Extract tokenizer info sha256 = bytes2hex(open(SHA.sha256, file)) - name = data["tokenizer"][:tokenizer_name] + tok_info = data["tokenizer"] + name = haskey(tok_info, :name) ? tok_info[:name] : tok_info[:tokenizer_name] name = startswith(name, "smirk-gpe") ? "./" * name : name tok = load_tokenizer(name) vocab_size = pyconvert(Int, length(tok)) diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 6e6f2bf8..5f0d2b26 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -302,6 +302,16 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles", mode= if mode == "batch": output = Path(output.parent, ".unmerged", f"usage_{encoding}", output.name) slurm["array"] = f"0-{slurm['ntasks'] - 1}" + + # Check if all array outputs are present + n_complete = len(list(output.parent.glob("*.jld2"))) + logging.debug("found %d array output files for %s", n_complete, output.parent) + witness = output.parent.with_suffix(".witness") + if n_complete == slurm["ntasks"]: + witness.touch() + else: + witness.unlink(missing_ok=True) + slurm["ntasks"] = 1 return Process( @@ -316,7 +326,7 @@ def usage(dataset, tokenizer, ds_name=None, slurm=None, encoding="smiles", mode= str(dataset), tokenizer, ], - output=output.parent if mode == "batch" else output, + output=output.parent.with_suffix(".witness") if mode == "batch" else output, slurm=slurm, meta={"dataset": dataset, "tokenizer": tokenizer, "task": "usage"}, ) @@ -459,6 +469,7 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): slurm={"ntasks": 32, "time": "1-0:0:0"}, ) ) + return wk.add_process( ngram_loss( realspace_path, From 45152540f274b496d5e2c5fed3d02085b00560fc Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 10:17:46 -0400 Subject: [PATCH 37/54] submit all jobs --- opt/TokenizerStats/submit_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index 5f0d2b26..d25c8fa0 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -469,7 +469,7 @@ def tokenizer_jobs(wk, tok, realspace_path, tmqm_path): slurm={"ntasks": 32, "time": "1-0:0:0"}, ) ) - return + wk.add_process( ngram_loss( realspace_path, From c83740da586960e78da3bfe45c58de5326408fba Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 10:41:24 -0400 Subject: [PATCH 38/54] support UInt32 for MaskedCode --- opt/TokenizerStats/src/ngrams.jl | 11 ++++++++--- opt/TokenizerStats/test/masked_tests.jl | 11 +++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/opt/TokenizerStats/src/ngrams.jl b/opt/TokenizerStats/src/ngrams.jl index 322ec1f6..43035a3b 100644 --- a/opt/TokenizerStats/src/ngrams.jl +++ b/opt/TokenizerStats/src/ngrams.jl @@ -109,9 +109,14 @@ struct MaskedCode{T,C<:AbstractVector{T},M<:AbstractVector{Bool}} <: AbstractVec code::C mask::M mask_value::T + function MaskedCode{T,C,M}(code, mask, mask_value) where {T,C,M} + @assert length(mask) == length(code) "length of code and mask must match" + @assert !any(==(mask_value), code[.!mask]) "mask value ($mask_value) occurs in unmasked code" + new(code, mask, mask_value) + end end -MaskedCode(code::Vector{T}, mask::Union{BitVector,Vector{Bool}}, mask_value::Integer) where {T} = MaskedCode{T}(code, BitVector(mask), T(mask_value)) -MaskedCode{T}(code::C, mask::M, mask_value) where {T,C<:AbstractVector{T},M<:AbstractVector{Bool}} = MaskedCode{T,C,M}(code, mask, mask_value) +MaskedCode(code::Vector{T}, mask::Union{BitVector,Vector{Bool}}, mask_value::Integer=typemax(T)) where {T} = MaskedCode{T}(code, BitVector(mask), mask_value) +MaskedCode{T}(code::C, mask::M, mask_value) where {T,C<:AbstractVector{T},M<:AbstractVector{Bool}} = MaskedCode{T,C,M}(code, mask, T(mask_value)) Base.getindex(mc::MaskedCode, i::Int) = mc.mask[i] ? mc.mask_value : mc.code[i] Base.eltype(::MaskedCode{T}) where {T} = T @@ -417,7 +422,7 @@ given n-gram model ctype = valtype(m.ngrams[N]) isa Integer ? UInt64 : Float64 P = Vector{ctype}(undef, m.vocab_size) Q = similar(P) - code = MaskedCode(code, mask, -100) + code = MaskedCode(code, mask) for idx in eachindex(code) # If nothing is masked, the information # loss is zero diff --git a/opt/TokenizerStats/test/masked_tests.jl b/opt/TokenizerStats/test/masked_tests.jl index 0c056453..715149e8 100644 --- a/opt/TokenizerStats/test/masked_tests.jl +++ b/opt/TokenizerStats/test/masked_tests.jl @@ -9,6 +9,11 @@ mcv = view(mc, 1:5) @test length(mcv) == 5 @test eltype(mcv) == Int + + # Check MaskedCode assertions hold + @test_throws AssertionError MaskedCode(rand(1:32, 10), rand(Bool, 9)) + @test_throws AssertionError MaskedCode([1, 2, 3, 4], falses(4), 3) + @test_throws InexactError MaskedCode(rand(UInt32, 1:32, 10), rand(Bool, 10), -100) end @testitem "indexing" begin @@ -17,3 +22,9 @@ end @test mc[1] == mc.code[1] @test mc[1:5] == mc.code[1:5] end + +@testitem "UInt32" begin + using TokenizerStats: MaskedCode + mc = MaskedCode(rand(UInt32, 10), falses(10)) + @test mc[1:5] == mc.code[1:5] +end From fbc9cda12da3bf18c1a64d2fbacb14b8a8a8835d Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 11:08:19 -0400 Subject: [PATCH 39/54] reduce log rate --- opt/TokenizerStats/src/collect.jl | 2 +- opt/TokenizerStats/submit_jobs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index bfa71ff7..29135e03 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -47,7 +47,7 @@ function rank_usage_stats(dataset::DatasetConfig, split::String; global_rank::In input_ids = pyconvert(Vector{UInt32}, example["input_ids"]) is_oov = unk_token_id in input_ids usage_stats!(local_stats, input_ids, is_oov) - if idx % 10 == 0 + if idx % 1_000_000 == 0 elapsed = time() - start_time @info "rank $global_rank on molecule $idx" idx elapsed idx / elapsed now() end diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index d25c8fa0..b7255ac6 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -358,7 +358,7 @@ def usage_array( inputs=[p.output], output=output, slurm=slurm, - meta=p.meta, + meta={**p.meta, "task": "merge"}, ) From df48d843abbbe5e876c2c3b5ecdf2dead6ecfc28 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Thu, 22 May 2025 11:27:01 -0400 Subject: [PATCH 40/54] squash some bugs --- opt/TokenizerStats/src/ngrams.jl | 9 +++++---- opt/TokenizerStats/test/cli_tests.jl | 6 +++--- opt/TokenizerStats/test/masked_tests.jl | 2 +- opt/TokenizerStats/test/ngram_tests.jl | 14 +++++++++++--- opt/TokenizerStats/test/python_tests.jl | 9 +++------ 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/opt/TokenizerStats/src/ngrams.jl b/opt/TokenizerStats/src/ngrams.jl index 43035a3b..62c04158 100644 --- a/opt/TokenizerStats/src/ngrams.jl +++ b/opt/TokenizerStats/src/ngrams.jl @@ -115,7 +115,7 @@ struct MaskedCode{T,C<:AbstractVector{T},M<:AbstractVector{Bool}} <: AbstractVec new(code, mask, mask_value) end end -MaskedCode(code::Vector{T}, mask::Union{BitVector,Vector{Bool}}, mask_value::Integer=typemax(T)) where {T} = MaskedCode{T}(code, BitVector(mask), mask_value) +MaskedCode(code::AbstractVector{T}, mask::Union{BitVector,AbstractVector{Bool}}, mask_value::Integer=typemax(T)) where {T} = MaskedCode{T}(code, BitVector(mask), mask_value) MaskedCode{T}(code::C, mask::M, mask_value) where {T,C<:AbstractVector{T},M<:AbstractVector{Bool}} = MaskedCode{T,C,M}(code, mask, T(mask_value)) Base.getindex(mc::MaskedCode, i::Int) = mc.mask[i] ? mc.mask_value : mc.code[i] @@ -452,10 +452,11 @@ function fb_log_dist!(Pf::Vector{Float64}, m::NGramModel, code::AbstractVector{< fgram = condgram(code, idx, N) bgram = condgram_backward(code, idx, N) V = nonspecial_vocab_size(m) + T = eltype(code) for (idx, id) in enumerate(token_ids(m)) - fc = first(gram_odds(m, (fgram..., id))) + fc = first(gram_odds(m, (fgram..., T(id)))) Pf[idx] = fc - bc = first(gram_odds(m, (id, bgram...))) + bc = first(gram_odds(m, (T(id), bgram...))) Pb[idx] = bc end @@ -568,7 +569,7 @@ function compute_unknown_mask(tok, ref_tok, smi) end end -function decode_non_special(tok::Py, ids::Vector{Int}) +function decode_non_special(tok::Py, ids::Vector{<:Integer}) ids = rm_special_tokens(tok, ids) tokens = pyconvert(Vector{String}, tok.convert_ids_to_tokens(ids)) return rm_special_tokens(tok, tokens) diff --git a/opt/TokenizerStats/test/cli_tests.jl b/opt/TokenizerStats/test/cli_tests.jl index 13be4421..c15cc797 100644 --- a/opt/TokenizerStats/test/cli_tests.jl +++ b/opt/TokenizerStats/test/cli_tests.jl @@ -7,8 +7,8 @@ end @testitem "parse_splits" begin using TokenizerStats: parse_splits - @test parse_splits("val") == ["val"] - @test parse_splits("val,train") == ["val", "train"] - @test parse_splits("all") == ["train", "val", "test"] + @test Set(parse_splits("val")) == Set(["val"]) + @test Set(parse_splits("val,train")) == Set(["val", "train"]) + @test Set(parse_splits("all")) == Set(["train", "val", "test"]) end diff --git a/opt/TokenizerStats/test/masked_tests.jl b/opt/TokenizerStats/test/masked_tests.jl index 715149e8..0c8e4353 100644 --- a/opt/TokenizerStats/test/masked_tests.jl +++ b/opt/TokenizerStats/test/masked_tests.jl @@ -13,7 +13,7 @@ # Check MaskedCode assertions hold @test_throws AssertionError MaskedCode(rand(1:32, 10), rand(Bool, 9)) @test_throws AssertionError MaskedCode([1, 2, 3, 4], falses(4), 3) - @test_throws InexactError MaskedCode(rand(UInt32, 1:32, 10), rand(Bool, 10), -100) + @test_throws InexactError MaskedCode(rand(UInt32, 10), rand(Bool, 10), -100) end @testitem "indexing" begin diff --git a/opt/TokenizerStats/test/ngram_tests.jl b/opt/TokenizerStats/test/ngram_tests.jl index 0dc3b923..bacd6f86 100644 --- a/opt/TokenizerStats/test/ngram_tests.jl +++ b/opt/TokenizerStats/test/ngram_tests.jl @@ -21,9 +21,9 @@ using OnlineStats export randngram # Build random n-gram counts -function randngram(N=3; vocab_size=9) - stats = map(n -> CountMap(NTuple{n,Int}), 1:N) - corpus = rand(range(0; length=vocab_size), 8, 32) +function randngram(N=3; vocab_size=9, T=Int) + stats = map(n -> CountMap(NTuple{n,T}), 1:N) + corpus = rand(range(T(0); length=T(vocab_size)), 8, 32) for i in axes(corpus, 2) for (n, s) in enumerate(stats) fit!(s, SlidingWindow(corpus[:, i], n)) @@ -55,6 +55,14 @@ end @test 1 <= n <= m.total @test d == m.total end + @testset "UInt32" begin + m = NGramModel(randngram(N; T=UInt32), 9) + @test m.total == 8 * 32 + @test length(m) == N + @test TokenizerStats.nonspecial_vocab_size(m) == 9 + @test isempty(setdiff(token_ids(m), 0:9)) + c, m = gram_odds(m, (0,)) + end end @testitem "ngram/condgram" begin diff --git a/opt/TokenizerStats/test/python_tests.jl b/opt/TokenizerStats/test/python_tests.jl index 950be7d8..b0b9fc19 100644 --- a/opt/TokenizerStats/test/python_tests.jl +++ b/opt/TokenizerStats/test/python_tests.jl @@ -8,6 +8,7 @@ end @testitem "dataset loader" begin using TokenizerStats: DatasetConfig, dataset_split, dataset_name, tokenizer + using PythonCall: Py, pyconvert @testset "tokenizer" begin dc = DatasetConfig("qm9", "smirk", "smiles") @@ -24,13 +25,9 @@ end ds = dataset_split(dc, "train") item = first(ds) @test item isa Py - @test item.haskey("input_ids") + @test haskey(item, "input_ids") @test pyconvert(Vector{Int}, item["input_ids"]) isa Vector{Int} - @test item.haskey("smi") + @test haskey(item, "smi") @test pyconvert(String, item["smi"]) isa String - - # Check val => validation remapping - @test dataset_split(dc, "val") == dataset_split(dc, "validation") - @test dataset_split(dc, "train") != dataset_split(dc, "validation") end end From 66c532e99da2a43b646b4d7f33f6cc667ba4fda6 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Sun, 23 Feb 2025 14:24:43 -0800 Subject: [PATCH 41/54] feat: switch dgx to apptainer Now that apptainer builds on dgx, switch to using apptainer --- container/build.sh | 6 ++++++ container/training.def | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 container/build.sh create mode 100644 container/training.def diff --git a/container/build.sh b/container/build.sh new file mode 100644 index 00000000..8a5e5745 --- /dev/null +++ b/container/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +apptainer build \ + --build-arg SSH_AUTH_SOCK=$SSH_AUTH_SOCK \ + /tmp/mist.sif \ + container/training.def diff --git a/container/training.def b/container/training.def new file mode 100644 index 00000000..8d57a45d --- /dev/null +++ b/container/training.def @@ -0,0 +1,38 @@ +Bootstrap: docker +From: nvcr.io/nvidia/pytorch:{{ NGC_VERSION }} + +%arguments + NGC_VERSION=25.01-py3 + POETRY_VERSION=1.8.3 + PROJECT_DIR=/mist + +%files + pyproject.toml {{ PROJECT_DIR }}/pyproject.toml + poetry.lock {{ PROJECT_DIR }}/poetry.lock + README.md {{ PROJECT_DIR }}/README.md + +%post + apt-get update + apt-get install -y python3-venv cargo + + # Install Poetry + export POETRY_HOME=/opt/poetry + export POETRY_VERSION={{ POETRY_VERSION }} + export POETRY_VIRTUALENVS_IN_PROJECT=1 + export POETRY_VIRTUALENVS_CREATE=1 \ + export POETRY_NO_INTERACTION=1 + export POETRY_CACHE_DIR=/tmp/poetry_cache + curl -sSL https://install.python-poetry.org | python3 - + + # Setup SSH + export SSH_AUTH_SOCK={{ SSH_AUTH_SOCK }} + mkdir -p -m 0600 ~/.ssh + ssh-keyscan -H github.com >> ~/.ssh/known_hosts + + # Install project + $POETRY_HOME/bin/poetry install --compile --no-root --all-extras --directory {{ PROJECT_DIR }} + rm -rf $POETRY_CACHE_DIR + +%runscript + source {{ PROJECT_DIR }}/.venv/bin/activate + exec $@ From a45e300985028c7aa58f0dec04195e05c1720a52 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 2 Jun 2025 08:03:14 -0700 Subject: [PATCH 42/54] feat: batched async molecular transcoding --- electrolyte_fm/data_modules/utils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index 53fab1e4..744d00c3 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -105,17 +105,21 @@ def encode_molecules( tasks = Semaphore(max_workers) - async def async_encode(smi): + async def async_encode(batch: list[str]) -> dict: async with tasks: - return {output_column: encode(smi)} + return {output_column: [encode(smi) for smi in batch]} + + async def async_filter(batch: list[str | None]) -> list[bool]: + async with tasks: + return [x is not None for x in batch] ds = ds.map( async_encode, input_columns=input_column, - batched=False, + batched=True, **kwargs, ) - return ds.filter(lambda x: x[output_column] is not None, batched=False, **kwargs) + return ds.filter(async_filter, batched=True, input_columns=output_column, **kwargs) def train_val_test_split(ds, **kwargs): From 87eae38b156b1f91bb876c09e239dbb478e28f8e Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 2 Jun 2025 08:20:29 -0700 Subject: [PATCH 43/54] feat: add randomization support to pretraining --- .../data_modules/roberta_dataset.py | 4 +- submit/default.jsonnet | 1 + submit/dgx.j2 | 46 ++++++------------- submit/dgx.jsonnet | 1 + 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/electrolyte_fm/data_modules/roberta_dataset.py b/electrolyte_fm/data_modules/roberta_dataset.py index 9fafde45..35db425a 100644 --- a/electrolyte_fm/data_modules/roberta_dataset.py +++ b/electrolyte_fm/data_modules/roberta_dataset.py @@ -24,6 +24,7 @@ def __init__( persistent_workers=False, canonical: Optional[bool] = None, # Deprecated: Use encoding instead encoding: str | MolEncoding = "smiles", + random: bool = False, ): super().__init__() @@ -48,6 +49,7 @@ def __init__( self.prefetch_factor = prefetch_factor self.persistent_workers = persistent_workers self.encoding = MolEncoding(encoding) + self.random = random self.hparams["vocab_size"] = self.vocab_size self.save_hyperparameters(logger=False) @@ -85,7 +87,7 @@ def setup(self, stage: str) -> None: ) # Transcode - ds = encode_molecules(ds, "text", encoding=self.encoding) + ds = encode_molecules(ds, "text", encoding=self.encoding, random=self.random) # Tokenize ds = ds.map( diff --git a/submit/default.jsonnet b/submit/default.jsonnet index b4d43733..eb25c34c 100644 --- a/submit/default.jsonnet +++ b/submit/default.jsonnet @@ -1,4 +1,5 @@ { + stage: 'fit', env: { // Set cache directories to the working directory TORCH_EXTENSIONS_DIR: '${PWD}/.cache/torch_extensions', diff --git a/submit/dgx.j2 b/submit/dgx.j2 index 8e29fff0..0c88d7d3 100644 --- a/submit/dgx.j2 +++ b/submit/dgx.j2 @@ -29,40 +29,25 @@ module load slurm cd $(git rev-parse --show-toplevel) # Set env variables +ENV_FILE="${TMPDIR}/env-${SLURM_JOB_ID}.sh" +cat > $ENV_FILE< $TMPDIR/deepspeed.json< $PL_CONFIG -export JOB_CONFIG=$(cat $PL_CONFIG) -{% else %} - -{{- export_job_config( __config__ ) }} +{{ export_job_config( __config__ ) }} # Write out lightning config -export PL_CONFIG=$TMPDIR/lightning.json +export PL_CONFIG=$TMPDIR/lightning-${SLURM_JOB_ID}.json cat > ${PL_CONFIG}< Date: Tue, 3 Jun 2025 12:26:16 -0400 Subject: [PATCH 44/54] feat: tracy support --- opt/TokenizerStats/.gitignore | 1 + opt/TokenizerStats/Project.toml | 4 ++-- opt/TokenizerStats/main.jl | 8 ++++++++ opt/TokenizerStats/src/TokenizerStats.jl | 2 +- opt/TokenizerStats/src/cli.jl | 2 +- opt/TokenizerStats/src/collect.jl | 4 ++-- opt/TokenizerStats/src/ngrams.jl | 4 ++-- opt/TokenizerStats/test/run_with_tracy.jl | 11 +++++++++++ 8 files changed, 28 insertions(+), 8 deletions(-) create mode 100755 opt/TokenizerStats/test/run_with_tracy.jl diff --git a/opt/TokenizerStats/.gitignore b/opt/TokenizerStats/.gitignore index 60128efc..60b42393 100644 --- a/opt/TokenizerStats/.gitignore +++ b/opt/TokenizerStats/.gitignore @@ -12,3 +12,4 @@ fig/ *.tar.* archive/ *.slurm +*.jld2 diff --git a/opt/TokenizerStats/Project.toml b/opt/TokenizerStats/Project.toml index 1653f4e8..1a53de8a 100644 --- a/opt/TokenizerStats/Project.toml +++ b/opt/TokenizerStats/Project.toml @@ -12,13 +12,13 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MPIPreferences = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -NVTX = "5da4648a-3479-48b8-97b9-01cb529c0a1f" OnlineStats = "a15396b6-48d5-5d58-9928-6d29437db91e" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Tracy = "e689c965-62c8-4b79-b2c5-8359227902fd" [compat] ArgParse = "1.2.0" @@ -27,12 +27,12 @@ JSON = "0.21" LinearAlgebra = "1.10" MPI = "0.20" MPIPreferences = "0.1" -NVTX = "0.3.4" OnlineStats = "1.7" PythonCall = "0.9" Serialization = "1.10" SparseArrays = "1.10" StatsBase = "0.34" +Tracy = "0.1.4" [extras] ReTestItems = "817f1d60-ba6b-4fd5-9520-3cf149f6a823" diff --git a/opt/TokenizerStats/main.jl b/opt/TokenizerStats/main.jl index 0feb2539..70e9f21d 100755 --- a/opt/TokenizerStats/main.jl +++ b/opt/TokenizerStats/main.jl @@ -1,3 +1,11 @@ #!/usr/bin/env -S julia --project --threads=4 --startup-file=no using TokenizerStats: main +using Tracy: wait_for_tracy + +if haskey(ENV, "TRACYJL_WAIT_FOR_TRACY") + @info "Waiting for tracy to connect..." + wait_for_tracy() + @info "Connected!" +end + exit(main(ARGS)) diff --git a/opt/TokenizerStats/src/TokenizerStats.jl b/opt/TokenizerStats/src/TokenizerStats.jl index 0b65bf8c..c8bdcb75 100644 --- a/opt/TokenizerStats/src/TokenizerStats.jl +++ b/opt/TokenizerStats/src/TokenizerStats.jl @@ -1,5 +1,6 @@ module TokenizerStats +using Tracy using PythonCall: Py, pyimport, pyconvert, @pyconst using ArgParse: ArgParseSettings, parse_args, @add_arg_table! using OnlineStats: OnlineStats, CountMap, HyperLogLog, Extrema, KHist, Counter, fit!, merge!, value @@ -13,7 +14,6 @@ using Dates: now using SparseArrays: sparse using LogExpFunctions: logsumexp, log1pexp, xexpy using Serialization: serialize, deserialize -using NVTX: @annotate function find(dir, pattern) found = String[] diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index a34aa93b..60a83761 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -26,7 +26,7 @@ function maybe_parse_env(T::Type, x::String) return parse(T, x) end -@annotate function main(args::Vector{String}) +@tracepoint function main(args::Vector{String}) s = ArgParseSettings() @add_arg_table! s begin "usage" diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 29135e03..54e1b934 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -27,7 +27,7 @@ function usage_stats!(stats, code::Vector{UInt32}, is_oov::Bool) return stats end -function leader_reduce(f, x; comm=MPI.COMM_WORLD) +@tracepoint function leader_reduce(f, x; comm=MPI.COMM_WORLD) g = MPI.gather(x, comm; root=0) if MPI.Comm_rank(comm) == 0 @assert length(g) == MPI.Comm_size(comm) @@ -231,7 +231,7 @@ function model_loss(dataset::DatasetConfig, ref_file::String, output::String) end -@annotate function avg_information_loss(dataset::DatasetConfig, ref_file::String, output::String; split="val") +@tracepoint function avg_information_loss(dataset::DatasetConfig, ref_file::String, output::String; split="val") # Init MPI MPI.Init() comm = MPI.COMM_WORLD diff --git a/opt/TokenizerStats/src/ngrams.jl b/opt/TokenizerStats/src/ngrams.jl index 62c04158..673985e7 100644 --- a/opt/TokenizerStats/src/ngrams.jl +++ b/opt/TokenizerStats/src/ngrams.jl @@ -415,7 +415,7 @@ end Computes the Information Loss (KL-Divergence) from masking out tokens in an input code for a given n-gram model """ -@annotate function information_loss(m::NGramModel, code::Vector{<:Integer}, mask::Union{BitVector,Vector{Bool}}; N=length(m)) +@tracepoint function information_loss(m::NGramModel, code::Vector{<:Integer}, mask::Union{BitVector,Vector{Bool}}; N=length(m)) @assert 0 < N <= length(m) @assert length(code) == length(mask) loss = 0.0 @@ -508,7 +508,7 @@ end """ Compute the information_loss from unknown tokens using a character-tokenizer as a reference """ -@annotate function unk_information_loss(ngram::NGramModel, ref_tok::Py, tok::Py, encoding::Py; N=1:length(ngram), smi_column="smiles") +@tracepoint function unk_information_loss(ngram::NGramModel, ref_tok::Py, tok::Py, encoding::Py; N=1:length(ngram), smi_column="smiles") unk_token_id = pyconvert(Int, tok.unk_token_id) code = pyconvert(Vector{UInt32}, encoding["input_ids"]) (unk_token_id ∉ code) && return zeros(length(N)) diff --git a/opt/TokenizerStats/test/run_with_tracy.jl b/opt/TokenizerStats/test/run_with_tracy.jl new file mode 100755 index 00000000..7575e816 --- /dev/null +++ b/opt/TokenizerStats/test/run_with_tracy.jl @@ -0,0 +1,11 @@ +#!/usr/bin/env -S julia --project --startup-file=no +using Tracy +@info "Waiting for tracy to connect..." +wait_for_tracy() +@info "Tracy connected!" + +using TokenizerStats +using ReTestItems +@tracepoint "runtests" begin + runtests(TokenizerStats) +end From 5b397a1c537fdba8a76a0eb710d3b63490d62416 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 2 Jun 2025 15:36:28 -0400 Subject: [PATCH 45/54] fix: avoid converting ngrams to dict during serialization Doing so causes a large increase in peak memory usage (Dict effectively is copied). Keeping it as a Dict from the get-go avoids this Added a test to check for regressions in serialization overhead --- opt/TokenizerStats/src/collect.jl | 6 +++--- opt/TokenizerStats/src/serialize.jl | 6 +++--- opt/TokenizerStats/test/serialize_tests.jl | 22 +++++++++++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/opt/TokenizerStats/src/collect.jl b/opt/TokenizerStats/src/collect.jl index 54e1b934..15ddf596 100644 --- a/opt/TokenizerStats/src/collect.jl +++ b/opt/TokenizerStats/src/collect.jl @@ -1,9 +1,9 @@ function tracked_stats() return (; - fertility=CountMap(Int), - nunique=CountMap(Int), + fertility=CountMap(Dict{Int,Int}()), + nunique=CountMap(Dict{Int,Int}()), out_of_vocab=Counter(Int), - ngrams=ntuple(i -> CountMap(NTuple{i,UInt32}), 5), + ngrams=ntuple(i -> CountMap(Dict{NTuple{i,UInt32}, Int}()), 5), ) end diff --git a/opt/TokenizerStats/src/serialize.jl b/opt/TokenizerStats/src/serialize.jl index d04d84ab..7796da17 100644 --- a/opt/TokenizerStats/src/serialize.jl +++ b/opt/TokenizerStats/src/serialize.jl @@ -1,10 +1,10 @@ function serialize_usage!(f::JLD2.JLDFile, path::AbstractString, x::OnlineStats.Series) f[joinpath(path, "samples")] = nobs(x) f[joinpath(path, "out_of_vocab")] = value(x[:out_of_vocab]) - f[joinpath(path, "fertility")] = Dict(value(x[:fertility])) - f[joinpath(path, "nunique")] = Dict(value(x[:nunique])) + f[joinpath(path, "fertility")] = value(x[:fertility]) + f[joinpath(path, "nunique")] = value(x[:nunique]) for n in eachindex(x[:ngrams].stats) - f[joinpath(path, "ngrams", string(n))] = Dict(value(x[:ngrams][n])) + f[joinpath(path, "ngrams", string(n))] = value(x[:ngrams][n]) end return nothing end diff --git a/opt/TokenizerStats/test/serialize_tests.jl b/opt/TokenizerStats/test/serialize_tests.jl index cb199904..d239180d 100644 --- a/opt/TokenizerStats/test/serialize_tests.jl +++ b/opt/TokenizerStats/test/serialize_tests.jl @@ -1,10 +1,17 @@ @testitem "Usage" begin - using TokenizerStats: tracked_stats, serialize_usage! + using TokenizerStats: tracked_stats, serialize_usage!, usage_stats! using JLD2: jldopen using OnlineStats: Series # Create some stats ts = tracked_stats() + for i in 1:10000 + code = rand(UInt32, rand(40:100)) + is_oov = rand(Bool) + usage_stats!(ts, code, is_oov) + end + + # Populate stats s = Series(; fertility=ts.fertility, nunique=ts.nunique, @@ -15,9 +22,22 @@ @test length(s[:ngrams].stats) == length(ts[:ngrams]) mktemp() do file, io + # Precompile jldopen(file, "w") do f serialize_usage!(f, "val", s) end + # Record serialization overhead + stats = @timed jldopen(file, "w") do f + serialize_usage!(f, "val", s) + end + + # Check for regressions in serialization overhead + io_bytes = stats.bytes + file_bytes = stat(file).size + mem_overhead = io_bytes / file_bytes + @test mem_overhead < 4.5 + + # Validate data jldopen(file, "r") do f @test Set(keys(f["val"])) == Set(["samples", string.(keys(s.stats))...]) @test f["val"]["samples"] isa Int From 2ad4d759c2c2e2710ac09718bb408ca8db9c3907 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 2 Jun 2025 15:37:53 -0400 Subject: [PATCH 46/54] feat: fix merge and refactor to reduce memory usage --- opt/TokenizerStats/src/cli.jl | 9 ++++----- opt/TokenizerStats/submit_jobs.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index 60a83761..6e66e421 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -73,7 +73,7 @@ end end @add_arg_table! s["merge"] begin "--pattern" - default = r"usage.+?_rank_\d+\.jld2" + default = r"usage.+?_rank_\d+\.jld2$" arg_type = Regex "directory" help = "Directory to search for files to merge" @@ -152,12 +152,11 @@ function merge_usage_stats(files::Vector{String}; output::String="merged.jld2", merged[split]["out_of_vocab"] += other["out_of_vocab"] merged[split]["samples"] += other["samples"] merged[split]["out_of_vocab"] += other["out_of_vocab"] - merged[split]["fertility"] = Dict(mergewith(+, merged[split]["fertility"], other["fertility"])) - merged[split]["nunique"] = Dict(mergewith(+, merged[split]["nunique"], other["nunique"])) + mergewith!(+, merged[split]["fertility"], other["fertility"]) + mergewith!(+, merged[split]["nunique"], other["nunique"]) for n in 1:length(merged[split]["ngrams"]) - a_ngram = merged[split]["ngrams"]["$n"] b_ngram = compact_ngrams(other["ngrams"]["$n"]) - merged[split]["ngrams"]["$n"] = Dict(mergewith(+, a_ngram, b_ngram)) + mergewith!(+, merged[split]["ngrams"]["$n"], b_ngram) end else merged[split]["out_of_vocab"] = other[split]["out_of_vocab"] diff --git a/opt/TokenizerStats/submit_jobs.py b/opt/TokenizerStats/submit_jobs.py index b7255ac6..8a0baeae 100755 --- a/opt/TokenizerStats/submit_jobs.py +++ b/opt/TokenizerStats/submit_jobs.py @@ -354,7 +354,7 @@ def usage_array( } wk._add_process(p) wk.add_process( - ["submit_tok_stats.sh", "merge", p.output, output], + ["submit_tok_stats.sh", "merge", p.output.with_suffix(""), output], inputs=[p.output], output=output, slurm=slurm, From 693e99f4b06d228d3004b99d4048c929c5649d54 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 13 Jun 2025 09:50:56 -0400 Subject: [PATCH 47/54] add tracy profile option --- opt/TokenizerStats/activate | 2 +- opt/TokenizerStats/main.jl | 2 +- opt/TokenizerStats/src/cli.jl | 71 +++++++++++++++---------- opt/TokenizerStats/submit_precompile.sh | 2 +- opt/TokenizerStats/submit_tok_stats.sh | 11 +++- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/opt/TokenizerStats/activate b/opt/TokenizerStats/activate index 6c072d92..94d901db 100755 --- a/opt/TokenizerStats/activate +++ b/opt/TokenizerStats/activate @@ -5,7 +5,7 @@ DIR="$(git rev-parse --show-toplevel)/opt/TokenizerStats" # Load modules if command -v module > /dev/null; then module purge - module --ignore_cache load gcc python/3.11.5 openmpi/4.1.6 + module --ignore_cache load gcc/10.3.0 openmpi/4.1.6 python/3.11.5 export SSL_CERT_DIR=/etc/pki/tls/certs export SSL_CERT_FILE=/etc/pki/tls/cert.pem export JULIA_CPU_TARGET="generic;znver4,clone_all;znver3,clone_all;haswell" diff --git a/opt/TokenizerStats/main.jl b/opt/TokenizerStats/main.jl index 70e9f21d..c079f1df 100755 --- a/opt/TokenizerStats/main.jl +++ b/opt/TokenizerStats/main.jl @@ -2,7 +2,7 @@ using TokenizerStats: main using Tracy: wait_for_tracy -if haskey(ENV, "TRACYJL_WAIT_FOR_TRACY") +if haskey(ENV, "TRACY_ENABLE") && get(ENV, "PMIX_RANK", -1) == 0 @info "Waiting for tracy to connect..." wait_for_tracy() @info "Connected!" diff --git a/opt/TokenizerStats/src/cli.jl b/opt/TokenizerStats/src/cli.jl index 6e66e421..4d4e5404 100644 --- a/opt/TokenizerStats/src/cli.jl +++ b/opt/TokenizerStats/src/cli.jl @@ -135,42 +135,57 @@ function parse_splits(x::Vector{<:AbstractString}) end function merge_usage_stats(files::Vector{String}; output::String="merged.jld2", splits::Vector{String}=["train", "val", "test"]) - merged = jldopen(output, "w") + merged_stats = Dict{String, Int}() + rm(output * ".tmp", force=true) for file in files - jldopen(file, "r") do other - @info "Merging $file" other - if haskey(other, "tokenizer") - if haskey(merged, "tokenizer") - @assert other["tokenizer"] == merged["tokenizer"] "N-gram models must use the same tokenizer" - else - merged["tokenizer"] = other["tokenizer"] + jldopen(output * ".tmp", "a+") do merged + jldopen(file, "r") do other + @info "Merging $file" other + if haskey(other, "tokenizer") + if haskey(merged, "tokenizer") + @assert other["tokenizer"] == merged["tokenizer"] "N-gram models must use the same tokenizer" + else + merged["tokenizer"] = other["tokenizer"] + end end - end - for split in splits - if haskey(merged, split) - merged[split]["out_of_vocab"] += other["out_of_vocab"] - merged[split]["samples"] += other["samples"] - merged[split]["out_of_vocab"] += other["out_of_vocab"] - mergewith!(+, merged[split]["fertility"], other["fertility"]) - mergewith!(+, merged[split]["nunique"], other["nunique"]) - for n in 1:length(merged[split]["ngrams"]) - b_ngram = compact_ngrams(other["ngrams"]["$n"]) - mergewith!(+, merged[split]["ngrams"]["$n"], b_ngram) - end - else - merged[split]["out_of_vocab"] = other[split]["out_of_vocab"] - merged[split]["samples"] = other[split]["samples"] - merged[split]["out_of_vocab"] = other[split]["out_of_vocab"] - merged[split]["fertility"] = other[split]["fertility"] - merged[split]["nunique"] = other[split]["nunique"] - for n in 1:length(other[split]["ngrams"]) - merged[split]["ngrams"]["$n"] = compact_ngrams(other[split]["ngrams"]["$n"]) + for split in splits + if haskey(merged, split) + merged_stats[joinpath(split, "out_of_vocab")] += other[split]["out_of_vocab"] + merged_stats[joinpath(split, "samples")] += other[split]["samples"] + mergewith!(+, merged[split]["fertility"], other[split]["fertility"]) + mergewith!(+, merged[split]["nunique"], other[split]["nunique"]) + for n in 1:length(merged[split]["ngrams"]) + b_ngram = compact_ngrams(other[split]["ngrams"]["$n"]) + mergewith!(+, merged[split]["ngrams"]["$n"], b_ngram) + end + else + merged_stats[joinpath(split, "out_of_vocab")] = other[split]["out_of_vocab"] + merged_stats[joinpath(split, "samples")] = other[split]["samples"] + merged[joinpath(split, "fertility")] = other[split]["fertility"] + merged[joinpath(split, "nunique")] = other[split]["nunique"] + for n in 1:length(other[split]["ngrams"]) + merged[joinpath(split, "ngrams", string(n))] = compact_ngrams(other[split]["ngrams"]["$n"]) + end end end end end end + # Write merged single-value stats + jldopen(output * ".tmp", "a+") do merged + for split in splits + for k in ["out_of_vocab", "samples"] + merged[joinpath(split, k)] = merged_stats[joinpath(split, k)] + end + end + end + + # Finalize merged file + mv(output * ".tmp", output; force=true) + @info "saved results to $output" now() + chmod(output, 0o444) + return output end diff --git a/opt/TokenizerStats/submit_precompile.sh b/opt/TokenizerStats/submit_precompile.sh index 64f7ac77..d7701a40 100755 --- a/opt/TokenizerStats/submit_precompile.sh +++ b/opt/TokenizerStats/submit_precompile.sh @@ -15,7 +15,7 @@ export JULIA_PKG_USE_CLI_GIT=true export JULIA_NUM_PRECOMPILE_TASKS=$SLURM_CPUS_ON_NODE env -julia --color=no --startup-file=no --project -e 'using MPIPreferences; MPIPreferences.use_system_binary()' +julia --color=no --startup-file=no --project -e 'using Pkg; Pkg.instantiate(); using MPIPreferences; MPIPreferences.use_system_binary()' julia --color=no --startup-file=no --project -e 'using Pkg; Pkg.resolve(); Pkg.instantiate(); Pkg.precompile(timing=true)' julia --color=no --startup-file=no --project=plots -e 'using Pkg; Pkg.resolve(); Pkg.instantiate(); Pkg.precompile(timing=true)' exit 0 diff --git a/opt/TokenizerStats/submit_tok_stats.sh b/opt/TokenizerStats/submit_tok_stats.sh index ab2028cb..8b5e23e0 100755 --- a/opt/TokenizerStats/submit_tok_stats.sh +++ b/opt/TokenizerStats/submit_tok_stats.sh @@ -13,8 +13,15 @@ cd "$(git rev-parse --show-toplevel)/opt/TokenizerStats" source ./activate env -# module load cuda/12.2.1 -# nsys profile -o "nsys_multinode_%q{SLURM_JOB_ID}_%q{PMIX_RANK}" --trace=mpi,nvtx \ +# # Start Tracy Profiler +# module --ignore_cache load spack +# module --ignore_cache load tracy +# export TRACY_WORKLOAD="tracy_${SLURM_JOB_ID}.tracy" +# export TRACY_PORT=$(( 9000 + $SLURM_JOB_ID % 1024 )) +# export TRACY_ENABLE=1 +# tracy-capture --output-path $TRACY_WORKLOAD --port $TRACY_PORT + +# Launch the job srun --mpi=pmix \ julia --project --threads=${SLURM_CPUS_PER_TASK:-1} --color=no --startup-file=no -- \ ./main.jl $@ From a95ed3d2007a7e7dbc35215916af8977979cd38c Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Fri, 13 Jun 2025 09:51:09 -0400 Subject: [PATCH 48/54] feat: transfer tokenizer stats archive to dataden --- opt/TokenizerStats/submit_archive.sh | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh index a2c36cec..f319be7d 100644 --- a/opt/TokenizerStats/submit_archive.sh +++ b/opt/TokenizerStats/submit_archive.sh @@ -6,18 +6,21 @@ set -x my_job_header +# Locate directory with tokenizers stats +STATS_DIR=$(realpath stats/) + # Add tokenizers.json -cp tokenizers.json stats/ +cp tokenizers.json $STATS_DIR # Compress stats export XZ_OPT="-9 -v --extreme --memlimit=4000000000 --threads=${SLURM_CPUS_PER_TASK}" stats_archive="$(realpath ~/scratch)/tokenizer_stats_$(date +"%d%m%Y").tar.xz" -tar -cavf $stats_archive \ +tar -cavf "$stats_archive" \ --xz \ --exclude="*.slurm" \ --exclude="*.tmp" \ - --exclude='*/.unmerged/*' - -C stats \ + --exclude='*/.unmerged/*' \ + -C "$STATS_DIR" \ . mkdir -p ./archive ln -sf $stats_archive ./archive/ngram_tokenizer_stats.tar.xz @@ -34,3 +37,13 @@ ln -sf $code_archive ./archive/TokenizerStats.tar.xz SMIRK_VERSION="v0.1.1" curl -L https://github.com/BattModels/smirk/archive/refs/tags/$(SMIRK_VERSION).tar.gz \ --output "./archive/smirk_${SMIRK_VERSION}.tar.gz" + +# Archive to DataDen +SRC="3242c149-a2b9-4dba-9406-ae3717981621" # Artemis +DST="ab65757f-00f5-4e5b-aa21-133187732a01" # DataDen +if [[ $(type -t globus) ]]; then + find $(realpath ./archive) -not -type d -printf '%f %f\n' | \ + globus transfer --batch - \ + $SRC:$(realpath ./archive) \ + $DST:"/coe-venkvis/awadell/smirk-paper-archive_$(date +"%d%m%Y")/" +fi From 18640c039da9199bb8d83699773486af30421ea6 Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 1 Sep 2025 17:06:26 -0400 Subject: [PATCH 49/54] fix pre-commit and import error --- container/build.sh | 0 electrolyte_fm/utils/cache.py | 6 +++++- opt/TokenizerStats/plots/plots.jl | 1 - opt/TokenizerStats/plots/src/figures/fe_models.jl | 1 - opt/TokenizerStats/plots/src/figures/info_loss.jl | 1 - opt/TokenizerStats/plots/src/figures/intrinsic.jl | 1 - opt/TokenizerStats/plots/src/plot_utils.jl | 2 -- opt/TokenizerStats/plots/src/prognostics.jl | 1 - opt/TokenizerStats/python/helper/loader.py | 1 - opt/TokenizerStats/test/cli_tests.jl | 1 - test/test_dataset.py | 2 +- 11 files changed, 6 insertions(+), 11 deletions(-) mode change 100644 => 100755 container/build.sh diff --git a/container/build.sh b/container/build.sh old mode 100644 new mode 100755 diff --git a/electrolyte_fm/utils/cache.py b/electrolyte_fm/utils/cache.py index e7e2fb99..b8a753ee 100644 --- a/electrolyte_fm/utils/cache.py +++ b/electrolyte_fm/utils/cache.py @@ -51,7 +51,11 @@ def cached_download(url: str, path: Path, disable_ssl=False) -> Path: import ssl import urllib.request - ctx = ssl.create_default_context() if not disable_ssl else ssl._create_unverified_context() + ctx = ( + ssl.create_default_context() + if not disable_ssl + else ssl._create_unverified_context() + ) user_agent = "Wget/1.19.5" # Pretend to be wget req = urllib.request.Request(url, headers={"User-Agent": user_agent}) diff --git a/opt/TokenizerStats/plots/plots.jl b/opt/TokenizerStats/plots/plots.jl index cdaa84ff..5b97d2f6 100644 --- a/opt/TokenizerStats/plots/plots.jl +++ b/opt/TokenizerStats/plots/plots.jl @@ -175,4 +175,3 @@ with_theme(SmirkPaperPlots.theme()) do end end end - diff --git a/opt/TokenizerStats/plots/src/figures/fe_models.jl b/opt/TokenizerStats/plots/src/figures/fe_models.jl index 76ab19ae..3a6f8830 100644 --- a/opt/TokenizerStats/plots/src/figures/fe_models.jl +++ b/opt/TokenizerStats/plots/src/figures/fe_models.jl @@ -204,4 +204,3 @@ function val_ci_dodge(models; dodge::Union{AbstractVector,Nothing}=nothing, scal ) return val, ci, kwargs end - diff --git a/opt/TokenizerStats/plots/src/figures/info_loss.jl b/opt/TokenizerStats/plots/src/figures/info_loss.jl index 4ae27369..46b98f5b 100644 --- a/opt/TokenizerStats/plots/src/figures/info_loss.jl +++ b/opt/TokenizerStats/plots/src/figures/info_loss.jl @@ -578,4 +578,3 @@ function figure_ngram_metrics(tok_info, df_loss, df_info; ngram=5) return f end - diff --git a/opt/TokenizerStats/plots/src/figures/intrinsic.jl b/opt/TokenizerStats/plots/src/figures/intrinsic.jl index 192d9c35..b9e1be08 100644 --- a/opt/TokenizerStats/plots/src/figures/intrinsic.jl +++ b/opt/TokenizerStats/plots/src/figures/intrinsic.jl @@ -144,4 +144,3 @@ function Makie.plot!(plt::VSpan) poly!(plt, p; Makie.shared_attributes(plt, Poly)...) return plt end - diff --git a/opt/TokenizerStats/plots/src/plot_utils.jl b/opt/TokenizerStats/plots/src/plot_utils.jl index 46fd4a25..2b853de7 100644 --- a/opt/TokenizerStats/plots/src/plot_utils.jl +++ b/opt/TokenizerStats/plots/src/plot_utils.jl @@ -319,5 +319,3 @@ function barploterrors!(ax, x, y, dodge; std=nothing, colormap, colorrange=nothi return h end - - diff --git a/opt/TokenizerStats/plots/src/prognostics.jl b/opt/TokenizerStats/plots/src/prognostics.jl index ebe95e92..3b625006 100644 --- a/opt/TokenizerStats/plots/src/prognostics.jl +++ b/opt/TokenizerStats/plots/src/prognostics.jl @@ -84,4 +84,3 @@ function Base.show(io::IO, mime::MIME"text/plain", stt::SpearmanTTest) println(io, "t: $(round(stt.t; sigdigits=3))") println(io, "p-value: $(round(pvalue(stt); sigdigits=3))") end - diff --git a/opt/TokenizerStats/python/helper/loader.py b/opt/TokenizerStats/python/helper/loader.py index 3fffcbf4..9433c799 100644 --- a/opt/TokenizerStats/python/helper/loader.py +++ b/opt/TokenizerStats/python/helper/loader.py @@ -1,4 +1,3 @@ -from itertools import islice from pathlib import Path from dataclasses import dataclass from datasets import load_dataset diff --git a/opt/TokenizerStats/test/cli_tests.jl b/opt/TokenizerStats/test/cli_tests.jl index c15cc797..7b7fcc8c 100644 --- a/opt/TokenizerStats/test/cli_tests.jl +++ b/opt/TokenizerStats/test/cli_tests.jl @@ -11,4 +11,3 @@ end @test Set(parse_splits("val,train")) == Set(["val", "train"]) @test Set(parse_splits("all")) == Set(["train", "val", "test"]) end - diff --git a/test/test_dataset.py b/test/test_dataset.py index 79c00566..20798d0a 100644 --- a/test/test_dataset.py +++ b/test/test_dataset.py @@ -10,10 +10,10 @@ RobertaDataSet, tmQMDataModule, ) -from electrolyte_fm.data_modules.molnet_dataset import strip_unk_tokens from electrolyte_fm.data_modules.utils import ( MolEncoding, encode_molecules, + strip_unk_tokens, ) from electrolyte_fm.utils.tokenizer import load_tokenizer From ebfa2e7fae77663e3fc12dbaa31832ed22aecb9f Mon Sep 17 00:00:00 2001 From: Alexius Wadell Date: Mon, 1 Sep 2025 17:14:35 -0400 Subject: [PATCH 50/54] merge updates from main --- container/training.def | 41 ++++++++++++++++++++++------------------- submit/dgx.j2 | 6 ++++-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/container/training.def b/container/training.def index 8d57a45d..224ac1fd 100644 --- a/container/training.def +++ b/container/training.def @@ -3,36 +3,39 @@ From: nvcr.io/nvidia/pytorch:{{ NGC_VERSION }} %arguments NGC_VERSION=25.01-py3 - POETRY_VERSION=1.8.3 PROJECT_DIR=/mist %files pyproject.toml {{ PROJECT_DIR }}/pyproject.toml - poetry.lock {{ PROJECT_DIR }}/poetry.lock + uv.lock {{ PROJECT_DIR }}/uv.lock README.md {{ PROJECT_DIR }}/README.md %post - apt-get update - apt-get install -y python3-venv cargo - - # Install Poetry - export POETRY_HOME=/opt/poetry - export POETRY_VERSION={{ POETRY_VERSION }} - export POETRY_VIRTUALENVS_IN_PROJECT=1 - export POETRY_VIRTUALENVS_CREATE=1 \ - export POETRY_NO_INTERACTION=1 - export POETRY_CACHE_DIR=/tmp/poetry_cache - curl -sSL https://install.python-poetry.org | python3 - + # Install cargo + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal + source $HOME/.cargo/env + + # Install UV + export UV_UNMANAGED_INSTALL=/opt/uv + curl -LsSf https://astral.sh/uv/install.sh | sh # Setup SSH export SSH_AUTH_SOCK={{ SSH_AUTH_SOCK }} mkdir -p -m 0600 ~/.ssh - ssh-keyscan -H github.com >> ~/.ssh/known_hosts + ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts + ssh git@github.com || echo "success" - # Install project - $POETRY_HOME/bin/poetry install --compile --no-root --all-extras --directory {{ PROJECT_DIR }} - rm -rf $POETRY_CACHE_DIR + # Install dependencies + $UV_UNMANAGED_INSTALL/uv sync \ + --directory {{ PROJECT_DIR }} \ + --frozen \ + --no-install-project \ + --link-mode=copy \ + --compile-bytecode \ + --no-cache %runscript - source {{ PROJECT_DIR }}/.venv/bin/activate - exec $@ + /opt/uv/uv run --project {{ PROJECT_DIR }} $@ + +%environment + export TRITON_LIBCUDA_PATH=/usr/local/cuda/compat/lib.real/libcuda.so diff --git a/submit/dgx.j2 b/submit/dgx.j2 index 0c88d7d3..b4cd52a3 100644 --- a/submit/dgx.j2 +++ b/submit/dgx.j2 @@ -49,6 +49,7 @@ cat > ${PL_CONFIG}< Date: Mon, 20 Oct 2025 22:43:26 -0400 Subject: [PATCH 51/54] fix: pre-commit --- opt/TokenizerStats/check_ambi_smiles.py | 38 ------------------------- 1 file changed, 38 deletions(-) diff --git a/opt/TokenizerStats/check_ambi_smiles.py b/opt/TokenizerStats/check_ambi_smiles.py index 92661a36..e258b77b 100755 --- a/opt/TokenizerStats/check_ambi_smiles.py +++ b/opt/TokenizerStats/check_ambi_smiles.py @@ -141,44 +141,6 @@ def process_smiles( typer.echo(f"Results written incrementally to: {output_file}") -@app.command("merge") -def merge_ambiguous_files( - output_file: str, - input_files: list[str] = typer.Argument( - ..., help="List of ambiguous.csv files to merge." - ), -): - """ - Merge multiple ambiguous.csv files and deduplicate using InChIKey. - """ - combined_df = pd.DataFrame() - for file in input_files: - input_path = Path(file) - if not input_path.exists(): - typer.echo(f"File {file} does not exist.") - raise typer.Exit() - - typer.echo(f"Reading file: {file}") - df = pd.read_csv(file) - - if "InChI" not in df or "smiles" not in df: - typer.echo(f"File {file} must contain 'InChI' and 'smiles' columns.") - raise typer.Exit() - - combined_df = pd.concat([combined_df, df], ignore_index=True) - - # Deduplicate using InChIKey - if "InChI" in combined_df.columns: - combined_df = combined_df.drop_duplicates(subset=["InChI"]) - - typer.echo(f"Merged and deduplicated {len(combined_df)} rows based on InChIKey.") - - # Write to output file - output_path = Path(output_file) - combined_df.to_csv(output_path, index=False) - typer.echo(f"Final merged file written to: {output_path}") - - @app.command("stats") def tabulate_ambiguities( output_file: str, From 3fcd19df65285d89135bf2e65cf7ddf5b51aa2f9 Mon Sep 17 00:00:00 2001 From: Anoushka Bhutani Date: Mon, 20 Oct 2025 22:47:33 -0400 Subject: [PATCH 52/54] mark shebang executable --- opt/TokenizerStats/submit_archive.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 opt/TokenizerStats/submit_archive.sh diff --git a/opt/TokenizerStats/submit_archive.sh b/opt/TokenizerStats/submit_archive.sh old mode 100644 new mode 100755 From a9db85a815a65b6c30e9fc2a3e005e852070871d Mon Sep 17 00:00:00 2001 From: Anoushka Bhutani Date: Mon, 20 Oct 2025 22:51:44 -0400 Subject: [PATCH 53/54] formatting --- electrolyte_fm/data_modules/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index d8a39014..576befc2 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -3,7 +3,6 @@ from enum import Enum import random from asyncio import Semaphore -from enum import Enum from typing import Optional, TypeVar from datasets import Dataset, DatasetDict, IterableDatasetDict From f13512c23bdb7e7d802d62ad7d19862b86bf40c5 Mon Sep 17 00:00:00 2001 From: Anoushka Bhutani Date: Mon, 20 Oct 2025 23:32:03 -0400 Subject: [PATCH 54/54] formatting --- electrolyte_fm/data_modules/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/electrolyte_fm/data_modules/utils.py b/electrolyte_fm/data_modules/utils.py index 904c98fd..0dc3132a 100644 --- a/electrolyte_fm/data_modules/utils.py +++ b/electrolyte_fm/data_modules/utils.py @@ -8,7 +8,6 @@ from rdkit import Chem from datasets import Dataset, DatasetDict, IterableDatasetDict from datasets.distributed import split_dataset_by_node -from rdkit import Chem from rdkit.Chem.Scaffolds.MurckoScaffold import MurckoScaffoldSmiles from sklearn.model_selection import GroupShuffleSplit