From 5a9822033680faac90fc15c93ae94b47c3f9748c Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 19 Nov 2024 18:30:01 +0100 Subject: [PATCH 1/3] bump `optype` to `0.7.1` and `scipy-stubs` to `1.14.1.4` --- pyproject.toml | 14 ++--- uv.lock | 160 +++++++++++++++++++++++++------------------------ 2 files changed, 88 insertions(+), 86 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f3f03e86..d9bd9098 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,8 @@ dependencies = [ "typing_extensions>=4.10; python_version<'3.13'", "numpy>=1.24", "scipy>=1.10", - "scipy-stubs>=1.14.1.3", - "optype[numpy]>=0.6.1,<0.7", + "scipy-stubs>=1.14.1.4", + "optype[numpy]>=0.7.1", ] [project.optional-dependencies] @@ -63,11 +63,11 @@ docs = [ "mkdocs>=1.6.1", "mkdocs-bibtex>=2.16.2", "mkdocs-git-revision-date-localized-plugin>=1.3.0", - "mkdocs-include-markdown-plugin>=7.0.0", + "mkdocs-include-markdown-plugin>=7.0.1", "mkdocs-jupyter>=0.25.1", "mkdocs-material>=9.5.44", "mkdocs-minify-plugin>=0.8.0", - "mkdocstrings[python]>=0.26.2,<0.27.0", + "mkdocstrings[python]>=0.27.0", ] pandas = [ "pandas>=2.2.3", @@ -76,12 +76,12 @@ pandas = [ dev = [ "blacken-docs>=1.19.1", "codespell>=2.3.0", - "hypothesis[numpy]>=6.118.8", + "hypothesis[numpy]>=6.119.3", "pre-commit>=4.0.1", - "basedpyright>=1.21.0", + "basedpyright>=1.21.1", "pytest>=8.3.3", "pytest-doctestplus>=1.2.1", - "ruff>=0.7.3", + "ruff>=0.7.4", "sp-repo-review[cli]>=2024.8.19", "tox>=4.23.2", ] diff --git a/uv.lock b/uv.lock index 990b0600..7c74ab04 100644 --- a/uv.lock +++ b/uv.lock @@ -46,14 +46,14 @@ wheels = [ [[package]] name = "basedpyright" -version = "1.21.0" +version = "1.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodejs-wheel-binaries" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/91/d66f9ece468b3daa2f3d357304c816438be4595006928ea5639fb6b5139e/basedpyright-1.21.0.tar.gz", hash = "sha256:e3a9f5b89acc7f23d5a10d95ea6056b2247fcd15b8b248ad44c560c85c39d5e3", size = 20789741 } +sdist = { url = "https://files.pythonhosted.org/packages/7a/57/ea77a6bad60ad7cb7b457957968a93f62730629316301b7d48fc439cb87f/basedpyright-1.21.1.tar.gz", hash = "sha256:c27e5921768db2141360889978678152a685fe1c6ff95652b2157240a5fd575c", size = 20840717 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/f7/97af385e43208d600f892da1b8c94f35d4e9165d5c774013bb202617dd7d/basedpyright-1.21.0-py3-none-any.whl", hash = "sha256:48902c476d6301c556df6eeae9acf1e34b176b14f8702ad5c770f4e6a747018a", size = 11148428 }, + { url = "https://files.pythonhosted.org/packages/f6/94/06f6a4b34167e838b81e46e40d5acdb03cb56dd2c229319aff091f9f43cb/basedpyright-1.21.1-py3-none-any.whl", hash = "sha256:e4c30c84d5fe0f33ba2f7bd5b20019aa6f031ef4c5032705b4960dad52066667", size = 11173914 }, ] [[package]] @@ -449,33 +449,35 @@ wheels = [ [[package]] name = "fonttools" -version = "4.54.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/1d/70b58e342e129f9c0ce030029fb4b2b0670084bbbfe1121d008f6a1e361c/fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285", size = 3463867 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/2c/8b5d82fe2d9c7f260fb73121418f5e07d4e38c329ea3886a5b0e55586113/fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20", size = 2768112 }, - { url = "https://files.pythonhosted.org/packages/37/2e/f94118b92f7b6a9ec93840101b64bfdd09f295b266133857e8e852a5c35c/fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2", size = 2254739 }, - { url = "https://files.pythonhosted.org/packages/45/4b/8a32f56a13e78256192f77d6b65583c43538c7955f5420887bb574b91ddf/fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7", size = 4879772 }, - { url = "https://files.pythonhosted.org/packages/96/13/748b7f7239893ff0796de11074b0ad8aa4c3da2d9f4d79a128b0b16147f3/fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07", size = 4927686 }, - { url = "https://files.pythonhosted.org/packages/7c/82/91bc5a378b4a0593fa90ea706f68ce7e9e871c6873e0d91e134d107758db/fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8", size = 4890789 }, - { url = "https://files.pythonhosted.org/packages/ea/ca/82be5d4f8b78405cdb3f7f3f1316af5e8db93216121f19da9f684a35beee/fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a", size = 5061351 }, - { url = "https://files.pythonhosted.org/packages/da/2f/fd6e1b01c80c473c3ac52492dcf8d26cdf5f4a89b4f30875ecfbda55e7ff/fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc", size = 2166210 }, - { url = "https://files.pythonhosted.org/packages/63/f1/3a081cd047d83b5966cb0d7ef3fea929ee6eddeb94d8fbfdb2a19bd60cc7/fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6", size = 2211946 }, - { url = "https://files.pythonhosted.org/packages/27/b6/f9d365932dcefefdcc794985f8846471e60932070c557e0f66ed195fccec/fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d", size = 2761873 }, - { url = "https://files.pythonhosted.org/packages/67/9d/cfbfe36e5061a8f68b154454ba2304eb01f40d4ba9b63e41d9058909baed/fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08", size = 2251828 }, - { url = "https://files.pythonhosted.org/packages/90/41/5573e074739efd9227dd23647724f01f6f07ad062fe09d02e91c5549dcf7/fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263", size = 4792544 }, - { url = "https://files.pythonhosted.org/packages/08/07/aa85cc62abcc940b25d14b542cf585eebf4830032a7f6a1395d696bb3231/fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab", size = 4875892 }, - { url = "https://files.pythonhosted.org/packages/47/23/c5726c2615446c498a976bed21c35a242a97eee39930a2655d616ca885cc/fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d", size = 4769822 }, - { url = "https://files.pythonhosted.org/packages/8f/7b/87f7f7d35e0732ac67422dfa6f05e2b568fb6ca2dcd7f3e4f500293cfd75/fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714", size = 5029455 }, - { url = "https://files.pythonhosted.org/packages/e0/09/241aa498587889576838aa73c78d22b70ce06970807a5475d372baa7ccb7/fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac", size = 2154411 }, - { url = "https://files.pythonhosted.org/packages/b9/0a/a57caaff3bc880779317cb157e5b49dc47fad54effe027016abd355b0651/fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e", size = 2200412 }, - { url = "https://files.pythonhosted.org/packages/05/3d/cc515cae84a11d696f2cb7c139a90997b15f02e2e97ec09a5d79302cbcd7/fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff", size = 2749174 }, - { url = "https://files.pythonhosted.org/packages/03/03/05d4b22d1a674d066380657f60bbc0eda2d206446912e676d1a33a206878/fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb", size = 2246267 }, - { url = "https://files.pythonhosted.org/packages/52/c3/bb6086adb675e8b0963a7dbb7769e7118c95b687dd318cd660aefd4b4c8c/fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a", size = 4855090 }, - { url = "https://files.pythonhosted.org/packages/80/a1/d7192b6a104e3f9ea8e5b1c3463a6240399f0fa826a782eff636cbe0495a/fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c", size = 5005449 }, - { url = "https://files.pythonhosted.org/packages/5a/6c/ecfd5c6cd8c9006e85b128d073af26bb263e8aa47506374cb14b25bcf65f/fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58", size = 2152496 }, - { url = "https://files.pythonhosted.org/packages/63/da/f7a1d837de419e3d4cccbd0dbf53c7399f610f65ceb9bcbf2480f3ae7950/fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d", size = 2197257 }, - { url = "https://files.pythonhosted.org/packages/57/5e/de2e6e51cb6894f2f2bc2641f6c845561361b622e96df3cca04df77222c9/fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd", size = 1096920 }, +version = "4.55.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4e/053fe1b5c0ce346c0a9d0557492c654362bafb14f026eae0d3ee98009152/fonttools-4.55.0.tar.gz", hash = "sha256:7636acc6ab733572d5e7eec922b254ead611f1cdad17be3f0be7418e8bfaca71", size = 3490431 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/50/75461e050ded02b9eaa8097df52c2a8752cf4c24db8b44b150755b76c8f1/fonttools-4.55.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fa34aa175c91477485c44ddfbb51827d470011e558dfd5c7309eb31bef19ec51", size = 2771444 }, + { url = "https://files.pythonhosted.org/packages/de/5e/98130db3770e8d12f70aa61f2555c32284d4e9c592862469d32b7ee48626/fonttools-4.55.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:37dbb3fdc2ef7302d3199fb12468481cbebaee849e4b04bc55b77c24e3c49189", size = 2296439 }, + { url = "https://files.pythonhosted.org/packages/17/35/36fe271296fe7624811f5261a0662155e075b43b79ffacea85a03f36593d/fonttools-4.55.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5263d8e7ef3c0ae87fbce7f3ec2f546dc898d44a337e95695af2cd5ea21a967", size = 4883141 }, + { url = "https://files.pythonhosted.org/packages/47/2b/9bf7527260d265281dd812951aa22f3d1c331bcc91e86e7038dc6b9737cb/fonttools-4.55.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f307f6b5bf9e86891213b293e538d292cd1677e06d9faaa4bf9c086ad5f132f6", size = 4931050 }, + { url = "https://files.pythonhosted.org/packages/0b/7b/7324d3aa8424c71b63ba2e76eb4a46d6947e23065996e755c1682e666f42/fonttools-4.55.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f0a4b52238e7b54f998d6a56b46a2c56b59c74d4f8a6747fb9d4042190f37cd3", size = 4894154 }, + { url = "https://files.pythonhosted.org/packages/2c/53/a54926be69e43d277877106a6cbfab467cb02f9c756258c7c9932e8eb382/fonttools-4.55.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3e569711464f777a5d4ef522e781dc33f8095ab5efd7548958b36079a9f2f88c", size = 5064715 }, + { url = "https://files.pythonhosted.org/packages/0c/f7/9602868af9a2dfc4659637a752da8691655e81a2d6138231dcaa1efe8840/fonttools-4.55.0-cp311-cp311-win32.whl", hash = "sha256:2b3ab90ec0f7b76c983950ac601b58949f47aca14c3f21eed858b38d7ec42b05", size = 2169536 }, + { url = "https://files.pythonhosted.org/packages/30/57/9e2ddd16ad84ab26616ae4346b3b15e9a50669ca1b442cbe760af073807c/fonttools-4.55.0-cp311-cp311-win_amd64.whl", hash = "sha256:aa046f6a63bb2ad521004b2769095d4c9480c02c1efa7d7796b37826508980b6", size = 2215265 }, + { url = "https://files.pythonhosted.org/packages/ec/79/38209f8f6235021b6209147ec7b2f748afde65c59c6274ac96fef3912094/fonttools-4.55.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:838d2d8870f84fc785528a692e724f2379d5abd3fc9dad4d32f91cf99b41e4a7", size = 2765205 }, + { url = "https://files.pythonhosted.org/packages/e3/07/434a21eab80524613c9753db2ff21d6bc3cf264412d8833a85022fd39088/fonttools-4.55.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f46b863d74bab7bb0d395f3b68d3f52a03444964e67ce5c43ce43a75efce9246", size = 2293908 }, + { url = "https://files.pythonhosted.org/packages/c8/63/aa3274d9be36aaaef8c087e413cbc1dd682ff94776a82c111bad88482947/fonttools-4.55.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33b52a9cfe4e658e21b1f669f7309b4067910321757fec53802ca8f6eae96a5a", size = 4795901 }, + { url = "https://files.pythonhosted.org/packages/fc/0b/dbe13f2c8d745ffdf5c2bc25391263927d4ec2b927e44d5d5f70cd372873/fonttools-4.55.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:732a9a63d6ea4a81b1b25a1f2e5e143761b40c2e1b79bb2b68e4893f45139a40", size = 4879252 }, + { url = "https://files.pythonhosted.org/packages/46/85/eefb400ec66e9e7c159d13c72aba473d9c2a0c556d812b0916418aa9019e/fonttools-4.55.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7dd91ac3fcb4c491bb4763b820bcab6c41c784111c24172616f02f4bc227c17d", size = 4773177 }, + { url = "https://files.pythonhosted.org/packages/93/75/f06d175df4d7dbad97061c8698210ce4231cce9aa56cc263f3b6b5340540/fonttools-4.55.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f0e115281a32ff532118aa851ef497a1b7cda617f4621c1cdf81ace3e36fb0c", size = 5032809 }, + { url = "https://files.pythonhosted.org/packages/78/eb/f3520ba63b5e4a034f2bfd34d8ab32eb95a1bf37a1f792ea48461fba08f6/fonttools-4.55.0-cp312-cp312-win32.whl", hash = "sha256:6c99b5205844f48a05cb58d4a8110a44d3038c67ed1d79eb733c4953c628b0f6", size = 2157762 }, + { url = "https://files.pythonhosted.org/packages/aa/d1/5f007861cab890f2a35a19a1d2a2815655ec10b0ea7fd881b1d3aaab0076/fonttools-4.55.0-cp312-cp312-win_amd64.whl", hash = "sha256:f8c8c76037d05652510ae45be1cd8fb5dd2fd9afec92a25374ac82255993d57c", size = 2203746 }, + { url = "https://files.pythonhosted.org/packages/c3/87/a669ac26c6077e37ffb06abf29c5571789eefe518d06c52df392181ee694/fonttools-4.55.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8118dc571921dc9e4b288d9cb423ceaf886d195a2e5329cc427df82bba872cd9", size = 2752519 }, + { url = "https://files.pythonhosted.org/packages/0c/e9/4822ad238fe215133c7df20f1cdb1a58cfb634a31523e77ff0fb2033970a/fonttools-4.55.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01124f2ca6c29fad4132d930da69158d3f49b2350e4a779e1efbe0e82bd63f6c", size = 2286819 }, + { url = "https://files.pythonhosted.org/packages/3e/a4/d7941c3897129e60fe68d20e4819fda4d0c4858d77badae0e80ca6440b36/fonttools-4.55.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ffd58d2691f11f7c8438796e9f21c374828805d33e83ff4b76e4635633674c", size = 4770382 }, + { url = "https://files.pythonhosted.org/packages/31/cf/c51ea1348f9fba9c627439afad9dee0090040809ab431f4422b5bfdda34c/fonttools-4.55.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5435e5f1eb893c35c2bc2b9cd3c9596b0fcb0a59e7a14121562986dd4c47b8dd", size = 4858336 }, + { url = "https://files.pythonhosted.org/packages/73/be/36c1fe0e5c9a96b068ddd7e82001243bbe7fe12549c8d14e1bd025bf40c9/fonttools-4.55.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d12081729280c39d001edd0f4f06d696014c26e6e9a0a55488fabc37c28945e4", size = 4756072 }, + { url = "https://files.pythonhosted.org/packages/5c/18/6dd381c29f215a017f79aa9fea0722424a0046b47991c4390a78ff87ce0c/fonttools-4.55.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7ad1f1b98ab6cb927ab924a38a8649f1ffd7525c75fe5b594f5dab17af70e18", size = 5008668 }, + { url = "https://files.pythonhosted.org/packages/b8/95/316f20092b389b927dba1d1dccd3f541853f96e707e210f1b9f4e7bacdd5/fonttools-4.55.0-cp313-cp313-win32.whl", hash = "sha256:abe62987c37630dca69a104266277216de1023cf570c1643bb3a19a9509e7a1b", size = 2155841 }, + { url = "https://files.pythonhosted.org/packages/35/ca/b4638aa3e446184892e2f9cc8ef44bb506f47fea04580df7fb84f5a4363d/fonttools-4.55.0-cp313-cp313-win_amd64.whl", hash = "sha256:2863555ba90b573e4201feaf87a7e71ca3b97c05aa4d63548a4b69ea16c9e998", size = 2200587 }, + { url = "https://files.pythonhosted.org/packages/b4/4a/786589606d4989cb34d8bc766cd687d955aaf3039c367fe7104bcf82dc98/fonttools-4.55.0-py3-none-any.whl", hash = "sha256:12db5888cd4dd3fcc9f0ee60c6edd3c7e1fd44b7dd0f31381ea03df68f8a153f", size = 1100249 }, ] [[package]] @@ -536,15 +538,15 @@ wheels = [ [[package]] name = "hypothesis" -version = "6.118.8" +version = "6.119.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/00/c7a62a6bd0b4e8ac299f8d83cf237196bf60c965482ff3aee7815a70fb16/hypothesis-6.118.8.tar.gz", hash = "sha256:2a564996f60b8961b5112672564516a522fbd6ab4b536f8c6f3e4adb125b7dd2", size = 410665 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/6f/98262d753772030d140d8cd04eadebac545ba4945702f4471decc37b8cb5/hypothesis-6.119.3.tar.gz", hash = "sha256:1403676d95bc9f118a30ce2c97fcbdd28dd99f3a1ffe3456970d98a56b370f36", size = 412497 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/1a/b56f2d3bc2c0e129634891b3f0000cf07e96db5c4a460b7cf2351d19081e/hypothesis-6.118.8-py3-none-any.whl", hash = "sha256:9363bde019f9d11d584d3c96af8273ab8a6a43009a7c9b1c2fd503c646062304", size = 471777 }, + { url = "https://files.pythonhosted.org/packages/67/9d/32e2277a2dbc48b687f4acedbc6421c33a5f03177c8cb0d262d5ffc2ccf3/hypothesis-6.119.3-py3-none-any.whl", hash = "sha256:dff13689c4ceb0d84d92e0309fca3ccc1b547ac30552037c712a9080eb75cd05", size = 473632 }, ] [package.optional-dependencies] @@ -880,10 +882,10 @@ pandas = [ [package.metadata] requires-dist = [ { name = "numpy", specifier = ">=1.24" }, - { name = "optype", extras = ["numpy"], specifier = ">=0.6.1,<0.7" }, + { name = "optype", extras = ["numpy"], specifier = ">=0.7.1" }, { name = "pandas", marker = "extra == 'pandas'", specifier = ">=2.0" }, { name = "scipy", specifier = ">=1.10" }, - { name = "scipy-stubs", specifier = ">=1.14.1.3" }, + { name = "scipy-stubs", specifier = ">=1.14.1.4" }, { name = "typing-extensions", marker = "python_full_version < '3.13'", specifier = ">=4.10" }, ] @@ -896,14 +898,14 @@ debug = [ { name = "typing-extensions", specifier = ">=4.12.2" }, ] dev = [ - { name = "basedpyright", specifier = ">=1.21.0" }, + { name = "basedpyright", specifier = ">=1.21.1" }, { name = "blacken-docs", specifier = ">=1.19.1" }, { name = "codespell", specifier = ">=2.3.0" }, - { name = "hypothesis", extras = ["numpy"], specifier = ">=6.118.8" }, + { name = "hypothesis", extras = ["numpy"], specifier = ">=6.119.3" }, { name = "pre-commit", specifier = ">=4.0.1" }, { name = "pytest", specifier = ">=8.3.3" }, { name = "pytest-doctestplus", specifier = ">=1.2.1" }, - { name = "ruff", specifier = ">=0.7.3" }, + { name = "ruff", specifier = ">=0.7.4" }, { name = "sp-repo-review", extras = ["cli"], specifier = ">=2024.8.19" }, { name = "tox", specifier = ">=4.23.2" }, ] @@ -911,11 +913,11 @@ docs = [ { name = "mkdocs", specifier = ">=1.6.1" }, { name = "mkdocs-bibtex", specifier = ">=2.16.2" }, { name = "mkdocs-git-revision-date-localized-plugin", specifier = ">=1.3.0" }, - { name = "mkdocs-include-markdown-plugin", specifier = ">=7.0.0" }, + { name = "mkdocs-include-markdown-plugin", specifier = ">=7.0.1" }, { name = "mkdocs-jupyter", specifier = ">=0.25.1" }, { name = "mkdocs-material", specifier = ">=9.5.44" }, { name = "mkdocs-minify-plugin", specifier = ">=0.8.0" }, - { name = "mkdocstrings", extras = ["python"], specifier = ">=0.26.2,<0.27.0" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.27.0" }, ] pandas = [ { name = "pandas", specifier = ">=2.2.3" }, @@ -1170,15 +1172,15 @@ wheels = [ [[package]] name = "mkdocs-include-markdown-plugin" -version = "7.0.0" +version = "7.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mkdocs" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/00/0809a87912b7018669bb4d909ab88aafd78c048dcf74a72c06a46707c15f/mkdocs_include_markdown_plugin-7.0.0.tar.gz", hash = "sha256:a8eac8f2e6aa391d82d1d5e473b819b52393d91464060c02db5741834fe9008b", size = 21275 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/84/5e4479bc3d37896d4b787ee299c0d2f6d67907d4047a2e098e9b91034426/mkdocs_include_markdown_plugin-7.0.1.tar.gz", hash = "sha256:d619c206109dab4bab281e2d29b645838d55b0576c761b1fbb17e6bff1170206", size = 21359 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/62/c085d8744d7f8c93741b439d0ecfd44e6da9859d09a257c8a66c42c611ec/mkdocs_include_markdown_plugin-7.0.0-py3-none-any.whl", hash = "sha256:bf8d19245ae3fb2eea395888e80c60bc91806a0d879279707d707896c24319c3", size = 24916 }, + { url = "https://files.pythonhosted.org/packages/1e/19/eef6c91f023256895f5eaf9276be66f01b86b2a01f2a13d3fff776739880/mkdocs_include_markdown_plugin-7.0.1-py3-none-any.whl", hash = "sha256:4abd341cb1c5eac60ddd1a21540fdff714f1acc99e3b26f37641db60cd175a8d", size = 24996 }, ] [[package]] @@ -1246,7 +1248,7 @@ wheels = [ [[package]] name = "mkdocstrings" -version = "0.26.2" +version = "0.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1258,9 +1260,9 @@ dependencies = [ { name = "platformdirs" }, { name = "pymdown-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/76/0475d10d27f3384df3a6ddfdf4a4fdfef83766f77cd4e327d905dc956c15/mkdocstrings-0.26.2.tar.gz", hash = "sha256:34a8b50f1e6cfd29546c6c09fbe02154adfb0b361bb758834bf56aa284ba876e", size = 92512 } +sdist = { url = "https://files.pythonhosted.org/packages/e2/5a/5de70538c2cefae7ac3a15b5601e306ef3717290cb2aab11d51cbbc2d1c0/mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657", size = 94830 } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/b6/4ee320d7c313da3774eff225875eb278f7e6bb26a9cd8e680b8dbc38fdea/mkdocstrings-0.26.2-py3-none-any.whl", hash = "sha256:1248f3228464f3b8d1a15bd91249ce1701fe3104ac517a5f167a0e01ca850ba5", size = 29716 }, + { url = "https://files.pythonhosted.org/packages/cd/10/4c27c3063c2b3681a4b7942f8dbdeb4fa34fecb2c19b594e7345ebf4f86f/mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332", size = 30658 }, ] [package.optional-dependencies] @@ -1429,14 +1431,14 @@ wheels = [ [[package]] name = "optype" -version = "0.6.1" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/38/2d936e807e1559993f3b20d643c023470e31d0f9c3bea2d9d72835e88436/optype-0.6.1.tar.gz", hash = "sha256:3a410d65632a449e63a03182eb504d03f404427a99724868e7e393eb8e376207", size = 74229 } +sdist = { url = "https://files.pythonhosted.org/packages/de/16/96828a4863630ea958e2867c1ace868d8d6c07d5f65e9d1c26d65fc60e5b/optype-0.7.1.tar.gz", hash = "sha256:001d255ebe7391782730c8154f96c2b5f4a32bd4024c29408e92f33272b34256", size = 79816 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/58/39a30061163df33b5ffed0c1deeca3af3fa61f00ba2bb7f5af39a31dfb77/optype-0.6.1-py3-none-any.whl", hash = "sha256:ba45f940b7d858225c2995e273f404f1c7c8b2f1d71041b1413e5105104d2515", size = 62528 }, + { url = "https://files.pythonhosted.org/packages/14/55/f649c95da150b9851020637d91f030c0559faf14ac374ac44d9e276c0fbe/optype-0.7.1-py3-none-any.whl", hash = "sha256:ba36d5d72322adfdf3dccf3d4cb2dbbac4a85785e12f96637d912ec0ffdddb56", size = 69170 }, ] [package.optional-dependencies] @@ -2043,16 +2045,16 @@ wheels = [ [[package]] name = "rich-click" -version = "1.8.3" +version = "1.8.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/a9/a1f1af87e83832d794342fbc09c96cc7cd6798b8dfb8adfbe6ccbef8d70c/rich_click-1.8.3.tar.gz", hash = "sha256:6d75bdfa7aa9ed2c467789a0688bc6da23fbe3a143e19aa6ad3f8bac113d2ab3", size = 38209 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f4/e48dc2850662526a26fb0961aacb0162c6feab934312b109b748ae4efee2/rich_click-1.8.4.tar.gz", hash = "sha256:0f49471f04439269d0e66a6f43120f52d11d594869a2a0be600cfb12eb0616b9", size = 38247 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ea/5a0c5a8e6532e971983d1b0fc99268eb66a10f489da35d9022ce01044191/rich_click-1.8.3-py3-none-any.whl", hash = "sha256:636d9c040d31c5eee242201b5bf4f2d358bfae4db14bb22ec1cafa717cfd02cd", size = 35032 }, + { url = "https://files.pythonhosted.org/packages/84/f3/72f93d8494ee641bde76bfe1208cf4abc44c6f9448673762f6077bc162d6/rich_click-1.8.4-py3-none-any.whl", hash = "sha256:2d2841b3cebe610d5682baa1194beaf78ab00c4fa31931533261b5eba2ee80b7", size = 35071 }, ] [[package]] @@ -2104,27 +2106,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/06/09d1276df977eece383d0ed66052fc24ec4550a61f8fbc0a11200e690496/ruff-0.7.3.tar.gz", hash = "sha256:e1d1ba2e40b6e71a61b063354d04be669ab0d39c352461f3d789cac68b54a313", size = 3243664 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/56/933d433c2489e4642487b835f53dd9ff015fb3d8fa459b09bb2ce42d7c4b/ruff-0.7.3-py3-none-linux_armv6l.whl", hash = "sha256:34f2339dc22687ec7e7002792d1f50712bf84a13d5152e75712ac08be565d344", size = 10372090 }, - { url = "https://files.pythonhosted.org/packages/20/ea/1f0a22a6bcdd3fc26c73f63a025d05bd565901b729d56bcb093c722a6c4c/ruff-0.7.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb397332a1879b9764a3455a0bb1087bda876c2db8aca3a3cbb67b3dbce8cda0", size = 10190037 }, - { url = "https://files.pythonhosted.org/packages/16/74/aca75666e0d481fe394e76a8647c44ea919087748024924baa1a17371e3e/ruff-0.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:37d0b619546103274e7f62643d14e1adcbccb242efda4e4bdb9544d7764782e9", size = 9811998 }, - { url = "https://files.pythonhosted.org/packages/20/a1/cf446a0d7f78ea1f0bd2b9171c11dfe746585c0c4a734b25966121eb4f5d/ruff-0.7.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59f0c3ee4d1a6787614e7135b72e21024875266101142a09a61439cb6e38a5", size = 10620626 }, - { url = "https://files.pythonhosted.org/packages/cd/c1/82b27d09286ae855f5d03b1ad37cf243f21eb0081732d4d7b0d658d439cb/ruff-0.7.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:44eb93c2499a169d49fafd07bc62ac89b1bc800b197e50ff4633aed212569299", size = 10177598 }, - { url = "https://files.pythonhosted.org/packages/b9/42/c0acac22753bf74013d035a5ef6c5c4c40ad4d6686bfb3fda7c6f37d9b37/ruff-0.7.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d0242ce53f3a576c35ee32d907475a8d569944c0407f91d207c8af5be5dae4e", size = 11171963 }, - { url = "https://files.pythonhosted.org/packages/43/18/bb0befb7fb9121dd9009e6a72eb98e24f1bacb07c6f3ecb55f032ba98aed/ruff-0.7.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6b6224af8b5e09772c2ecb8dc9f3f344c1aa48201c7f07e7315367f6dd90ac29", size = 11856157 }, - { url = "https://files.pythonhosted.org/packages/5e/91/04e98d7d6e32eca9d1372be595f9abc7b7f048795e32eb2edbd8794d50bd/ruff-0.7.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c50f95a82b94421c964fae4c27c0242890a20fe67d203d127e84fbb8013855f5", size = 11440331 }, - { url = "https://files.pythonhosted.org/packages/f5/dc/3fe99f2ce10b76d389041a1b9f99e7066332e479435d4bebcceea16caff5/ruff-0.7.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f3eff9961b5d2644bcf1616c606e93baa2d6b349e8aa8b035f654df252c8c67", size = 12725354 }, - { url = "https://files.pythonhosted.org/packages/43/7b/1daa712de1c5bc6cbbf9fa60e9c41cc48cda962dc6d2c4f2a224d2c3007e/ruff-0.7.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8963cab06d130c4df2fd52c84e9f10d297826d2e8169ae0c798b6221be1d1d2", size = 11010091 }, - { url = "https://files.pythonhosted.org/packages/b6/db/1227a903587432eb569e57a95b15a4f191a71fe315cde4c0312df7bc85da/ruff-0.7.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:61b46049d6edc0e4317fb14b33bd693245281a3007288b68a3f5b74a22a0746d", size = 10610687 }, - { url = "https://files.pythonhosted.org/packages/db/e2/dc41ee90c3085aadad4da614d310d834f641aaafddf3dfbba08210c616ce/ruff-0.7.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:10ebce7696afe4644e8c1a23b3cf8c0f2193a310c18387c06e583ae9ef284de2", size = 10254843 }, - { url = "https://files.pythonhosted.org/packages/6f/09/5f6cac1c91542bc5bd33d40b4c13b637bf64d7bb29e091dadb01b62527fe/ruff-0.7.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3f36d56326b3aef8eeee150b700e519880d1aab92f471eefdef656fd57492aa2", size = 10730962 }, - { url = "https://files.pythonhosted.org/packages/d3/42/89a4b9a24ef7d00269e24086c417a006f9a3ffeac2c80f2629eb5ce140ee/ruff-0.7.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5d024301109a0007b78d57ab0ba190087b43dce852e552734ebf0b0b85e4fb16", size = 11101907 }, - { url = "https://files.pythonhosted.org/packages/b0/5c/efdb4777686683a8edce94ffd812783bddcd3d2454d38c5ac193fef7c500/ruff-0.7.3-py3-none-win32.whl", hash = "sha256:4ba81a5f0c5478aa61674c5a2194de8b02652f17addf8dfc40c8937e6e7d79fc", size = 8611095 }, - { url = "https://files.pythonhosted.org/packages/bb/b8/28fbc6a4efa50178f973972d1c84b2d0a33cdc731588522ab751ac3da2f5/ruff-0.7.3-py3-none-win_amd64.whl", hash = "sha256:588a9ff2fecf01025ed065fe28809cd5a53b43505f48b69a1ac7707b1b7e4088", size = 9418283 }, - { url = "https://files.pythonhosted.org/packages/3f/77/b587cba6febd5e2003374f37eb89633f79f161e71084f94057c8653b7fb3/ruff-0.7.3-py3-none-win_arm64.whl", hash = "sha256:1713e2c5545863cdbfe2cbce21f69ffaf37b813bfd1fb3b90dc9a6f1963f5a8c", size = 8725228 }, +version = "0.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8b/bc4e0dfa1245b07cf14300e10319b98e958a53ff074c1dd86b35253a8c2a/ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2", size = 3275547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/4b/f5094719e254829766b807dadb766841124daba75a37da83e292ae5ad12f/ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478", size = 10447512 }, + { url = "https://files.pythonhosted.org/packages/9e/1d/3d2d2c9f601cf6044799c5349ff5267467224cefed9b35edf5f1f36486e9/ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63", size = 10235436 }, + { url = "https://files.pythonhosted.org/packages/62/83/42a6ec6216ded30b354b13e0e9327ef75a3c147751aaf10443756cb690e9/ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20", size = 9888936 }, + { url = "https://files.pythonhosted.org/packages/4d/26/e1e54893b13046a6ad05ee9b89ee6f71542ba250f72b4c7a7d17c3dbf73d/ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109", size = 10697353 }, + { url = "https://files.pythonhosted.org/packages/21/24/98d2e109c4efc02bfef144ec6ea2c3e1217e7ce0cfddda8361d268dfd499/ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452", size = 10228078 }, + { url = "https://files.pythonhosted.org/packages/ad/b7/964c75be9bc2945fc3172241b371197bb6d948cc69e28bc4518448c368f3/ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea", size = 11264823 }, + { url = "https://files.pythonhosted.org/packages/12/8d/20abdbf705969914ce40988fe71a554a918deaab62c38ec07483e77866f6/ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7", size = 11951855 }, + { url = "https://files.pythonhosted.org/packages/b8/fc/6519ce58c57b4edafcdf40920b7273dfbba64fc6ebcaae7b88e4dc1bf0a8/ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05", size = 11516580 }, + { url = "https://files.pythonhosted.org/packages/37/1a/5ec1844e993e376a86eb2456496831ed91b4398c434d8244f89094758940/ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06", size = 12692057 }, + { url = "https://files.pythonhosted.org/packages/50/90/76867152b0d3c05df29a74bb028413e90f704f0f6701c4801174ba47f959/ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc", size = 11085137 }, + { url = "https://files.pythonhosted.org/packages/c8/eb/0a7cb6059ac3555243bd026bb21785bbc812f7bbfa95a36c101bd72b47ae/ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172", size = 10681243 }, + { url = "https://files.pythonhosted.org/packages/5e/76/2270719dbee0fd35780b05c08a07b7a726c3da9f67d9ae89ef21fc18e2e5/ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a", size = 10319187 }, + { url = "https://files.pythonhosted.org/packages/9f/e5/39100f72f8ba70bec1bd329efc880dea8b6c1765ea1cb9d0c1c5f18b8d7f/ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd", size = 10803715 }, + { url = "https://files.pythonhosted.org/packages/a5/89/40e904784f305fb56850063f70a998a64ebba68796d823dde67e89a24691/ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a", size = 11162912 }, + { url = "https://files.pythonhosted.org/packages/8d/1b/dd77503b3875c51e3dbc053fd8367b845ab8b01c9ca6d0c237082732856c/ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac", size = 8702767 }, + { url = "https://files.pythonhosted.org/packages/63/76/253ddc3e89e70165bba952ecca424b980b8d3c2598ceb4fc47904f424953/ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6", size = 9497534 }, + { url = "https://files.pythonhosted.org/packages/aa/70/f8724f31abc0b329ca98b33d73c14020168babcf71b0cba3cded5d9d0e66/ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f", size = 8851590 }, ] [[package]] @@ -2164,23 +2166,23 @@ wheels = [ [[package]] name = "scipy-stubs" -version = "1.14.1.3" +version = "1.14.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "optype" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/e0/b7b469f0fe0fe21436e9a866b0cf7f9ab436918ec37a3d1ce16a7f79441f/scipy_stubs-1.14.1.3.tar.gz", hash = "sha256:5859a835287624f2eaa4375c054057f24b1f642d357f6c6fbebef709df3df575", size = 213937 } +sdist = { url = "https://files.pythonhosted.org/packages/75/74/432fbf4677c38351f5a2f4b23800d63a00333eb65382bd839907d62ea08d/scipy_stubs-1.14.1.4.tar.gz", hash = "sha256:215a2c9d865e355a5c5cc088ec6d87f489a584396256113d72d7d24cfb0e889c", size = 214040 } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/00/35c05caea48fef95a641135a06acd4829ca8a38aa2267fee2c778a119517/scipy_stubs-1.14.1.3-py3-none-any.whl", hash = "sha256:821e5bb5adbc758ab2933a758d6a40d97c532b62df0e2b054e9ab9dbd7421c6c", size = 367699 }, + { url = "https://files.pythonhosted.org/packages/58/dc/80df791d316b9d190cfc2db1b3f9283751a18a1d72ee7123d480b2f09c67/scipy_stubs-1.14.1.4-py3-none-any.whl", hash = "sha256:137523d0eb4936ec249ddb01a3afd172ae8ad52cef9fddffc913a32a5f71c412", size = 367725 }, ] [[package]] name = "setuptools" -version = "75.4.0" +version = "75.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/73/c1ccf3e057ef6331cc6861412905dc218203bde46dfe8262c1631aa7fb11/setuptools-75.4.0.tar.gz", hash = "sha256:1dc484f5cf56fd3fe7216d7b8df820802e7246cfb534a1db2aa64f14fcb9cdcb", size = 1336593 } +sdist = { url = "https://files.pythonhosted.org/packages/c8/db/722a42ffdc226e950c4757b3da7b56ff5c090bb265dccd707f7b8a3c6fee/setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef", size = 1336032 } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/df/7c6bb83dcb45b35dc35b310d752f254211cde0bcd2a35290ea6e2862b2a9/setuptools-75.4.0-py3-none-any.whl", hash = "sha256:b3c5d862f98500b06ffdf7cc4499b48c46c317d8d56cb30b5c8bce4d88f5c216", size = 1223131 }, + { url = "https://files.pythonhosted.org/packages/fe/df/88ccbee85aefbca071db004fdc8f8d2507d55d5a9dc27ebb93c92edb1bd8/setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829", size = 1222710 }, ] [[package]] From 8a8a5f1c7f2bd5670f9469cc72e6b58e429ae396 Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 19 Nov 2024 19:38:24 +0100 Subject: [PATCH 2/3] use the conventional `optype` import aliases --- lmo/_lm.py | 32 +++++++++++------------ lmo/_lm_co.py | 6 ++--- lmo/_poly.py | 22 ++++++++-------- lmo/_utils.py | 42 +++++++++++++++---------------- lmo/contrib/scipy_stats.py | 6 +++-- lmo/distributions/_kumaraswamy.py | 4 +-- lmo/distributions/_lm.py | 10 ++++---- lmo/distributions/_wakeby.py | 4 +-- lmo/inference/_l_gmm.py | 4 +-- lmo/linalg.py | 18 ++++++------- lmo/ostats.py | 10 ++++---- lmo/pwm_beta.py | 10 ++++---- lmo/special.py | 8 +++--- lmo/typing/__init__.py | 6 ++--- lmo/typing/np.py | 12 ++++----- lmo/typing/scipy.py | 4 +-- pyproject.toml | 4 ++- 17 files changed, 103 insertions(+), 99 deletions(-) diff --git a/lmo/_lm.py b/lmo/_lm.py index bf8c6fd5..e5eb0370 100644 --- a/lmo/_lm.py +++ b/lmo/_lm.py @@ -7,7 +7,7 @@ import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp from . import ostats, pwm_beta from ._utils import ( @@ -65,7 +65,7 @@ # (dtype.char, n, s, t) _CacheKey: TypeAlias = tuple[str, int, int, int] | tuple[str, int, float, float] # `r: _T_order >= 4` -_CacheArray: TypeAlias = onpt.Array[tuple[_OrderT, _SizeT], np.longdouble] +_CacheArray: TypeAlias = onp.Array[tuple[_OrderT, _SizeT], np.longdouble] _Cache: TypeAlias = dict[_CacheKey, _CacheArray[Any, Any]] # depends on `dtype`, `n`, and `trim` @@ -79,7 +79,7 @@ def _l_weights_pwm( trim: tuple[int, int], *, dtype: _DType[_SCT_f], -) -> onpt.Array[tuple[_OrderT, _SizeT], _SCT_f]: +) -> onp.Array[tuple[_OrderT, _SizeT], _SCT_f]: s, t = trim r0 = r + s + t @@ -108,7 +108,7 @@ def _l_weights_ostat( trim: tuple[int, int] | tuple[float, float], *, dtype: _DType[_SCT_f], -) -> onpt.Array[tuple[_OrderT, _SizeT], _SCT_f]: +) -> onp.Array[tuple[_OrderT, _SizeT], _SCT_f]: assert r >= 1, r s, t = trim @@ -139,7 +139,7 @@ def l_weights( *, dtype: _DType[_SCT_f] = np.float64, cache: bool | None = None, -) -> onpt.Array[tuple[_OrderT, _SizeT], _SCT_f]: +) -> onp.Array[tuple[_OrderT, _SizeT], _SCT_f]: r""" Projection matrix of the first $r$ (T)L-moments for $n$ samples. @@ -219,7 +219,7 @@ def l_weights( key = dtype.char, n, s, t if (_w := _CACHE.get(key)) is not None and _w.shape[0] >= r_max: - w = cast(onpt.Array[tuple[_OrderT, _SizeT], _SCT_f], _w) + w = cast(onp.Array[tuple[_OrderT, _SizeT], _SCT_f], _w) else: # when caching, use at least 4 orders, to avoid cache misses _r_max = 4 if cache and r_max < 4 else r_max @@ -263,7 +263,7 @@ def l_moment( axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_moment( a: lnpt.AnyVectorFloat, @@ -285,7 +285,7 @@ def l_moment( axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... def l_moment( a: lnpt.AnyArrayFloat, r: AnyOrder | AnyOrderND, @@ -455,7 +455,7 @@ def l_ratio( axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_ratio( a: lnpt.AnyArrayFloat, @@ -479,7 +479,7 @@ def l_ratio( axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_ratio( a: lnpt.AnyArrayFloat, @@ -491,7 +491,7 @@ def l_ratio( axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... def l_ratio( a: lnpt.AnyArrayFloat, r: AnyOrder | AnyOrderND, @@ -559,7 +559,7 @@ def l_stats( axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: +) -> onp.Array[Any, _SCT_f]: """ Calculates the L-loc(ation), L-scale, L-skew(ness) and L-kurtosis. @@ -593,7 +593,7 @@ def l_loc( axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_loc( a: lnpt.AnyVectorFloat, @@ -707,7 +707,7 @@ def l_scale( axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_scale( a: lnpt.AnyVectorFloat, @@ -774,7 +774,7 @@ def l_variation( axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onpt.Array[Any, _SCT_f]: ... +) -> onp.Array[Any, _SCT_f]: ... @overload def l_variation( a: lnpt.AnyVectorFloat, @@ -1156,7 +1156,7 @@ def l_moment_influence( n = len(x_k) - w_k: onpt.Array[tuple[int], np.float64] = l_weights(_r, n, (s, t))[-1] + w_k: onp.Array[tuple[int], np.float64] = l_weights(_r, n, (s, t))[-1] l_r = cast(np.float64, w_k @ x_k) def influence_function(x: _T_x, /) -> _T_x: diff --git a/lmo/_lm_co.py b/lmo/_lm_co.py index 0ad4b5d0..5596c5bc 100644 --- a/lmo/_lm_co.py +++ b/lmo/_lm_co.py @@ -15,7 +15,7 @@ from typing_extensions import TypeVar if TYPE_CHECKING: - import optype.numpy as onpt + import optype.numpy as onp import lmo.typing.np as lnpt @@ -54,7 +54,7 @@ def l_comoment( rowvar: bool | None = None, sort: lnpt.SortKind | None = None, cache: bool | None = None, -) -> onpt.Array[Any, _T_float]: +) -> onp.Array[Any, _T_float]: r""" Multivariate extension of [`lmo.l_moment`][lmo.l_moment]. @@ -202,7 +202,7 @@ def l_coratio( *, dtype: _DType[_T_float] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> onpt.Array[Any, _T_float]: +) -> onp.Array[Any, _T_float]: r""" Estimate the generalized matrix of L-comoment ratio's. diff --git a/lmo/_poly.py b/lmo/_poly.py index e0cb40b5..7491412f 100644 --- a/lmo/_poly.py +++ b/lmo/_poly.py @@ -13,7 +13,7 @@ import numpy as np import numpy.polynomial as npp -import optype.numpy as onpt +import optype.numpy as onp import scipy.special as scs from numpy.polynomial._polybase import ABCPolyBase # noqa: PLC2701 @@ -41,7 +41,7 @@ PolySeries: TypeAlias = ABCPolyBase -_T_shape = TypeVar("_T_shape", bound=onpt.AtLeast1D) +_T_shape = TypeVar("_T_shape", bound=onp.AtLeast1D) _T_poly = TypeVar("_T_poly", bound=PolySeries) @@ -57,14 +57,14 @@ def eval_sh_jacobi( n: int, a: float | lnpt.Float, b: float | lnpt.Float, - x: onpt.Array[_T_shape, lnpt.Float], -) -> onpt.Array[_T_shape, np.float64]: ... + x: onp.Array[_T_shape, lnpt.Float], +) -> onp.Array[_T_shape, np.float64]: ... def eval_sh_jacobi( n: int | lnpt.Int, a: float | lnpt.Float, b: float | lnpt.Float, - x: float | lnpt.Float | onpt.Array[_T_shape, lnpt.Float], -) -> float | onpt.Array[_T_shape, np.float64]: + x: float | lnpt.Float | onp.Array[_T_shape, lnpt.Float], +) -> float | onp.Array[_T_shape, np.float64]: """ Fast evaluation of the n-th shifted Jacobi polynomial. Faster than pre-computing using np.Polynomial, and than @@ -117,7 +117,7 @@ def peaks_jacobi( n: int, a: float, b: float, -) -> onpt.Array[tuple[int], np.float64]: +) -> onp.Array[tuple[int], np.float64]: r""" Finds the \( x \in [-1, 1] \) s.t. \( /frac{\dd{\shjacobi{n}{a}{b}{x}}}{\dd{x}} = 0 \) of a Jacobi polynomial, @@ -303,7 +303,7 @@ def extrema_jacobi(n: int, a: float, b: float) -> tuple[float, float]: return cast(float, np.min(p)), cast(float, np.max(p)) -def _jacobi_coefs(n: int, a: float, b: float) -> onpt.Array[tuple[int], np.float64]: +def _jacobi_coefs(n: int, a: float, b: float) -> onp.Array[tuple[int], np.float64]: p_n: np.poly1d p_n = scs.jacobi(n, a, b) return p_n.coef[::-1] @@ -366,7 +366,7 @@ def jacobi_series( Todo: - Create a `Jacobi` class, as extension to `numpy.polynomial.` """ - w = cast(onpt.Array[tuple[int], np.float64], np.asarray(coef)) + w = cast(onp.Array[tuple[int], np.float64], np.asarray(coef)) if w.ndim != 1: msg = "coefs must be 1-D" raise ValueError(msg) @@ -389,7 +389,7 @@ def roots( p: PolySeries, /, outside: bool = False, -) -> onpt.Array[tuple[int], np.float64]: +) -> onp.Array[tuple[int], np.float64]: """ Return the $x$ in the domain of $p$, where $p(x) = 0$. @@ -397,7 +397,7 @@ def roots( interval will be not be included. """ z = cast( - onpt.Array[tuple[int], np.float64], + onp.Array[tuple[int], np.float64], p.roots(), ) x = z[np.isreal(z)].real if not np.isrealobj(z) and np.isrealobj(p.domain) else z diff --git a/lmo/_utils.py b/lmo/_utils.py index adeefb69..bd88db54 100644 --- a/lmo/_utils.py +++ b/lmo/_utils.py @@ -13,7 +13,7 @@ from typing_extensions import TypeVar if TYPE_CHECKING: - import optype.numpy as onpt + import optype.numpy as onp import lmo.typing as lmt import lmo.typing.np as lnpt @@ -44,8 +44,8 @@ _SizeT = TypeVar("_SizeT", bound=int) _ShapeT = TypeVar("_ShapeT", bound=tuple[int, ...]) -_ShapeT1 = TypeVar("_ShapeT1", bound="onpt.AtLeast1D") -_ShapeT2 = TypeVar("_ShapeT2", bound="onpt.AtLeast2D") +_ShapeT1 = TypeVar("_ShapeT1", bound="onp.AtLeast1D") +_ShapeT2 = TypeVar("_ShapeT2", bound="onp.AtLeast2D") _DType: TypeAlias = np.dtype[_SCT] | type[_SCT] @@ -78,7 +78,7 @@ def plotting_positions( /, alpha: float = 0.4, beta: float | None = None, -) -> onpt.Array[tuple[_SizeT], np.float64]: +) -> onp.Array[tuple[_SizeT], np.float64]: """ A re-implementation of [`scipy.stats.mstats.plotting_positions` ](scipy.stats.mstats.plotting_positions), but without the ridiculous @@ -93,17 +93,17 @@ def plotting_positions( def round0(a: _AT_f, /, tol: float | None = ...) -> _AT_f: ... @overload def round0( - a: onpt.CanArray[_ShapeT, _DT_f], + a: onp.CanArray[_ShapeT, _DT_f], /, tol: float | None = ..., ) -> np.ndarray[_ShapeT, _DT_f]: ... @overload def round0(a: float, /, tol: float | None = ...) -> np.float64: ... def round0( - a: float | onpt.CanArray[_ShapeT, np.dtype[_SCT_f]], + a: float | onp.CanArray[_ShapeT, np.dtype[_SCT_f]], /, tol: float | None = None, -) -> onpt.Array[_ShapeT, _SCT_f] | _SCT_f: +) -> onp.Array[_ShapeT, _SCT_f] | _SCT_f: """ Replace all values `<= tol` with `0`. @@ -118,7 +118,7 @@ def round0( def _apply_aweights( x: np.ndarray[_ShapeT1, _DT_f], - v: np.ndarray[_ShapeT1 | onpt.AtLeast1D, _DT_f | np.dtype[lnpt.Float]], + v: np.ndarray[_ShapeT1 | onp.AtLeast1D, _DT_f | np.dtype[lnpt.Float]], axis: int, ) -> np.ndarray[_ShapeT1, _DT_f]: # interpret the weights as horizontal coordinates using cumsum @@ -153,11 +153,11 @@ def _apply_aweights( def _sort_like( - a: onpt.Array[_ShapeT1, _SCT_uifc], - i: onpt.Array[tuple[int], lnpt.Int], + a: onp.Array[_ShapeT1, _SCT_uifc], + i: onp.Array[tuple[int], lnpt.Int], /, axis: int | None, -) -> onpt.Array[_ShapeT1, _SCT_uifc]: +) -> onp.Array[_ShapeT1, _SCT_uifc]: return ( np.take(a, i, axis=None if a.ndim == i.ndim else axis) if min(a.ndim, i.ndim) <= 1 @@ -166,13 +166,13 @@ def _sort_like( def sort_maybe( - x: onpt.Array[_ShapeT1, _SCT_uifc], + x: onp.Array[_ShapeT1, _SCT_uifc], /, axis: int = -1, *, sort: bool | lnpt.SortKind = True, inplace: bool = False, -) -> onpt.Array[_ShapeT1, _SCT_uifc]: +) -> onp.Array[_ShapeT1, _SCT_uifc]: if not sort: return x @@ -194,7 +194,7 @@ def ordered( # noqa: C901 fweights: lmt.AnyFWeights | None = None, aweights: lmt.AnyAWeights | None = None, sort: lnpt.SortKind | bool = True, -) -> onpt.Array[onpt.AtLeast1D, lnpt.Float]: +) -> onp.Array[onp.AtLeast1D, lnpt.Float]: """ Calculate `n = len(x)` order stats of `x`, optionally weighted. If `y` is provided, the order of `y` is used instead. @@ -351,8 +351,8 @@ def clean_trim(trim: lmt.AnyTrim, /) -> tuple[int, int] | tuple[float, float]: def moments_to_ratio( - rs: onpt.Array[tuple[int, ...], lnpt.Int], - l_rs: onpt.Array[onpt.AtLeast1D, _SCT_f], + rs: onp.Array[tuple[int, ...], lnpt.Int], + l_rs: onp.Array[onp.AtLeast1D, _SCT_f], /, ) -> _SCT_f | npt.NDArray[_SCT_f]: """ @@ -380,9 +380,9 @@ def moments_to_ratio( def moments_to_stats_cov( - t_0r: onpt.Array[tuple[int], lnpt.Float], - ll_kr: onpt.Array[_ShapeT2, _SCT_f], -) -> onpt.Array[_ShapeT2, _SCT_f]: + t_0r: onp.Array[tuple[int], lnpt.Float], + ll_kr: onp.Array[_ShapeT2, _SCT_f], +) -> onp.Array[_ShapeT2, _SCT_f]: # t_0r are L-ratio's for r = 0, 1, ..., R (t_0r[0] == 1 / L-scale) # t_0r[1] isn't used, and can be set to anything # ll_kr is the L-moment cov of size R**2 (orders start at 1 here) @@ -414,8 +414,8 @@ def l_stats_orders( /, dtype: _DType[_SCT_ui] = np.int_, ) -> tuple[ - onpt.Array[tuple[_SizeT], _SCT_ui], - onpt.Array[tuple[_SizeT], _SCT_ui], + onp.Array[tuple[_SizeT], _SCT_ui], + onp.Array[tuple[_SizeT], _SCT_ui], ]: """ Create the L-moment order array `[1, 2, ..., r]` and corresponding diff --git a/lmo/contrib/scipy_stats.py b/lmo/contrib/scipy_stats.py index a99c6e81..e47f0bde 100644 --- a/lmo/contrib/scipy_stats.py +++ b/lmo/contrib/scipy_stats.py @@ -51,9 +51,11 @@ if TYPE_CHECKING: from collections.abc import Callable, Mapping, Sequence + import optype.numpy as onp from scipy.stats import rv_discrete -__all__ = "install", "l_rv_frozen", "l_rv_generic" + +__all__ = ["install", "l_rv_frozen", "l_rv_generic"] _T = TypeVar("_T") @@ -142,7 +144,7 @@ class l_rv_generic(PatchClass): _shape_info: Callable[[], list[_ShapeInfo]] _stats: Callable[..., _Tuple4[float | None]] _unpack_loc_scale: Callable[ - [lnpt.AnyVector], + [onp.ToFloat1D], tuple[float, float, tuple[float, ...]], ] cdf: lspt.RVFunction[...] diff --git a/lmo/distributions/_kumaraswamy.py b/lmo/distributions/_kumaraswamy.py index 64f5fd3b..727aefea 100644 --- a/lmo/distributions/_kumaraswamy.py +++ b/lmo/distributions/_kumaraswamy.py @@ -6,7 +6,7 @@ import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp import scipy.special as sc from scipy.stats.distributions import rv_continuous @@ -21,7 +21,7 @@ __all__ = ("kumaraswamy_gen",) -_ArrF8: TypeAlias = onpt.Array[tuple[int, ...], np.float64] +_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] _XT = TypeVar("_XT", float | np.float64, _ArrF8) diff --git a/lmo/distributions/_lm.py b/lmo/distributions/_lm.py index 073086ff..d2fd1f58 100644 --- a/lmo/distributions/_lm.py +++ b/lmo/distributions/_lm.py @@ -26,7 +26,7 @@ import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp import scipy.special as sps from lmo.special import harmonic @@ -95,7 +95,7 @@ "weibull_max", } -_ArrF8: TypeAlias = onpt.Array[tuple[int, ...], np.float64] +_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] _Tss = ParamSpec("_Tss") # (r, s, t, *params) -> float @@ -128,13 +128,13 @@ class _LmVFunc(Protocol[_Tss]): @overload def __call__( self, - r: onpt.CanArray[_ShapeT, np.dtype[lnpt.Integral]], + r: onp.CanArray[_ShapeT, np.dtype[lnpt.Integral]], s: float, t: float, /, *args: _Tss.args, **kwds: _Tss.kwargs, - ) -> onpt.Array[_ShapeT, np.float64]: ... + ) -> onp.Array[_ShapeT, np.float64]: ... @overload def __call__( self, @@ -144,7 +144,7 @@ def __call__( /, *args: _Tss.args, **kwds: _Tss.kwargs, - ) -> onpt.Array[tuple[()], np.float64]: ... + ) -> onp.Array[tuple[()], np.float64]: ... def register_lm_func( diff --git a/lmo/distributions/_wakeby.py b/lmo/distributions/_wakeby.py index cc2027f5..cdfd924a 100644 --- a/lmo/distributions/_wakeby.py +++ b/lmo/distributions/_wakeby.py @@ -6,7 +6,7 @@ from typing import Final, TypeAlias, TypeVar import numpy as np -import optype.numpy as onpt +import optype.numpy as onp import scipy.special as sps from scipy.stats.distributions import rv_continuous @@ -25,7 +25,7 @@ # NOTE: this is equivalent to `float` IFF `numpy >= 2.2`, see: # https://github.com/numpy/numpy/pull/27334 _F8: TypeAlias = float | np.float64 -_ArrF8: TypeAlias = onpt.Array[tuple[int, ...], np.float64] +_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] _XT = TypeVar("_XT", _F8, _ArrF8) diff --git a/lmo/inference/_l_gmm.py b/lmo/inference/_l_gmm.py index 6b67afe5..db57fb7c 100644 --- a/lmo/inference/_l_gmm.py +++ b/lmo/inference/_l_gmm.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: from collections.abc import Callable - import optype.numpy as onpt + import optype.numpy as onp import lmo.typing as lmt import lmo.typing.np as lnpt @@ -232,7 +232,7 @@ def _get_weights_mc( def _ensure_1d_f8( arr: lnpt.AnyVectorFloat, -) -> onpt.Array[tuple[int], np.float64]: +) -> onp.Array[tuple[int], np.float64]: out = np.asarray_chkfinite(arr) if out.ndim != 1: err = f"expected 1D array, got {out.shape}" diff --git a/lmo/linalg.py b/lmo/linalg.py index 9ab627a2..bbef9178 100644 --- a/lmo/linalg.py +++ b/lmo/linalg.py @@ -9,7 +9,7 @@ import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp import lmo.typing.np as lnpt @@ -37,15 +37,15 @@ _R = TypeVar("_R", bound=int) _DType: TypeAlias = np.dtype[_T] | type[_T] -_Square: TypeAlias = onpt.Array[tuple[_K, _K], _T] +_Square: TypeAlias = onp.Array[tuple[_K, _K], _T] def sandwich( - A: onpt.Array[tuple[_K, _R], np.floating[Any]], - X: onpt.Array[tuple[_R, *tuple[_R, ...]], np.floating[Any]], + A: onp.Array[tuple[_K, _R], np.floating[Any]], + X: onp.Array[tuple[_R, *tuple[_R, ...]], np.floating[Any]], /, dtype: _DType[_TF] = np.float64, -) -> onpt.Array[tuple[_K, *tuple[_K, ...]], _TF]: +) -> onp.Array[tuple[_K, *tuple[_K, ...]], _TF]: """ Calculates the "sandwich" matrix product (`A @ X @ A.T`) along the specified `X` axis. @@ -318,9 +318,9 @@ def sh_jacobi( def succession_matrix( - c: onpt.Array[tuple[_K, int], _T] | onpt.Array[tuple[_K], _T], + c: onp.Array[tuple[_K, int], _T] | onp.Array[tuple[_K], _T], /, -) -> onpt.Array[tuple[_K, int], _T]: +) -> onp.Array[tuple[_K, int], _T]: r""" A toeplitz-like transformation matrix construction, that prepends $i$ zeroes to $i$-th row, so that the input shape is mapped from `(n, k)` @@ -348,7 +348,7 @@ def succession_matrix( [0, 0, 5, 6, 0], [0, 0, 0, 7, 8]]) """ - _c: onpt.Array[tuple[_K, int], _T] = np.atleast_2d(c) + _c: onp.Array[tuple[_K, int], _T] = np.atleast_2d(c) n, k = _c.shape i = np.arange(n) @@ -364,7 +364,7 @@ def trim_matrix( /, trim: tuple[int, int], dtype: _DType[_TF] = np.float64, -) -> onpt.Array[tuple[_R, int], _TF]: +) -> onp.Array[tuple[_R, int], _TF]: r""" Linearization of the trimmed L-moment recurrence relations, following the (corrected) derivation by Hosking (2007) from the (shifted) Jacobi diff --git a/lmo/ostats.py b/lmo/ostats.py index f28a923d..e89d5dcd 100644 --- a/lmo/ostats.py +++ b/lmo/ostats.py @@ -19,7 +19,7 @@ from scipy.special import betainc, betaln if TYPE_CHECKING: - import optype.numpy as onpt + import optype.numpy as onp import lmo.typing.np as lnpt @@ -35,7 +35,7 @@ def _weights( n: float | lnpt.Float, N: _T_size, /, -) -> onpt.Array[tuple[_T_size], np.float64]: +) -> onp.Array[tuple[_T_size], np.float64]: assert 0 <= i < n <= N j = np.arange(floor(i), N) @@ -60,7 +60,7 @@ def weights( /, *, cached: bool = False, -) -> onpt.Array[tuple[_T_size], np.float64]: +) -> onp.Array[tuple[_T_size], np.float64]: r""" Compute the linear weights $w_{i:n|j:N}$ for $j = 0, \dots, N-1$. @@ -129,14 +129,14 @@ def from_cdf( F: lnpt.AnyArrayFloat, i: float, n: float, -) -> onpt.Array[onpt.AtLeast1D, np.float64]: ... +) -> onp.Array[onp.AtLeast1D, np.float64]: ... def from_cdf( F: lnpt.AnyScalarFloat | lnpt.AnyArrayFloat, i: float, n: float, -) -> np.float64 | onpt.Array[onpt.AtLeast1D, np.float64]: +) -> np.float64 | onp.Array[onp.AtLeast1D, np.float64]: r""" Transform $F(X)$ to $F_{i:n}(X)$, of the $i$th variate within subsamples of size, i.e. $0 \le i \le n - 1$. diff --git a/lmo/pwm_beta.py b/lmo/pwm_beta.py index 9c82531d..90d3c778 100644 --- a/lmo/pwm_beta.py +++ b/lmo/pwm_beta.py @@ -22,7 +22,7 @@ if TYPE_CHECKING: - import optype.numpy as onpt + import optype.numpy as onp import lmo.typing.np as lnpt @@ -42,7 +42,7 @@ def weights( n: _N, /, dtype: _DType[_F] = np.float64, -) -> onpt.Array[tuple[_R, _N], _F]: +) -> onp.Array[tuple[_R, _N], _F]: r""" Probability Weighted moment (PWM) projection matrix $B$ of the unbiased estimator for $\beta_k = M_{1,k,0}$ for $k = 0, \dots, r - 1$. @@ -94,7 +94,7 @@ def cov( axis: None = ..., dtype: _DType[_F] = np.float64, **kwds: Any, -) -> onpt.Array[tuple[_R, _R], _F]: ... +) -> onp.Array[tuple[_R, _R], _F]: ... @overload def cov( a: lnpt.AnyArrayFloat, @@ -103,7 +103,7 @@ def cov( axis: int, dtype: _DType[_F] = np.float64, **kwds: Any, -) -> onpt.Array[tuple[_R, _R, *tuple[int, ...]], _F]: ... +) -> onp.Array[tuple[_R, _R, *tuple[int, ...]], _F]: ... def cov( a: lnpt.AnyArrayFloat, r: int, @@ -111,7 +111,7 @@ def cov( axis: int | None = None, dtype: _DType[_F] = np.float64, **kwds: Any, -) -> onpt.Array[Any, _F]: +) -> onp.Array[Any, _F]: r""" Distribution-free variance-covariance matrix of the probability weighted moment (PWM) point estimates $\beta_k = M_{1,k,0}$, with orders diff --git a/lmo/special.py b/lmo/special.py index b46b6f7e..766ff88a 100644 --- a/lmo/special.py +++ b/lmo/special.py @@ -8,7 +8,7 @@ import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp import scipy.special as sc from ._utils import clean_orders @@ -26,19 +26,19 @@ _DTYPE_CHARS: Final[str] = "?bBhHiIlLqQpP" -_ArrayT = TypeVar("_ArrayT", bound=onpt.Array) +_ArrayT = TypeVar("_ArrayT", bound=onp.Array) _ShapeT = TypeVar("_ShapeT", infer_variance=True, bound=tuple[int, ...]) _SCT = TypeVar("_SCT", infer_variance=True, bound=np.number[Any] | np.bool_) # this includes `np.ndarray` but excludes `np.generic` @runtime_checkable -class _CanLenArray(onpt.CanArray[_ShapeT, np.dtype[_SCT]], Protocol[_ShapeT, _SCT]): +class _CanLenArray(onp.CanArray[_ShapeT, np.dtype[_SCT]], Protocol[_ShapeT, _SCT]): def __len__(self, /) -> int: ... _F8: TypeAlias = np.float64 -_F8ND: TypeAlias = onpt.Array[onpt.AtLeast1D, np.float64] +_F8ND: TypeAlias = onp.Array[onp.AtLeast1D, np.float64] _Real: TypeAlias = np.floating[Any] | np.integer[Any] | np.bool_ _Real_in: TypeAlias = float | _Real diff --git a/lmo/typing/__init__.py b/lmo/typing/__init__.py index d73693b3..ecf4c099 100644 --- a/lmo/typing/__init__.py +++ b/lmo/typing/__init__.py @@ -8,7 +8,7 @@ # pyright: reportPrivateUsage=false import numpy as np import numpy.typing as npt -import optype.numpy as onpt +import optype.numpy as onp from numpy._typing import _NestedSequence # noqa: PLC2701 if sys.version_info >= (3, 13): @@ -50,8 +50,8 @@ def __array__(self, /) -> npt.NDArray[np.integer[Any]]: ... AnyOrder: TypeAlias = int | np.integer[Any] AnyOrderND: TypeAlias = _CanIntegerArray | _NestedSequence[int | np.integer[Any]] -AnyFWeights: TypeAlias = onpt.Array[tuple[int], np.integer[Any]] -AnyAWeights: TypeAlias = onpt.Array[onpt.AtLeast1D, np.floating[Any]] +AnyFWeights: TypeAlias = onp.Array[tuple[int], np.integer[Any]] +AnyAWeights: TypeAlias = onp.Array[onp.AtLeast1D, np.floating[Any]] class LMomentOptions(TypedDict, total=False): diff --git a/lmo/typing/np.py b/lmo/typing/np.py index 8d29203b..d496487c 100644 --- a/lmo/typing/np.py +++ b/lmo/typing/np.py @@ -6,7 +6,7 @@ from typing import Any, Literal, TypeAlias, TypeVar import numpy as np -import optype.numpy as onpt +import optype.numpy as onp __all__ = ( "AnyArrayFloat", @@ -56,13 +56,13 @@ def __dir__() -> tuple[str, ...]: _PyVector: TypeAlias = Sequence[_T] -_AnyScalar: TypeAlias = _ST | _ST_py | onpt.CanArray[tuple[()], np.dtype[_ST]] +_AnyScalar: TypeAlias = _ST | _ST_py | onp.CanArray[tuple[()], np.dtype[_ST]] _AnyVector: TypeAlias = ( - onpt.CanArray[tuple[int], np.dtype[_ST]] + onp.CanArray[tuple[int], np.dtype[_ST]] | _PyVector[_AnyScalar[_ST, _ST_py]] ) # fmt: skip _AnyMatrix: TypeAlias = ( - onpt.CanArray[tuple[int, int], np.dtype[_ST]] + onp.CanArray[tuple[int, int], np.dtype[_ST]] | _PyVector[_AnyVector[_ST, _ST_py]] ) # fmt: skip @@ -75,7 +75,7 @@ def __dir__() -> tuple[str, ...]: AnyVectorInt: TypeAlias = _AnyVector[Integral, int] AnyMatrixInt: TypeAlias = _AnyMatrix[Integral, int] AnyTensorInt: TypeAlias = ( - onpt.CanArray[onpt.AtLeast3D, np.dtype[Integral]] + onp.CanArray[onp.AtLeast3D, np.dtype[Integral]] | _PyVector[AnyMatrixInt] | _PyVector["AnyTensorInt"] ) @@ -85,7 +85,7 @@ def __dir__() -> tuple[str, ...]: AnyVectorFloat: TypeAlias = _AnyVector[Real, float] AnyMatrixFloat: TypeAlias = _AnyMatrix[Real, float] AnyTensorFloat: TypeAlias = ( - onpt.CanArray[onpt.AtLeast1D, np.dtype[Real]] + onp.CanArray[onp.AtLeast1D, np.dtype[Real]] | _PyVector[AnyMatrixFloat] | _PyVector["AnyTensorFloat"] ) diff --git a/lmo/typing/scipy.py b/lmo/typing/scipy.py index 426d6475..e127b263 100644 --- a/lmo/typing/scipy.py +++ b/lmo/typing/scipy.py @@ -17,7 +17,7 @@ from typing_extensions import ParamSpec, Protocol if TYPE_CHECKING: - import optype.numpy as onpt + import optype.numpy as onp from numpy._typing import _ArrayLikeFloat_co # pyright: ignore[reportPrivateUsage] import lmo.typing.np as lnpt @@ -71,7 +71,7 @@ def __call__( /, *args: _Tss.args, **kwds: _Tss.kwargs, - ) -> onpt.Array[Any, np.float64]: ... + ) -> onp.Array[Any, np.float64]: ... @overload def __call__( self, diff --git a/pyproject.toml b/pyproject.toml index d9bd9098..c7f5ac3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -253,7 +253,9 @@ banned-from = [ "lmo.typing.scipy" = "lspt" "numpy.polynomial" = "npp" "numpy.typing" = "npt" -"optype.numpy" = "onpt" +"optype" = "op" +"optype.numpy" = "onp" +"optype.typing" = "opt" [tool.ruff.lint.flake8-type-checking] exempt-modules = ["typing", "typing_extensions"] From a239da260d34f05cdbad0f16c1c9e38b92dd7c5d Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 19 Nov 2024 23:12:36 +0100 Subject: [PATCH 3/3] simplify typing with `optype 0.7` --- lmo/_lm.py | 306 ++++++++++++------------ lmo/_lm_co.py | 121 +++++----- lmo/_poly.py | 119 +++++---- lmo/_utils.py | 92 ++++--- lmo/contrib/pandas.py | 68 +++--- lmo/contrib/scipy_stats.py | 268 ++++++++++----------- lmo/diagnostic.py | 80 +++---- lmo/distributions/__init__.py | 8 +- lmo/distributions/_genlambda.py | 17 +- lmo/distributions/_kumaraswamy.py | 18 +- lmo/distributions/_lm.py | 77 ++---- lmo/distributions/_nonparametric.py | 83 +++---- lmo/distributions/_utils.py | 5 - lmo/distributions/_wakeby.py | 15 +- lmo/inference/_l_gmm.py | 19 +- lmo/linalg.py | 21 +- lmo/ostats.py | 82 +++---- lmo/pwm_beta.py | 46 ++-- lmo/special.py | 8 +- lmo/theoretical/_f_to_f.py | 8 +- lmo/theoretical/_f_to_h.py | 10 +- lmo/theoretical/_f_to_lcm.py | 35 ++- lmo/theoretical/_f_to_lm.py | 129 +++++----- lmo/theoretical/_f_to_lm_cov.py | 13 +- lmo/theoretical/_f_to_lm_eif.py | 15 +- lmo/theoretical/_lm_to_f.py | 22 +- lmo/theoretical/_utils.py | 4 +- lmo/typing.py | 167 +++++++++++++ lmo/typing/__init__.py | 71 ------ lmo/typing/np.py | 124 ---------- lmo/typing/scipy.py | 87 ------- pyproject.toml | 6 +- tests/test_distributions.py | 2 +- typetests/l_comoment.py | 19 -- typetests/l_comoment.pyi | 16 ++ typetests/{l_moment.py => l_moment.pyi} | 26 +- typetests/{l_ratio.py => l_ratio.pyi} | 22 +- typetests/l_stats.py | 27 --- typetests/l_stats.pyi | 27 +++ 39 files changed, 1034 insertions(+), 1249 deletions(-) delete mode 100644 lmo/distributions/_utils.py create mode 100644 lmo/typing.py delete mode 100644 lmo/typing/__init__.py delete mode 100644 lmo/typing/np.py delete mode 100644 lmo/typing/scipy.py delete mode 100644 typetests/l_comoment.py create mode 100644 typetests/l_comoment.pyi rename typetests/{l_moment.py => l_moment.pyi} (63%) rename typetests/{l_ratio.py => l_ratio.pyi} (72%) delete mode 100644 typetests/l_stats.py create mode 100644 typetests/l_stats.pyi diff --git a/lmo/_lm.py b/lmo/_lm.py index e5eb0370..22f3994b 100644 --- a/lmo/_lm.py +++ b/lmo/_lm.py @@ -3,6 +3,7 @@ from __future__ import annotations import sys +from collections.abc import Callable, Sequence from typing import TYPE_CHECKING, Any, Final, TypeAlias, Unpack, cast, overload import numpy as np @@ -28,12 +29,7 @@ from typing_extensions import TypeVar if TYPE_CHECKING: - from collections.abc import Callable - import lmo.typing as lmt - import lmo.typing.np as lnpt - from .typing import AnyOrder, AnyOrderND - __all__ = ( "l_kurt", @@ -59,8 +55,10 @@ _SCT_f = TypeVar("_SCT_f", bound=np.floating[Any], default=np.float64) _DType: TypeAlias = np.dtype[_SCT_f] | type[_SCT_f] -_Vectorized: TypeAlias = _SCT_f | npt.NDArray[_SCT_f] -_Floating: TypeAlias = np.floating[Any] +_ArrayOrScalar: TypeAlias = _SCT_f | onp.ArrayND[_SCT_f] +_ToFloat1D: TypeAlias = ( + Sequence[onp.ToFloat] | onp.Array1D[np.floating[Any] | np.integer[Any] | np.bool_] +) # (dtype.char, n, s, t) _CacheKey: TypeAlias = tuple[str, int, int, int] | tuple[str, int, float, float] @@ -135,7 +133,7 @@ def l_weights( r_max: _OrderT, n: _SizeT, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, dtype: _DType[_SCT_f] = np.float64, cache: bool | None = None, @@ -242,63 +240,63 @@ def l_weights( return w -@overload +@overload # a: 1d, r: 0d def l_moment( - a: lnpt.AnyArrayFloat, - r: AnyOrder, + a: _ToFloat1D, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - axis: None = None, + axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], ) -> _SCT_f: ... -@overload +@overload # r: 0d, axis: None def l_moment( - a: lnpt.AnyMatrixFloat | lnpt.AnyTensorFloat, - r: AnyOrder | AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - axis: int, + axis: None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> _SCT_f: ... +@overload # r: n-d def l_moment( - a: lnpt.AnyVectorFloat, - r: AnyOrder, + a: onp.ToFloatND, + r: lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - axis: int, + axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _SCT_f: ... +) -> onp.ArrayND[_SCT_f]: ... @overload def l_moment( - a: lnpt.AnyArrayFloat, - r: AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - axis: int | None = None, + axis: int, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... +) -> _SCT_f | onp.ArrayND[_SCT_f]: ... def l_moment( - a: lnpt.AnyArrayFloat, - r: AnyOrder | AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, - fweights: lmt.AnyFWeights | None = None, - aweights: lmt.AnyAWeights | None = None, - sort: lnpt.SortKind | bool = True, + fweights: lmt.ToFWeights | None = None, + aweights: lmt.ToAWeights | None = None, + sort: lmt.SortKind | bool = True, cache: bool | None = None, -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" Estimates the generalized trimmed L-moment $\lambda^{(s, t)}_r$ from the samples along the specified axis. By default, this will be the regular @@ -433,76 +431,76 @@ def l_moment( @overload -def l_ratio( - a: lnpt.AnyVectorFloat, - r: AnyOrder, - s: AnyOrder, +def l_ratio( # a: 1d, r: 0d, s: 0d + a: _ToFloat1D, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int | None, + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], ) -> _SCT_f: ... -@overload +@overload # r: 0d, s: 0d, axis: None def l_ratio( - a: lnpt.AnyMatrixFloat | lnpt.AnyTensorFloat, - r: AnyOrder, - s: AnyOrder, + a: onp.ToFloatND, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> _SCT_f: ... +@overload # r: 0d, s: 0d def l_ratio( - a: lnpt.AnyArrayFloat, - r: AnyOrder, - s: AnyOrder, + a: onp.ToFloatND, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: None = ..., + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _SCT_f: ... -@overload +) -> _SCT_f | onp.ArrayND[_SCT_f]: ... +@overload # r: n-d def l_ratio( - a: lnpt.AnyArrayFloat, - r: AnyOrder, - s: AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> onp.ArrayND[_SCT_f]: ... +@overload # r: 0d, s: n-d def l_ratio( - a: lnpt.AnyArrayFloat, - r: AnyOrderND, - s: AnyOrder | AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D, + s: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... +) -> onp.ArrayND[_SCT_f]: ... def l_ratio( - a: lnpt.AnyArrayFloat, - r: AnyOrder | AnyOrderND, - s: AnyOrder | AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" Estimates the generalized L-moment ratio. @@ -551,15 +549,15 @@ def l_ratio( def l_stats( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, num: int = 4, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: +) -> onp.ArrayND[_SCT_f]: """ Calculates the L-loc(ation), L-scale, L-skew(ness) and L-kurtosis. @@ -585,44 +583,44 @@ def l_stats( @overload -def l_loc( - a: lnpt.AnyMatrixFloat | lnpt.AnyTensorFloat, +def l_loc( # a: 1d + a: _ToFloat1D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> _SCT_f: ... +@overload # axis: None def l_loc( - a: lnpt.AnyVectorFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], ) -> _SCT_f: ... @overload def l_loc( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: None = ..., + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _SCT_f: ... +) -> _SCT_f | onp.ArrayND[_SCT_f]: ... def l_loc( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" *L-location* (or *L-loc*): unbiased estimator of the first L-moment, $\lambda^{(s, t)}_1$. @@ -698,45 +696,45 @@ def l_loc( return l_moment(a, 1, trim=trim, axis=axis, dtype=dtype, **kwds) -@overload +@overload # a: 1d def l_scale( - a: lnpt.AnyMatrixFloat | lnpt.AnyTensorFloat, + a: _ToFloat1D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> _SCT_f: ... +@overload # axis: None def l_scale( - a: lnpt.AnyVectorFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], ) -> _SCT_f: ... @overload def l_scale( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: None = ..., + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _SCT_f: ... +) -> _SCT_f | onp.ArrayND[_SCT_f]: ... def l_scale( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" *L-scale* unbiased estimator for the second L-moment, $\lambda^{(s, t)}_2$. @@ -765,45 +763,45 @@ def l_scale( return l_moment(a, 2, trim=trim, axis=axis, dtype=dtype, **kwds) -@overload +@overload # a: 1d def l_variation( - a: lnpt.AnyMatrixFloat | lnpt.AnyTensorFloat, + a: _ToFloat1D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> onp.Array[Any, _SCT_f]: ... -@overload +) -> _SCT_f: ... +@overload # axis: None def l_variation( - a: lnpt.AnyVectorFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: int, + axis: None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], ) -> _SCT_f: ... @overload def l_variation( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, - axis: None = ..., + axis: int | None = ..., dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _SCT_f: ... +) -> _SCT_f | onp.ArrayND[_SCT_f]: ... def l_variation( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" The *coefficient of L-variation* (or *L-CV*) unbiased sample estimator: @@ -840,14 +838,14 @@ def l_variation( def l_skew( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" Unbiased sample estimator for the *L-skewness* coefficient. @@ -875,14 +873,14 @@ def l_skew( def l_kurtosis( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: r""" L-kurtosis coefficient; the 4th sample L-moment ratio. @@ -919,15 +917,15 @@ def l_kurtosis( def l_moment_cov( - a: lnpt.AnyArrayFloat, - r_max: AnyOrder, + a: onp.ToFloatND, + r_max: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Any, -) -> npt.NDArray[_SCT_f]: +) -> onp.Array[onp.AtLeast2D, _SCT_f]: """ Non-parmateric auto-covariance matrix of the generalized trimmed L-moment point estimates with orders `r = 1, ..., r_max`. @@ -996,16 +994,16 @@ def l_moment_cov( def l_ratio_se( - a: lnpt.AnyArrayFloat, - r: AnyOrder | AnyOrderND, - s: AnyOrder | AnyOrderND, + a: onp.ToFloatND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: """ Non-parametric estimates of the Standard Error (SE) in the L-ratio estimates from [`lmo.l_ratio`][lmo.l_ratio]. @@ -1067,15 +1065,15 @@ def l_ratio_se( def l_stats_se( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, num: int = 4, *, axis: int | None = None, dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LMomentOptions], -) -> _Vectorized[_SCT_f]: +) -> _ArrayOrScalar[_SCT_f]: """ Calculates the standard errors (SE's) of the [`L-stats`][lmo.l_stats]. @@ -1102,16 +1100,16 @@ def l_stats_se( return l_ratio_se(a, r, s, trim=trim, axis=axis, dtype=dtype, **kwds) -_T_x = TypeVar("_T_x", bound=float | npt.NDArray[_Floating]) +_T_x = TypeVar("_T_x", bound=float | onp.ArrayND[np.floating[Any]]) def l_moment_influence( - a: lnpt.AnyVectorFloat, - r: AnyOrder, + a: onp.ToFloat1D, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - sort: lnpt.SortKind | bool = True, + sort: lmt.SortKind | bool = True, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: r""" @@ -1156,7 +1154,7 @@ def l_moment_influence( n = len(x_k) - w_k: onp.Array[tuple[int], np.float64] = l_weights(_r, n, (s, t))[-1] + w_k: onp.Array1D[np.float64] = l_weights(_r, n, (s, t))[-1] l_r = cast(np.float64, w_k @ x_k) def influence_function(x: _T_x, /) -> _T_x: @@ -1187,13 +1185,13 @@ def influence_function(x: _T_x, /) -> _T_x: def l_ratio_influence( - a: lnpt.AnyVectorFloat, - r: AnyOrder, - s: AnyOrder = 2, + a: onp.ToFloat1D, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D = 2, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - sort: lnpt.SortKind | bool = True, + sort: lmt.SortKind | bool = True, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: r""" diff --git a/lmo/_lm_co.py b/lmo/_lm_co.py index 5596c5bc..aef24409 100644 --- a/lmo/_lm_co.py +++ b/lmo/_lm_co.py @@ -1,9 +1,10 @@ from __future__ import annotations import sys -from typing import TYPE_CHECKING, Any, Literal, TypeAlias, Unpack, cast +from typing import Any, TypeAlias, Unpack, cast import numpy as np +import optype.numpy as onp import lmo.typing as lmt from ._lm import l_weights @@ -14,12 +15,6 @@ else: from typing_extensions import TypeVar -if TYPE_CHECKING: - import optype.numpy as onp - - import lmo.typing.np as lnpt - - __all__ = ( "l_cokurt", "l_cokurtosis", @@ -33,28 +28,24 @@ ) -_T_scalar = TypeVar("_T_scalar", bound=np.generic) -_T_float = TypeVar("_T_float", bound=np.floating[Any], default=np.float64) -_DType: TypeAlias = np.dtype[_T_scalar] | type[_T_scalar] +_SCT = TypeVar("_SCT", bound=np.generic) +_SCT_f = TypeVar("_SCT_f", bound=np.floating[Any], default=np.float64) -_N0 = TypeVar("_N0", bound=int) -_N1 = TypeVar("_N1", bound=int) -_N2 = TypeVar("_N2", bound=int) -_Array2D: TypeAlias = np.ndarray[tuple[_N0, _N1], np.dtype[_T_scalar]] -_Array3D: TypeAlias = np.ndarray[tuple[_N0, _N1, _N2], np.dtype[_T_scalar]] +_DType: TypeAlias = np.dtype[_SCT] | type[_SCT] +_Array3D: TypeAlias = onp.Array[tuple[int, int, int], _SCT] def l_comoment( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, - r: lmt.AnyOrder | lmt.AnyOrderND, + a: onp.ToFloat1D | onp.ToFloat2D, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, rowvar: bool | None = None, - sort: lnpt.SortKind | None = None, + sort: lmt.SortKind | None = None, cache: bool | None = None, -) -> onp.Array[Any, _T_float]: +) -> onp.ArrayND[_SCT_f]: r""" Multivariate extension of [`lmo.l_moment`][lmo.l_moment]. @@ -158,9 +149,9 @@ def l_comoment( m, n = x.shape if np.isscalar(r): - _r = np.array(clean_order(cast(lmt.AnyOrder, r))) + _r = np.array(clean_order(cast(lmt.ToOrder0D, r))) else: - _r = clean_orders(cast(lmt.AnyOrderND, r)) + _r = clean_orders(cast(lmt.ToOrderND, r)) if not m: return np.empty((*np.shape(_r), 0, 0), dtype=dtype) @@ -194,15 +185,15 @@ def l_comoment( def l_coratio( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, - r: lmt.AnyOrder | lmt.AnyOrderND, - s: lmt.AnyOrder | lmt.AnyOrderND, + a: onp.ToFloat1D | onp.ToFloat2D, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> onp.Array[Any, _T_float]: +) -> onp.ArrayND[_SCT_f]: r""" Estimate the generalized matrix of L-comoment ratio's. @@ -224,13 +215,13 @@ def l_coratio( def l_costats( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array3D[Literal[4], Any, Any, _T_float]: +) -> _Array3D[_SCT_f]: """ Calculates the L-*co*scale, L-corr(elation), L-*co*skew(ness) and L-*co*kurt(osis). @@ -242,17 +233,19 @@ def l_costats( - [`lmo.l_coratio`][lmo.l_coratio] """ r, s = [2, 2, 3, 4], [0, 2, 2, 2] - return l_coratio(a, r, s, trim=trim, dtype=dtype, **kwds) + out = l_coratio(a, r, s, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 3 + return cast(_Array3D[_SCT_f], out) def l_coloc( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array2D[Any, Any, _T_float]: +) -> onp.Array2D[_SCT_f]: r""" L-colocation matrix of 1st L-comoment estimates, $\Lambda^{(s, t)}_1$. @@ -291,17 +284,19 @@ def l_coloc( - [`lmo.l_loc`][lmo.l_loc] - [`numpy.mean`][numpy.mean] """ - return l_comoment(a, 1, trim=trim, dtype=dtype, **kwds) + out = l_comoment(a, 1, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 2 + return cast(onp.Array2D[_SCT_f], out) def l_coscale( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array2D[Any, Any, _T_float]: +) -> onp.Array2D[_SCT_f]: r""" L-coscale matrix of 2nd L-comoment estimates, $\Lambda^{(s, t)}_2$. @@ -327,17 +322,19 @@ def l_coscale( - [`lmo.l_scale`][lmo.l_scale] - [`numpy.cov`][numpy.cov] """ - return l_comoment(a, 2, trim=trim, dtype=dtype, **kwds) + out = l_comoment(a, 2, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 2 + return cast(onp.Array2D[_SCT_f], out) def l_corr( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array2D[Any, Any, _T_float]: +) -> onp.Array2D[_SCT_f]: r""" Sample L-correlation coefficient matrix $\tilde\Lambda^{(s, t)}_2$; the ratio of the L-coscale matrix over the L-scale **column**-vectors. @@ -373,17 +370,19 @@ def l_corr( - [`lmo.l_coratio`][lmo.l_coratio] - [`numpy.corrcoef`][numpy.corrcoef] """ - return l_coratio(a, 2, 2, trim=trim, dtype=dtype, **kwds) + out = l_coratio(a, 2, 2, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 2 + return cast(onp.Array2D[_SCT_f], out) def l_coskew( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array2D[Any, Any, _T_float]: +) -> onp.Array2D[_SCT_f]: r""" Sample L-coskewness coefficient matrix $\tilde\Lambda^{(s, t)}_3$. @@ -393,17 +392,19 @@ def l_coskew( - [`lmo.l_coratio`][lmo.l_coratio] - [`lmo.l_skew`][lmo.l_skew] """ - return l_coratio(a, 3, 2, trim=trim, dtype=dtype, **kwds) + out = l_coratio(a, 3, 2, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 2 + return cast(onp.Array2D[_SCT_f], out) def l_cokurtosis( - a: lnpt.AnyVectorFloat | lnpt.AnyMatrixFloat, + a: onp.ToFloat1D | onp.ToFloat2D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - dtype: _DType[_T_float] = np.float64, + dtype: _DType[_SCT_f] = np.float64, **kwds: Unpack[lmt.LComomentOptions], -) -> _Array2D[Any, Any, _T_float]: +) -> onp.Array2D[_SCT_f]: r""" Sample L-cokurtosis coefficient matrix $\tilde\Lambda^{(s, t)}_4$. @@ -413,7 +414,9 @@ def l_cokurtosis( - [`lmo.l_coratio`][lmo.l_coratio] - [`lmo.l_kurtosis`][lmo.l_kurtosis] """ - return l_coratio(a, 4, 2, trim=trim, dtype=dtype, **kwds) + out = l_coratio(a, 4, 2, trim=trim, dtype=dtype, **kwds) + assert out.ndim == 2 + return cast(onp.Array2D[_SCT_f], out) l_cokurt = l_cokurtosis diff --git a/lmo/_poly.py b/lmo/_poly.py index 7491412f..8aa0e444 100644 --- a/lmo/_poly.py +++ b/lmo/_poly.py @@ -13,12 +13,15 @@ import numpy as np import numpy.polynomial as npp +import optype as op import optype.numpy as onp import scipy.special as scs from numpy.polynomial._polybase import ABCPolyBase # noqa: PLC2701 if TYPE_CHECKING: - import lmo.typing.np as lnpt + from typing import LiteralString + + import lmo.typing as lmt __all__ = ( @@ -33,9 +36,11 @@ ) -if TYPE_CHECKING: - from typing import LiteralString +def __dir__() -> tuple[str, ...]: + return __all__ + +if TYPE_CHECKING: PolySeries: TypeAlias = ABCPolyBase[LiteralString | None] else: PolySeries: TypeAlias = ABCPolyBase @@ -46,24 +51,19 @@ @overload -def eval_sh_jacobi( - n: int, - a: float | lnpt.Float, - b: float | lnpt.Float, - x: float | lnpt.Float, -) -> float: ... +def eval_sh_jacobi(n: int, a: onp.ToFloat, b: onp.ToFloat, x: onp.ToFloat) -> float: ... @overload def eval_sh_jacobi( n: int, - a: float | lnpt.Float, - b: float | lnpt.Float, - x: onp.Array[_T_shape, lnpt.Float], + a: onp.ToFloat, + b: onp.ToFloat, + x: onp.Array[_T_shape, lmt.Floating], ) -> onp.Array[_T_shape, np.float64]: ... def eval_sh_jacobi( - n: int | lnpt.Int, - a: float | lnpt.Float, - b: float | lnpt.Float, - x: float | lnpt.Float | onp.Array[_T_shape, lnpt.Float], + n: int | lmt.Integer, + a: onp.ToFloat, + b: onp.ToFloat, + x: onp.ToFloat | onp.Array[_T_shape, lmt.Floating], ) -> float | onp.Array[_T_shape, np.float64]: """ Fast evaluation of the n-th shifted Jacobi polynomial. @@ -113,11 +113,7 @@ def eval_sh_jacobi( return scs.eval_jacobi(n, a, b, u) -def peaks_jacobi( - n: int, - a: float, - b: float, -) -> onp.Array[tuple[int], np.float64]: +def peaks_jacobi(n: int, a: float, b: float) -> onp.Array1D[np.float64]: r""" Finds the \( x \in [-1, 1] \) s.t. \( /frac{\dd{\shjacobi{n}{a}{b}{x}}}{\dd{x}} = 0 \) of a Jacobi polynomial, @@ -303,81 +299,78 @@ def extrema_jacobi(n: int, a: float, b: float) -> tuple[float, float]: return cast(float, np.min(p)), cast(float, np.max(p)) -def _jacobi_coefs(n: int, a: float, b: float) -> onp.Array[tuple[int], np.float64]: - p_n: np.poly1d - p_n = scs.jacobi(n, a, b) - return p_n.coef[::-1] - - def jacobi( - n: int, + n: onp.ToInt, /, - a: float, - b: float, - domain: tuple[float, float] = (-1, 1), - window: tuple[float, float] = (-1, 1), + a: onp.ToFloat, + b: onp.ToFloat, + domain: tuple[onp.ToFloat, onp.ToFloat] = (-1, 1), + window: tuple[onp.ToFloat, onp.ToFloat] = (-1, 1), symbol: str = "x", ) -> npp.Polynomial: - return npp.Polynomial(_jacobi_coefs(n, a, b), domain, window, symbol) + return npp.Polynomial( + scs.jacobi(n, a, b).coef[::-1], + domain=domain, + window=window, + symbol=symbol, + ) @overload def jacobi_series( - coef: lnpt.AnyArrayFloat, + coef: onp.ToFloat1D, /, - a: float, - b: float, + a: onp.ToFloat, + b: onp.ToFloat, *, kind: None = ..., - domain: tuple[float, float] = ..., - window: tuple[float, float] = ..., + domain: tuple[onp.ToFloat, onp.ToFloat] = ..., + window: tuple[onp.ToFloat, onp.ToFloat] = ..., symbol: str = ..., ) -> npp.Polynomial: ... @overload def jacobi_series( - coef: lnpt.AnyArrayFloat, + coef: onp.ToFloat1D, /, - a: float, - b: float, + a: onp.ToFloat, + b: onp.ToFloat, *, kind: type[_T_poly], - domain: tuple[float, float] = ..., - window: tuple[float, float] = ..., + domain: tuple[onp.ToFloat, onp.ToFloat] = ..., + window: tuple[onp.ToFloat, onp.ToFloat] = ..., symbol: str = ..., ) -> _T_poly: ... def jacobi_series( - coef: lnpt.AnyArrayFloat, + coef: onp.ToFloat1D, /, - a: float, - b: float, + a: onp.ToFloat, + b: onp.ToFloat, *, kind: type[_T_poly] | None = None, - domain: tuple[float, float] = (-1, 1), - window: tuple[float, float] = (-1, 1), + domain: tuple[onp.ToFloat, onp.ToFloat] = (-1, 1), + window: tuple[onp.ToFloat, onp.ToFloat] = (-1, 1), symbol: str = "x", ) -> _T_poly | npp.Polynomial: - r""" + """ Construct a polynomial from the weighted sum of shifted Jacobi polynomials. - Roughly equivalent to - `sum(w[n] * sh_jacobi(n, a, b) for n in range(len(w)))`. + Roughly equivalent to `sum(wn * sh_jacobi(r, a, b) for r, wn in enumerate(w))`. Todo: - Create a `Jacobi` class, as extension to `numpy.polynomial.` """ - w = cast(onp.Array[tuple[int], np.float64], np.asarray(coef)) + w = np.asarray(coef, np.float64) if w.ndim != 1: msg = "coefs must be 1-D" raise ValueError(msg) p = sum( - jacobi(r, a, b, domain, window=window, symbol=symbol) * w_r - for r, w_r in enumerate(w.flat) + wn * jacobi(n, a, b, domain=domain, window=window, symbol=symbol) + for n, wn in enumerate(w.flat) ) assert p - # see https://github.com/numpy/numpy/pull/27237 return p.convert( # pyright: ignore[reportUnknownMemberType] domain=domain, kind=kind or npp.Polynomial, @@ -385,24 +378,20 @@ def jacobi_series( ) -def roots( - p: PolySeries, - /, - outside: bool = False, -) -> onp.Array[tuple[int], np.float64]: +def roots(p: PolySeries, /, outside: op.CanBool = False) -> onp.Array1D[np.float64]: """ Return the $x$ in the domain of $p$, where $p(x) = 0$. If outside=False (default), the values that fall outside of the domain interval will be not be included. """ - z = cast( - onp.Array[tuple[int], np.float64], - p.roots(), + z = p.roots() + x = cast( + onp.Array1D[np.float64], + z[np.isreal(z)].real if np.isrealobj(p.domain) and not np.isrealobj(z) else z, ) - x = z[np.isreal(z)].real if not np.isrealobj(z) and np.isrealobj(p.domain) else z - if not outside and len(x): + if not outside and x.size: a, b = np.sort(p.domain) return x[(x >= a) & (x <= b)] diff --git a/lmo/_utils.py b/lmo/_utils.py index bd88db54..74e305f3 100644 --- a/lmo/_utils.py +++ b/lmo/_utils.py @@ -7,16 +7,16 @@ import numpy as np import numpy.typing as npt +import lmo.typing as lmt + +if TYPE_CHECKING: + import optype.numpy as onp + if sys.version_info >= (3, 13): from typing import TypeVar else: from typing_extensions import TypeVar -if TYPE_CHECKING: - import optype.numpy as onp - - import lmo.typing as lmt - import lmo.typing.np as lnpt __all__ = ( "clean_order", @@ -34,18 +34,16 @@ _SCT = TypeVar("_SCT", bound=np.generic) -_SCT_uifc = TypeVar("_SCT_uifc", bound="lnpt.Number") -_SCT_ui = TypeVar("_SCT_ui", bound=np.integer[Any], default=np.intp) -_SCT_f = TypeVar("_SCT_f", bound=np.floating[Any], default=np.float64) +_SCT_uifc = TypeVar("_SCT_uifc", bound=np.number[Any]) +_SCT_ui = TypeVar("_SCT_ui", bound=lmt.Integer, default=np.intp) +_SCT_f = TypeVar("_SCT_f", bound=lmt.Floating, default=np.float64) -_DT_f = TypeVar("_DT_f", bound=np.dtype[np.floating[Any]]) -_AT_f = TypeVar("_AT_f", bound=np.floating[Any] | npt.NDArray[np.floating[Any]]) +_DT_f = TypeVar("_DT_f", bound=np.dtype[lmt.Floating]) +_AT_f = TypeVar("_AT_f", bound=lmt.Floating | npt.NDArray[lmt.Floating]) _SizeT = TypeVar("_SizeT", bound=int) - _ShapeT = TypeVar("_ShapeT", bound=tuple[int, ...]) -_ShapeT1 = TypeVar("_ShapeT1", bound="onp.AtLeast1D") -_ShapeT2 = TypeVar("_ShapeT2", bound="onp.AtLeast2D") + _DType: TypeAlias = np.dtype[_SCT] | type[_SCT] @@ -56,7 +54,7 @@ def ensure_axis_at( source: int | None, destination: int, *, - order: lnpt.OrderReshape = "C", + order: lmt.OrderReshape = "C", ) -> npt.NDArray[_SCT]: """ Moves the from `source` to `destination` if needed, or returns a flattened @@ -90,15 +88,15 @@ def plotting_positions( @overload -def round0(a: _AT_f, /, tol: float | None = ...) -> _AT_f: ... +def round0(a: _AT_f, /, tol: float | None = None) -> _AT_f: ... @overload def round0( a: onp.CanArray[_ShapeT, _DT_f], /, - tol: float | None = ..., + tol: float | None = None, ) -> np.ndarray[_ShapeT, _DT_f]: ... @overload -def round0(a: float, /, tol: float | None = ...) -> np.float64: ... +def round0(a: float, /, tol: float | None = None) -> np.float64: ... def round0( a: float | onp.CanArray[_ShapeT, np.dtype[_SCT_f]], /, @@ -117,17 +115,17 @@ def round0( def _apply_aweights( - x: np.ndarray[_ShapeT1, _DT_f], - v: np.ndarray[_ShapeT1 | onp.AtLeast1D, _DT_f | np.dtype[lnpt.Float]], + x: np.ndarray[_ShapeT, _DT_f], + v: onp.ArrayND[lmt.Floating], axis: int, -) -> np.ndarray[_ShapeT1, _DT_f]: +) -> np.ndarray[_ShapeT, _DT_f]: # interpret the weights as horizontal coordinates using cumsum vv = np.cumsum(v, axis=axis) assert vv.shape == x.shape, (vv.shape, x.shape) # ensure that the samples are on the last axis, for easy iterating if swap_axes := axis % x.ndim != x.ndim - 1: - x = cast(np.ndarray[_ShapeT1, _DT_f], np.swapaxes(x, axis, -1)) + x = cast(np.ndarray[_ShapeT, _DT_f], np.swapaxes(x, axis, -1)) vv = np.moveaxis(vv, axis, -1) # cannot use np.apply_along_axis here, since both x_k and w_k need to be @@ -147,17 +145,17 @@ def _apply_aweights( # unswap the axes if previously swapped if swap_axes: - out = cast(np.ndarray[_ShapeT1, _DT_f], np.swapaxes(out, -1, axis)) + out = cast(np.ndarray[_ShapeT, _DT_f], np.swapaxes(out, -1, axis)) return out def _sort_like( - a: onp.Array[_ShapeT1, _SCT_uifc], - i: onp.Array[tuple[int], lnpt.Int], + a: onp.Array[_ShapeT, _SCT_uifc], + i: onp.Array1D[lmt.Integer], /, axis: int | None, -) -> onp.Array[_ShapeT1, _SCT_uifc]: +) -> onp.Array[_ShapeT, _SCT_uifc]: return ( np.take(a, i, axis=None if a.ndim == i.ndim else axis) if min(a.ndim, i.ndim) <= 1 @@ -166,13 +164,13 @@ def _sort_like( def sort_maybe( - x: onp.Array[_ShapeT1, _SCT_uifc], + x: onp.Array[_ShapeT, _SCT_uifc], /, axis: int = -1, *, - sort: bool | lnpt.SortKind = True, + sort: bool | lmt.SortKind = True, inplace: bool = False, -) -> onp.Array[_ShapeT1, _SCT_uifc]: +) -> onp.Array[_ShapeT, _SCT_uifc]: if not sort: return x @@ -185,16 +183,16 @@ def sort_maybe( def ordered( # noqa: C901 - x: lnpt.AnyArrayFloat, - y: lnpt.AnyArrayFloat | None = None, + x: onp.ToFloatND, + y: onp.ToFloatND | None = None, /, axis: int | None = None, - dtype: _DType[lnpt.Float] | None = None, + dtype: _DType[lmt.Floating] | None = None, *, - fweights: lmt.AnyFWeights | None = None, - aweights: lmt.AnyAWeights | None = None, - sort: lnpt.SortKind | bool = True, -) -> onp.Array[onp.AtLeast1D, lnpt.Float]: + fweights: lmt.ToFWeights | None = None, + aweights: lmt.ToAWeights | None = None, + sort: lmt.SortKind | bool = True, +) -> onp.Array[onp.AtLeast1D, lmt.Floating]: """ Calculate `n = len(x)` order stats of `x`, optionally weighted. If `y` is provided, the order of `y` is used instead. @@ -260,16 +258,16 @@ def ordered( # noqa: C901 @overload -def clean_order(r: lmt.AnyOrder, /, name: str = "r", rmin: int = 0) -> int: ... +def clean_order(r: lmt.ToOrder0D, /, name: str = "r", rmin: int = 0) -> int: ... @overload def clean_order( - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, name: str = "r", rmin: int = 0, ) -> npt.NDArray[np.intp]: ... def clean_order( - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, name: str = "r", rmin: int = 0, @@ -286,7 +284,7 @@ def clean_order( def clean_orders( - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, name: str = "r", rmin: int = 0, @@ -310,10 +308,10 @@ def clean_orders( @overload -def clean_trim(trim: lmt.AnyTrimInt, /) -> tuple[int, int]: ... +def clean_trim(trim: lmt.ToIntTrim, /) -> tuple[int, int]: ... @overload -def clean_trim(trim: lmt.AnyTrimFloat, /) -> tuple[float, float]: ... -def clean_trim(trim: lmt.AnyTrim, /) -> tuple[int, int] | tuple[float, float]: +def clean_trim(trim: lmt.ToTrim, /) -> tuple[float, float]: ... +def clean_trim(trim: lmt.ToTrim, /) -> tuple[int, int] | tuple[float, float]: """ Validates and cleans the passed trim; and return a 2-tuple of either ints or floats. @@ -351,8 +349,8 @@ def clean_trim(trim: lmt.AnyTrim, /) -> tuple[int, int] | tuple[float, float]: def moments_to_ratio( - rs: onp.Array[tuple[int, ...], lnpt.Int], - l_rs: onp.Array[onp.AtLeast1D, _SCT_f], + rs: onp.ArrayND[lmt.Integer], + l_rs: onp.ArrayND[_SCT_f], /, ) -> _SCT_f | npt.NDArray[_SCT_f]: """ @@ -380,9 +378,9 @@ def moments_to_ratio( def moments_to_stats_cov( - t_0r: onp.Array[tuple[int], lnpt.Float], - ll_kr: onp.Array[_ShapeT2, _SCT_f], -) -> onp.Array[_ShapeT2, _SCT_f]: + t_0r: onp.ArrayND[lmt.Floating], + ll_kr: onp.Array[_ShapeT, _SCT_f], +) -> onp.Array[_ShapeT, _SCT_f]: # t_0r are L-ratio's for r = 0, 1, ..., R (t_0r[0] == 1 / L-scale) # t_0r[1] isn't used, and can be set to anything # ll_kr is the L-moment cov of size R**2 (orders start at 1 here) diff --git a/lmo/contrib/pandas.py b/lmo/contrib/pandas.py index 912f1d8b..290c87cc 100644 --- a/lmo/contrib/pandas.py +++ b/lmo/contrib/pandas.py @@ -144,9 +144,9 @@ def fn(obj: pd.Series[Any]) -> Callable[..., float | pd.Series[float]]: def l_moment( self, - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float | pd.Series[float]: """ @@ -169,10 +169,10 @@ def l_moment( def l_ratio( self, - r: lmt.AnyOrder | lmt.AnyOrderND, - k: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + k: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float | pd.Series[float]: """ @@ -196,7 +196,7 @@ def l_ratio( def l_stats( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, num: int = 4, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -215,7 +215,7 @@ def l_stats( def l_loc( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float: """ @@ -228,7 +228,7 @@ def l_loc( def l_scale( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float: """ @@ -241,7 +241,7 @@ def l_scale( def l_variation( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float: """ @@ -254,7 +254,7 @@ def l_variation( def l_skew( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float: """ @@ -267,7 +267,7 @@ def l_skew( def l_kurtosis( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> float: """ @@ -308,9 +308,9 @@ def fn( def l_moment( self, - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float] | pd.DataFrame: @@ -338,10 +338,10 @@ def l_moment( def l_ratio( self, - r: lmt.AnyOrder | lmt.AnyOrderND, - k: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + k: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float] | pd.DataFrame: @@ -374,7 +374,7 @@ def l_ratio( def l_stats( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, num: int = 4, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], @@ -400,7 +400,7 @@ def l_stats( def l_loc( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -418,7 +418,7 @@ def l_loc( def l_scale( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -436,7 +436,7 @@ def l_scale( def l_variation( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -454,7 +454,7 @@ def l_variation( def l_skew( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -472,7 +472,7 @@ def l_skew( def l_kurtosis( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -490,7 +490,7 @@ def l_kurtosis( def l_kurt( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, axis: _Axis = 0, **kwargs: Unpack[lmt.LMomentOptions], ) -> pd.Series[float]: @@ -502,9 +502,9 @@ def l_kurt( def l_comoment( self, - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -541,10 +541,10 @@ def l_comoment( def l_coratio( self, - r: lmt.AnyOrder, - k: lmt.AnyOrder = 2, + r: lmt.ToOrder0D, + k: lmt.ToOrder0D = 2, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -584,7 +584,7 @@ def l_coratio( def l_coloc( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -596,7 +596,7 @@ def l_coloc( def l_coscale( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -608,7 +608,7 @@ def l_coscale( def l_corr( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -620,7 +620,7 @@ def l_corr( def l_coskew( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -632,7 +632,7 @@ def l_coskew( def l_cokurtosis( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ @@ -644,7 +644,7 @@ def l_cokurtosis( def l_cokurt( self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Unpack[lmt.LComomentOptions], ) -> pd.DataFrame: """ diff --git a/lmo/contrib/scipy_stats.py b/lmo/contrib/scipy_stats.py index e47f0bde..f6190ae7 100644 --- a/lmo/contrib/scipy_stats.py +++ b/lmo/contrib/scipy_stats.py @@ -7,11 +7,14 @@ from __future__ import annotations import contextlib +from collections.abc import Callable from typing import ( TYPE_CHECKING, Any, ClassVar, + Concatenate, Literal, + ParamSpec, Protocol, TypeAlias, TypeVar, @@ -23,11 +26,10 @@ import numpy as np import numpy.typing as npt +import optype.numpy as onp from scipy.stats.distributions import rv_continuous, rv_frozen import lmo.typing as lmt -import lmo.typing.np as lnpt -import lmo.typing.scipy as lspt from lmo import inference from lmo._lm import l_moment as l_moment_est from lmo._utils import ( @@ -49,9 +51,8 @@ ) if TYPE_CHECKING: - from collections.abc import Callable, Mapping, Sequence + from collections.abc import Mapping - import optype.numpy as onp from scipy.stats import rv_discrete @@ -68,23 +69,7 @@ def __call__(self, x: _T_x, /) -> _T_x: ... _Tuple2: TypeAlias = tuple[_T, _T] _Tuple4: TypeAlias = tuple[_T, _T, _T, _T] -_ArrF8: TypeAlias = npt.NDArray[np.float64] - - -class _ShapeInfo(Protocol): - """Stub for `scipy.stats._distn_infrastructure._ShapeInfo`.""" - - name: str - integrality: bool - domain: Sequence[float] # in practice a list of size 2 (y no tuple?) - - def __init__( - self, - name: str, - integrality: bool = ..., - domain: _Tuple2[float] = ..., - inclusive: _Tuple2[bool] = ..., - ) -> None: ... +_Float64ND: TypeAlias = npt.NDArray[np.float64] class PatchClass: @@ -114,6 +99,13 @@ class _TrimKwargs(TypedDict, total=False): trim: tuple[int, int] | tuple[float, float] +_Tss = ParamSpec("_Tss") +_RVFunc: TypeAlias = lmt.Callable2[ + Callable[Concatenate[onp.ToFloat, _Tss], float | np.float64], + Callable[Concatenate[onp.ToFloatND, _Tss], onp.ArrayND[np.float64]], +] + + class l_rv_generic(PatchClass): """ Additional methods that are patched into @@ -134,31 +126,31 @@ class l_rv_generic(PatchClass): shapes: str _argcheck: Callable[..., int] - _logpxf: lspt.RVFunction[...] - _cdf: lspt.RVFunction[...] + _logpxf: _RVFunc[...] + _cdf: _RVFunc[...] _fitstart: Callable[..., tuple[float, ...]] _get_support: Callable[..., _Tuple2[float]] - _param_info: Callable[[], list[_ShapeInfo]] + _param_info: Callable[[], list[lmt.ShapeInfo]] _parse_args: Callable[..., tuple[tuple[Any, ...], float, float]] - _ppf: lspt.RVFunction[...] - _shape_info: Callable[[], list[_ShapeInfo]] + _ppf: _RVFunc[...] + _shape_info: Callable[[], list[lmt.ShapeInfo]] _stats: Callable[..., _Tuple4[float | None]] _unpack_loc_scale: Callable[ [onp.ToFloat1D], tuple[float, float, tuple[float, ...]], ] - cdf: lspt.RVFunction[...] + cdf: _RVFunc[...] fit: Callable[..., tuple[float, ...]] mean: Callable[..., float] ppf: _Fn1 std: Callable[..., float] @np.errstate(divide="ignore") - def _logqdf(self, u: _ArrF8, *args: Any) -> _ArrF8: + def _logqdf(self, u: _Float64ND, *args: Any) -> _Float64ND: """Overridable log quantile distribution function (QDF).""" return -self._logpxf(self._ppf(u, *args), *args) - def _qdf(self, u: _ArrF8, *args: Any) -> _ArrF8: + def _qdf(self, /, u: _Float64ND, *args: Any) -> _Float64ND: r""" Overridable quantile distribution function (QDF). @@ -170,32 +162,32 @@ def _qdf(self, u: _ArrF8, *args: Any) -> _ArrF8: @overload def l_moment( self, - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, *args: Any, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., **kwds: Any, - ) -> _ArrF8: ... + ) -> _Float64ND: ... @overload def l_moment( self, - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, *args: Any, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., **kwds: Any, ) -> np.float64: ... def l_moment( self, - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, *args: Any, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, **kwds: Any, - ) -> np.float64 | _ArrF8: + ) -> np.float64 | _Float64ND: r""" Population L-moment(s) $\lambda^{(s,t)}_r$. @@ -266,9 +258,9 @@ def l_moment( - [`lmo.l_moment`][lmo.l_moment]: sample L-moment """ if np.isscalar(r): - _r = np.asarray(clean_order(cast(lmt.AnyOrder, r))) + _r = np.asarray(clean_order(cast(lmt.ToOrder0D, r))) else: - _r = clean_orders(cast(lmt.AnyOrderND, r)) + _r = clean_orders(cast(lmt.ToOrderND, r)) (s, t) = _trim = clean_trim(trim) shapes, loc, scale = self._parse_args(*args, **kwds) @@ -301,35 +293,35 @@ def l_moment( @overload def l_ratio( self, - order: lmt.AnyOrderND, - order_denom: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrderND, + order_denom: lmt.ToOrder0D | lmt.ToOrderND, /, *args: Any, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., **kwds: Any, - ) -> _ArrF8: ... + ) -> _Float64ND: ... @overload def l_ratio( self, - order: lmt.AnyOrder, - order_denom: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrder0D, + order_denom: lmt.ToOrder0D | lmt.ToOrderND, /, *args: Any, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., **kwds: Any, ) -> np.float64: ... def l_ratio( self, - r: lmt.AnyOrder | lmt.AnyOrderND, - k: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + k: lmt.ToOrder0D | lmt.ToOrderND, /, *args: Any, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, **kwds: Any, - ) -> np.float64 | _ArrF8: + ) -> np.float64 | _Float64ND: r""" L-moment ratio('s) $\tau^{(s,t)}_{r,k}$. @@ -410,11 +402,11 @@ def l_ratio( def l_stats( self, *args: Any, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, moments: int = 4, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, **kwds: Any, - ) -> _ArrF8: + ) -> _Float64ND: r""" The L-moments (for $r \le 2$) and L-ratio's (for $r > 2$). @@ -480,7 +472,7 @@ def l_stats( **kwds, ) - def l_loc(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: + def l_loc(self, *args: Any, trim: lmt.ToTrim = 0, **kwds: Any) -> float: """ L-location of the distribution, i.e. the 1st L-moment. @@ -491,7 +483,7 @@ def l_loc(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: return float(self.l_moment(1, *args, trim=trim, **kwds)) - def l_scale(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: + def l_scale(self, *args: Any, trim: lmt.ToTrim = 0, **kwds: Any) -> float: """ L-scale of the distribution, i.e. the 2nd L-moment. @@ -499,14 +491,14 @@ def l_scale(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: """ return float(self.l_moment(2, *args, trim=trim, **kwds)) - def l_skew(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: + def l_skew(self, *args: Any, trim: lmt.ToTrim = 0, **kwds: Any) -> float: """L-skewness coefficient of the distribution; the 3rd L-moment ratio. Alias for `X.l_ratio(3, 2, ...)`. """ return float(self.l_ratio(3, 2, *args, trim=trim, **kwds)) - def l_kurt(self, *args: Any, trim: lmt.AnyTrim = 0, **kwds: Any) -> float: + def l_kurt(self, *args: Any, trim: lmt.ToTrim = 0, **kwds: Any) -> float: """L-kurtosis coefficient of the distribution; the 4th L-moment ratio. Alias for `X.l_ratio(4, 2, ...)`. @@ -520,10 +512,10 @@ def l_moments_cov( r_max: int, /, *args: Any, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, **kwds: Any, - ) -> _ArrF8: + ) -> _Float64ND: r""" Variance/covariance matrix of the L-moment estimators. @@ -651,10 +643,10 @@ def l_stats_cov( self, *args: Any, moments: int = 4, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, **kwds: Any, - ) -> _ArrF8: + ) -> _Float64ND: r""" Similar to [`l_moments_cov` ][lmo.contrib.scipy_stats.l_rv_generic.l_moments_cov], but for the @@ -761,11 +753,11 @@ def l_stats_cov( def l_moment_influence( self, - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, *args: Any, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, tol: float = 1e-8, **kwds: Any, ) -> _Fn1: @@ -850,12 +842,12 @@ def l_moment_influence( def l_ratio_influence( self, - r: lmt.AnyOrder, - k: lmt.AnyOrder, + r: lmt.ToOrder0D, + k: lmt.ToOrder0D, /, *args: Any, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, tol: float = 1e-8, **kwds: Any, ) -> Callable[[_T_x], _T_x]: @@ -946,8 +938,8 @@ def l_ratio_influence( def _reduce_param_bounds( self, - **kwds: lnpt.AnyScalarFloat, - ) -> tuple[dict[str, lnpt.AnyScalarFloat], list[_Tuple2[float | None]]]: + **kwds: onp.ToFloat, + ) -> tuple[dict[str, onp.ToFloat], list[_Tuple2[float | None]]]: """ Based on `scipy.stats.rv_continuous._reduce_func`. @@ -989,10 +981,10 @@ def _reduce_param_bounds( def _l_gmm_error( self, - theta: _ArrF8, + theta: _Float64ND, trim: _Tuple2[float], - l_data: _ArrF8, - weights: _ArrF8, + l_data: _Float64ND, + weights: _Float64ND, ) -> float: """L-GMM objective function.""" loc, scale, args = self._unpack_loc_scale(theta) @@ -1019,10 +1011,10 @@ def _l_gmm_error( @overload def l_fit( self, - data: lnpt.AnyVectorFloat, + data: onp.ToFloat1D, *args: float, n_extra: int = ..., - trim: lmt.AnyTrimInt = ..., + trim: lmt.ToIntTrim = ..., full_output: Literal[True], fit_kwargs: Mapping[str, Any] | None = ..., **kwds: Any, @@ -1030,20 +1022,20 @@ def l_fit( @overload def l_fit( self, - data: lnpt.AnyVectorFloat, + data: onp.ToFloat1D, *args: float, n_extra: int = ..., - trim: lmt.AnyTrimInt = ..., + trim: lmt.ToIntTrim = ..., full_output: bool = ..., fit_kwargs: Mapping[str, Any] | None = ..., **kwds: Any, ) -> tuple[float, ...]: ... def l_fit( self, - data: lnpt.AnyVectorFloat, + data: onp.ToFloat1D, *args: float, n_extra: int = 0, - trim: lmt.AnyTrimInt = 0, + trim: lmt.ToIntTrim = 0, full_output: bool = False, fit_kwargs: Mapping[str, Any] | None = None, random_state: int | np.random.Generator | None = None, @@ -1166,7 +1158,7 @@ def l_fit( r = np.arange(1, len(args0) + n_extra + 1) - _lmo_cache: dict[tuple[float, ...], _ArrF8] = {} + _lmo_cache: dict[tuple[float, ...], _Float64ND] = {} _lmo_fn = _l_moment # temporary cache to speed up L-moment calculations with the same @@ -1175,7 +1167,7 @@ def lmo_fn( _r: npt.NDArray[np.intp], *_args: float, **kwds: Unpack[_TrimKwargs], - ) -> _ArrF8: + ) -> _Float64ND: shapes, loc, scale = _args[:-2], _args[-2], _args[-1] # r and trim are constants, so it's safe to ignore them here @@ -1218,9 +1210,9 @@ def lmo_fn( def l_fit_loc_scale( self, - data: lnpt.AnyArrayFloat, + data: onp.ToFloatND, *args: Any, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwds: Any, ) -> tuple[float, float]: """ @@ -1271,26 +1263,26 @@ class l_rv_frozen(PatchClass): # noqa: D101 @overload def l_moment( self, - order: lmt.AnyOrderND, + order: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., - ) -> _ArrF8: ... + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., + ) -> _Float64ND: ... @overload def l_moment( self, - order: lmt.AnyOrder, + order: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., ) -> np.float64: ... def l_moment( # noqa: D102 self, - order: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, - ) -> np.float64 | _ArrF8: + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, + ) -> np.float64 | _Float64ND: return self.dist.l_moment( order, *self.args, @@ -1302,29 +1294,29 @@ def l_moment( # noqa: D102 @overload def l_ratio( self, - order: lmt.AnyOrderND, - order_denom: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrderND, + order_denom: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., - ) -> _ArrF8: ... + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., + ) -> _Float64ND: ... @overload def l_ratio( self, - order: lmt.AnyOrder, - order_denom: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrder0D, + order_denom: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., - quad_opts: lspt.QuadOptions | None = ..., + trim: lmt.ToTrim = ..., + quad_opts: lmt.QuadOptions | None = ..., ) -> np.float64: ... def l_ratio( # noqa: D102 self, - order: lmt.AnyOrder | lmt.AnyOrderND, - order_denom: lmt.AnyOrder | lmt.AnyOrderND, + order: lmt.ToOrder0D | lmt.ToOrderND, + order_denom: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, - ) -> np.float64 | _ArrF8: + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, + ) -> np.float64 | _Float64ND: return self.dist.l_ratio( order, order_denom, @@ -1336,10 +1328,10 @@ def l_ratio( # noqa: D102 def l_stats( # noqa: D102 self, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, moments: int = 4, - quad_opts: lspt.QuadOptions | None = None, - ) -> np.float64 | _ArrF8: + quad_opts: lmt.QuadOptions | None = None, + ) -> np.float64 | _Float64ND: return self.dist.l_stats( *self.args, trim=trim, @@ -1348,16 +1340,16 @@ def l_stats( # noqa: D102 **self.kwds, ) - def l_loc(self, trim: lmt.AnyTrim = 0) -> float: # noqa: D102 + def l_loc(self, trim: lmt.ToTrim = 0) -> float: # noqa: D102 return self.dist.l_loc(*self.args, trim=trim, **self.kwds) - def l_scale(self, trim: lmt.AnyTrim = 0) -> float: # noqa: D102 + def l_scale(self, trim: lmt.ToTrim = 0) -> float: # noqa: D102 return self.dist.l_scale(*self.args, trim=trim, **self.kwds) - def l_skew(self, trim: lmt.AnyTrim = 0) -> float: # noqa: D102 + def l_skew(self, trim: lmt.ToTrim = 0) -> float: # noqa: D102 return self.dist.l_skew(*self.args, trim=trim, **self.kwds) - def l_kurtosis(self, trim: lmt.AnyTrim = 0) -> float: # noqa: D102 + def l_kurtosis(self, trim: lmt.ToTrim = 0) -> float: # noqa: D102 return self.dist.l_kurtosis(*self.args, trim=trim, **self.kwds) l_kurt = l_kurtosis @@ -1366,9 +1358,9 @@ def l_moments_cov( # noqa: D102 self, r_max: int, /, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, - ) -> _ArrF8: + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, + ) -> _Float64ND: return self.dist.l_moments_cov( r_max, *self.args, @@ -1380,9 +1372,9 @@ def l_moments_cov( # noqa: D102 def l_stats_cov( # noqa: D102 self, moments: int = 4, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, - ) -> _ArrF8: + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, + ) -> _Float64ND: return self.dist.l_stats_cov( *self.args, moments=moments, @@ -1393,10 +1385,10 @@ def l_stats_cov( # noqa: D102 def l_moment_influence( # noqa: D102 self, - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: return self.dist.l_moment_influence( @@ -1410,11 +1402,11 @@ def l_moment_influence( # noqa: D102 def l_ratio_influence( # noqa: D102 self, - r: lmt.AnyOrder, - k: lmt.AnyOrder, + r: lmt.ToOrder0D, + k: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, - quad_opts: lspt.QuadOptions | None = None, + trim: lmt.ToTrim = 0, + quad_opts: lmt.QuadOptions | None = None, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: return self.dist.l_ratio_influence( @@ -1434,8 +1426,8 @@ def _l_moment( /, *args: Any, trim: _Tuple2[int] | _Tuple2[float] = (0, 0), - quad_opts: lspt.QuadOptions | None = None, -) -> _ArrF8: + quad_opts: lmt.QuadOptions | None = None, +) -> _Float64ND: """ Population L-moments of the standard distribution (i.e. `loc, scale = 0, 1`). diff --git a/lmo/diagnostic.py b/lmo/diagnostic.py index 06dd553d..66dd1720 100644 --- a/lmo/diagnostic.py +++ b/lmo/diagnostic.py @@ -20,26 +20,24 @@ ) import numpy as np -import numpy.typing as npt +import optype.numpy as onp +import lmo.typing as lmt from . import constants from ._lm import l_ratio from ._poly import extrema_jacobi from ._utils import clean_orders, clean_trim from .special import fpow -from .typing import AnyOrder, AnyOrderND, AnyTrim if sys.version_info >= (3, 13): from typing import TypeIs else: from typing_extensions import TypeIs - if TYPE_CHECKING: - import lmo.typing.np as lnpt - import lmo.typing.scipy as lspt from .contrib.scipy_stats import l_rv_generic + __all__ = ( "error_sensitivity", "l_moment_bounds", @@ -53,7 +51,7 @@ _T = TypeVar("_T") -_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) +_T_x = TypeVar("_T_x", float, onp.ArrayND[np.float64]) class _Fn1(Protocol): @@ -61,7 +59,7 @@ def __call__(self, x: _T_x, /) -> _T_x: ... _Tuple2: TypeAlias = tuple[_T, _T] -_ArrF8: TypeAlias = npt.NDArray[np.float64] +_FloatND: TypeAlias = onp.ArrayND[np.float64] _MIN_RHO: Final[float] = 1e-5 @@ -79,19 +77,19 @@ class HypothesisTestResult(NamedTuple): hypothesis, $H_0$. """ - statistic: float | _ArrF8 - pvalue: float | _ArrF8 + statistic: float | _FloatND + pvalue: float | _FloatND @property - def is_valid(self) -> np.bool_ | npt.NDArray[np.bool_]: + def is_valid(self) -> np.bool_ | onp.ArrayND[np.bool_]: """Check if the statistic is finite and not `nan`.""" return np.isfinite(self.statistic) def is_significant( self, - level: float | np.floating[Any] = 0.05, + level: float | lmt.Floating = 0.05, /, - ) -> np.bool_ | npt.NDArray[np.bool_]: + ) -> np.bool_ | onp.ArrayND[np.bool_]: """ Whether or not the null hypothesis can be rejected, with a certain confidence level (5% by default). @@ -103,7 +101,7 @@ def is_significant( def normaltest( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, /, *, axis: int | None = None, @@ -178,7 +176,7 @@ def normaltest( return HypothesisTestResult(k2, p_value) -def _gof_stat_single(l_obs: _ArrF8, l_exp: _ArrF8, cov: _ArrF8) -> float: +def _gof_stat_single(l_obs: _FloatND, l_exp: _FloatND, cov: _FloatND) -> float: err = l_obs - l_exp prec = np.linalg.inv(cov) # precision matrix return float(err.T @ prec @ err) @@ -192,7 +190,7 @@ def _gof_stat_single(l_obs: _ArrF8, l_exp: _ArrF8, cov: _ArrF8) -> float: ) -def _is_rv(x: object) -> TypeIs[lspt.RVFrozen | lspt.RV]: +def _is_rv(x: object) -> TypeIs[lmt.rv_frozen | lmt.rv_generic]: from scipy.stats.distributions import ( rv_continuous, rv_discrete, @@ -205,11 +203,11 @@ def _is_rv(x: object) -> TypeIs[lspt.RVFrozen | lspt.RV]: def l_moment_gof( - rv_or_cdf: lspt.RV | lspt.RVFrozen | _Fn1, - l_moments: _ArrF8, + rv_or_cdf: lmt.rv_generic | lmt.rv_frozen | _Fn1, + l_moments: _FloatND, n_obs: int, /, - trim: AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Any, ) -> HypothesisTestResult: r""" @@ -303,11 +301,11 @@ def l_moment_gof( def l_stats_gof( - rv_or_cdf: lspt.RV | lspt.RVFrozen | _Fn1, - l_stats: _ArrF8, + rv_or_cdf: lmt.rv_generic | lmt.rv_frozen | _Fn1, + l_stats: _FloatND, n_obs: int, /, - trim: AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwargs: Any, ) -> HypothesisTestResult: """ @@ -367,7 +365,7 @@ def _lm2_bounds_single(r: int, trim: _Tuple2[float]) -> float: _lm2_bounds = cast( - Callable[[AnyOrderND, _Tuple2[float]], _ArrF8], + Callable[[lmt.ToOrderND, _Tuple2[float]], _FloatND], np.vectorize( _lm2_bounds_single, otypes=[float], @@ -379,24 +377,24 @@ def _lm2_bounds_single(r: int, trim: _Tuple2[float]) -> float: @overload def l_moment_bounds( - r: AnyOrderND, + r: lmt.ToOrderND, /, - trim: AnyTrim = ..., + trim: lmt.ToTrim = ..., scale: float = ..., -) -> _ArrF8: ... +) -> _FloatND: ... @overload def l_moment_bounds( - r: AnyOrder, + r: lmt.ToOrder0D, /, - trim: AnyTrim = ..., + trim: lmt.ToTrim = ..., scale: float = ..., ) -> float: ... def l_moment_bounds( - r: AnyOrder | AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: AnyTrim = 0, + trim: lmt.ToTrim = 0, scale: float = 1.0, -) -> float | _ArrF8: +) -> float | _FloatND: r""" Returns the absolute upper bounds $L^{(s,t)}_r$ on L-moments $\lambda^{(s,t)}_r$, proportional to the scale $\sigma_X$ (standard @@ -496,27 +494,27 @@ def l_moment_bounds( @overload def l_ratio_bounds( - r: AnyOrderND, + r: lmt.ToOrderND, /, - trim: AnyTrim = ..., + trim: lmt.ToTrim = ..., *, legacy: bool = ..., -) -> _Tuple2[_ArrF8]: ... +) -> _Tuple2[_FloatND]: ... @overload def l_ratio_bounds( - r: AnyOrder, + r: lmt.ToOrder0D, /, - trim: AnyTrim = ..., + trim: lmt.ToTrim = ..., *, legacy: bool = ..., ) -> _Tuple2[float]: ... def l_ratio_bounds( - r: AnyOrder | AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: AnyTrim = 0, + trim: lmt.ToTrim = 0, *, legacy: bool = False, -) -> _Tuple2[float | _ArrF8]: +) -> _Tuple2[float | _FloatND]: r""" Unlike the standardized product-moments, the L-moment ratio's with \( r \ge 2 \) are bounded above and below. @@ -748,7 +746,7 @@ def rejection_point( def integrand(x: float) -> float: return max(abs(influence_fn(-x)), abs(influence_fn(x))) - def obj(r: _ArrF8) -> float: + def obj(r: _FloatND) -> float: return quad(integrand, r[0], np.inf)[0] from scipy.optimize import minimize @@ -808,7 +806,7 @@ def error_sensitivity( if np.isinf(influence_fn(a)) or np.isinf(influence_fn(b)): return np.inf - def obj(xs: _ArrF8) -> float: + def obj(xs: _FloatND) -> float: return -abs(influence_fn(xs[0])) bounds = None if np.isneginf(a) and np.isposinf(b) else [(a, b)] @@ -881,7 +879,7 @@ def shift_sensitivity( """ - def obj(xs: _ArrF8) -> float: + def obj(xs: _FloatND) -> float: x, y = xs if y == x: return 0 diff --git a/lmo/distributions/__init__.py b/lmo/distributions/__init__.py index 0294fc2f..e7dffb73 100644 --- a/lmo/distributions/__init__.py +++ b/lmo/distributions/__init__.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Final, cast -import lmo.typing.scipy as lspt +import lmo.typing as lmt from . import _lm from ._genlambda import genlambda_gen from ._kumaraswamy import kumaraswamy_gen @@ -32,7 +32,7 @@ [Distributions - Wakeby](../distributions.md#wakeby). """ else: - wakeby: Final = cast(lspt.RVContinuous, wakeby_gen(a=0.0, name="wakeby")) + wakeby: Final = cast(lmt.rv_continuous, wakeby_gen(a=0.0, name="wakeby")) # mkdocstring workaround if not TYPE_CHECKING: @@ -57,7 +57,7 @@ """ else: kumaraswamy: Final = cast( - lspt.RVContinuous, + lmt.rv_continuous, kumaraswamy_gen(a=0.0, b=1.0, name="kumaraswamy"), ) @@ -77,4 +77,4 @@ [Distributions - GLD](../distributions.md#gld). """ else: - genlambda: Final = cast(lspt.RVContinuous, genlambda_gen(name="genlambda")) + genlambda: Final = cast(lmt.rv_continuous, genlambda_gen(name="genlambda")) diff --git a/lmo/distributions/_genlambda.py b/lmo/distributions/_genlambda.py index c7e2a59e..0fe3c836 100644 --- a/lmo/distributions/_genlambda.py +++ b/lmo/distributions/_genlambda.py @@ -7,26 +7,23 @@ import functools import math import sys -from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar +from typing import Final, TypeAlias, TypeVar import numpy as np import numpy.typing as npt import scipy.special as sps from scipy.stats.distributions import rv_continuous +import lmo.typing as lmt from lmo.special import harmonic from lmo.theoretical import entropy_from_qdf, l_moment_from_ppf from ._lm import get_lm_func -from ._utils import ShapeInfo if sys.version_info >= (3, 13): from typing import override else: from typing_extensions import override -if TYPE_CHECKING: - import lmo.typing.scipy as lspt - __all__ = ("genlambda_gen",) @@ -141,10 +138,10 @@ def _argcheck(self, /, b: _F8, d: _F8, f: _F8) -> np.bool_: # pyright: ignore[r return np.isfinite(b) & np.isfinite(d) & (f >= -1) & (f <= 1) @override - def _shape_info(self, /) -> list[ShapeInfo]: - ibeta = ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) - idelta = ShapeInfo("d", False, (-np.inf, np.inf), (False, False)) - iphi = ShapeInfo("f", False, (-1, 1), (True, True)) + def _shape_info(self, /) -> list[lmt.ShapeInfo]: + ibeta = lmt.ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) + idelta = lmt.ShapeInfo("d", False, (-np.inf, np.inf), (False, False)) + iphi = lmt.ShapeInfo("f", False, (-1, 1), (True, True)) return [ibeta, idelta, iphi] @override @@ -239,7 +236,7 @@ def _l_moment( f: float, *, trim: tuple[int, int] | tuple[float, float], - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, ) -> _ArrF8: s, t = trim diff --git a/lmo/distributions/_kumaraswamy.py b/lmo/distributions/_kumaraswamy.py index 727aefea..8795629c 100644 --- a/lmo/distributions/_kumaraswamy.py +++ b/lmo/distributions/_kumaraswamy.py @@ -8,11 +8,10 @@ import numpy.typing as npt import optype.numpy as onp import scipy.special as sc -from scipy.stats.distributions import rv_continuous +import lmo.typing as lmt from lmo.special import harmonic from ._lm import get_lm_func -from ._utils import ShapeInfo if sys.version_info >= (3, 13): from typing import override @@ -21,9 +20,10 @@ __all__ = ("kumaraswamy_gen",) -_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] -_XT = TypeVar("_XT", float | np.float64, _ArrF8) +_FloatND: TypeAlias = onp.ArrayND[np.float64] + +_XT = TypeVar("_XT", float | np.float64, _FloatND) _lm_kumaraswamy = get_lm_func("kumaraswamy") @@ -33,15 +33,15 @@ @final -class kumaraswamy_gen(rv_continuous): +class kumaraswamy_gen(lmt.rv_continuous): @override def _argcheck(self, /, a: float, b: float) -> bool | np.bool_: return (a > 0) & (b > 0) @override - def _shape_info(self, /) -> list[ShapeInfo]: - ia = ShapeInfo("a", False, (0, np.inf), (False, False)) - ib = ShapeInfo("b", False, (0, np.inf), (False, False)) + def _shape_info(self, /) -> list[lmt.ShapeInfo]: + ia = lmt.ShapeInfo("a", False, (0, np.inf), (False, False)) + ib = lmt.ShapeInfo("b", False, (0, np.inf), (False, False)) return [ia, ib] @override @@ -81,5 +81,5 @@ def _entropy(self, a: float, b: float) -> float: return (1 - 1 / b) + (1 - 1 / a) * harmonic(b) - math.log(a * b) @override - def _munp(self, /, n: int | npt.NDArray[np.intp], a: float, b: float) -> _ArrF8: + def _munp(self, /, n: int | npt.NDArray[np.intp], a: float, b: float) -> _FloatND: return b * sc.beta(1 + n / a, b) diff --git a/lmo/distributions/_lm.py b/lmo/distributions/_lm.py index d2fd1f58..65a48013 100644 --- a/lmo/distributions/_lm.py +++ b/lmo/distributions/_lm.py @@ -11,24 +11,13 @@ import sys from collections.abc import Callable from math import gamma, log -from typing import ( - TYPE_CHECKING, - Concatenate, - Final, - Literal, - ParamSpec, - Protocol, - TypeAlias, - TypeVar, - cast, - overload, -) +from typing import Any, Concatenate, Final, Literal, ParamSpec, TypeAlias, cast import numpy as np -import numpy.typing as npt import optype.numpy as onp import scipy.special as sps +import lmo.typing as lmt from lmo.special import harmonic from lmo.theoretical import l_moment_from_ppf @@ -37,9 +26,6 @@ else: from typing_extensions import TypeIs -if TYPE_CHECKING: - import lmo.typing.np as lnpt - __all__ = [ "lm_expon", @@ -95,56 +81,27 @@ "weibull_max", } -_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] +_ArrF8: TypeAlias = onp.ArrayND[np.float64] _Tss = ParamSpec("_Tss") # (r, s, t, *params) -> float -_LmFunc: TypeAlias = Callable[ - Concatenate[int, float, float, _Tss], - float | np.float64, -] - -_ShapeT = TypeVar("_ShapeT", bound=tuple[int, ...]) - +_LmFunc: TypeAlias = Callable[Concatenate[int, float, float, _Tss], float | np.float64] _LN2: Final = np.log(2) _LN3: Final = np.log(3) _LN5: Final = np.log(5) -# workaround for partial type annotations (i.e. missing and un-inferrable) -_binom = cast( +_LmVFunc: TypeAlias = lmt.Callable2[ Callable[ - [npt.NDArray[np.intp] | int, npt.NDArray[np.intp] | int], - npt.NDArray[np.intp], + Concatenate[onp.ToIntND, float, float, _Tss], + onp.ArrayND[np.float64], ], - sps.comb, -) - - -class _LmVFunc(Protocol[_Tss]): - pyfunc: _LmFunc[_Tss] - - @overload - def __call__( - self, - r: onp.CanArray[_ShapeT, np.dtype[lnpt.Integral]], - s: float, - t: float, - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, - ) -> onp.Array[_ShapeT, np.float64]: ... - @overload - def __call__( - self, - r: int | lnpt.Integral, - s: float, - t: float, - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, - ) -> onp.Array[tuple[()], np.float64]: ... + Callable[ + Concatenate[onp.ToInt, float, float, _Tss], + onp.Array[tuple[()], np.float64], + ], +] def register_lm_func( @@ -318,7 +275,7 @@ def lm_gumbel_r(r: int, s: float, t: float, /) -> np.float64 | float: return (_LN2 * -107 + _LN3 * 6 + _LN5 * 42) * 5 / 4 case _: - return lm_genextreme.pyfunc(r, s, t, 0) + return cast(Any, lm_genextreme).pyfunc(r, s, t, 0) @register_lm_func("genextreme") @@ -350,8 +307,8 @@ def lm_genextreme(r: int, s: float, t: float, /, a: float) -> np.float64 | float return np.sum( (-1) ** k - * _binom(kn, k) - * _binom(r - 2 + k, r - 2 + k0) + * sps.comb(kn, k) + * sps.comb(r - 2 + k, r - 2 + k0) * pwm, ) * (-1) ** (r + s) / r # fmt: skip @@ -394,9 +351,9 @@ def lm_genpareto(r: int, s: float, t: float, /, a: float) -> np.float64 | float: if r == 0: return 1 if a == 0: - return lm_expon.pyfunc(r, s, t) + return cast(Any, lm_expon).pyfunc(r, s, t) if a == 1: - return lm_uniform.pyfunc(r, s, t) + return cast(Any, lm_uniform).pyfunc(r, s, t) if not isinstance(t, int): msg = "fractional trimming" diff --git a/lmo/distributions/_nonparametric.py b/lmo/distributions/_nonparametric.py index 050f3e5f..666ef667 100644 --- a/lmo/distributions/_nonparametric.py +++ b/lmo/distributions/_nonparametric.py @@ -17,9 +17,9 @@ import numpy as np import numpy.typing as npt +import optype.numpy as onp import lmo.typing as lmt -import lmo.typing.np as lnpt from lmo._utils import clean_trim, l_stats_orders, moments_to_ratio, round0 from lmo.theoretical import ( cdf_from_ppf, @@ -49,12 +49,7 @@ def __call__(self, x: _T_x, /) -> _T_x: ... _Trim: TypeAlias = tuple[int, int] | tuple[float, float] _MomentType: TypeAlias = Literal[0, 1] -_LPolyParams: TypeAlias = ( - tuple[lnpt.AnyVectorFloat] | tuple[lnpt.AnyVectorFloat, lmt.AnyTrim] -) - -_AnyReal0D: TypeAlias = float | np.bool_ | lnpt.Int | lnpt.Float -_AnyRealND: TypeAlias = lnpt.AnyArrayInt | lnpt.AnyArrayFloat +_LPolyParams: TypeAlias = tuple[onp.ToFloat1D] | tuple[onp.ToFloat1D, lmt.ToTrim] _Stats0: TypeAlias = Literal[""] _Stats1: TypeAlias = Literal["m", "v", "s", "k"] @@ -71,7 +66,7 @@ def __call__(self, x: _T_x, /) -> _T_x: ... _Tuple4m: TypeAlias = tuple[()] | _Tuple1[_T] | _Tuple2[_T] | _Tuple3[_T] | _Tuple4[_T] -def _get_rng(s: lnpt.Seed | None = None, /) -> np.random.Generator: +def _get_rng(s: lmt.Seed | None = None, /) -> np.random.Generator: return s if isinstance(s, np.random.Generator) else np.random.default_rng(s) @@ -98,11 +93,11 @@ class l_poly: # noqa: N801 def __init__( self, - lmbda: lnpt.AnyVectorFloat, + lmbda: onp.ToFloat1D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, - seed: lnpt.Seed | None = None, + seed: lmt.Seed | None = None, ) -> None: r""" Create a new `l_poly` instance. @@ -154,16 +149,16 @@ def random_state(self, /) -> np.random.Generator: return self._random_state @random_state.setter - def random_state(self, seed: lnpt.Seed, /) -> None: # pyright: ignore[reportPropertyTypeMismatch] + def random_state(self, seed: lmt.Seed, /) -> None: # pyright: ignore[reportPropertyTypeMismatch] self._random_state = _get_rng(seed) @classmethod def fit( cls, - data: _AnyRealND, + data: onp.ToFloatND, /, moments: int | None = None, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, ) -> Self: r""" Fit distribution using the (trimmed) L-moment estimates of the given @@ -217,20 +212,20 @@ def rvs( self, /, size: Literal[1] | None = None, - random_state: lnpt.Seed | None = None, + random_state: lmt.Seed | None = None, ) -> _F8: ... @overload def rvs( self, /, size: int | tuple[int, ...], - random_state: lnpt.Seed | None = None, + random_state: lmt.Seed | None = None, ) -> _ArrF8: ... def rvs( self, /, size: int | tuple[int, ...] | None = None, - random_state: lnpt.Seed | None = None, + random_state: lmt.Seed | None = None, ) -> _F8 | _ArrF8: """ Draw random variates from the relevant distribution. @@ -498,12 +493,12 @@ def support(self, /) -> tuple[_F8, _F8]: return self._support @overload - def interval(self, confidence: _AnyReal0D, /) -> _Tuple2[_F8]: ... + def interval(self, confidence: onp.ToFloat, /) -> _Tuple2[_F8]: ... @overload - def interval(self, confidence: _AnyRealND, /) -> _Tuple2[_ArrF8]: ... + def interval(self, confidence: onp.ToFloatND, /) -> _Tuple2[_ArrF8]: ... def interval( self, - confidence: _AnyReal0D | _AnyRealND, + confidence: onp.ToFloat | onp.ToFloatND, /, ) -> _Tuple2[_F8] | _Tuple2[_ArrF8]: r""" @@ -651,19 +646,19 @@ def i(u: float, /) -> float: return quad(i, a, b)[0] + quad(i, b, c)[0] + quad(i, c, d)[0] @overload - def l_moment(self, r: lmt.AnyOrder, /, trim: lmt.AnyTrim | None = None) -> _F8: ... + def l_moment(self, r: lmt.ToOrder0D, /, trim: lmt.ToTrim | None = None) -> _F8: ... @overload def l_moment( self, - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _ArrF8: ... def l_moment( self, - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _F8 | _ArrF8: r""" Evaluate the population L-moment(s) $\lambda^{(s,t)}_r$. @@ -682,33 +677,33 @@ def l_moment( @overload def l_ratio( self, - r: lmt.AnyOrder, - k: lmt.AnyOrder, + r: lmt.ToOrder0D, + k: lmt.ToOrder0D, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _F8: ... @overload def l_ratio( self, - r: lmt.AnyOrderND, - k: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrderND, + k: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _ArrF8: ... @overload def l_ratio( self, - r: lmt.AnyOrder | lmt.AnyOrderND, - k: lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + k: lmt.ToOrderND, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _ArrF8: ... def l_ratio( self, - r: lmt.AnyOrder | lmt.AnyOrderND, - k: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + k: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim | None = None, + trim: lmt.ToTrim | None = None, ) -> _F8 | _ArrF8: r""" Evaluate the population L-moment ratio('s) $\tau^{(s,t)}_{r,k}$. @@ -727,7 +722,7 @@ def l_ratio( lms = self.l_moment(rs, trim=trim) return moments_to_ratio(rs, lms) - def l_stats(self, /, trim: lmt.AnyTrim | None = None, moments: int = 4) -> _ArrF8: + def l_stats(self, /, trim: lmt.ToTrim | None = None, moments: int = 4) -> _ArrF8: r""" Evaluate the L-moments (for $r \le 2$) and L-ratio's (for $r > 2$). @@ -741,7 +736,7 @@ def l_stats(self, /, trim: lmt.AnyTrim | None = None, moments: int = 4) -> _ArrF r, s = l_stats_orders(moments) return self.l_ratio(r, s, trim=trim) - def l_loc(self, /, trim: lmt.AnyTrim | None = None) -> float: + def l_loc(self, /, trim: lmt.ToTrim | None = None) -> float: """ L-location of the distribution, i.e. the 1st L-moment. @@ -752,7 +747,7 @@ def l_loc(self, /, trim: lmt.AnyTrim | None = None) -> float: """ return float(self.l_moment(1, trim=trim)) - def l_scale(self, /, trim: lmt.AnyTrim | None = None) -> float: + def l_scale(self, /, trim: lmt.ToTrim | None = None) -> float: """ L-scale of the distribution, i.e. the 2nd L-moment. @@ -763,7 +758,7 @@ def l_scale(self, /, trim: lmt.AnyTrim | None = None) -> float: """ return float(self.l_moment(2, trim=trim)) - def l_skew(self, /, trim: lmt.AnyTrim | None = None) -> float: + def l_skew(self, /, trim: lmt.ToTrim | None = None) -> float: """L-skewness coefficient of the distribution; the 3rd L-moment ratio. Alias for `l_poly.l_ratio(3, 2, ...)`. @@ -773,7 +768,7 @@ def l_skew(self, /, trim: lmt.AnyTrim | None = None) -> float: """ return float(self.l_ratio(3, 2, trim=trim)) - def l_kurtosis(self, /, trim: lmt.AnyTrim | None = None) -> float: + def l_kurtosis(self, /, trim: lmt.ToTrim | None = None) -> float: """L-kurtosis coefficient of the distribution; the 4th L-moment ratio. Alias for `l_poly.l_ratio(4, 2, ...)`. @@ -802,9 +797,9 @@ def kwds(self, /) -> dict[str, object]: @classmethod def freeze( cls, - lmbda: lnpt.AnyVectorFloat, + lmbda: onp.ToFloat1D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, **kwds: Any, ) -> Self: return cls(lmbda, trim, **kwds) diff --git a/lmo/distributions/_utils.py b/lmo/distributions/_utils.py deleted file mode 100644 index f35a0c08..00000000 --- a/lmo/distributions/_utils.py +++ /dev/null @@ -1,5 +0,0 @@ -from scipy.stats._distn_infrastructure import ( - _ShapeInfo as ShapeInfo, # noqa: PLC2701 # pyright: ignore[reportPrivateUsage] -) - -__all__ = ("ShapeInfo",) diff --git a/lmo/distributions/_wakeby.py b/lmo/distributions/_wakeby.py index cdfd924a..811aa04d 100644 --- a/lmo/distributions/_wakeby.py +++ b/lmo/distributions/_wakeby.py @@ -8,10 +8,9 @@ import numpy as np import optype.numpy as onp import scipy.special as sps -from scipy.stats.distributions import rv_continuous +import lmo.typing as lmt from ._lm import get_lm_func -from ._utils import ShapeInfo if sys.version_info >= (3, 13): from typing import override @@ -25,7 +24,7 @@ # NOTE: this is equivalent to `float` IFF `numpy >= 2.2`, see: # https://github.com/numpy/numpy/pull/27334 _F8: TypeAlias = float | np.float64 -_ArrF8: TypeAlias = onp.Array[tuple[int, ...], np.float64] +_ArrF8: TypeAlias = onp.ArrayND[np.float64] _XT = TypeVar("_XT", _F8, _ArrF8) @@ -174,7 +173,7 @@ def _wakeby_sf0(x: _F8, /, b: _F8, d: _F8, f: _F8) -> _F8: # noqa: C901 _wakeby_lm: Final = get_lm_func("wakeby") -class wakeby_gen(rv_continuous): +class wakeby_gen(lmt.rv_continuous): @override def _argcheck(self, /, b: _F8, d: _F8, f: _F8) -> bool | np.bool_: # pyright: ignore[reportIncompatibleMethodOverride] return ( @@ -189,10 +188,10 @@ def _argcheck(self, /, b: _F8, d: _F8, f: _F8) -> bool | np.bool_: # pyright: i ) @override - def _shape_info(self) -> list[ShapeInfo]: - ibeta = ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) - idelta = ShapeInfo("d", False, (-np.inf, np.inf), (False, False)) - iphi = ShapeInfo("f", False, (0, 1), (True, True)) + def _shape_info(self) -> list[lmt.ShapeInfo]: + ibeta = lmt.ShapeInfo("b", False, (-np.inf, np.inf), (False, False)) + idelta = lmt.ShapeInfo("d", False, (-np.inf, np.inf), (False, False)) + iphi = lmt.ShapeInfo("f", False, (0, 1), (True, True)) return [ibeta, idelta, iphi] @override diff --git a/lmo/inference/_l_gmm.py b/lmo/inference/_l_gmm.py index db57fb7c..7833db38 100644 --- a/lmo/inference/_l_gmm.py +++ b/lmo/inference/_l_gmm.py @@ -27,7 +27,6 @@ import optype.numpy as onp import lmo.typing as lmt - import lmo.typing.np as lnpt __all__ = "GMMResult", "fit" @@ -165,7 +164,7 @@ def _loss_step( l_fn: Callable[..., _ArrF8], r: npt.NDArray[np.intp], l_r: _ArrF8, - trim: lmt.AnyTrim, + trim: lmt.ToTrim, w_rr: _ArrF8, ) -> np.float64: """ @@ -187,8 +186,8 @@ def _loss_step( return np.sqrt(g_r.T @ w_rr @ g_r) # pyright: ignore[reportReturnType] -def _get_l_moment_fn(ppf: _Fn1) -> Callable[Concatenate[lmt.AnyOrderND, ...], _ArrF8]: - def l_moment_fn(r: lmt.AnyOrderND, /, *args: Any, trim: lmt.AnyTrim = 0) -> _ArrF8: +def _get_l_moment_fn(ppf: _Fn1) -> Callable[Concatenate[lmt.ToOrderND, ...], _ArrF8]: + def l_moment_fn(r: lmt.ToOrderND, /, *args: Any, trim: lmt.ToTrim = 0) -> _ArrF8: def _ppf(q: _T_x, /) -> _T_x: return ppf(q, *args) @@ -230,9 +229,7 @@ def _get_weights_mc( return np.linalg.pinv(l_rr) -def _ensure_1d_f8( - arr: lnpt.AnyVectorFloat, -) -> onp.Array[tuple[int], np.float64]: +def _ensure_1d_f8(arr: onp.ToFloat1D) -> onp.Array1D[np.float64]: out = np.asarray_chkfinite(arr) if out.ndim != 1: err = f"expected 1D array, got {out.shape}" @@ -242,10 +239,10 @@ def _ensure_1d_f8( def fit( # noqa: C901 ppf: _Fn1, - args0: lnpt.AnyVectorFloat, + args0: onp.ToFloat1D, n_obs: int, - l_moments: lnpt.AnyVectorFloat, - r: lmt.AnyOrderND | None = None, + l_moments: onp.ToFloat1D, + r: lmt.ToOrderND | None = None, trim: int | tuple[int, int] = 0, *, k: int | None = None, @@ -253,7 +250,7 @@ def fit( # noqa: C901 l_tol: float = 1e-4, l_moment_fn: Callable[..., _ArrF8] | None = None, n_mc_samples: int = 9999, - random_state: lnpt.Seed | None = None, + random_state: lmt.Seed | None = None, **kwds: Any, ) -> GMMResult: r""" diff --git a/lmo/linalg.py b/lmo/linalg.py index bbef9178..3e52b98c 100644 --- a/lmo/linalg.py +++ b/lmo/linalg.py @@ -5,19 +5,18 @@ import sys from math import comb, lgamma -from typing import Any, TypeAlias, assert_never, cast +from typing import TYPE_CHECKING, Any, TypeAlias, assert_never import numpy as np -import numpy.typing as npt -import optype.numpy as onp - -import lmo.typing.np as lnpt if sys.version_info >= (3, 13): from typing import TypeVar else: from typing_extensions import TypeVar +if TYPE_CHECKING: + import optype.numpy as onp + __all__ = ( "ir_pascal", @@ -29,15 +28,19 @@ "trim_matrix", ) + _T = TypeVar("_T", bound=np.generic) _TF = TypeVar("_TF", bound=np.floating[Any], default=np.float64) -_TI = TypeVar("_TI", bound=lnpt.Real | np.object_, default=np.int64) - +_TI = TypeVar( + "_TI", + bound=np.floating[Any] | np.integer[Any] | np.object_, + default=np.int64, +) _K = TypeVar("_K", bound=int) _R = TypeVar("_R", bound=int) _DType: TypeAlias = np.dtype[_T] | type[_T] -_Square: TypeAlias = onp.Array[tuple[_K, _K], _T] +_Square: TypeAlias = np.ndarray[tuple[_K, _K], np.dtype[_T]] def sandwich( @@ -454,4 +457,4 @@ def trim_matrix( case _ as wtf: # pyright: ignore[reportUnnecessaryComparison] assert_never(wtf) - return cast(npt.NDArray[_TF], out) + return out # pyright: ignore[reportReturnType] diff --git a/lmo/ostats.py b/lmo/ostats.py index e89d5dcd..87d929ba 100644 --- a/lmo/ostats.py +++ b/lmo/ostats.py @@ -12,55 +12,51 @@ from __future__ import annotations import functools -from math import floor -from typing import TYPE_CHECKING, TypeVar, overload +from math import floor, log1p +from typing import TYPE_CHECKING, Any, TypeAlias, overload import numpy as np from scipy.special import betainc, betaln if TYPE_CHECKING: + import optype as op import optype.numpy as onp - import lmo.typing.np as lnpt - __all__ = "from_cdf", "weights" -_T_size = TypeVar("_T_size", bound=int) +_ToReal: TypeAlias = float | np.floating[Any] | np.integer[Any] -def _weights( - i: float | lnpt.Float, - n: float | lnpt.Float, - N: _T_size, - /, -) -> onp.Array[tuple[_T_size], np.float64]: +def _weights(i: float, n: float, N: int, /) -> onp.Array1D[np.float64]: assert 0 <= i < n <= N - j = np.arange(floor(i), N) - return np.r_[ - np.zeros(j[0]), - np.exp( - betaln(j + 1, N - j) - - betaln(i + 1, n - i) - - betaln(j - i + 1, N - j - (n - i) + 1) - - np.log(N - n + 1), - ), - ] + out = np.zeros(N) + + i0 = floor(i) + j = np.arange(i0, N) + + out[i0:] = np.exp( + betaln(j + 1, N - j) + - betaln(i + 1, n - i) + - betaln(j - i + 1, N - j - (n - i) + 1) + - log1p(N - n), + ) + return out _weights_cached = functools.lru_cache(1 << 10)(_weights) def weights( - i: float | lnpt.Float, - n: float | lnpt.Float, - N: _T_size, + i: _ToReal, + n: _ToReal, + N: op.CanIndex, /, *, cached: bool = False, -) -> onp.Array[tuple[_T_size], np.float64]: +) -> onp.Array1D[np.float64]: r""" Compute the linear weights $w_{i:n|j:N}$ for $j = 0, \dots, N-1$. @@ -105,37 +101,37 @@ def weights( 1d array of size $N$ with (ordered) sample weights. """ - if i < 0: + _i = int(i) if float(i).is_integer() else float(i) + _n = int(n) if float(n).is_integer() else float(n) + _N = int(N) # noqa: N806 + + if _i < 0: # negative indexing - i = n + i + _i += _n - if (i, n) == (0, 1): + if (_i, _n) == (0, 1): # identity case - return np.full(N, 1 / N) - if i >= n: + return np.full(_N, 1 / _N) + if _i >= _n: # impossible case - return np.full(N, np.nan) + return np.full(_N, np.nan) _fn = _weights_cached if cached else _weights - # this return type incosnsitency is due to the first `np.ndarray` type - # parameter not being covariant, which is incorrect, but is being worked on - return _fn(i, n, N) + return _fn(_i, _n, _N) @overload -def from_cdf(F: lnpt.AnyScalarFloat, i: float, n: float) -> np.float64: ... +def from_cdf(F: onp.ToFloat, i: _ToReal, n: _ToReal) -> np.float64: ... @overload def from_cdf( - F: lnpt.AnyArrayFloat, - i: float, - n: float, + F: onp.ToFloatND, + i: _ToReal, + n: _ToReal, ) -> onp.Array[onp.AtLeast1D, np.float64]: ... - - def from_cdf( - F: lnpt.AnyScalarFloat | lnpt.AnyArrayFloat, - i: float, - n: float, + F: onp.ToFloat | onp.ToFloatND, + i: _ToReal, + n: _ToReal, ) -> np.float64 | onp.Array[onp.AtLeast1D, np.float64]: r""" Transform $F(X)$ to $F_{i:n}(X)$, of the $i$th variate within subsamples diff --git a/lmo/pwm_beta.py b/lmo/pwm_beta.py index 90d3c778..52e6c252 100644 --- a/lmo/pwm_beta.py +++ b/lmo/pwm_beta.py @@ -7,42 +7,28 @@ from __future__ import annotations -import sys -from typing import TYPE_CHECKING, Any, TypeAlias, cast, overload +from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar, Unpack, cast, overload import numpy as np import numpy.typing as npt from ._utils import ordered -if sys.version_info >= (3, 13): - from typing import TypeVar -else: - from typing_extensions import TypeVar - - if TYPE_CHECKING: import optype.numpy as onp - import lmo.typing.np as lnpt + import lmo.typing as lmt __all__ = "cov", "weights" -_F = TypeVar("_F", bound=np.floating[Any], default=np.float64) -_R = TypeVar("_R", bound=int) -_N = TypeVar("_N", bound=int) +_F = TypeVar("_F", bound=np.floating[Any]) _DType: TypeAlias = np.dtype[_F] | type[_F] -def weights( - r: _R, - n: _N, - /, - dtype: _DType[_F] = np.float64, -) -> onp.Array[tuple[_R, _N], _F]: +def weights(r: int, n: int, /, dtype: _DType[_F] = np.float64) -> onp.Array2D[_F]: r""" Probability Weighted moment (PWM) projection matrix $B$ of the unbiased estimator for $\beta_k = M_{1,k,0}$ for $k = 0, \dots, r - 1$. @@ -88,30 +74,30 @@ def weights( @overload def cov( - a: lnpt.AnyArrayFloat, - r: _R, + a: onp.ToFloatND, + r: int, /, - axis: None = ..., + axis: None = None, dtype: _DType[_F] = np.float64, - **kwds: Any, -) -> onp.Array[tuple[_R, _R], _F]: ... + **kwds: Unpack[lmt.UnivariateOptions], +) -> onp.Array2D[_F]: ... @overload def cov( - a: lnpt.AnyArrayFloat, - r: _R, + a: onp.ToFloatND, + r: int, /, axis: int, dtype: _DType[_F] = np.float64, - **kwds: Any, -) -> onp.Array[tuple[_R, _R, *tuple[int, ...]], _F]: ... + **kwds: Unpack[lmt.UnivariateOptions], +) -> onp.Array[onp.AtLeast2D, _F]: ... def cov( - a: lnpt.AnyArrayFloat, + a: onp.ToFloatND, r: int, /, axis: int | None = None, dtype: _DType[_F] = np.float64, - **kwds: Any, -) -> onp.Array[Any, _F]: + **kwds: Unpack[lmt.UnivariateOptions], +) -> onp.ArrayND[_F]: r""" Distribution-free variance-covariance matrix of the probability weighted moment (PWM) point estimates $\beta_k = M_{1,k,0}$, with orders diff --git a/lmo/special.py b/lmo/special.py index 766ff88a..7ca99ea4 100644 --- a/lmo/special.py +++ b/lmo/special.py @@ -14,7 +14,7 @@ from ._utils import clean_orders if TYPE_CHECKING: - from lmo.typing import AnyOrder, AnyOrderND + from lmo.typing import ToOrder0D, ToOrderND if sys.version_info >= (3, 13): from typing import Protocol, TypeVar, runtime_checkable @@ -174,10 +174,10 @@ def harmonic( @overload -def norm_sh_jacobi(n: AnyOrder, alpha: float, beta: float) -> _F8: ... +def norm_sh_jacobi(n: ToOrder0D, alpha: float, beta: float) -> _F8: ... @overload -def norm_sh_jacobi(n: AnyOrderND, alpha: float, beta: float) -> _F8ND: ... -def norm_sh_jacobi(n: AnyOrder | AnyOrderND, alpha: float, beta: float) -> _F8 | _F8ND: +def norm_sh_jacobi(n: ToOrderND, alpha: float, beta: float) -> _F8ND: ... +def norm_sh_jacobi(n: ToOrder0D | ToOrderND, alpha: float, beta: float) -> _F8 | _F8ND: r""" Evaluate the (weighted) \( L^2 \)-norm of a shifted Jacobi polynomial. diff --git a/lmo/theoretical/_f_to_f.py b/lmo/theoretical/_f_to_f.py index 4ac1cba8..ffa1ae7b 100644 --- a/lmo/theoretical/_f_to_f.py +++ b/lmo/theoretical/_f_to_f.py @@ -1,14 +1,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Concatenate, ParamSpec +from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec import numpy as np if TYPE_CHECKING: from collections.abc import Callable - import lmo.typing.np as lnpt - __all__: list[str] = ["cdf_from_ppf"] @@ -17,7 +15,7 @@ def cdf_from_ppf( - ppf: Callable[Concatenate[float, _Tss], lnpt.Float | float], + ppf: Callable[Concatenate[float, _Tss], float | np.floating[Any]], /, ) -> Callable[Concatenate[float, _Tss], float]: """ @@ -36,7 +34,7 @@ def cdf(x: float, /, *args: _Tss.args, **kwds: _Tss.kwargs) -> float: if x >= ppf(1, *args, **kwds): return 1 - def _ppf_to_solve(p: float) -> lnpt.Float | float: + def _ppf_to_solve(p: float, /) -> float | np.floating[Any]: return ppf(p, *args, **kwds) - x result = root_scalar(_ppf_to_solve, bracket=[0, 1], method="brentq") diff --git a/lmo/theoretical/_f_to_h.py b/lmo/theoretical/_f_to_h.py index c948c123..5514a742 100644 --- a/lmo/theoretical/_f_to_h.py +++ b/lmo/theoretical/_f_to_h.py @@ -21,10 +21,16 @@ def entropy_from_qdf(qdf: _QDF[[]], /) -> float: ... @overload def entropy_from_qdf( - qdf: _QDF[_Tss], /, *args: _Tss.args, **kwds: _Tss.kwargs + qdf: _QDF[_Tss], + /, + *args: _Tss.args, + **kwds: _Tss.kwargs, ) -> float: ... def entropy_from_qdf( - qdf: _QDF[_Tss], /, *args: _Tss.args, **kwds: _Tss.kwargs + qdf: _QDF[_Tss], + /, + *args: _Tss.args, + **kwds: _Tss.kwargs, ) -> float: r""" Evaluate the (differential / continuous) entropy \( H(X) \) of a diff --git a/lmo/theoretical/_f_to_lcm.py b/lmo/theoretical/_f_to_lcm.py index 2c558442..12066d7e 100644 --- a/lmo/theoretical/_f_to_lcm.py +++ b/lmo/theoretical/_f_to_lcm.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Protocol, TypeAlias, TypeVar import numpy as np -import numpy.typing as npt from lmo._poly import eval_sh_jacobi from lmo._utils import clean_order, clean_trim, round0 @@ -17,33 +16,33 @@ from collections.abc import Callable, Sequence import lmo.typing as lmt - import lmo.typing.scipy as lspt __all__ = ["l_comoment_from_pdf", "l_coratio_from_pdf"] + _T = TypeVar("_T") -_T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) +_Pair: TypeAlias = tuple[_T, _T] +_FloatND: TypeAlias = np.ndarray[tuple[int, ...], np.dtype[np.float64]] -class _Fn1(Protocol): - def __call__(self, x: _T_x, /) -> _T_x: ... +_AnyFloatND = TypeVar("_AnyFloatND", float, _FloatND) -_Pair: TypeAlias = tuple[_T, _T] -_ArrF8: TypeAlias = npt.NDArray[np.float64] +class _Fn1(Protocol): + def __call__(self, x: _AnyFloatND, /) -> _AnyFloatND: ... def l_comoment_from_pdf( - pdf: Callable[[_ArrF8], float] | Callable[[_ArrF8], np.float64], + pdf: Callable[[_FloatND], float] | Callable[[_FloatND], np.float64], cdfs: Sequence[_Fn1], - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, supports: Sequence[_Pair[float]] | None = None, - quad_opts: lspt.QuadOptions | None = None, -) -> _ArrF8: + quad_opts: lmt.QuadOptions | None = None, +) -> _FloatND: r""" Evaluate the theoretical L-*co*moment matrix of a multivariate probability distribution, using the joint PDF @@ -247,16 +246,16 @@ def integrand(*xs: float, i: int, j: int) -> float: def l_coratio_from_pdf( - pdf: Callable[[_ArrF8], float] | Callable[[_ArrF8], np.float64], + pdf: Callable[[_FloatND], float] | Callable[[_FloatND], np.float64], cdfs: Sequence[_Fn1], - r: lmt.AnyOrder, - r0: lmt.AnyOrder = 2, + r: lmt.ToOrder0D, + r0: lmt.ToOrder0D = 2, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, supports: Sequence[_Pair[float]] | None = None, - quad_opts: lspt.QuadOptions | None = None, -) -> _ArrF8: + quad_opts: lmt.QuadOptions | None = None, +) -> _FloatND: r""" Evaluate the theoretical L-*co*moment ratio matrix of a multivariate probability distribution, using the joint PDF $f_{\vec{X}}(\vec{x})$ and diff --git a/lmo/theoretical/_f_to_lm.py b/lmo/theoretical/_f_to_lm.py index 5ea2e487..a7139b4a 100644 --- a/lmo/theoretical/_f_to_lm.py +++ b/lmo/theoretical/_f_to_lm.py @@ -19,7 +19,6 @@ from collections.abc import Callable import lmo.typing as lmt - import lmo.typing.scipy as lspt __all__ = [ @@ -53,7 +52,7 @@ def _df_quad3( c: float | np.float64, d: float | np.float64, r: int, - **kwds: Unpack[lspt.QuadOptions], + **kwds: Unpack[lmt.QuadOptions], ) -> float: import scipy.integrate as spi @@ -69,35 +68,35 @@ def _df_quad3( @overload def l_moment_from_cdf( cdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] | None = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ppf: _Fn1 | None = ..., ) -> _ArrF8: ... @overload def l_moment_from_cdf( cdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] | None = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ppf: _Fn1 | None = ..., ) -> np.float64: ... def l_moment_from_cdf( cdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ppf: _Fn1 | None = None, ) -> np.float64 | _ArrF8: @@ -268,33 +267,33 @@ def _l_moment_single(_r: int) -> float: @overload def l_moment_from_ppf( ppf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> _ArrF8: ... @overload def l_moment_from_ppf( ppf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> np.float64: ... def l_moment_from_ppf( ppf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] = (0, 1), - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ) -> np.float64 | _ArrF8: r""" @@ -420,33 +419,33 @@ def _l_moment_single(_r: int) -> float: @overload def l_moment_from_qdf( qdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrderND, + r: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> _ArrF8: ... @overload def l_moment_from_qdf( qdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> np.float64: ... def l_moment_from_qdf( qdf: _Fn1 | Callable[[float], float], - r: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] = (0, 1), - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ) -> np.float64 | _ArrF8: r""" @@ -479,50 +478,50 @@ def l_moment_from_qdf( @overload def l_ratio_from_cdf( cdf: _Fn1, - r: lmt.AnyOrderND, - s: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] | None = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ppf: _Fn1 | None = ..., ) -> _ArrF8: ... @overload def l_ratio_from_cdf( cdf: _Fn1, - r: lmt.AnyOrder | lmt.AnyOrderND, - s: lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] | None = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ppf: _Fn1 | None = ..., ) -> _ArrF8: ... @overload def l_ratio_from_cdf( cdf: _Fn1, - r: lmt.AnyOrder, - s: lmt.AnyOrder, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] | None = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> np.float64: ... def l_ratio_from_cdf( cdf: _Fn1, - r: lmt.AnyOrder | lmt.AnyOrderND, - s: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ppf: _Fn1 | None = None, ) -> np.float64 | _ArrF8: @@ -549,48 +548,48 @@ def l_ratio_from_cdf( @overload def l_ratio_from_ppf( ppf: _Fn1, - r: lmt.AnyOrderND, - s: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> _ArrF8: ... @overload def l_ratio_from_ppf( ppf: _Fn1, - r: lmt.AnyOrder | lmt.AnyOrderND, - s: lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrderND, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> _ArrF8: ... @overload def l_ratio_from_ppf( ppf: _Fn1, - r: lmt.AnyOrder, - s: lmt.AnyOrder, + r: lmt.ToOrder0D, + s: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = ..., + trim: lmt.ToTrim = ..., *, support: _Pair[float] = ..., - quad_opts: lspt.QuadOptions | None = ..., + quad_opts: lmt.QuadOptions | None = ..., alpha: float = ..., ) -> np.float64: ... def l_ratio_from_ppf( ppf: _Fn1, - r: lmt.AnyOrder | lmt.AnyOrderND, - s: lmt.AnyOrder | lmt.AnyOrderND, + r: lmt.ToOrder0D | lmt.ToOrderND, + s: lmt.ToOrder0D | lmt.ToOrderND, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] = (0, 1), - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ) -> np.float64 | _ArrF8: """ @@ -616,10 +615,10 @@ def l_stats_from_cdf( cdf: _Fn1, num: int = 4, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ppf: _Fn1 | None = None, ) -> _ArrF8: @@ -664,10 +663,10 @@ def l_stats_from_ppf( ppf: _Fn1, num: int = 4, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] = (0, 1), - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ) -> _ArrF8: r""" diff --git a/lmo/theoretical/_f_to_lm_cov.py b/lmo/theoretical/_f_to_lm_cov.py index 8daf2e58..838babaa 100644 --- a/lmo/theoretical/_f_to_lm_cov.py +++ b/lmo/theoretical/_f_to_lm_cov.py @@ -13,7 +13,6 @@ if TYPE_CHECKING: import lmo.typing as lmt - import lmo.typing.scipy as lspt __all__ = ["l_moment_cov_from_cdf", "l_stats_cov_from_cdf"] @@ -33,12 +32,12 @@ def __call__(self, x: _T_x, /) -> _T_x: ... def l_moment_cov_from_cdf( cdf: _Fn1, - r_max: lmt.AnyOrder, + r_max: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, ) -> _ArrF8: r""" L-moments that are estimated from $n$ samples of a distribution with CDF @@ -217,11 +216,11 @@ def range_x(y: float, *_: int) -> tuple[float, float]: def l_stats_cov_from_cdf( cdf: _Fn1, /, - num: lmt.AnyOrder = 4, - trim: lmt.AnyTrim = 0, + num: lmt.ToOrder0D = 4, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, ppf: _Fn1 | None = None, ) -> _ArrF8: diff --git a/lmo/theoretical/_f_to_lm_eif.py b/lmo/theoretical/_f_to_lm_eif.py index 0e4a44c4..c6535a9c 100644 --- a/lmo/theoretical/_f_to_lm_eif.py +++ b/lmo/theoretical/_f_to_lm_eif.py @@ -14,7 +14,6 @@ from collections.abc import Callable import lmo.typing as lmt - import lmo.typing.scipy as lspt __all__ = ["l_moment_influence_from_cdf", "l_ratio_influence_from_cdf"] @@ -33,13 +32,13 @@ def __call__(self, x: _T_x, /) -> _T_x: ... def l_moment_influence_from_cdf( cdf: _Fn1, - r: lmt.AnyOrder, + r: lmt.ToOrder0D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, l_moment: float | np.float64 | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: @@ -157,14 +156,14 @@ def influence(x: _T_x, /) -> _T_x: def l_ratio_influence_from_cdf( cdf: _Fn1, - r: lmt.AnyOrder, - k: lmt.AnyOrder = 2, + r: lmt.ToOrder0D, + k: lmt.ToOrder0D = 2, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] | None = None, l_moments: _Pair[float] | None = None, - quad_opts: lspt.QuadOptions | None = None, + quad_opts: lmt.QuadOptions | None = None, alpha: float = ALPHA, tol: float = 1e-8, ) -> Callable[[_T_x], _T_x]: diff --git a/lmo/theoretical/_lm_to_f.py b/lmo/theoretical/_lm_to_f.py index f810a318..b229530f 100644 --- a/lmo/theoretical/_lm_to_f.py +++ b/lmo/theoretical/_lm_to_f.py @@ -11,24 +11,24 @@ if TYPE_CHECKING: from collections.abc import Callable + import optype.numpy as onp + import lmo.typing as lmt - import lmo.typing.np as lnpt __all__ = ["ppf_from_l_moments", "qdf_from_l_moments"] _T = TypeVar("_T") _T_x = TypeVar("_T_x", float, npt.NDArray[np.float64]) +_Pair: TypeAlias = tuple[_T, _T] +_FloatND: TypeAlias = npt.NDArray[np.float64] + class _Fn1(Protocol): def __call__(self, x: _T_x, /) -> _T_x: ... -_Pair: TypeAlias = tuple[_T, _T] -_ArrF8: TypeAlias = npt.NDArray[np.float64] - - -def _validate_l_bounds(l_r: _ArrF8, s: float, t: float) -> None: +def _validate_l_bounds(l_r: _FloatND, s: float, t: float) -> None: if (l2 := l_r[1]) <= 0: msg = f"L-scale must be >0, got lmda[1] = {l2}" raise ValueError(msg) @@ -74,7 +74,7 @@ def _validate_l_bounds(l_r: _ArrF8, s: float, t: float) -> None: def _monotonic( - f: Callable[[_ArrF8], np.float64 | _ArrF8], + f: Callable[[_FloatND], np.float64 | _FloatND], a: float, b: float, n: int = 100, @@ -90,9 +90,9 @@ def _monotonic( def ppf_from_l_moments( - lmbda: lnpt.AnyVectorFloat, + lmbda: onp.ToFloat1D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, support: _Pair[float] = (-np.inf, np.inf), validate: bool = True, @@ -205,9 +205,9 @@ def ppf( def qdf_from_l_moments( - lmbda: lnpt.AnyVectorFloat, + lmbda: onp.ToFloat1D, /, - trim: lmt.AnyTrim = 0, + trim: lmt.ToTrim = 0, *, validate: bool = True, extrapolate: bool = False, diff --git a/lmo/theoretical/_utils.py b/lmo/theoretical/_utils.py index b7a8de4a..38889179 100644 --- a/lmo/theoretical/_utils.py +++ b/lmo/theoretical/_utils.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from collections.abc import Callable, Sequence - import lmo.typing.scipy as lspt + import lmo.typing as lmt __all__ = ("ALPHA", "QUAD_LIMIT", "l_coef_factor", "l_const", "tighten_cdf_support") @@ -106,7 +106,7 @@ def tighten_cdf_support( def nquad( integrand: Callable[Concatenate[float, float, _Tss], float], domains: Sequence[tuple[float, float] | Callable[..., tuple[float, float]],], - opts: lspt.QuadOptions | None = None, + opts: lmt.QuadOptions | None = None, *args: _Tss.args, **kwds: _Tss.kwargs, ) -> float: diff --git a/lmo/typing.py b/lmo/typing.py new file mode 100644 index 00000000..a68eba17 --- /dev/null +++ b/lmo/typing.py @@ -0,0 +1,167 @@ +"""Typing utilities meant for internal usage.""" # noqa: A005 + +# pyright: reportPrivateUsage=false +# ruff: noqa: PLC2701 + +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import ( + TYPE_CHECKING, + Any, + Literal, + ParamSpec, + Protocol, + TypeAlias, + TypeVar, + TypedDict, + overload, +) + +import numpy as np +import optype.numpy as onp +from scipy.stats._distn_infrastructure import ( + _ShapeInfo as ShapeInfo, + rv_continuous, + rv_frozen, + rv_generic, +) + +if TYPE_CHECKING: + from numpy._typing import _ArrayLikeFloat_co + +__all__ = [ + "Callable2", + "Floating", + "Integer", + "LComomentOptions", + "LMomentOptions", + "OrderReshape", + "QuadOptions", + "ShapeInfo", + "SortKind", + "ToAWeights", + "ToFWeights", + "ToIntTrim", + "ToOrder0D", + "ToOrder1D", + "ToOrderND", + "ToTrim", + "rv_continuous", + "rv_frozen", + "rv_generic", +] + + +def __dir__() -> list[str]: + return __all__ + + +Integer: TypeAlias = np.integer[Any] +Floating: TypeAlias = np.floating[Any] + +OrderReshape: TypeAlias = Literal["C", "F", "A"] +"""Type of the `order` parameter of e.g. [`np.reshape`][numpy.array].""" + +# matches `_SortKind` in `numpy/__init__.pyi` and `numpy >= 2.1` +SortKind: TypeAlias = Literal[ + "Q", "quick", "quicksort", + "M", "merge", "mergesort", + "H", "heap", "heapsort", + "S", "stable", "stablesort", +] # fmt: skip +""" +Type of the `kind` parameter of e.g. [`np.sort`][numpy.sort], as +allowed by numpy's own stubs. +Note that the actual implementation just looks at `kind[0].lower() == 'q'`. +This means that it's possible to select stable-sort by passing +`kind='SnailSort'` instead of `kind='stable'` (although your typechecker might +ruin the fun). +""" + +RNG: TypeAlias = np.random.SeedSequence | np.random.BitGenerator | np.random.Generator +Seed: TypeAlias = int | Integer | onp.ArrayND[Integer] | RNG +"""The accepted type of [`numpy.random.default_rng`][numpy.random.default_rng].""" + + +ToIntTrim: TypeAlias = int | tuple[int, int] +ToTrim: TypeAlias = float | tuple[float, float] + +ToOrder0D: TypeAlias = int | Integer +ToOrder1D: TypeAlias = onp.CanArrayND[Integer] | Sequence[ToOrder0D] +ToOrderND: TypeAlias = ToOrder1D | Sequence["ToOrderND"] +ToOrder: TypeAlias = ToOrder0D | ToOrderND + +ToFWeights: TypeAlias = onp.Array1D[Integer] +ToAWeights: TypeAlias = onp.ArrayND[Floating] + + +class UnivariateOptions(TypedDict, total=False): + """Use as e.g. `**kwds: Unpack[UnivariateOptions]`.""" + + sort: bool | SortKind + fweights: ToFWeights + aweights: ToAWeights + + +class LMomentOptions(UnivariateOptions, TypedDict, total=False): + """Use as e.g. `**kwds: Unpack[LMomentOptions]`.""" + + cache: bool | None + + +class LComomentOptions(TypedDict, total=False): + """Use as e.g. `**kwds: Unpack[LComomentOptions]`.""" + + sort: SortKind + cache: bool | None + rowvar: bool | None + + +_Tss = ParamSpec("_Tss") +_T = TypeVar("_T") +_F1_co = TypeVar("_F1_co", bound=Callable[..., object], covariant=True) +_F2_co = TypeVar("_F2_co", bound=Callable[..., object], covariant=True) + + +class Callable2(Protocol[_F1_co, _F2_co]): + """ + The intersection of two callable types, i.e. a callable with two overloads, one + for each of the callable type params. + """ + + @overload + def __call__( + self: Callable2[Callable[_Tss, _T], _F2_co], + /, + *args: _Tss.args, + **kwds: _Tss.kwargs, + ) -> _T: ... + @overload + def __call__( + self: Callable2[_F1_co, Callable[_Tss, _T]], + /, + *args: _Tss.args, + **kwds: _Tss.kwargs, + ) -> _T: ... + + +# scipy stuff + +_IntLike: TypeAlias = int | Integer +_FloatLike: TypeAlias = float | Floating + + +class QuadOptions(TypedDict, total=False): + """ + Optional quadrature options to be passed to the integration routine, e.g. + [`scipy.integrate.quad`][scipy.integrate.quad]. + """ + + epsabs: _FloatLike + epsrel: _FloatLike + limit: _IntLike + points: _ArrayLikeFloat_co + weight: Literal["cos", "sin", "alg", "alg-loga", "alg-logb", "alg-log", "cauchy"] + wvar: _FloatLike | tuple[_FloatLike, _FloatLike] + wopts: tuple[_IntLike, onp.ArrayND[np.float32 | np.float64]] diff --git a/lmo/typing/__init__.py b/lmo/typing/__init__.py deleted file mode 100644 index ecf4c099..00000000 --- a/lmo/typing/__init__.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Typing utilities meant for internal usage.""" - -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, Any, Protocol, TypeAlias - -# pyright: reportPrivateUsage=false -import numpy as np -import numpy.typing as npt -import optype.numpy as onp -from numpy._typing import _NestedSequence # noqa: PLC2701 - -if sys.version_info >= (3, 13): - from typing import TypedDict -else: - from typing_extensions import TypedDict - -if TYPE_CHECKING: - import lmo.typing.np as lnpt - - -__all__ = [ - "AnyAWeights", - "AnyFWeights", - "AnyOrder", - "AnyOrderND", - "AnyTrim", - "AnyTrimFloat", - "AnyTrimInt", - "LComomentOptions", - "LMomentOptions", -] - - -def __dir__() -> list[str]: - return __all__ - - -AnyTrimInt: TypeAlias = int | tuple[int, int] -AnyTrimFloat: TypeAlias = float | tuple[float, float] -AnyTrim: TypeAlias = AnyTrimInt | AnyTrimFloat - - -class _CanIntegerArray(Protocol): - def __len__(self, /) -> int: ... # this excludes scalar types - def __array__(self, /) -> npt.NDArray[np.integer[Any]]: ... - - -AnyOrder: TypeAlias = int | np.integer[Any] -AnyOrderND: TypeAlias = _CanIntegerArray | _NestedSequence[int | np.integer[Any]] - -AnyFWeights: TypeAlias = onp.Array[tuple[int], np.integer[Any]] -AnyAWeights: TypeAlias = onp.Array[onp.AtLeast1D, np.floating[Any]] - - -class LMomentOptions(TypedDict, total=False): - """Use as e.g. `**kwds: Unpack[LMomentOptions]`.""" - - sort: lnpt.SortKind | bool - cache: bool | None - fweights: AnyFWeights - aweights: AnyAWeights - - -class LComomentOptions(TypedDict, total=False): - """Use as e.g. `**kwds: Unpack[LComomentOptions]`.""" - - sort: lnpt.SortKind - cache: bool | None - rowvar: bool | None diff --git a/lmo/typing/np.py b/lmo/typing/np.py deleted file mode 100644 index d496487c..00000000 --- a/lmo/typing/np.py +++ /dev/null @@ -1,124 +0,0 @@ -"""NumPy-related type aliases for internal use.""" - -from __future__ import annotations - -from collections.abc import Sequence -from typing import Any, Literal, TypeAlias, TypeVar - -import numpy as np -import optype.numpy as onp - -__all__ = ( - "AnyArrayFloat", - "AnyArrayInt", - "AnyMatrixFloat", - "AnyMatrixInt", - "AnyScalarFloat", - "AnyScalarInt", - "AnyTensorFloat", - "AnyTensorInt", - "AnyVector", - "AnyVectorFloat", - "AnyVectorFloat", - "Float", - "Int", - "Integral", - "OrderReshape", - "Real", - "SortKind", -) - - -def __dir__() -> tuple[str, ...]: - return __all__ - - -# Some handy scalar type aliases - - -Int: TypeAlias = np.integer[Any] -Float: TypeAlias = np.floating[Any] -Number: TypeAlias = np.number[Any] - -Integral: TypeAlias = Int | np.bool_ -Real: TypeAlias = Float | Integral - - -# Array and array-likes, with generic shape - -_ST = TypeVar("_ST", bound=np.generic) - - -_PyScalar: TypeAlias = complex | str | bytes -_ST_py = TypeVar("_ST_py", bound=_PyScalar) - -_T = TypeVar("_T") -_PyVector: TypeAlias = Sequence[_T] - - -_AnyScalar: TypeAlias = _ST | _ST_py | onp.CanArray[tuple[()], np.dtype[_ST]] -_AnyVector: TypeAlias = ( - onp.CanArray[tuple[int], np.dtype[_ST]] - | _PyVector[_AnyScalar[_ST, _ST_py]] -) # fmt: skip -_AnyMatrix: TypeAlias = ( - onp.CanArray[tuple[int, int], np.dtype[_ST]] - | _PyVector[_AnyVector[_ST, _ST_py]] -) # fmt: skip - -# these will result in {0,1,2,N}-D arrays when passed to `np.array` (no need -# for a broken "nested sequence" type) - -AnyVector: TypeAlias = _AnyVector[np.generic, _PyScalar] - -AnyScalarInt: TypeAlias = _AnyScalar[Integral, int] -AnyVectorInt: TypeAlias = _AnyVector[Integral, int] -AnyMatrixInt: TypeAlias = _AnyMatrix[Integral, int] -AnyTensorInt: TypeAlias = ( - onp.CanArray[onp.AtLeast3D, np.dtype[Integral]] - | _PyVector[AnyMatrixInt] - | _PyVector["AnyTensorInt"] -) -AnyArrayInt: TypeAlias = AnyVectorInt | AnyMatrixInt | AnyTensorInt - -AnyScalarFloat: TypeAlias = _AnyScalar[Real, float] -AnyVectorFloat: TypeAlias = _AnyVector[Real, float] -AnyMatrixFloat: TypeAlias = _AnyMatrix[Real, float] -AnyTensorFloat: TypeAlias = ( - onp.CanArray[onp.AtLeast1D, np.dtype[Real]] - | _PyVector[AnyMatrixFloat] - | _PyVector["AnyTensorFloat"] -) -AnyArrayFloat: TypeAlias = AnyVectorFloat | AnyMatrixFloat | AnyTensorFloat - - -# Various type aliases - - -OrderReshape: TypeAlias = Literal["C", "F", "A"] -"""Type of the `order` parameter of e.g. [`np.reshape`][numpy.array].""" - -SortKind: TypeAlias = Literal[ - "quick", "quicksort", - "stable", "stablesort", - "heap", "heapsort", -] # fmt: skip -""" -Type of the `kind` parameter of e.g. [`np.sort`][numpy.sort], as -allowed by numpy's own stubs. -Note that the actual implementation just looks at `kind[0].lower() == 'q'`. -This means that it's possible to select stable-sort by passing -`kind='SnailSort'` instead of `kind='stable'` (although your typechecker might -ruin the fun). -""" - -Seed: TypeAlias = ( - int - | np.random.SeedSequence - | np.random.BitGenerator - | np.random.Generator -) # fmt: skip -""" -Any acceptable "seed" type that can be passed to -[`numpy.random.default_rng`][numpy.random.default_rng]. -""" diff --git a/lmo/typing/scipy.py b/lmo/typing/scipy.py deleted file mode 100644 index e127b263..00000000 --- a/lmo/typing/scipy.py +++ /dev/null @@ -1,87 +0,0 @@ -"""SciPy-related type aliases for internal use.""" - -# ruff: noqa: PLC2701, D102 - -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, Any, Literal, TypeAlias, TypedDict, overload - -import numpy as np -import numpy.typing as npt -from scipy.stats._distn_infrastructure import rv_continuous, rv_frozen, rv_generic - -if sys.version_info >= (3, 13): - from typing import ParamSpec, Protocol -else: - from typing_extensions import ParamSpec, Protocol - -if TYPE_CHECKING: - import optype.numpy as onp - from numpy._typing import _ArrayLikeFloat_co # pyright: ignore[reportPrivateUsage] - - import lmo.typing.np as lnpt - - -__all__ = "RV", "QuadOptions", "QuadWeights", "RVContinuous", "RVFrozen", "RVFunction" - - -def __dir__() -> tuple[str, ...]: - return __all__ - - -_Tss = ParamSpec("_Tss") - -# scipy.integrate - -QuadWeights: TypeAlias = Literal[ - "cos", "sin", "alg", "alg-loga", "alg-logb", "alg-log", "cauchy" -] - -_IntLike: TypeAlias = int | np.integer[Any] -_FloatLike: TypeAlias = float | np.floating[Any] - - -class QuadOptions(TypedDict, total=False): - """ - Optional quadrature options to be passed to - [`scipy.integrate.quad`][scipy.integrate.quad]. - """ - - epsabs: _FloatLike - epsrel: _FloatLike - limit: _IntLike - points: _ArrayLikeFloat_co - weight: QuadWeights - wvar: _FloatLike | tuple[_FloatLike, _FloatLike] - wopts: tuple[_IntLike, npt.NDArray[np.float32 | np.float64]] - - -class RVFunction(Protocol[_Tss]): - """ - Callable protocol for a vectorized distribution function. E.g. for - the `cdf` and `ppf` methods of `scipy,stats.rv_generic`. In practice, - the returned dtype is always `float64` (even `rv_discrete.ppf`). - """ - - @overload - def __call__( - self, - x: lnpt.AnyArrayFloat, - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, - ) -> onp.Array[Any, np.float64]: ... - @overload - def __call__( - self, - x: lnpt.AnyScalarFloat, - /, - *args: _Tss.args, - **kwds: _Tss.kwargs, - ) -> float: ... - - -RV: TypeAlias = rv_generic -RVFrozen: TypeAlias = rv_frozen -RVContinuous: TypeAlias = rv_continuous diff --git a/pyproject.toml b/pyproject.toml index c7f5ac3f..cd75a476 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -239,18 +239,16 @@ split-on-trailing-comma = false [tool.ruff.lint.flake8-import-conventions] banned-from = [ - "lmo.typing.np", - "lmo.typing.scipy", "numpy", "numpy.polynomial", "numpy.typing", + "optype", "optype.numpy", + "optype.typing", ] [tool.ruff.lint.flake8-import-conventions.extend-aliases] "lmo.typing" = "lmt" -"lmo.typing.np" = "lnpt" -"lmo.typing.scipy" = "lspt" "numpy.polynomial" = "npp" "numpy.typing" = "npt" "optype" = "op" diff --git a/tests/test_distributions.py b/tests/test_distributions.py index f648ae91..c8fce47d 100644 --- a/tests/test_distributions.py +++ b/tests/test_distributions.py @@ -20,7 +20,7 @@ "trim", [0, 1, (0, 1), (1, 0), (13, 17), (2 / 3, 3 / 4)], ) -def test_l_poly_eq_uniform(trim: lmt.AnyTrim): +def test_l_poly_eq_uniform(trim: lmt.ToTrim): p0 = x0 = np.linspace(0, 1) X = cast(Any, uniform()) diff --git a/typetests/l_comoment.py b/typetests/l_comoment.py deleted file mode 100644 index f008c416..00000000 --- a/typetests/l_comoment.py +++ /dev/null @@ -1,19 +0,0 @@ -# pyright: reportUnusedCallResult=false -from typing import TypeAlias, assert_type - -import numpy as np -import numpy.typing as npt - -import lmo - -_ArrayF8: TypeAlias = npt.NDArray[np.float64] - -X = [ - [1.9517689, -0.39353141, -0.46680832, -0.43176034, 0.03754792, -0.2559433], - [-0.18679035, -0.30584785, -1.32954, 0.27871746, -0.19124341, -2.1717801], -] - -assert_type(lmo.l_comoment(X, 2), _ArrayF8) -assert_type(lmo.l_comoment(np.array(X), 2), _ArrayF8) -assert_type(lmo.l_comoment(np.array(X).T, 2, rowvar=False), _ArrayF8) -assert_type(lmo.l_comoment(X, 2, dtype=np.float16), npt.NDArray[np.float16]) diff --git a/typetests/l_comoment.pyi b/typetests/l_comoment.pyi new file mode 100644 index 00000000..028ded4d --- /dev/null +++ b/typetests/l_comoment.pyi @@ -0,0 +1,16 @@ +# pyright: reportUnusedCallResult=false, reportInvalidStubStatement=false +from typing import TypeAlias, assert_type + +import numpy as np +import optype.numpy as onp + +import lmo + +_Float2ND: TypeAlias = onp.ArrayND[np.float64] + +X: list[list[float]] + +assert_type(lmo.l_comoment(X, 2), _Float2ND) +assert_type(lmo.l_comoment(np.array(X), 2), _Float2ND) +assert_type(lmo.l_comoment(np.array(X).T, 2, rowvar=False), _Float2ND) +assert_type(lmo.l_comoment(X, 2, dtype=np.float16), onp.ArrayND[np.float16]) diff --git a/typetests/l_moment.py b/typetests/l_moment.pyi similarity index 63% rename from typetests/l_moment.py rename to typetests/l_moment.pyi index 0cd4e403..ca7430e6 100644 --- a/typetests/l_moment.py +++ b/typetests/l_moment.pyi @@ -1,15 +1,17 @@ -# pyright: reportUnusedCallResult=false +# pyright: reportUnusedCallResult=false, reportInvalidStubStatement=false from typing import TypeAlias, assert_type import numpy as np -import numpy.typing as npt +import optype.numpy as onp import lmo -_ArrayF8: TypeAlias = npt.NDArray[np.float64] +_FloatND: TypeAlias = onp.ArrayND[np.float64] -X = [0.14543334, 2.17509751, 0.60844233, 1.47809552, -1.32510269, 1.0979731] -XX = [X, X] +X: list[float] +XX: list[list[float]] + +XX_np: onp.Array2D[np.float64] # default assert_type(lmo.l_moment(X, 2), np.float64) @@ -31,17 +33,17 @@ assert_type(lmo.l_moment(X, 2, trim=(0.5, 1)), np.float64) # vectorized r -assert_type(lmo.l_moment(X, [1, 2, 3, 4]), _ArrayF8) -assert_type(lmo.l_moment(X, (1, 2, 3, 4)), _ArrayF8) -assert_type(lmo.l_moment(X, np.arange(1, 5)), _ArrayF8) +assert_type(lmo.l_moment(X, [1, 2, 3, 4]), _FloatND) +assert_type(lmo.l_moment(X, (1, 2, 3, 4)), _FloatND) +assert_type(lmo.l_moment(X, np.arange(1, 5)), _FloatND) # sctype assert_type(lmo.l_moment(X, 2, dtype=np.float32), np.float32) assert_type(lmo.l_moment(X, 2, dtype=np.longdouble), np.longdouble) assert_type(lmo.l_moment(X, 2, dtype=np.dtype(np.float16)), np.float16) -assert_type(lmo.l_moment(X, [1, 2, 3, 4], dtype=np.half), npt.NDArray[np.half]) +assert_type(lmo.l_moment(X, [1, 2, 3, 4], dtype=np.half), onp.ArrayND[np.half]) # axis -assert_type(lmo.l_moment(XX, 2, axis=0), _ArrayF8) -assert_type(lmo.l_moment(np.array(XX), 2, axis=0), _ArrayF8) -assert_type(lmo.l_moment(XX, 2, axis=0, dtype=np.half), npt.NDArray[np.half]) +assert_type(lmo.l_moment(XX, 2, axis=0), np.float64 | _FloatND) +assert_type(lmo.l_moment(XX_np, 2, axis=0), np.float64 | _FloatND) +assert_type(lmo.l_moment(XX, 2, axis=0, dtype=np.half), np.half | onp.ArrayND[np.half]) diff --git a/typetests/l_ratio.py b/typetests/l_ratio.pyi similarity index 72% rename from typetests/l_ratio.py rename to typetests/l_ratio.pyi index ca05f3ff..040a5158 100644 --- a/typetests/l_ratio.py +++ b/typetests/l_ratio.pyi @@ -1,14 +1,14 @@ -# pyright: reportUnusedCallResult=false +# pyright: reportUnusedCallResult=false, reportInvalidStubStatement=false from typing import TypeAlias, assert_type import numpy as np -import numpy.typing as npt +import optype.numpy as onp import lmo -X = [0.14543334, 2.17509751, 0.60844233, 1.47809552, -1.32510269, 1.0979731] +X: list[float] -_ArrF8: TypeAlias = npt.NDArray[np.float64] +_FloatND: TypeAlias = onp.ArrayND[np.float64] # default assert_type(lmo.l_ratio(X, 4, 2), np.float64) @@ -34,10 +34,10 @@ assert_type(lmo.l_ratio(X, 4, 2, dtype=np.dtype(np.float16)), np.float16) # vectorized r -assert_type(lmo.l_ratio(X, [3, 4], 2), _ArrF8) -assert_type(lmo.l_ratio(X, np.array([3, 4]), 2), _ArrF8) -assert_type(lmo.l_ratio(X, [1, 2, 3, 4], [0, 0, 2, 2]), _ArrF8) -assert_type(lmo.l_ratio(X, np.array([1, 2, 3, 4]), [0, 0, 2, 2]), _ArrF8) -assert_type(lmo.l_ratio(X, [1, 2, 3, 4], np.array([0, 0, 2, 2])), _ArrF8) -assert_type(lmo.l_ratio(X, 3, [0, 2]), _ArrF8) -assert_type(lmo.l_ratio(X, 3, np.array([0, 2])), _ArrF8) +assert_type(lmo.l_ratio(X, [3, 4], 2), _FloatND) +assert_type(lmo.l_ratio(X, np.array([3, 4]), 2), _FloatND) +assert_type(lmo.l_ratio(X, [1, 2, 3, 4], [0, 0, 2, 2]), _FloatND) +assert_type(lmo.l_ratio(X, np.array([1, 2, 3, 4]), [0, 0, 2, 2]), _FloatND) +assert_type(lmo.l_ratio(X, [1, 2, 3, 4], np.array([0, 0, 2, 2])), _FloatND) +assert_type(lmo.l_ratio(X, 3, [0, 2]), _FloatND) +assert_type(lmo.l_ratio(X, 3, np.array([0, 2])), _FloatND) diff --git a/typetests/l_stats.py b/typetests/l_stats.py deleted file mode 100644 index 36c732e4..00000000 --- a/typetests/l_stats.py +++ /dev/null @@ -1,27 +0,0 @@ -# pyright: reportUnusedCallResult=false -from typing import TypeAlias, assert_type - -import numpy as np -import numpy.typing as npt - -import lmo - -X = [0.14543334, 2.17509751, 0.60844233, 1.47809552, -1.32510269, 1.0979731] -XX = [X, X] - -_ArrF8: TypeAlias = npt.NDArray[np.float64] - -# default -assert_type(lmo.l_stats(X), _ArrF8) -assert_type(lmo.l_stats(np.array(X, dtype=np.float32)), _ArrF8) -assert_type(lmo.l_stats(np.array(X, dtype=np.int32)), _ArrF8) -assert_type(lmo.l_stats(XX), _ArrF8) -assert_type(lmo.l_stats(np.array(XX)), _ArrF8) - -# num -assert_type(lmo.l_stats(X, num=3), _ArrF8) -assert_type(lmo.l_stats(X, 0, 3), _ArrF8) - -# axis -assert_type(lmo.l_stats(XX, axis=0), _ArrF8) -assert_type(lmo.l_stats(np.array(XX), axis=0), _ArrF8) diff --git a/typetests/l_stats.pyi b/typetests/l_stats.pyi new file mode 100644 index 00000000..afe90e44 --- /dev/null +++ b/typetests/l_stats.pyi @@ -0,0 +1,27 @@ +# pyright: reportUnusedCallResult=false, reportInvalidStubStatement=false +from typing import TypeAlias, assert_type + +import numpy as np +import optype.numpy as onp + +import lmo + +X: list[float] +XX: list[list[float]] + +FloatND: TypeAlias = onp.ArrayND[np.float64] + +# default +assert_type(lmo.l_stats(X), FloatND) +assert_type(lmo.l_stats(np.array(X, dtype=np.float32)), FloatND) +assert_type(lmo.l_stats(np.array(X, dtype=np.int32)), FloatND) +assert_type(lmo.l_stats(XX), FloatND) +assert_type(lmo.l_stats(np.array(XX)), FloatND) + +# num +assert_type(lmo.l_stats(X, num=3), FloatND) +assert_type(lmo.l_stats(X, 0, 3), FloatND) + +# axis +assert_type(lmo.l_stats(XX, axis=0), FloatND) +assert_type(lmo.l_stats(np.array(XX), axis=0), FloatND)