diff --git a/pixi.lock b/pixi.lock index 4f9960725..31006c4e1 100644 --- a/pixi.lock +++ b/pixi.lock @@ -333,7 +333,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -606,7 +605,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl @@ -890,7 +888,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -1268,7 +1265,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -1578,7 +1574,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl @@ -1899,7 +1894,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -2291,7 +2285,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -2681,7 +2674,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -3072,7 +3064,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -3415,7 +3406,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -3688,7 +3678,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl @@ -3973,7 +3962,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -4316,7 +4304,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -4589,7 +4576,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl @@ -4874,7 +4860,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -5218,7 +5203,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -5492,7 +5476,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl @@ -5778,7 +5761,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -6118,7 +6100,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/ae/b3/a0f0f4faac229b0011d8c4a7ee6da7c2dca0b6fd08039c95920846f23ca4/kaleido-0.2.1-py2.py3-none-manylinux1_x86_64.whl - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/37/9a/0c28b6371e0cdcb14f8f1930778cb3123acfcbd2c95bb9cf6b4a2ba0cce3/sqlalchemy-2.0.48-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl @@ -6385,7 +6366,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/45/8e/4297556be5a07b713bb42dde0f748354de9a6918dee251c0e6bdcda341e7/kaleido-0.2.1-py2.py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ef/91/a42ae716f8925e9659df2da21ba941f158686856107a61cc97a95e7647a3/sqlalchemy-2.0.48-cp312-cp312-macosx_11_0_arm64.whl @@ -6664,7 +6644,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/f7/9a/0408b02a4bcb3cf8b338a2b074ac7d1b2099e2b092b42473def22f7b625f/kaleido-0.2.1-py2.py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl @@ -7004,7 +6983,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -7275,7 +7253,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl @@ -7558,7 +7535,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -7899,7 +7875,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -8170,7 +8145,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl @@ -8453,7 +8427,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -8796,7 +8769,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -9069,7 +9041,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl @@ -9353,7 +9324,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -9696,7 +9666,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -9976,7 +9945,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl @@ -10267,7 +10235,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/51/fe/53ac0cd932db5dcaf55961bc7cb7afdca8d80d8cc7406ed661f0c7dc111a/pdbp-1.8.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/d2/c6e44dba74f17c6216ce1b56044a9b93a929f1c2d5bdaff892512b260f5e/plotly-6.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f4/1e/0d44a4e3a291c009a357fbd1d61511d9306c2c4db9a7ceb6e8104d8d385f/Py_BOBYQA-1.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/fd/5c2baba82425b75baf7dbec5af57219cd252aa8a1ace4f5cd1d88e472276/pyswarms-1.3.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl @@ -19402,16 +19369,16 @@ packages: timestamp: 1733688053334 - pypi: ./ name: optimagic - version: 0.5.4.dev5+g8654d6292.d20260312 - sha256: 72e7ed28837a3da869c13448a83bee607a85ab9f1e2d9dc5b5e7604d8ce2bf94 + version: 0.1.dev492+g0e76b38a2.d20260407 + sha256: 442217a96b1de117660470d0018f79165700dbbfaa9cdd600b5feeaf196773d2 requires_dist: - annotated-types>=0.4 - cloudpickle>=2.2 - joblib>=1.1 - numpy>=1.26 + - optree>=0.19 - pandas>=2.1 - plotly>=5.14 - - pybaum>=0.1.2 - scipy>=1.11 - sqlalchemy>=2.0 - typing-extensions>=4.5 @@ -19477,7 +19444,7 @@ packages: license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/optree?source=compressed-mapping + - pkg:pypi/optree?source=hash-mapping size: 429640 timestamp: 1771868574672 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.0-py313h5c29297_0.conda @@ -19525,7 +19492,7 @@ packages: license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/optree?source=compressed-mapping + - pkg:pypi/optree?source=hash-mapping size: 378894 timestamp: 1771868468546 - conda: https://conda.anaconda.org/conda-forge/win-64/optree-0.19.0-py313hf069bd2_0.conda @@ -19541,7 +19508,7 @@ packages: license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/optree?source=compressed-mapping + - pkg:pypi/optree?source=hash-mapping size: 385763 timestamp: 1771868441594 - conda: https://conda.anaconda.org/conda-forge/win-64/optree-0.19.0-py314h909e829_0.conda @@ -19557,7 +19524,7 @@ packages: license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/optree?source=compressed-mapping + - pkg:pypi/optree?source=hash-mapping size: 394633 timestamp: 1771868448953 - pypi: https://files.pythonhosted.org/packages/00/04/c6f72daca5092e3117840a1b1e88dfc809cc1470cf0734890d0366b684a1/orjson-3.11.7-cp313-cp313-win_amd64.whl @@ -20954,11 +20921,6 @@ packages: - sphinx-rtd-theme ; extra == 'dev' - trustregion>=1.1 ; extra == 'trustregion' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/d3/20/bcd8f317a17900e5daeb3f87a87327399c9bbe9dcd97f4025f4663c3bdf1/pybaum-0.1.3-py3-none-any.whl - name: pybaum - version: 0.1.3 - sha256: a1d74200d0477c7da121af2f67a236d658ec62aaef5f5c35562ec959f52efa3d - requires_python: '>=3.7' - conda: https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.1-pyh7a1b43c_0.conda sha256: 2558727093f13d4c30e124724566d16badd7de532fd8ee7483628977117d02be md5: 70ece62498c769280f791e836ac53fff diff --git a/pyproject.toml b/pyproject.toml index af1ac9a23..3c577a8b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,11 +11,11 @@ dependencies = [ "numpy>=1.26", "pandas>=2.1", "plotly>=5.14", - "pybaum>=0.1.2", "scipy>=1.11", "sqlalchemy>=2.0", "annotated-types>=0.4", "typing-extensions>=4.5", + "optree>=0.19", ] dynamic = ["version"] keywords = [ @@ -349,7 +349,6 @@ ignore_errors = true [[tool.mypy.overrides]] module = [ - "pybaum", "scipy", "scipy.linalg", "scipy.linalg.lapack", diff --git a/src/estimagic/bootstrap.py b/src/estimagic/bootstrap.py index 76d75c4fb..d1411c31d 100644 --- a/src/estimagic/bootstrap.py +++ b/src/estimagic/bootstrap.py @@ -5,7 +5,6 @@ import numpy as np import pandas as pd -from pybaum import leaf_names, tree_flatten, tree_just_flatten, tree_unflatten from estimagic.bootstrap_ci import calculate_ci from estimagic.bootstrap_helpers import check_inputs @@ -13,7 +12,13 @@ from estimagic.shared_covs import calculate_estimation_summary from optimagic.batch_evaluators import joblib_batch_evaluator from optimagic.parameters.block_trees import matrix_to_block_tree -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE from optimagic.utilities import get_rng @@ -102,9 +107,8 @@ def bootstrap( # Process results # ================================================================================== - registry = get_registry(extended=True) flat_outcomes = [ - tree_just_flatten(_outcome, registry=registry) for _outcome in all_outcomes + tree_leaves(_outcome, namespace=VALUE_NAMESPACE) for _outcome in all_outcomes ] internal_outcomes = np.array(flat_outcomes) @@ -162,11 +166,10 @@ def outcomes(self): List[Any]: The boostrap outcomes as a list of pytrees. """ - registry = get_registry(extended=True) - _, treedef = tree_flatten(self._base_outcome, registry=registry) + _, treedef = tree_flatten(self._base_outcome, namespace=VALUE_NAMESPACE) outcomes = [ - tree_unflatten(treedef, out, registry=registry) + tree_unflatten(treedef, out, namespace=VALUE_NAMESPACE) for out in self._internal_outcomes ] return outcomes @@ -182,10 +185,9 @@ def se(self): cov = self._internal_cov se = np.sqrt(np.diagonal(cov)) - registry = get_registry(extended=True) - _, treedef = tree_flatten(self._base_outcome, registry=registry) + _, treedef = tree_flatten(self._base_outcome, namespace=VALUE_NAMESPACE) - se = tree_unflatten(treedef, se, registry=registry) + se = tree_unflatten(treedef, se, namespace=VALUE_NAMESPACE) return se def cov(self, return_type="pytree"): @@ -206,8 +208,7 @@ def cov(self, return_type="pytree"): cov = self._internal_cov if return_type == "dataframe": - registry = get_registry(extended=True) - names = np.array(leaf_names(self._base_outcome, registry=registry)) + names = np.array(leaf_names(self._base_outcome, namespace=VALUE_NAMESPACE)) cov = pd.DataFrame(cov, columns=names, index=names) elif return_type == "pytree": cov = matrix_to_block_tree(cov, self._base_outcome, self._base_outcome) @@ -234,15 +235,16 @@ def ci(self, ci_method="percentile", ci_level=0.95): bounds of confidence intervals. """ - registry = get_registry(extended=True) - base_outcome_flat, treedef = tree_flatten(self._base_outcome, registry=registry) + base_outcome_flat, treedef = tree_flatten( + self._base_outcome, namespace=VALUE_NAMESPACE + ) lower_flat, upper_flat = calculate_ci( base_outcome_flat, self._internal_outcomes, ci_method, ci_level ) - lower = tree_unflatten(treedef, lower_flat, registry=registry) - upper = tree_unflatten(treedef, upper_flat, registry=registry) + lower = tree_unflatten(treedef, lower_flat, namespace=VALUE_NAMESPACE) + upper = tree_unflatten(treedef, upper_flat, namespace=VALUE_NAMESPACE) return lower, upper def p_values(self): @@ -271,8 +273,7 @@ def summary(self, ci_method="percentile", ci_level=0.95): Soon this will be a pytree. """ - registry = get_registry(extended=True) - names = leaf_names(self.base_outcome, registry=registry) + names = leaf_names(self.base_outcome, namespace=VALUE_NAMESPACE) summary_data = _calulcate_summary_data_bootstrap( self, ci_method=ci_method, ci_level=ci_level ) diff --git a/src/estimagic/estimate_msm.py b/src/estimagic/estimate_msm.py index bf17d8f73..7d068a366 100644 --- a/src/estimagic/estimate_msm.py +++ b/src/estimagic/estimate_msm.py @@ -9,7 +9,6 @@ import numpy as np import pandas as pd -from pybaum import leaf_names, tree_just_flatten from estimagic.msm_covs import cov_optimal, cov_robust from estimagic.msm_sensitivity import ( @@ -51,10 +50,14 @@ from optimagic.parameters.bounds import Bounds, pre_process_bounds from optimagic.parameters.conversion import Converter, get_converter from optimagic.parameters.space_conversion import InternalParams -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_leaves, +) from optimagic.shared.check_option_dicts import ( check_optimization_options, ) +from optimagic.typing import VALUE_NAMESPACE from optimagic.utilities import get_rng, to_pickle @@ -318,8 +321,7 @@ def func(x): sim_mom = simulate_moments(params, **simulate_moments_kwargs) if isinstance(sim_mom, dict) and "simulated_moments" in sim_mom: sim_mom = sim_mom["simulated_moments"] - registry = get_registry(extended=True) - out = np.array(tree_just_flatten(sim_mom, registry=registry)) + out = np.array(tree_leaves(sim_mom, namespace=VALUE_NAMESPACE)) return out int_jac = first_derivative( @@ -418,8 +420,7 @@ def get_msm_optimization_functions( chol_weights = np.linalg.cholesky(flat_weights) - registry = get_registry(extended=True) - flat_emp_mom = tree_just_flatten(empirical_moments, registry=registry) + flat_emp_mom = tree_leaves(empirical_moments, namespace=VALUE_NAMESPACE) _simulate_moments = _partial_kwargs(simulate_moments, simulate_moments_kwargs) _jacobian = _partial_kwargs(jacobian, jacobian_kwargs) @@ -430,7 +431,7 @@ def get_msm_optimization_functions( simulate_moments=_simulate_moments, flat_empirical_moments=flat_emp_mom, chol_weights=chol_weights, - registry=registry, + namespace=VALUE_NAMESPACE, ) ) @@ -445,7 +446,7 @@ def get_msm_optimization_functions( def _msm_criterion( - params, simulate_moments, flat_empirical_moments, chol_weights, registry + params, simulate_moments, flat_empirical_moments, chol_weights, namespace ): """Calculate msm criterion given parameters and building blocks.""" simulated = simulate_moments(params) @@ -454,7 +455,7 @@ def _msm_criterion( if isinstance(simulated, np.ndarray) and simulated.ndim == 1: simulated_flat = simulated else: - simulated_flat = np.array(tree_just_flatten(simulated, registry=registry)) + simulated_flat = np.array(tree_leaves(simulated, namespace=namespace)) deviations = simulated_flat - flat_empirical_moments residuals = deviations @ chol_weights @@ -975,9 +976,8 @@ def sensitivity( inner_tree=self._empirical_moments, ) elif return_type == "dataframe": - registry = get_registry(extended=True) row_names = self._internal_estimates.names - col_names = leaf_names(self._empirical_moments, registry=registry) + col_names = leaf_names(self._empirical_moments, namespace=VALUE_NAMESPACE) out = pd.DataFrame( data=raw, index=row_names, diff --git a/src/estimagic/msm_weighting.py b/src/estimagic/msm_weighting.py index 991e9b54f..234fb17b2 100644 --- a/src/estimagic/msm_weighting.py +++ b/src/estimagic/msm_weighting.py @@ -2,12 +2,12 @@ import numpy as np import pandas as pd -from pybaum import tree_just_flatten from scipy.linalg import block_diag from estimagic.bootstrap import bootstrap from optimagic.parameters.block_trees import block_tree_to_matrix, matrix_to_block_tree -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE from optimagic.utilities import robust_inverse @@ -51,13 +51,11 @@ def get_moments_cov( first_eval = calculate_moments(data, **moment_kwargs) - registry = get_registry(extended=True) - @functools.wraps(calculate_moments) def func(data, **kwargs): raw = calculate_moments(data, **kwargs) out = pd.Series( - tree_just_flatten(raw, registry=registry) + tree_leaves(raw, namespace=VALUE_NAMESPACE) ) # xxxx won't be necessary soon! return out diff --git a/src/estimagic/shared_covs.py b/src/estimagic/shared_covs.py index c4cccc3a2..dbdfd446c 100644 --- a/src/estimagic/shared_covs.py +++ b/src/estimagic/shared_covs.py @@ -3,10 +3,13 @@ import numpy as np import pandas as pd import scipy -from pybaum import tree_just_flatten, tree_unflatten from optimagic.parameters.block_trees import matrix_to_block_tree -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE def transform_covariance( @@ -146,9 +149,8 @@ def calculate_estimation_summary( # Flatten summary and construct data frame for flat estimates # ================================================================================== - registry = get_registry(extended=True) flat_data = { - key: tree_just_flatten(val, registry=registry) + key: tree_leaves(val, namespace=VALUE_NAMESPACE) for key, val in summary_data.items() } @@ -167,10 +169,10 @@ def calculate_estimation_summary( # ================================================================================== # create tree with values corresponding to indices of df - indices = tree_unflatten(summary_data["value"], names, registry=registry) + indices = tree_unflatten(summary_data["value"], names, namespace=VALUE_NAMESPACE) - estimates_flat = tree_just_flatten(summary_data["value"]) - indices_flat = tree_just_flatten(indices) + estimates_flat = tree_leaves(summary_data["value"]) + indices_flat = tree_leaves(indices) # use index chunks in indices_flat to access the corresponding sub data frame of df, # and use the index information stored in estimates_flat to form the correct (multi) @@ -316,8 +318,7 @@ def calculate_free_estimates(estimates, internal_estimates): mask = internal_estimates.free_mask names = internal_estimates.names - registry = get_registry(extended=True) - external_flat = np.array(tree_just_flatten(estimates, registry=registry)) + external_flat = np.array(tree_leaves(estimates, namespace=VALUE_NAMESPACE)) free_estimates = FreeParams( values=external_flat[mask], @@ -351,8 +352,7 @@ def transform_free_values_to_params_tree(values, free_params, params): mask = free_params.free_mask flat = np.full(len(mask), np.nan) flat[np.ix_(mask)] = values - registry = get_registry(extended=True) - pytree = tree_unflatten(params, flat, registry=registry) + pytree = tree_unflatten(params, flat, namespace=VALUE_NAMESPACE) return pytree diff --git a/src/optimagic/benchmarking/run_benchmark.py b/src/optimagic/benchmarking/run_benchmark.py index cd6d844c4..46bae183c 100644 --- a/src/optimagic/benchmarking/run_benchmark.py +++ b/src/optimagic/benchmarking/run_benchmark.py @@ -9,12 +9,12 @@ """ import numpy as np -from pybaum import tree_just_flatten from optimagic import batch_evaluators from optimagic.algorithms import AVAILABLE_ALGORITHMS from optimagic.optimization.optimize import minimize -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE def run_benchmark( @@ -180,7 +180,6 @@ def _process_one_result(optimize_result, problem): dict: Processed result. """ - _registry = get_registry(extended=True) _criterion = problem["noise_free_fun"] _start_x = problem["inputs"]["params"] _start_crit_value = _criterion(_start_x) @@ -191,7 +190,7 @@ def _process_one_result(optimize_result, problem): # This will happen if the optimization raised an error if isinstance(optimize_result, str): - params_history_flat = [tree_just_flatten(_start_x, registry=_registry)] + params_history_flat = [tree_leaves(_start_x, namespace=VALUE_NAMESPACE)] criterion_history = [_start_crit_value] time_history = [np.inf] batches_history = [0] @@ -199,7 +198,7 @@ def _process_one_result(optimize_result, problem): history = optimize_result.history params_history = history.params params_history_flat = [ - tree_just_flatten(p, registry=_registry) for p in params_history + tree_leaves(p, namespace=VALUE_NAMESPACE) for p in params_history ] if _is_noisy: criterion_history = np.array([_criterion(p) for p in params_history]) diff --git a/src/optimagic/differentiation/derivatives.py b/src/optimagic/differentiation/derivatives.py index e2caf5daf..98f45bcd7 100644 --- a/src/optimagic/differentiation/derivatives.py +++ b/src/optimagic/differentiation/derivatives.py @@ -8,8 +8,6 @@ import numpy as np import pandas as pd from numpy.typing import NDArray -from pybaum import tree_flatten, tree_just_flatten, tree_unflatten -from pybaum import tree_just_flatten as tree_leaves from optimagic import batch_evaluators, deprecations from optimagic.config import DEFAULT_N_CORES @@ -22,8 +20,12 @@ from optimagic.differentiation.richardson_extrapolation import richardson_extrapolation from optimagic.parameters.block_trees import hessian_to_block_tree, matrix_to_block_tree from optimagic.parameters.bounds import Bounds, get_internal_bounds, pre_process_bounds -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import BatchEvaluatorLiteral, PyTree +from optimagic.parameters.tree_registry import ( + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE, BatchEvaluatorLiteral, PyTree @dataclass(frozen=True) @@ -214,24 +216,23 @@ def first_derivative( # ================================================================================== # Convert scalar | pytree arguments to 1d arrays of floats # ================================================================================== - registry = get_registry(extended=True) is_fast_path = _is_1d_array(params) if not is_fast_path: - x, params_treedef = tree_flatten(params, registry=registry) - x = np.array(x, dtype=np.float64) + params_leaves, params_treedef = tree_flatten(params, namespace=VALUE_NAMESPACE) + x = np.array(params_leaves, dtype=np.float64) if scaling_factor is not None and not np.isscalar(scaling_factor): scaling_factor = np.array( - tree_just_flatten(scaling_factor, registry=registry) + tree_leaves(scaling_factor, namespace=VALUE_NAMESPACE) ) if min_steps is not None and not np.isscalar(min_steps): - min_steps = np.array(tree_just_flatten(min_steps, registry=registry)) + min_steps = np.array(tree_leaves(min_steps, namespace=VALUE_NAMESPACE)) if step_size is not None and not np.isscalar(step_size): - step_size = np.array(tree_just_flatten(step_size, registry=registry)) + step_size = np.array(tree_leaves(step_size, namespace=VALUE_NAMESPACE)) else: x = params.astype(np.float64) @@ -271,7 +272,7 @@ def first_derivative( step_size = cast(NDArray[np.float64], step_size) # generate parameter vectors at which func has to be evaluated as numpy arrays - evaluation_points = [] + evaluation_points: list[float | np.ndarray] = [] for step_arr in step_size: for i, j in product(range(n_steps), range(len(x))): if np.isnan(step_arr[i, j]): @@ -285,7 +286,7 @@ def first_derivative( if not is_fast_path: evaluation_points = [ # entries are either a numpy.ndarray or np.nan - _unflatten_if_not_nan(p, params_treedef, registry) + _unflatten_if_not_nan(p, params_treedef, VALUE_NAMESPACE) for p in evaluation_points ] @@ -324,14 +325,14 @@ def first_derivative( elif vector_out: f0 = f0_tree.astype(float) else: - f0 = tree_leaves(f0_tree, registry=registry) + f0 = tree_leaves(f0_tree, namespace=VALUE_NAMESPACE) f0 = np.array(f0, dtype=np.float64) # convert the raw evaluations to numpy arrays raw_evals_arr = _convert_evals_to_numpy( raw_evals=raw_evals, unpacker=unpacker, - registry=registry, + namespace=VALUE_NAMESPACE, is_scalar_out=scalar_out, is_vector_out=vector_out, ) @@ -529,24 +530,23 @@ def second_derivative( # ================================================================================== # Convert scalar | pytree arguments to 1d arrays of floats # ================================================================================== - registry = get_registry(extended=True) is_fast_path = _is_1d_array(params) if not is_fast_path: - x, params_treedef = tree_flatten(params, registry=registry) - x = np.array(x, dtype=np.float64) + params_leaves, params_treedef = tree_flatten(params, namespace=VALUE_NAMESPACE) + x = np.array(params_leaves, dtype=np.float64) if scaling_factor is not None and not np.isscalar(scaling_factor): scaling_factor = np.array( - tree_just_flatten(scaling_factor, registry=registry) + tree_leaves(scaling_factor, namespace=VALUE_NAMESPACE) ) if min_steps is not None and not np.isscalar(min_steps): - min_steps = np.array(tree_just_flatten(min_steps, registry=registry)) + min_steps = np.array(tree_leaves(min_steps, namespace=VALUE_NAMESPACE)) if step_size is not None and not np.isscalar(step_size): - step_size = np.array(tree_just_flatten(step_size, registry=registry)) + step_size = np.array(tree_leaves(step_size, namespace=VALUE_NAMESPACE)) else: x = params.astype(np.float64) @@ -622,7 +622,8 @@ def second_derivative( evaluation_points = { # entries are either a numpy.ndarray or np.nan, we unflatten only step_type: [ - _unflatten_if_not_nan(p, params_treedef, registry) for p in points + _unflatten_if_not_nan(p, params_treedef, VALUE_NAMESPACE) + for p in points ] for step_type, points in evaluation_points.items() } @@ -661,13 +662,13 @@ def second_derivative( func_value = f0 f0_tree = unpacker(f0) - f0 = tree_leaves(f0_tree, registry=registry) + f0 = tree_leaves(f0_tree, namespace=VALUE_NAMESPACE) f0 = np.array(f0, dtype=np.float64) # convert the raw evaluations to numpy arrays raw_evals = { step_type: _convert_evals_to_numpy( - raw_evals=evals, unpacker=unpacker, registry=registry + raw_evals=evals, unpacker=unpacker, namespace=VALUE_NAMESPACE ) for step_type, evals in raw_evals.items() } @@ -921,7 +922,7 @@ def _convert_richardson_candidates_to_frame(jac, err): def _convert_evals_to_numpy( - raw_evals, unpacker, registry, is_scalar_out=False, is_vector_out=False + raw_evals, unpacker, namespace, is_scalar_out=False, is_vector_out=False ): """Harmonize the output of the function evaluations. @@ -945,7 +946,7 @@ def _convert_evals_to_numpy( else: evals = [ ( - np.array(tree_leaves(val, registry=registry), dtype=np.float64) + np.array(tree_leaves(val, namespace=namespace), dtype=np.float64) if not _is_scalar_nan(val) else val ) @@ -1204,9 +1205,9 @@ def _is_scalar_nan(value): return isinstance(value, float) and np.isnan(value) -def _unflatten_if_not_nan(leaves, treedef, registry): +def _unflatten_if_not_nan(leaves, treedef, namespace): if isinstance(leaves, np.ndarray): - out = tree_unflatten(treedef, leaves, registry=registry) + out = tree_unflatten(treedef, leaves, namespace=namespace) else: out = leaves return out diff --git a/src/optimagic/examples/criterion_functions.py b/src/optimagic/examples/criterion_functions.py index bb925d399..a368d32a5 100644 --- a/src/optimagic/examples/criterion_functions.py +++ b/src/optimagic/examples/criterion_functions.py @@ -10,17 +10,17 @@ import numpy as np import pandas as pd from numpy.typing import NDArray -from pybaum import tree_just_flatten, tree_unflatten from optimagic import mark from optimagic.optimization.fun_value import ( FunctionValue, ) from optimagic.parameters.block_trees import matrix_to_block_tree -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import PyTree - -REGISTRY = get_registry(extended=True) +from optimagic.parameters.tree_registry import ( + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE, PyTree @mark.scalar @@ -214,11 +214,10 @@ def _get_x(params: PyTree) -> NDArray[np.float64]: if isinstance(params, np.ndarray) and params.ndim == 1: x = params.astype(float) else: - registry = get_registry(extended=True) - x = np.array(tree_just_flatten(params, registry=registry), dtype=np.float64) + x = np.array(tree_leaves(params, namespace=VALUE_NAMESPACE), dtype=np.float64) return x def _unflatten_gradient(flat: NDArray[np.float64], params: PyTree) -> PyTree: - out = tree_unflatten(params, flat.tolist(), registry=REGISTRY) + out = tree_unflatten(params, flat.tolist(), namespace=VALUE_NAMESPACE) return out diff --git a/src/optimagic/optimization/fun_value.py b/src/optimagic/optimization/fun_value.py index aeb4f3dda..f49f94b1e 100644 --- a/src/optimagic/optimization/fun_value.py +++ b/src/optimagic/optimization/fun_value.py @@ -5,11 +5,10 @@ import numpy as np from numpy.typing import NDArray -from pybaum import tree_just_flatten from optimagic.exceptions import InvalidFunctionError -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import AggregationLevel, PyTree, Scalar +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE, AggregationLevel, PyTree, Scalar from optimagic.utilities import isscalar @@ -124,8 +123,8 @@ def _get_flat_value(value: PyTree) -> NDArray[np.float64]: elif isinstance(value, np.ndarray): flat = value.flatten() else: - registry = get_registry(extended=True) - flat = tree_just_flatten(value, registry=registry) + value_leaves = tree_leaves(value, namespace=VALUE_NAMESPACE) + flat = np.asarray(value_leaves, dtype=np.float64) flat_arr = np.asarray(flat, dtype=np.float64) return flat_arr diff --git a/src/optimagic/optimization/history.py b/src/optimagic/optimization/history.py index 73bea2d93..8e7ae77b2 100644 --- a/src/optimagic/optimization/history.py +++ b/src/optimagic/optimization/history.py @@ -6,11 +6,13 @@ import numpy as np import pandas as pd from numpy.typing import NDArray -from pybaum import leaf_names, tree_just_flatten -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_leaves, +) from optimagic.timing import CostModel -from optimagic.typing import Direction, EvalTask, PyTree +from optimagic.typing import VALUE_NAMESPACE, Direction, EvalTask, PyTree @dataclass(frozen=True) @@ -398,8 +400,7 @@ def _get_flat_params(params: list[PyTree]) -> list[list[float]]: if fast_path: flatten = lambda x: x.tolist() else: - registry = get_registry(extended=True) - flatten = partial(tree_just_flatten, registry=registry) + flatten = partial(tree_leaves, namespace=VALUE_NAMESPACE) return [flatten(p) for p in params] @@ -411,8 +412,7 @@ def _get_flat_param_names(param: PyTree) -> list[str]: # arrays, but the fast path is only taken for 1d arrays, so it can be ignored. return np.arange(param.size).astype(str).tolist() - registry = get_registry(extended=True) - return leaf_names(param, registry=registry) + return leaf_names(param, namespace=VALUE_NAMESPACE) def _is_1d_array(param: PyTree) -> bool: diff --git a/src/optimagic/parameters/block_trees.py b/src/optimagic/parameters/block_trees.py index 269898b0d..ea62dbf7d 100644 --- a/src/optimagic/parameters/block_trees.py +++ b/src/optimagic/parameters/block_trees.py @@ -2,10 +2,13 @@ import numpy as np import pandas as pd -from pybaum import tree_flatten, tree_unflatten -from pybaum import tree_just_flatten as tree_leaves -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE def matrix_to_block_tree(matrix, outer_tree, inner_tree): @@ -329,9 +332,8 @@ def _is_pd_object(obj): def _check_dimensions_matrix(matrix, outer_tree, inner_tree): - extended_registry = get_registry(extended=True) - flat_outer = tree_leaves(outer_tree, registry=extended_registry) - flat_inner = tree_leaves(inner_tree, registry=extended_registry) + flat_outer = tree_leaves(outer_tree, namespace=VALUE_NAMESPACE) + flat_inner = tree_leaves(inner_tree, namespace=VALUE_NAMESPACE) if matrix.shape[0] != len(flat_outer): raise ValueError("First dimension of matrix does not match that of outer_tree.") @@ -342,9 +344,8 @@ def _check_dimensions_matrix(matrix, outer_tree, inner_tree): def _check_dimensions_hessian(hessian, f_tree, params_tree): - extended_registry = get_registry(extended=True) - flat_f = tree_leaves(f_tree, registry=extended_registry) - flat_p = tree_leaves(params_tree, registry=extended_registry) + flat_f = tree_leaves(f_tree, namespace=VALUE_NAMESPACE) + flat_p = tree_leaves(params_tree, namespace=VALUE_NAMESPACE) if len(flat_f) == 1: # consider only dimensions with non trivial size (larger than 1) diff --git a/src/optimagic/parameters/bounds.py b/src/optimagic/parameters/bounds.py index 344dca4f4..827660d94 100644 --- a/src/optimagic/parameters/bounds.py +++ b/src/optimagic/parameters/bounds.py @@ -5,13 +5,15 @@ import numpy as np from numpy.typing import NDArray -from pybaum import leaf_names, tree_map -from pybaum import tree_just_flatten as tree_leaves from scipy.optimize import Bounds as ScipyBounds from optimagic.exceptions import InvalidBoundsError -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import PyTree, PyTreeRegistry +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_leaves, + tree_map, +) +from optimagic.typing import VALUE_NAMESPACE, PyTree from optimagic.utilities import fast_numpy_full @@ -75,7 +77,7 @@ def _process_bounds_sequence(bounds: Sequence[tuple[float, float]]) -> Bounds: def get_internal_bounds( params: PyTree, bounds: Bounds | None = None, - registry: PyTreeRegistry | None = None, + namespace: str = VALUE_NAMESPACE, add_soft_bounds: bool = False, ) -> tuple[NDArray[np.float64] | None, NDArray[np.float64] | None]: """Create consolidated and flattened bounds for params. @@ -92,7 +94,7 @@ def get_internal_bounds( Args: params: The parameter pytree. bounds: The lower and upper bounds. - registry: pybaum registry. + namespace: optree namespace. add_soft_bounds: If True, the element-wise maximum (minimum) of the lower and soft_lower (upper and soft_upper) bounds are taken. If False, the lower (upper) bounds are returned. @@ -116,12 +118,11 @@ def get_internal_bounds( # None-valued bounds are replaced with arrays of np.inf and -np.inf, and then # translated back to None if all entries are non-finite. - registry = get_registry(extended=True) if registry is None else registry - n_params = len(tree_leaves(params, registry=registry)) + n_params = len(tree_leaves(params, namespace=namespace)) # Fill leaves with np.nan. If params contains a data frame with bounds as a column, # that column is NOT overwritten (as long as an extended registry is used). - nan_tree = tree_map(lambda leaf: np.nan, params, registry=registry) # noqa: ARG005 + nan_tree = tree_map(lambda leaf: np.nan, params, namespace=namespace) # noqa: ARG005 lower_flat = _update_bounds_and_flatten(nan_tree, bounds.lower, kind="lower_bound") upper_flat = _update_bounds_and_flatten(nan_tree, bounds.upper, kind="upper_bound") @@ -176,16 +177,17 @@ def _update_bounds_and_flatten( np.ndarray: The updated and flattened bounds. """ - registry = get_registry(extended=True, data_col=kind) - flat_nan_tree = tree_leaves(nan_tree, registry=registry) - + flat_nan_tree = tree_leaves(nan_tree, namespace=kind) if bounds is not None: - registry = get_registry(extended=True) - flat_bounds = tree_leaves(bounds, registry=registry) + flat_bounds = tree_leaves(bounds, namespace=VALUE_NAMESPACE) seperator = 10 * "$" - params_names = leaf_names(nan_tree, registry=registry, separator=seperator) - bounds_names = leaf_names(bounds, registry=registry, separator=seperator) + params_names = leaf_names( + nan_tree, namespace=VALUE_NAMESPACE, separator=seperator + ) + bounds_names = leaf_names( + bounds, namespace=VALUE_NAMESPACE, separator=seperator + ) flat_nan_dict = dict(zip(params_names, flat_nan_tree, strict=False)) diff --git a/src/optimagic/parameters/nonlinear_constraints.py b/src/optimagic/parameters/nonlinear_constraints.py index 0cdd8e345..71171b7b9 100644 --- a/src/optimagic/parameters/nonlinear_constraints.py +++ b/src/optimagic/parameters/nonlinear_constraints.py @@ -4,13 +4,17 @@ import numpy as np import pandas as pd -from pybaum import tree_flatten, tree_just_flatten, tree_unflatten from optimagic.differentiation.derivatives import first_derivative from optimagic.exceptions import InvalidConstraintError, InvalidFunctionError from optimagic.optimization.algo_options import CONSTRAINTS_ABSOLUTE_TOLERANCE from optimagic.parameters.block_trees import block_tree_to_matrix -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE def process_nonlinear_constraints( @@ -361,14 +365,13 @@ def _extend_jacobian(jac_mat, selection_indices, n_params): def _get_selection_indices(params, selector): """Get index of selected flat params and number of flat params.""" - registry = get_registry(extended=True) - flat_params, params_treedef = tree_flatten(params, registry=registry) + flat_params, params_treedef = tree_flatten(params, namespace=VALUE_NAMESPACE) n_params = len(flat_params) indices = np.arange(n_params, dtype=int) - params_indices = tree_unflatten(params_treedef, indices, registry=registry) + params_indices = tree_unflatten(params_treedef, indices, namespace=VALUE_NAMESPACE) selected = selector(params_indices) selection_indices = np.array( - tree_just_flatten(selected, registry=registry), dtype=int + tree_leaves(selected, namespace=VALUE_NAMESPACE), dtype=int ) return selection_indices, n_params diff --git a/src/optimagic/parameters/process_selectors.py b/src/optimagic/parameters/process_selectors.py index 8a9276852..8b529518b 100644 --- a/src/optimagic/parameters/process_selectors.py +++ b/src/optimagic/parameters/process_selectors.py @@ -3,11 +3,11 @@ import numpy as np import pandas as pd -from pybaum import tree_just_flatten from optimagic.constraints import Constraint from optimagic.exceptions import InvalidConstraintError -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE def process_selectors(constraints, params, tree_converter, param_names): @@ -37,7 +37,6 @@ def process_selectors(constraints, params, tree_converter, param_names): if isinstance(constraints, dict): constraints = [constraints] - registry = get_registry(extended=True) n_params = len(tree_converter.params_flatten(params)) helper = tree_converter.params_unflatten(np.arange(n_params)) params_case = _get_params_case(params) @@ -53,7 +52,7 @@ def process_selectors(constraints, params, tree_converter, param_names): field=field, constraint=constr, params_case=params_case, - registry=registry, + namespace=VALUE_NAMESPACE, ) try: with warnings.catch_warnings(): @@ -136,19 +135,19 @@ def _get_selection_field(constraint, selector_case, params_case): return field -def _get_selection_evaluator(field, constraint, params_case, registry): +def _get_selection_evaluator(field, constraint, params_case, namespace): if field == "selector": def evaluator(params): raw = constraint["selector"](params) - flat = tree_just_flatten(raw, registry=registry) + flat = tree_leaves(raw, namespace=namespace) return flat elif field == "selectors": def evaluator(params): raw = [sel(params) for sel in constraint["selectors"]] - flat = [tree_just_flatten(r, registry=registry) for r in raw] + flat = [tree_leaves(r, namespace=namespace) for r in raw] return flat elif field == "loc": diff --git a/src/optimagic/parameters/tree_conversion.py b/src/optimagic/parameters/tree_conversion.py index 2e29fd87e..972ec017a 100644 --- a/src/optimagic/parameters/tree_conversion.py +++ b/src/optimagic/parameters/tree_conversion.py @@ -1,13 +1,17 @@ from typing import Callable, NamedTuple import numpy as np -from pybaum import leaf_names, tree_flatten, tree_just_flatten, tree_unflatten from optimagic.exceptions import InvalidFunctionError from optimagic.parameters.block_trees import block_tree_to_matrix from optimagic.parameters.bounds import get_internal_bounds -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import AggregationLevel +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE, AggregationLevel def get_tree_converter( @@ -45,26 +49,25 @@ def get_tree_converter( FlatParams: NamedTuple of 1d arrays with flattened bounds and param names. """ - _registry = get_registry(extended=True) - _params_vec, _params_treedef = tree_flatten(params, registry=_registry) + _params_vec, _params_treedef = tree_flatten(params, namespace=VALUE_NAMESPACE) _params_vec = np.array(_params_vec).astype(float) _lower, _upper = get_internal_bounds( params=params, bounds=bounds, - registry=_registry, + namespace=VALUE_NAMESPACE, ) if add_soft_bounds: _soft_lower, _soft_upper = get_internal_bounds( params=params, bounds=bounds, - registry=_registry, + namespace=VALUE_NAMESPACE, add_soft_bounds=add_soft_bounds, ) else: _soft_lower, _soft_upper = None, None - _param_names = leaf_names(params, registry=_registry) + _param_names = leaf_names(params, namespace=VALUE_NAMESPACE) flat_params = FlatParams( values=_params_vec, @@ -75,13 +78,13 @@ def get_tree_converter( soft_upper_bounds=_soft_upper, ) - _params_flatten = _get_params_flatten(registry=_registry) + _params_flatten = _get_params_flatten(namespace=VALUE_NAMESPACE) _params_unflatten = _get_params_unflatten( - registry=_registry, treedef=_params_treedef + namespace=VALUE_NAMESPACE, treedef=_params_treedef ) _derivative_flatten = _get_derivative_flatten( - registry=_registry, + namespace=VALUE_NAMESPACE, solver_type=solver_type, params=params, func_eval=func_eval, @@ -97,16 +100,16 @@ def get_tree_converter( return converter, flat_params -def _get_params_flatten(registry): +def _get_params_flatten(namespace): def params_flatten(params): - return np.array(tree_just_flatten(params, registry=registry)).astype(float) + return np.array(tree_leaves(params, namespace=namespace)).astype(float) return params_flatten -def _get_params_unflatten(registry, treedef): +def _get_params_unflatten(namespace, treedef): def params_unflatten(x): - return tree_unflatten(treedef=treedef, leaves=list(x), registry=registry) + return tree_unflatten(treedef=treedef, leaves=list(x), namespace=namespace) return params_unflatten @@ -138,14 +141,14 @@ def _get_best_key_and_aggregator(needed_key, available_keys): return key, aggregate -def _get_derivative_flatten(registry, solver_type, params, func_eval, derivative_eval): +def _get_derivative_flatten(namespace, solver_type, params, func_eval, derivative_eval): # gradient case if solver_type == AggregationLevel.SCALAR: def derivative_flatten(derivative_eval): - flat = np.array( - tree_just_flatten(derivative_eval, registry=registry) - ).astype(float) + flat = np.array(tree_leaves(derivative_eval, namespace=namespace)).astype( + float + ) return flat # jacobian case diff --git a/src/optimagic/parameters/tree_registry.py b/src/optimagic/parameters/tree_registry.py index 808ad81ac..e8e23cb88 100644 --- a/src/optimagic/parameters/tree_registry.py +++ b/src/optimagic/parameters/tree_registry.py @@ -1,50 +1,211 @@ -"""Wrapper around pybaum get_registry to tailor it to optimagic.""" +"""Wrapper around optree to tailor it to optimagic.""" +import warnings from functools import partial from itertools import product +from typing import Any, Callable, Iterable import numpy as np +import optree import pandas as pd -from pybaum import get_registry as get_pybaum_registry +from optree.pytree import PyTreeSpec +from optimagic.config import IS_JAX_INSTALLED +from optimagic.typing import DEFAULT_NAMESPACE, OPTREE_NAMESPACES, PyTree -def get_registry(extended=False, data_col="value"): - """Return pytree registry. +if IS_JAX_INSTALLED: + import jax.numpy as jnp # type: ignore[import-not-found] + import jaxlib # type: ignore[import-not-found] - Special Rules - ------------- - If extended is True the registry contains pd.DataFrame. In optimagic a data frame - can represent a 1d object with extra information, instead of a 2d object. This is - only allowed for params data frames, in which case they contain a 'value' column. - The extra information of such an object can be accessed using the data_col argument. - By default the 'value' column is extracted. If data_col is not 'value' but the data - frame contains a 'value' column, a list of np.nan is returned. - Args: - extended (bool): If True appends types 'numpy.ndarray', 'pandas.Series' and - 'pandas.DataFrame' to the registry. - data_col (str): This column is used as the data source in a data frame when - flattening and unflattening a pytree. Defaults to 'value'; see special rules - above for behavior with non-default values. +_are_namespaces_registered = False - Returns: - dict: The pytree registry. - """ - types = ( - ["numpy.ndarray", "pandas.Series", "jax.numpy.ndarray"] if extended else None +def tree_flatten( + tree: PyTree, + is_leaf: Callable[[PyTree], bool] | None = None, + namespace: str = DEFAULT_NAMESPACE, +) -> tuple[list, PyTreeSpec]: + """Flatten a pytree.""" + _register_namespaces() + _check_namespace(namespace) + with optree.dict_insertion_ordered(True, namespace=namespace): + return optree.tree_flatten(tree, is_leaf=is_leaf, namespace=namespace) + + +def tree_leaves( + tree: PyTree, + is_leaf: Callable[[PyTree], bool] | None = None, + namespace: str = DEFAULT_NAMESPACE, +) -> list: + """Get the leaves of a pytree.""" + _register_namespaces() + _check_namespace(namespace) + with optree.dict_insertion_ordered(True, namespace=namespace): + return optree.tree_leaves(tree, is_leaf=is_leaf, namespace=namespace) + + +def tree_unflatten( + treedef: PyTree | PyTreeSpec, + leaves: Iterable, + namespace: str = DEFAULT_NAMESPACE, +) -> PyTree: + """Reconstruct a pytree from the tree definition and the leaves.""" + _register_namespaces() + + if not isinstance(treedef, PyTreeSpec): + _check_namespace(namespace) + with optree.dict_insertion_ordered(True, namespace=namespace): + treedef = optree.tree_structure(treedef, namespace=namespace) + + # Doesn't need to be wrapped with dict_insertion_ordered + # because it keeps the insertion order for dictionaries by default. + return optree.tree_unflatten(treedef, leaves) + + +def tree_map( + func: Callable[[PyTree], PyTree], + tree: PyTree, + is_leaf: Callable[[PyTree], bool] | None = None, + namespace: str = DEFAULT_NAMESPACE, +) -> PyTree: + """Map an input function over pytree args to produce a new pytree.""" + _register_namespaces() + _check_namespace(namespace) + + # Doesn't need to be wrapped with dict_insertion_ordered + # because it keeps the insertion order for dictionaries by default. + return optree.tree_map(func, tree, is_leaf=is_leaf, namespace=namespace) + + +def leaf_names( + tree: PyTree, + is_leaf: Callable[[PyTree], bool] | None = None, + namespace: str = DEFAULT_NAMESPACE, + separator: str = "_", +) -> list[str]: + """Get the path names for tree leaves.""" + _register_namespaces() + _check_namespace(namespace) + + with optree.dict_insertion_ordered(True, namespace=namespace): + paths, _, _ = optree.tree_flatten_with_path( + tree, is_leaf=is_leaf, namespace=namespace + ) + return [separator.join(str(p) for p in path) for path in paths] + + +def tree_equal( + tree: PyTree, + other: PyTree, + is_leaf: Callable[[PyTree], bool] | None = None, + namespace: str = DEFAULT_NAMESPACE, + equality_checkers: dict[str, Callable[[Any, Any], bool]] | None = None, +) -> bool: + """Check the equality between two trees.""" + equality_checkers = ( + _get_equality_checkers() + if equality_checkers is None + else {**_get_equality_checkers(), **equality_checkers} + ) + + first_flat, first_treespec = tree_flatten( + tree, is_leaf=is_leaf, namespace=namespace + ) + second_flat, second_treespec = tree_flatten( + other, is_leaf=is_leaf, namespace=namespace ) - registry = get_pybaum_registry(types=types) - if extended: - registry[pd.DataFrame] = { - "flatten": partial(_flatten_df, data_col=data_col), - "unflatten": partial(_unflatten_df, data_col=data_col), - "names": _get_df_names, - } - return registry + + first_names = leaf_names(tree, is_leaf=is_leaf, namespace=namespace) + second_names = leaf_names(other, is_leaf=is_leaf, namespace=namespace) + + equal = first_names == second_names and first_treespec == second_treespec + + if equal: + for first, second in zip(first_flat, second_flat, strict=True): + check_func = equality_checkers.get( + type(first).__name__, lambda a, b: a == b + ) + equal = equal and check_func(first, second) + if not equal: + break + + return equal + + +def _get_equality_checkers(): + """Return type-specific equality checkers for array and DataFrame leaves. + + These are used during pytree operations to compare leaves that don't + support simple ``==`` equality (e.g. NumPy arrays, pandas objects). + """ + equality_checkers = {} + equality_checkers[np.ndarray.__name__] = lambda a, b: bool((a == b).all()) + equality_checkers[pd.Series.__name__] = lambda a, b: a.equals(b) + equality_checkers[pd.DataFrame.__name__] = lambda a, b: a.equals(b) + + if IS_JAX_INSTALLED: + equality_checkers[jnp.ndarray.__name__] = lambda a, b: bool((a == b).all()) + + return equality_checkers + + +def _check_namespace(namespace: str) -> None: + """Checks if the namespace is registered and raise a warning.""" + if namespace != DEFAULT_NAMESPACE and namespace not in OPTREE_NAMESPACES: + warnings.warn( + f"Namespace '{namespace}' is not registered. " + f"Registered namespaces are: {','.join(OPTREE_NAMESPACES)}. " + "Pytree method is being parsed with the default optree namespace." + ) + + +def _register_namespaces() -> None: + """Register pytree flatten/unflatten methods for each namespace. + + This method must only be called once as each namespace must only be registered + one time. + """ + global _are_namespaces_registered # noqa: PLW0603 + if _are_namespaces_registered is False: + _are_namespaces_registered = True + for namespace in OPTREE_NAMESPACES: + optree.register_pytree_node( + pd.DataFrame, + partial(_flatten_df, data_col=namespace), + partial(_unflatten_df, data_col=namespace), + namespace=namespace, + ) + + optree.register_pytree_node( + pd.Series, + _flatten_series, + _unflatten_series, + namespace=namespace, + ) + + optree.register_pytree_node( + np.ndarray, + _flatten_ndarray, + _unflatten_ndarray, + namespace=namespace, + ) + + if IS_JAX_INSTALLED: + optree.register_pytree_node( + jaxlib._jax.ArrayImpl, + lambda arr: ( + arr.flatten().tolist(), # type: ignore[attr-defined] + arr.shape, # type: ignore[attr-defined] + _array_element_names(arr), # type: ignore[arg-type] + ), + lambda aux_data, leaves: jnp.array(leaves).reshape(aux_data), + namespace=namespace, + ) def _flatten_df(df, data_col): + """Flatten a dataframe.""" is_value_df = "value" in df if is_value_df: flat = df.get(data_col, default=np.full(len(df), np.nan)).tolist() @@ -55,10 +216,11 @@ def _flatten_df(df, data_col): "is_value_df": is_value_df, "df": df, } - return flat, aux_data + return flat, aux_data, _get_df_names(df) def _unflatten_df(aux_data, leaves, data_col): + """Reconstruct a dataframe.""" if aux_data["is_value_df"]: out = aux_data["df"].assign(**{data_col: leaves}) else: @@ -70,7 +232,32 @@ def _unflatten_df(aux_data, leaves, data_col): return out -def _get_df_names(df): +def _flatten_series(series): + """Flatten a series.""" + return ( + series.tolist(), + {"index": series.index, "name": series.name}, + list(series.index.map(_index_element_to_string)), + ) + + +def _unflatten_series(aux_data, leaves): + """Reconstruct a series.""" + return pd.Series(leaves, **aux_data) + + +def _flatten_ndarray(arr): + """Flatten a numpy array.""" + return arr.flatten().tolist(), arr.shape, _array_element_names(arr) + + +def _unflatten_ndarray(aux_data, leaves): + """Reconstrut a numpy array.""" + return np.array(leaves).reshape(aux_data) + + +def _get_df_names(df: pd.DataFrame) -> list[str]: + """Get string names for dataframe leaf paths.""" index_strings = list(df.index.map(_index_element_to_string)) if "value" in df: out = index_strings @@ -80,7 +267,8 @@ def _get_df_names(df): return out -def _index_element_to_string(element): +def _index_element_to_string(element: Any) -> str: + """Convert an index element to its string representation.""" if isinstance(element, (tuple, list)): as_strings = [str(entry) for entry in element] res_string = "_".join(as_strings) @@ -88,3 +276,10 @@ def _index_element_to_string(element): res_string = str(element) return res_string + + +def _array_element_names(arr: np.ndarray) -> list[str]: + """Get string names for array like element leaf paths.""" + dim_names = [map(str, range(n)) for n in arr.shape] + names = list(map("_".join, product(*dim_names))) + return names diff --git a/src/optimagic/typing.py b/src/optimagic/typing.py index 9b389ced2..1b7a7e8b6 100644 --- a/src/optimagic/typing.py +++ b/src/optimagic/typing.py @@ -18,7 +18,6 @@ from numpy._typing import NDArray PyTree = Any -PyTreeRegistry = dict[type | str, dict[str, Callable[[Any], Any]]] Scalar = Any T = TypeVar("T") @@ -173,3 +172,14 @@ class MultiStartIterationHistory(TupleLikeAccess): history: IterationHistory local_histories: list[IterationHistory] | None = None exploration: IterationHistory | None = None + + +DEFAULT_NAMESPACE = "optimagic_namespace" +OPTREE_NAMESPACES = ( + "value", + "lower_bound", + "upper_bound", + "soft_lower_bound", + "soft_upper_bound", +) +VALUE_NAMESPACE = OPTREE_NAMESPACES[0] diff --git a/src/optimagic/visualization/history_plots.py b/src/optimagic/visualization/history_plots.py index 72dc7ab07..d23def1ac 100644 --- a/src/optimagic/visualization/history_plots.py +++ b/src/optimagic/visualization/history_plots.py @@ -5,15 +5,19 @@ from typing import Any, Callable, Literal import numpy as np -from pybaum import leaf_names, tree_flatten, tree_just_flatten, tree_unflatten from optimagic.config import DEFAULT_PALETTE from optimagic.logging.logger import LogReader, SQLiteLogOptions from optimagic.optimization.algorithm import Algorithm from optimagic.optimization.history import History from optimagic.optimization.optimize_result import OptimizeResult -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import IterationHistory, PyTree +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE, IterationHistory, PyTree from optimagic.visualization.backends import line_plot from optimagic.visualization.plotting_utilities import LineData, get_palette_cycle @@ -580,15 +584,15 @@ def _extract_params_plot_lines( history = data.history.params start_params = data.start_params - registry = get_registry(extended=True) - - hist_arr = np.array([tree_just_flatten(p, registry=registry) for p in history]).T - names = leaf_names(start_params, registry=registry) + hist_arr = np.array([tree_leaves(p, namespace=VALUE_NAMESPACE) for p in history]).T + names = leaf_names(start_params, namespace=VALUE_NAMESPACE) if selector is not None: - flat, treedef = tree_flatten(start_params, registry=registry) - helper = tree_unflatten(treedef, list(range(len(flat))), registry=registry) - selected = np.array(tree_just_flatten(selector(helper), registry=registry)) + flat, treedef = tree_flatten(start_params, namespace=VALUE_NAMESPACE) + helper = tree_unflatten( + treedef, list(range(len(flat))), namespace=VALUE_NAMESPACE + ) + selected = np.array(tree_leaves(selector(helper), namespace=VALUE_NAMESPACE)) names = [names[i] for i in selected] hist_arr = hist_arr[selected] diff --git a/src/optimagic/visualization/slice_plot.py b/src/optimagic/visualization/slice_plot.py index 92802cf3f..fc766c5ca 100644 --- a/src/optimagic/visualization/slice_plot.py +++ b/src/optimagic/visualization/slice_plot.py @@ -5,7 +5,6 @@ import numpy as np import pandas as pd from numpy.typing import NDArray -from pybaum import tree_just_flatten import optimagic as om from optimagic import deprecations @@ -24,9 +23,9 @@ from optimagic.parameters.bounds import pre_process_bounds from optimagic.parameters.conversion import get_converter from optimagic.parameters.space_conversion import InternalParams -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves from optimagic.shared.process_user_function import infer_aggregation_level -from optimagic.typing import AggregationLevel, PyTree +from optimagic.typing import VALUE_NAMESPACE, AggregationLevel, PyTree from optimagic.visualization.backends import grid_line_plot, line_plot from optimagic.visualization.plotting_utilities import LineData, MarkerData @@ -249,9 +248,8 @@ def _get_plot_data( selected = np.arange(n_params, dtype=int) if selector is not None: helper = converter.params_from_internal(selected) - registry = get_registry(extended=True) selected = np.array( - tree_just_flatten(selector(helper), registry=registry), dtype=int + tree_leaves(selector(helper), namespace=VALUE_NAMESPACE), dtype=int ).ravel() # Ensure the result is a 1D array if not np.isfinite(internal_params.lower_bounds[selected]).all(): diff --git a/src/optimagic/visualization/slice_plot_3d.py b/src/optimagic/visualization/slice_plot_3d.py index 1b6a7fc90..f2dca688a 100644 --- a/src/optimagic/visualization/slice_plot_3d.py +++ b/src/optimagic/visualization/slice_plot_3d.py @@ -8,7 +8,6 @@ import plotly.graph_objects as go from numpy.typing import NDArray from plotly.subplots import make_subplots -from pybaum import tree_just_flatten from optimagic import deprecations from optimagic.batch_evaluators import process_batch_evaluator @@ -20,9 +19,9 @@ ) from optimagic.parameters.bounds import pre_process_bounds from optimagic.parameters.conversion import get_converter -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves from optimagic.shared.process_user_function import infer_aggregation_level -from optimagic.typing import AggregationLevel +from optimagic.typing import VALUE_NAMESPACE, AggregationLevel def slice_plot_3d( # type: ignore[no-untyped-def] @@ -150,9 +149,8 @@ def slice_plot_3d( # type: ignore[no-untyped-def] selected = np.arange(n_params, dtype=int) if selector is not None: helper = converter.params_from_internal(selected) - registry = get_registry(extended=True) selected = np.array( - tree_just_flatten(selector(helper), registry=registry), dtype=int + tree_leaves(selector(helper), namespace=VALUE_NAMESPACE), dtype=int ).reshape(-1) n_params = len(selected) if not np.isfinite(internal_params.lower_bounds[selected]).all(): diff --git a/tests/estimagic/test_bootstrap_ci.py b/tests/estimagic/test_bootstrap_ci.py index 64562438d..a92286679 100644 --- a/tests/estimagic/test_bootstrap_ci.py +++ b/tests/estimagic/test_bootstrap_ci.py @@ -3,11 +3,11 @@ import numpy as np import pandas as pd import pytest -from pybaum import tree_just_flatten from estimagic.bootstrap_ci import calculate_ci, check_inputs from estimagic.bootstrap_samples import get_bootstrap_indices -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE from optimagic.utilities import get_rng @@ -69,10 +69,9 @@ def _outcome_func_arr(data): @pytest.mark.parametrize("outcome, method", TEST_CASES) def test_ci(outcome, method, setup, expected): - registry = get_registry(extended=True) def outcome_flat(data): - return tree_just_flatten(outcome(data), registry=registry) + return tree_leaves(outcome(data), namespace=VALUE_NAMESPACE) base_outcome = outcome_flat(setup["df"]) lower, upper = calculate_ci(base_outcome, setup["estimates"], ci_method=method) diff --git a/tests/estimagic/test_estimate_msm_dict_params_and_moments.py b/tests/estimagic/test_estimate_msm_dict_params_and_moments.py index b1cbcd250..b5c3e8769 100644 --- a/tests/estimagic/test_estimate_msm_dict_params_and_moments.py +++ b/tests/estimagic/test_estimate_msm_dict_params_and_moments.py @@ -3,10 +3,10 @@ import numpy as np import pandas as pd from numpy.testing import assert_array_almost_equal as aaae -from pybaum import tree_just_flatten from estimagic.estimate_msm import estimate_msm -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE def test_estimate_msm_dict_params_and_moments(): @@ -98,8 +98,7 @@ def assert_almost_equal(x, y, decimal=6): x_flat = x y_flat = y else: - registry = get_registry(extended=True) - x_flat = np.array(tree_just_flatten(x, registry=registry)) - y_flat = np.array(tree_just_flatten(x, registry=registry)) + x_flat = np.array(tree_leaves(x, namespace=VALUE_NAMESPACE)) + y_flat = np.array(tree_leaves(x, namespace=VALUE_NAMESPACE)) aaae(x_flat, y_flat, decimal=decimal) diff --git a/tests/estimagic/test_shared.py b/tests/estimagic/test_shared.py index 9a4240c74..6bcb80257 100644 --- a/tests/estimagic/test_shared.py +++ b/tests/estimagic/test_shared.py @@ -4,7 +4,6 @@ import pandas as pd import pytest from numpy.testing import assert_array_almost_equal as aaae -from pybaum import leaf_names, tree_equal from estimagic.shared_covs import ( _to_numpy, @@ -15,7 +14,8 @@ transform_free_cov_to_cov, transform_free_values_to_params_tree, ) -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import leaf_names, tree_equal +from optimagic.typing import VALUE_NAMESPACE from optimagic.utilities import get_rng @@ -240,8 +240,7 @@ def test_calculate_estimation_summary(): "free": np.array([True, True, True]), } - registry = get_registry(extended=True) - names = leaf_names(summary_data["value"], registry=registry) + names = leaf_names(summary_data["value"], namespace=VALUE_NAMESPACE) free_names = names # function call diff --git a/tests/optimagic/differentiation/test_compare_derivatives_with_jax.py b/tests/optimagic/differentiation/test_compare_derivatives_with_jax.py index 87b5554d8..56cb39a0b 100644 --- a/tests/optimagic/differentiation/test_compare_derivatives_with_jax.py +++ b/tests/optimagic/differentiation/test_compare_derivatives_with_jax.py @@ -7,10 +7,10 @@ import numpy as np import pytest from numpy.testing import assert_array_almost_equal as aaae -from pybaum import tree_equal from optimagic.config import IS_JAX_INSTALLED from optimagic.differentiation.derivatives import first_derivative, second_derivative +from optimagic.parameters.tree_registry import tree_equal if not IS_JAX_INSTALLED: pytestmark = pytest.mark.skip(reason="jax is not installed.") diff --git a/tests/optimagic/logging/test_logger.py b/tests/optimagic/logging/test_logger.py index ff099d55f..8885fbf38 100644 --- a/tests/optimagic/logging/test_logger.py +++ b/tests/optimagic/logging/test_logger.py @@ -3,7 +3,6 @@ import numpy as np import pandas as pd import pytest -from pybaum import tree_equal, tree_just_flatten from optimagic.logging.logger import ( LogOptions, @@ -13,8 +12,11 @@ SQLiteLogReader, ) from optimagic.optimization.optimize import minimize -from optimagic.parameters.tree_registry import get_registry -from optimagic.typing import Direction +from optimagic.parameters.tree_registry import ( + tree_equal, + tree_leaves, +) +from optimagic.typing import VALUE_NAMESPACE, Direction @pytest.fixture() @@ -81,10 +83,9 @@ def test_log_reader_read_multistart_history(example_db): assert local_history is None assert exploration is None - registry = get_registry(extended=True) assert tree_equal( - tree_just_flatten(asdict(history), registry=registry), - tree_just_flatten(asdict(reader.read_history()), registry=registry), + tree_leaves(asdict(history), namespace=VALUE_NAMESPACE), + tree_leaves(asdict(reader.read_history()), namespace=VALUE_NAMESPACE), ) diff --git a/tests/optimagic/optimization/test_history.py b/tests/optimagic/optimization/test_history.py index cb03bc253..2dd4ed790 100644 --- a/tests/optimagic/optimization/test_history.py +++ b/tests/optimagic/optimization/test_history.py @@ -4,7 +4,6 @@ from numpy.testing import assert_array_almost_equal as aaae from numpy.testing import assert_array_equal from pandas.testing import assert_frame_equal -from pybaum import tree_map import optimagic as om from optimagic.optimization.history import ( @@ -19,6 +18,7 @@ _task_to_categorical, _validate_args_are_all_none_or_lists_of_same_length, ) +from optimagic.parameters.tree_registry import tree_map from optimagic.typing import Direction, EvalTask # ====================================================================================== diff --git a/tests/optimagic/optimization/test_params_versions.py b/tests/optimagic/optimization/test_params_versions.py index f3399cb12..49ee4cb0c 100644 --- a/tests/optimagic/optimization/test_params_versions.py +++ b/tests/optimagic/optimization/test_params_versions.py @@ -2,7 +2,6 @@ import pandas as pd import pytest from numpy.testing import assert_array_almost_equal as aaae -from pybaum import tree_just_flatten from optimagic.examples.criterion_functions import ( sos_gradient, @@ -11,9 +10,8 @@ sos_scalar, ) from optimagic.optimization.optimize import minimize -from optimagic.parameters.tree_registry import get_registry - -REGISTRY = get_registry(extended=True) +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE PARAMS = [ {"a": 1.0, "b": 2, "c": 3, "d": 4, "e": 5}, @@ -29,7 +27,7 @@ @pytest.mark.parametrize("params", PARAMS + SCALAR_PARAMS) def test_tree_params_numerical_derivative_scalar_criterion(params): - flat = np.array(tree_just_flatten(params, registry=REGISTRY)) + flat = np.array(tree_leaves(params, namespace=VALUE_NAMESPACE)) expected = np.zeros_like(flat) res = minimize( @@ -37,13 +35,13 @@ def test_tree_params_numerical_derivative_scalar_criterion(params): params=params, algorithm="scipy_lbfgsb", ) - calculated = np.array(tree_just_flatten(res.params, registry=REGISTRY)) + calculated = np.array(tree_leaves(res.params, namespace=VALUE_NAMESPACE)) aaae(calculated, expected) @pytest.mark.parametrize("params", PARAMS + SCALAR_PARAMS) def test_tree_params_scalar_criterion(params): - flat = np.array(tree_just_flatten(params, registry=REGISTRY)) + flat = np.array(tree_leaves(params, namespace=VALUE_NAMESPACE)) expected = np.zeros_like(flat) res = minimize( @@ -52,7 +50,7 @@ def test_tree_params_scalar_criterion(params): params=params, algorithm="scipy_lbfgsb", ) - calculated = np.array(tree_just_flatten(res.params, registry=REGISTRY)) + calculated = np.array(tree_leaves(res.params, namespace=VALUE_NAMESPACE)) aaae(calculated, expected) @@ -64,7 +62,7 @@ def test_tree_params_scalar_criterion(params): @pytest.mark.parametrize("params, algorithm", TEST_CASES_SOS_LS) def test_tree_params_numerical_derivative_sos_ls(params, algorithm): - flat = np.array(tree_just_flatten(params, registry=REGISTRY)) + flat = np.array(tree_leaves(params, namespace=VALUE_NAMESPACE)) expected = np.zeros_like(flat) res = minimize( @@ -72,13 +70,13 @@ def test_tree_params_numerical_derivative_sos_ls(params, algorithm): params=params, algorithm=algorithm, ) - calculated = np.array(tree_just_flatten(res.params, registry=REGISTRY)) + calculated = np.array(tree_leaves(res.params, namespace=VALUE_NAMESPACE)) aaae(calculated, expected) @pytest.mark.parametrize("params, algorithm", TEST_CASES_SOS_LS) def test_tree_params_sos_ls(params, algorithm): - flat = np.array(tree_just_flatten(params, registry=REGISTRY)) + flat = np.array(tree_leaves(params, namespace=VALUE_NAMESPACE)) expected = np.zeros_like(flat) derivatives = [sos_gradient, sos_ls_jacobian] @@ -88,5 +86,5 @@ def test_tree_params_sos_ls(params, algorithm): params=params, algorithm=algorithm, ) - calculated = np.array(tree_just_flatten(res.params, registry=REGISTRY)) + calculated = np.array(tree_leaves(res.params, namespace=VALUE_NAMESPACE)) aaae(calculated, expected) diff --git a/tests/optimagic/optimization/test_with_logging.py b/tests/optimagic/optimization/test_with_logging.py index b279f5202..41382e379 100644 --- a/tests/optimagic/optimization/test_with_logging.py +++ b/tests/optimagic/optimization/test_with_logging.py @@ -11,7 +11,6 @@ import pandas as pd import pytest from numpy.testing import assert_array_almost_equal as aaae -from pybaum import tree_just_flatten from optimagic import mark from optimagic.examples.criterion_functions import ( @@ -21,7 +20,8 @@ from optimagic.logging.logger import SQLiteLogOptions from optimagic.logging.types import ExistenceStrategy from optimagic.optimization.optimize import minimize -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE @mark.least_squares @@ -47,8 +47,7 @@ def test_optimization_with_valid_logging(algorithm, params): algorithm=algorithm, logging="logging.db", ) - registry = get_registry(extended=True) - flat = np.array(tree_just_flatten(res.params, registry=registry)) + flat = np.array(tree_leaves(res.params, namespace=VALUE_NAMESPACE)) aaae(flat, np.zeros(3)) diff --git a/tests/optimagic/parameters/test_block_trees.py b/tests/optimagic/parameters/test_block_trees.py index 08b2307cd..f3881c2ef 100644 --- a/tests/optimagic/parameters/test_block_trees.py +++ b/tests/optimagic/parameters/test_block_trees.py @@ -2,8 +2,6 @@ import pandas as pd import pytest from numpy.testing import assert_array_equal -from pybaum import tree_equal -from pybaum import tree_just_flatten as tree_leaves from optimagic import second_derivative from optimagic.parameters.block_trees import ( @@ -12,7 +10,8 @@ hessian_to_block_tree, matrix_to_block_tree, ) -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_equal, tree_leaves +from optimagic.typing import VALUE_NAMESPACE def test_matrix_to_block_tree_array_and_scalar(): @@ -129,9 +128,8 @@ def test_block_tree_to_hessian_bijection(): params = {"a": np.arange(4), "b": [{"c": (1, 2), "d": np.array([5, 6])}]} f_tree = {"e": np.arange(3), "f": (5, 6, [7, 8, {"g": 1.0}])} - registry = get_registry(extended=True) - n_p = len(tree_leaves(params, registry=registry)) - n_f = len(tree_leaves(f_tree, registry=registry)) + n_p = len(tree_leaves(params, namespace=VALUE_NAMESPACE)) + n_f = len(tree_leaves(f_tree, namespace=VALUE_NAMESPACE)) expected = np.arange(n_f * n_p**2).reshape(n_f, n_p, n_p) block_hessian = hessian_to_block_tree(expected, f_tree, params) diff --git a/tests/optimagic/parameters/test_nonlinear_constraints.py b/tests/optimagic/parameters/test_nonlinear_constraints.py index 2d0eeaa1c..34c0804d1 100644 --- a/tests/optimagic/parameters/test_nonlinear_constraints.py +++ b/tests/optimagic/parameters/test_nonlinear_constraints.py @@ -6,7 +6,6 @@ import pytest from numpy.testing import assert_array_equal from pandas.testing import assert_frame_equal -from pybaum import tree_just_flatten from optimagic.differentiation.numdiff_options import NumdiffOptions from optimagic.exceptions import InvalidConstraintError @@ -22,7 +21,8 @@ process_nonlinear_constraints, vector_as_list_of_scalar_constraints, ) -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import tree_leaves +from optimagic.typing import VALUE_NAMESPACE @dataclass @@ -31,8 +31,7 @@ def params_from_internal(self, x): return x def params_to_internal(self, params): - registry = get_registry(extended=True) - return np.array(tree_just_flatten(params, registry=registry)) + return np.array(tree_leaves(params, namespace=VALUE_NAMESPACE)) # ====================================================================================== diff --git a/tests/optimagic/parameters/test_process_selectors.py b/tests/optimagic/parameters/test_process_selectors.py index 7ad9c78e6..641046f83 100644 --- a/tests/optimagic/parameters/test_process_selectors.py +++ b/tests/optimagic/parameters/test_process_selectors.py @@ -2,12 +2,16 @@ import pandas as pd import pytest from numpy.testing import assert_array_equal as aae -from pybaum import tree_flatten, tree_just_flatten, tree_unflatten from optimagic.exceptions import InvalidConstraintError from optimagic.parameters.process_selectors import process_selectors from optimagic.parameters.tree_conversion import TreeConverter -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + tree_flatten, + tree_leaves, + tree_unflatten, +) +from optimagic.typing import VALUE_NAMESPACE @pytest.mark.parametrize("constraints", [None, []]) @@ -31,15 +35,14 @@ def tree_params(): @pytest.fixture() def tree_params_converter(tree_params): - registry = get_registry(extended=True) - _, treedef = tree_flatten(tree_params, registry=registry) + _, treedef = tree_flatten(tree_params, namespace=VALUE_NAMESPACE) converter = TreeConverter( params_flatten=lambda params: np.array( - tree_just_flatten(params, registry=registry) + tree_leaves(params, namespace=VALUE_NAMESPACE) ), params_unflatten=lambda x: tree_unflatten( - treedef, x.tolist(), registry=registry + treedef, x.tolist(), namespace=VALUE_NAMESPACE ), derivative_flatten=None, ) diff --git a/tests/optimagic/parameters/test_tree_registry.py b/tests/optimagic/parameters/test_tree_registry.py index 6f7362538..eccd27d25 100644 --- a/tests/optimagic/parameters/test_tree_registry.py +++ b/tests/optimagic/parameters/test_tree_registry.py @@ -2,9 +2,15 @@ import pandas as pd import pytest from pandas.testing import assert_frame_equal -from pybaum import leaf_names, tree_flatten, tree_unflatten -from optimagic.parameters.tree_registry import get_registry +from optimagic.parameters.tree_registry import ( + leaf_names, + tree_flatten, + tree_leaves, + tree_map, + tree_unflatten, +) +from optimagic.typing import OPTREE_NAMESPACES, VALUE_NAMESPACE @pytest.fixture() @@ -25,40 +31,171 @@ def other_df(): return df +@pytest.fixture +def example_tree(): + return ( + [0, np.array([1, 2]), {"a": pd.Series([3, 4], index=["c", "d"]), "b": 5}], + 6, + ) + + def test_flatten_df_with_value_column(value_df): - registry = get_registry(extended=True) - flat, _ = tree_flatten(value_df, registry=registry) + flat, _ = tree_flatten(value_df, namespace=VALUE_NAMESPACE) assert flat == [1, 3, 5] def test_unflatten_df_with_value_column(value_df): - registry = get_registry(extended=True) - _, treedef = tree_flatten(value_df, registry=registry) - unflat = tree_unflatten(treedef, [10, 11, 12], registry=registry) + _, treedef = tree_flatten(value_df, namespace=VALUE_NAMESPACE) + unflat = tree_unflatten(treedef, [10, 11, 12], namespace=VALUE_NAMESPACE) assert unflat.equals(value_df.assign(value=[10, 11, 12])) def test_leaf_names_df_with_value_column(value_df): - registry = get_registry(extended=True) - names = leaf_names(value_df, registry=registry) + names = leaf_names(value_df, namespace=VALUE_NAMESPACE) assert names == ["alpha", "beta", "gamma"] +def test_leaf_names_with_is_leaf(): + params = {"a": 1, "b": np.array([0, 1])} + names = leaf_names( + params, + is_leaf=lambda tree: isinstance(tree, np.ndarray), + namespace=VALUE_NAMESPACE, + ) + expected_names = ["a", "b"] + assert names == expected_names + + def test_flatten_partially_numeric_df(other_df): - registry = get_registry(extended=True) - flat, _ = tree_flatten(other_df, registry=registry) + flat, _ = tree_flatten(other_df, namespace=VALUE_NAMESPACE) assert flat == [0, 3.14, 1, 3.14, 2, 3.14] def test_unflatten_partially_numeric_df(other_df): - registry = get_registry(extended=True) - _, treedef = tree_flatten(other_df, registry=registry) - unflat = tree_unflatten(treedef, [1, 2, 3, 4, 5, 6], registry=registry) + _, treedef = tree_flatten(other_df, namespace=VALUE_NAMESPACE) + unflat = tree_unflatten(treedef, [1, 2, 3, 4, 5, 6], namespace=VALUE_NAMESPACE) other_df = other_df.assign(b=[1, 3, 5], c=[2, 4, 6]) assert_frame_equal(unflat, other_df, check_dtype=False) def test_leaf_names_partially_numeric_df(other_df): - registry = get_registry(extended=True) - names = leaf_names(other_df, registry=registry) + names = leaf_names(other_df, namespace=VALUE_NAMESPACE) assert names == ["alpha_b", "alpha_c", "beta_b", "beta_c", "gamma_b", "gamma_c"] + + +@pytest.fixture() +def bounds_df(): + return pd.DataFrame( + { + "value": [1, 2, 3], + "lower_bound": [0, 0, 0], + "upper_bound": [10, 20, 30], + "soft_lower_bound": [0.5, 0.5, 0.5], + "soft_upper_bound": [9, 19, 29], + }, + index=["alpha", "beta", "gamma"], + ) + + +def test_tree_methods_with_default_namespace(bounds_df): + leaves, treedef = tree_flatten(bounds_df) + assert len(leaves) == 1 + assert_frame_equal(leaves[0], bounds_df) + + leaves = tree_leaves(bounds_df) + assert len(leaves) == 1 + assert_frame_equal(leaves[0], bounds_df) + + tree = tree_unflatten(treedef, leaves) + assert_frame_equal(tree, bounds_df) + + names = leaf_names(bounds_df) + expected_names = [""] + assert names == expected_names + + tree = tree_map(lambda x: x * 2, bounds_df) + assert_frame_equal(tree, bounds_df * 2) + + +@pytest.mark.parametrize("namespace", OPTREE_NAMESPACES) +def test_tree_methods_with_registered_namespaces(namespace, bounds_df): + expected_leaves = bounds_df[namespace].tolist() + + leaves, treedef = tree_flatten(bounds_df, namespace=namespace) + assert leaves == expected_leaves + + leaves = tree_leaves(bounds_df, namespace=namespace) + assert leaves == expected_leaves + + tree = tree_unflatten(treedef, leaves, namespace=namespace) + assert_frame_equal(tree, bounds_df) + + names = leaf_names(bounds_df, namespace=namespace) + assert names == ["alpha", "beta", "gamma"] + + tree = tree_map(lambda x: x * 2, bounds_df, namespace=namespace) + doubled = [v * 2 for v in expected_leaves] + expected = bounds_df.copy() + expected[namespace] = doubled + assert_frame_equal(tree, expected) + + +def test_tree_methods_raise_warning_with_unregisted_namespace(): + unregistered_namespace = "unregistered_namespace" + tree, leaves = [0], [0] + match_str = "is not registered." + with pytest.warns(match=match_str): + tree_flatten(tree, namespace=unregistered_namespace) + with pytest.warns(match=match_str): + tree_leaves(tree, namespace=unregistered_namespace) + with pytest.warns(match=match_str): + tree_unflatten(tree, leaves, namespace=unregistered_namespace) + with pytest.warns(match=match_str): + leaf_names(tree, namespace=unregistered_namespace) + with pytest.warns(match=match_str): + tree_map(lambda x: x * 2, tree, namespace=unregistered_namespace) + + +def test_tree_flatten_and_unflatten_with_None(): + params = [None] + leaves, treespec = tree_flatten(params) + assert leaves == [] + tree = tree_unflatten(treespec, leaves) + assert tree == [None] + + +@pytest.mark.parametrize("namespace", OPTREE_NAMESPACES) +def test_dict_insertion_ordering_is_respected_for_registered_namespaces(namespace): + params = {"b": [1, 4], "a": [8, 9]} + leaves, _ = tree_flatten(params, namespace=namespace) + assert leaves == [1, 4, 8, 9] + + tree = tree_unflatten(params, [1, 4, 8, 9], namespace=namespace) + assert list(tree.items()) == [("b", [1, 4]), ("a", [8, 9])] + + leaves2 = tree_leaves(params, namespace=namespace) + assert leaves2 == [1, 4, 8, 9] + + tree = tree_map(lambda x: x, params, namespace=namespace) + assert list(tree.items()) == [("b", [1, 4]), ("a", [8, 9])] + + names = leaf_names(params, namespace=namespace) + assert names == ["b_0", "b_1", "a_0", "a_1"] + + +def test_dict_insertion_ordering_is_respected_for_default_namespace(): + params = {"b": [1, 4], "a": [8, 9]} + leaves, _ = tree_flatten(params) + assert leaves == [1, 4, 8, 9] + + tree = tree_unflatten(params, [1, 4, 8, 9]) + assert list(tree.items()) == [("b", [1, 4]), ("a", [8, 9])] + + leaves2 = tree_leaves(params) + assert leaves2 == [1, 4, 8, 9] + + tree = tree_map(lambda x: x, params) + assert list(tree.items()) == [("b", [1, 4]), ("a", [8, 9])] + + names = leaf_names(params) + assert names == ["b_0", "b_1", "a_0", "a_1"]