diff --git a/.gitattributes b/.gitattributes index d004eddbe7e056f34c44cea3f7ddae3736ab8049..83d5a476e51b065bd949cea624dbe904702bb4ce 100644 --- a/.gitattributes +++ b/.gitattributes @@ -296,3 +296,5 @@ tuning-competition-baseline/.venv/lib/python3.11/site-packages/nvidia/cudnn/lib/ .venv/lib/python3.11/site-packages/_cffi_backend.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text .venv/lib/python3.11/site-packages/jsonschema/tests/__pycache__/test_validators.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text .venv/lib/python3.11/site-packages/torchaudio/functional/__pycache__/functional.cpython-311.pyc filter=lfs diff=lfs merge=lfs -text +.venv/lib/python3.11/site-packages/torchaudio/lib/_torchaudio.so filter=lfs diff=lfs merge=lfs -text +.venv/lib/python3.11/site-packages/torchaudio/lib/libtorchaudio.so filter=lfs diff=lfs merge=lfs -text diff --git a/.venv/lib/python3.11/site-packages/dotenv/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/dotenv/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d1e992af7a4b3a710543422c128c1f29401fe4a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/dotenv/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/LICENSE b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/METADATA b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..2e8b9047d095da6ed52a50cc7172fb00639ca436 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/METADATA @@ -0,0 +1,150 @@ +Metadata-Version: 2.1 +Name: google-api-python-client +Version: 2.159.0 +Summary: Google API Client Library for Python +Home-page: https://github.com/googleapis/google-api-python-client/ +Author: Google LLC +Author-email: googleapis-packages@google.com +License: Apache 2.0 +Keywords: google api client +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: httplib2<1.dev0,>=0.19.0 +Requires-Dist: google-auth!=2.24.0,!=2.25.0,<3.0.0.dev0,>=1.32.0 +Requires-Dist: google-auth-httplib2<1.0.0,>=0.2.0 +Requires-Dist: google-api-core!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0,<3.0.0.dev0,>=1.31.5 +Requires-Dist: uritemplate<5,>=3.0.1 + +# Google API Client + +[![PyPI version](https://badge.fury.io/py/google-api-python-client.svg)](https://badge.fury.io/py/google-api-python-client) + +This is the [Google API Python client library](https://cloud.google.com/apis/docs/client-libraries-explained#google_api_client_libraries) +for Google's discovery based APIs. To get started, please see the +[docs folder](https://github.com/googleapis/google-api-python-client/blob/main/docs/README.md). + +This library is considered complete and is in maintenance mode. This means +that we will address critical bugs and security issues but will not add any +new features. + +This library is officially supported by Google. However, the maintainers of +this repository recommend using [Cloud Client Libraries for Python](https://github.com/googleapis/google-cloud-python), +where possible, for new code development. For more information, please visit +[Client Libraries Explained](https://cloud.google.com/apis/docs/client-libraries-explained). + +## Version 2.0 Release +The 2.0 release of `google-api-python-client` includes a substantial reliability +improvement, compared with 1.x, as discovery documents are now cached in the library +rather than fetched dynamically. It is highly recommended to upgrade from v1.x to v2.x. + +Only python 3.7 and newer is supported. If you are not able to upgrade python, then +please continue to use version 1.x as we will continue supporting python 2.7+ in +[v1](https://github.com/googleapis/google-api-python-client/tree/v1). + +Discovery documents will no longer be retrieved dynamically when +you call `discovery.build()`. The discovery documents will instead be retrieved +from the client library directly. New versions of this library are released weekly. +As a result of caching the discovery documents, the size of this package is at least +50 MB larger compared to the previous version. + +Please see the [Migration Guide](https://github.com/googleapis/google-api-python-client/blob/main/UPGRADING.md) +for more information. + +## Documentation + +See the [docs folder](https://github.com/googleapis/google-api-python-client/blob/main/docs/README.md) for more detailed instructions and additional documentation. + +## Other Google API libraries + +The maintainers of this repository recommend using +[Cloud Client Libraries for Python](https://github.com/googleapis/google-cloud-python), +where possible, for new code development due to the following reasons: + +With [Cloud Client Libraries for Python](https://github.com/googleapis/google-cloud-python): +- There is a separate client library for each API, so you can choose +which client libraries to download. Whereas, `google-api-python-client` is a +single client library for all APIs. As a result, the total package size for +`google-api-python-client` exceeds 50MB. +- There are stricter controls for breaking changes to the underlying APIs +as each client library is focused on a specific API. +- There are more features in these Cloud Client Libraries as each library is +focused on a specific API, and in some cases, the libraries are owned by team +who specialized in that API. +- Developers will benefit from intellisense. + +For more information, please visit +[Client Libraries Explained](https://cloud.google.com/apis/docs/client-libraries-explained). + +Although there are many benefits to moving to +[Cloud Client Libraries for Python](https://github.com/googleapis/google-cloud-python), +the maintainers want to emphasize that `google-api-python-client` will continue +to be supported. + +For Google Ads API, we recommend using [Google Ads API Client Library for Python](https://github.com/googleads/google-ads-python/). + +For Google Firebase Admin API, we recommend using [Firebase Admin Python SDK](https://github.com/firebase/firebase-admin-python). + +## Installation + +Install this library in a [virtualenv](https://virtualenv.pypa.io/en/latest/) using pip. virtualenv is a tool to +create isolated Python environments. The basic problem it addresses is one of +dependencies and versions, and indirectly permissions. + +With virtualenv, it's possible to install this library without needing system +install permissions, and without clashing with the installed system +dependencies. + +### Mac/Linux + +```bash +pip3 install virtualenv +virtualenv +source /bin/activate +/bin/pip install google-api-python-client +``` + +### Windows + +```batch +pip install virtualenv +virtualenv +\Scripts\activate +\Scripts\pip.exe install google-api-python-client +``` + +## Supported Python Versions + +Python 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 are fully supported and tested. This library may work on later versions of 3, but we do not currently run tests against those versions. + +## Unsupported Python Versions + +Python < 3.7 + +## Third Party Libraries and Dependencies + +The following libraries will be installed when you install the client library: +* [httplib2](https://github.com/httplib2/httplib2) +* [uritemplate](https://github.com/sigmavirus24/uritemplate) + +For development you will also need the following libraries: +* [WebTest](https://pypi.org/project/WebTest/) +* [pyopenssl](https://pypi.python.org/pypi/pyOpenSSL) + +## Contributing + +Please see our [Contribution Guide](https://github.com/googleapis/google-api-python-client/blob/main/CONTRIBUTING.rst). +In particular, we love pull requests - but please make sure to sign +the contributor license agreement. diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/RECORD b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..15a62d3528dda1878d4939bd8ce2587975957d57 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/RECORD @@ -0,0 +1,574 @@ +apiclient/__init__.py,sha256=U1SOZ1GeeF3uCr0fzO5Od-rpe17R12-Ppyq25NTSbow,746 +apiclient/__pycache__/__init__.cpython-311.pyc,, +google_api_python_client-2.159.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +google_api_python_client-2.159.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 +google_api_python_client-2.159.0.dist-info/METADATA,sha256=1bOByFTIV3avnDyYSM-tf-bJmzgQQH_1hYX3k0evMhE,6652 +google_api_python_client-2.159.0.dist-info/RECORD,, +google_api_python_client-2.159.0.dist-info/WHEEL,sha256=OpXWERl2xLPRHTvd2ZXo_iluPEQd8uSbYkJ53NAER_Y,109 +google_api_python_client-2.159.0.dist-info/top_level.txt,sha256=lbWgdDwQ3oww0ub0pmZ3fmIYAOTHf18Two4U9CxRlnw,58 +googleapiclient/__init__.py,sha256=kFWxKShJalbnrCuV0klL7mjZaiXfPpjlqmyKyI8yfTU,904 +googleapiclient/__pycache__/__init__.cpython-311.pyc,, +googleapiclient/__pycache__/_auth.cpython-311.pyc,, +googleapiclient/__pycache__/_helpers.cpython-311.pyc,, +googleapiclient/__pycache__/channel.cpython-311.pyc,, +googleapiclient/__pycache__/discovery.cpython-311.pyc,, +googleapiclient/__pycache__/errors.cpython-311.pyc,, +googleapiclient/__pycache__/http.cpython-311.pyc,, +googleapiclient/__pycache__/mimeparse.cpython-311.pyc,, +googleapiclient/__pycache__/model.cpython-311.pyc,, +googleapiclient/__pycache__/sample_tools.cpython-311.pyc,, +googleapiclient/__pycache__/schema.cpython-311.pyc,, +googleapiclient/__pycache__/version.cpython-311.pyc,, +googleapiclient/_auth.py,sha256=QttUwhmp7BmhW5CReaAOVGH6kEtITYmEHhVCVmsnDPs,5736 +googleapiclient/_helpers.py,sha256=iNxILG6iNNFukr7lhaYfVqX4oFM5SERvcsU3JxeOo6I,6723 +googleapiclient/channel.py,sha256=Fc4nxu-RxGkjY_STp9bwJfZaLan6VdX-DNniH-ANSuE,11054 +googleapiclient/discovery.py,sha256=9TKz9stsr8Q9TPTjKG1I6yHoPH1ISRvh58q_NoX3mOM,66341 +googleapiclient/discovery_cache/__init__.py,sha256=ww_vl0vhVLuHSEdRTv3-gq6EDG--Ff7rILYHHFifnzc,2315 +googleapiclient/discovery_cache/__pycache__/__init__.cpython-311.pyc,, +googleapiclient/discovery_cache/__pycache__/appengine_memcache.cpython-311.pyc,, +googleapiclient/discovery_cache/__pycache__/base.cpython-311.pyc,, +googleapiclient/discovery_cache/__pycache__/file_cache.cpython-311.pyc,, +googleapiclient/discovery_cache/appengine_memcache.py,sha256=6T1pQj-toAhDwfgLuiggFGhxKNGw5y-NnLUzLIF_M4s,1657 +googleapiclient/discovery_cache/base.py,sha256=yCDPtxnbNN-p5_9fzBacC6P3wcUPlaCQIy5v_dXTons,1389 +googleapiclient/discovery_cache/documents/abusiveexperiencereport.v1.json,sha256=S71sOgX6Jaa_CVbAYzdQANp6oxb8poocJieP6Q0KW0E,5671 +googleapiclient/discovery_cache/documents/acceleratedmobilepageurl.v1.json,sha256=pyMbFLPRkHJ-j_vpoXoTgLeJmB_Ff8aGoY94qtSIjC4,6729 +googleapiclient/discovery_cache/documents/accessapproval.v1.json,sha256=zk7qnoDt9iLceoLvoqKvyCfYMmMs4g_wO21SwHQktTY,49990 +googleapiclient/discovery_cache/documents/accesscontextmanager.v1.json,sha256=DWXJ0iCxI9UhxsxfOG0OFXic7cstDmHKAXGAV-5Fuoc,116991 +googleapiclient/discovery_cache/documents/accesscontextmanager.v1beta.json,sha256=Vsv_nz7ubkkbGwNgxirN2AWgUuYH0mADUx9LFaqKu_c,51062 +googleapiclient/discovery_cache/documents/acmedns.v1.json,sha256=l4yO_s08Dowy9AdSBd857ZVqw6Ea4PWt1KE1OqOb9mE,6858 +googleapiclient/discovery_cache/documents/addressvalidation.v1.json,sha256=QBLcYw1_WBJJs7tYZyZc_rlfzCz4w_dqZilvazf11HQ,47286 +googleapiclient/discovery_cache/documents/adexchangebuyer.v1.2.json,sha256=0-XVzUa3kcqFiCZYk_W0au5mIV5-w3AgBtQJsSnWL9w,20475 +googleapiclient/discovery_cache/documents/adexchangebuyer.v1.3.json,sha256=6lFs8ULCDsQuo8s48oBpFdDCVbwICk6MNaCvDVrhP9Q,59967 +googleapiclient/discovery_cache/documents/adexchangebuyer.v1.4.json,sha256=pgYvRj3gK2vBxH7Dh4YoCqd7TXTzTDPQqVqOlVRxITk,136987 +googleapiclient/discovery_cache/documents/adexchangebuyer2.v2beta1.json,sha256=DsPe2AUTiMhcL_RQl_DIP6jVj_C_6BwM9knbgdt6auY,231740 +googleapiclient/discovery_cache/documents/adexperiencereport.v1.json,sha256=ZcB2Sna6t35lyCS6gJZ-FBePqKfGK9y1m2K8zVNDkN0,6644 +googleapiclient/discovery_cache/documents/admin.datatransfer_v1.json,sha256=cvllELDkZ78kNbbgK7uz1DLCuPwJwg-2sB5jhPEYgB4,12021 +googleapiclient/discovery_cache/documents/admin.datatransferv1.json,sha256=cvllELDkZ78kNbbgK7uz1DLCuPwJwg-2sB5jhPEYgB4,12021 +googleapiclient/discovery_cache/documents/admin.directory_v1.json,sha256=S0rjTVAqMdFfXiZzPtDdLx64RzT2QA0kpPHIhkB4Vzg,274561 +googleapiclient/discovery_cache/documents/admin.directoryv1.json,sha256=S0rjTVAqMdFfXiZzPtDdLx64RzT2QA0kpPHIhkB4Vzg,274561 +googleapiclient/discovery_cache/documents/admin.reports_v1.json,sha256=D2jiASxzuDM7MVJOWXkwvAOrAUtGfHxBI0_BB24f1ls,52363 +googleapiclient/discovery_cache/documents/admin.reportsv1.json,sha256=D2jiASxzuDM7MVJOWXkwvAOrAUtGfHxBI0_BB24f1ls,52363 +googleapiclient/discovery_cache/documents/admob.v1.json,sha256=KqHlEbdrL6m9INFAwhX9wFuqvR5Kjk_rdfu6mtc4XY4,52164 +googleapiclient/discovery_cache/documents/admob.v1beta.json,sha256=gcGNiedgQiUsitwzTPPv-yTsWzcR7iCV3_A9VO5G2f8,91957 +googleapiclient/discovery_cache/documents/adsense.v2.json,sha256=4JKnIONHrO-Ihxq8y7YayZIXhazygAs8Y7KB1iRc7RQ,103441 +googleapiclient/discovery_cache/documents/adsensehost.v4.1.json,sha256=kKGnNG3aFbYwLFOLJkIGph3m7vC2heaob68rlmV4J28,37965 +googleapiclient/discovery_cache/documents/adsenseplatform.v1.json,sha256=sxqJvzMQS0OW7i_spxKrvmpI2BTZ-fPguABRUzEeKWs,20170 +googleapiclient/discovery_cache/documents/adsenseplatform.v1alpha.json,sha256=m0CwvUttBXWPHiOhje588U-g1KPe2XBgs9_xrSSe85I,23225 +googleapiclient/discovery_cache/documents/advisorynotifications.v1.json,sha256=LjKYP_4Fy_is5k-nccRqXa9usXF5_rBxhE_qdN9FkqE,20673 +googleapiclient/discovery_cache/documents/aiplatform.v1.json,sha256=kJ8yKsY0yymywj6zqjs6vzMgEZ9zXCmNPQe7_tBzXpU,1741177 +googleapiclient/discovery_cache/documents/aiplatform.v1beta1.json,sha256=KmKuUR-xAXK7rCxpM5MbKYof6KrzYWotTcbPDE4dBhE,1986081 +googleapiclient/discovery_cache/documents/airquality.v1.json,sha256=Jid1ijxffWKiYwOdpwu_toVra3fOeowW8QuMkz2wpao,40963 +googleapiclient/discovery_cache/documents/alertcenter.v1beta1.json,sha256=N6jVO1PseL3z9zGnMDcdALRExUU1iKKorZFkXOarqoM,67802 +googleapiclient/discovery_cache/documents/alloydb.v1.json,sha256=bAWuLVZQPdT3xOwso1EymTC5RW-hGlCBR0KjNXHD0nU,190970 +googleapiclient/discovery_cache/documents/alloydb.v1alpha.json,sha256=tB-PZws1VIuznlqzE989Tp3_fGJlX-hHLnwtbsqc3oI,199022 +googleapiclient/discovery_cache/documents/alloydb.v1beta.json,sha256=m5izNNXXbzCTuyXf0QwIgBPBShRRTbrxVdGUAJYxmeI,197279 +googleapiclient/discovery_cache/documents/analytics.v3.json,sha256=pq5nLmdNvFR-k8Sre800U3mI2tU6hVZGdQCNX_VJNiE,195428 +googleapiclient/discovery_cache/documents/analyticsadmin.v1alpha.json,sha256=LkYkxevtmPTe0RtWAT6T-eQvt46onnhYj0l4U4syrII,321489 +googleapiclient/discovery_cache/documents/analyticsadmin.v1beta.json,sha256=axYy3nO8tOpaxIK1KGkTJ9ZpIgIsdFDEvFu4M2QipOk,123177 +googleapiclient/discovery_cache/documents/analyticsdata.v1alpha.json,sha256=3rdVFqqDjQQ7-0qi_UkAYpRthvRKjoXJ8SJzulNHBn0,80445 +googleapiclient/discovery_cache/documents/analyticsdata.v1beta.json,sha256=-ptvrqUp-0rNGlell3IyH12yzL7LQbz9nBKOukMThkE,97076 +googleapiclient/discovery_cache/documents/analyticshub.v1.json,sha256=Kgg_XZ5mN7oj8MVyflAOa_lwkcUQKLwMM0Ix7uSIU2g,102096 +googleapiclient/discovery_cache/documents/analyticshub.v1beta1.json,sha256=GHYQ2yb544a4cAceel10LI-mbs53yErbGWvykJpPiJw,56324 +googleapiclient/discovery_cache/documents/analyticsreporting.v4.json,sha256=8S4FyrAstnlesJ3ss4fYM7V18YvJgMh9S9jIo-vAOY8,61333 +googleapiclient/discovery_cache/documents/androiddeviceprovisioning.v1.json,sha256=HAGzsWbVR9lfecRlHW6S55tgQy1o1UWZ4YjX_ruMkIA,60978 +googleapiclient/discovery_cache/documents/androidenterprise.v1.json,sha256=FCjc18uzyxU2-bQt2Lys-pNf3pKCnoy1Cz9D6RZqomE,155538 +googleapiclient/discovery_cache/documents/androidmanagement.v1.json,sha256=cfHKkkHnm9cET7qqPMfQFwJowrliS0DXHGZelFnnEag,268145 +googleapiclient/discovery_cache/documents/androidpublisher.v3.json,sha256=90iIRPt5cNZz88CoPh9cdSwy1sXuD_gCuG0fAFnulVY,313821 +googleapiclient/discovery_cache/documents/apigateway.v1.json,sha256=xEY_-eczVl_UTFcrEO0GHrGNEDIIdls-MxvVbcZYC1w,67399 +googleapiclient/discovery_cache/documents/apigateway.v1beta.json,sha256=xXuSF3RPRJ8q-_XaBNx4M2O-9IYlanSW5NVi5gtkoNs,69112 +googleapiclient/discovery_cache/documents/apigee.v1.json,sha256=8NT7GiKG2y5cZ-MehLN7UK9cUFl6baZKjm_ZyGG7kiE,664811 +googleapiclient/discovery_cache/documents/apigeeregistry.v1.json,sha256=hCu1irwJNFHGEQgiTM5HJtqvF1JUeqpBVA8SGUn9syQ,156027 +googleapiclient/discovery_cache/documents/apikeys.v2.json,sha256=FZBL7LZgNkiOp8cChbkg38Xd0oRqDyn9MQRFouSiqRg,22352 +googleapiclient/discovery_cache/documents/apim.v1alpha.json,sha256=kktY7aRouoMlByWAQ1dvEa89Uf_hu26exnk8D8mu5Fs,48179 +googleapiclient/discovery_cache/documents/appengine.v1.json,sha256=LyGXgvqMb0rp_KAPOFh-55tcH2-3JRIFutz28SvMsGY,152632 +googleapiclient/discovery_cache/documents/appengine.v1alpha.json,sha256=sNxfJZHgLFEZjEkogY8eA963iq6A3YGy3zfO8XWb-mQ,61632 +googleapiclient/discovery_cache/documents/appengine.v1beta.json,sha256=SlY5xrxPEPiVhCBFpl1B57XmT9f8fuTOZ-AMIbbsZ-0,159979 +googleapiclient/discovery_cache/documents/appengine.v1beta4.json,sha256=yELseCdExWSunuF7afIqEw_94UU7PvKCXDmWkyAbYWg,110912 +googleapiclient/discovery_cache/documents/appengine.v1beta5.json,sha256=OHCPs6izs9jPAEuG8s9dr76KCn3f5lKdfWE5NaQBhZo,110227 +googleapiclient/discovery_cache/documents/apphub.v1.json,sha256=ygrOA6zBTqjN_v4DgTxBbrbHl7Q1DKYkI2L-91zHG7c,99910 +googleapiclient/discovery_cache/documents/apphub.v1alpha.json,sha256=flxM20XL_iSm-40n239ICU_OhWEAjAqTakClsQl_R4g,104528 +googleapiclient/discovery_cache/documents/area120tables.v1alpha1.json,sha256=W3nRxsh01aYTWrEV05o-14Ubu4UvIqDelbu01_s9Ve0,26671 +googleapiclient/discovery_cache/documents/areainsights.v1.json,sha256=-lvkKcQIt9weSu_Ksg1nWjOTV8WCjBYxj-BQKwlwVe0,13722 +googleapiclient/discovery_cache/documents/artifactregistry.v1.json,sha256=xRYlS4T4C22eM_1v56tXuMwNhUEh52X2bfJ1saQJxlk,155456 +googleapiclient/discovery_cache/documents/artifactregistry.v1beta1.json,sha256=OGs1TZeDA3V64ZBxAuXI3C2wtHeZLxUHjeYpbHqU3Xw,63052 +googleapiclient/discovery_cache/documents/artifactregistry.v1beta2.json,sha256=Ch-AAmvOPXtRKwBrXZMHGxJlsMG5e621jsYITZ4FuHU,81649 +googleapiclient/discovery_cache/documents/assuredworkloads.v1.json,sha256=wiMc9LzAN6PVHMikIMftKOXU76QmD54tZzFMer2mqng,60769 +googleapiclient/discovery_cache/documents/assuredworkloads.v1beta1.json,sha256=MCzr0E1VbcRWXz1GO0wBbe-xPBEEcef2mP5cckG6Rrw,72924 +googleapiclient/discovery_cache/documents/authorizedbuyersmarketplace.v1.json,sha256=C6-kWPE5tO5iklauUh8pa-ooSli3rVy5CKn3fKVqSmc,110586 +googleapiclient/discovery_cache/documents/authorizedbuyersmarketplace.v1alpha.json,sha256=pH-ReY_ZuE4pkzlvkAa4godHOaSvmT3aULiwpwpIXnE,118917 +googleapiclient/discovery_cache/documents/backupdr.v1.json,sha256=JNIEeu9osXfSDCioNHhvSEwGYGSBT1zvQDz_4cgcaYU,176146 +googleapiclient/discovery_cache/documents/baremetalsolution.v1.json,sha256=DgpA2txkVyJ3FJmckMnRzB1LGtD8K6qo8F2eYT6Bi3c,13574 +googleapiclient/discovery_cache/documents/baremetalsolution.v1alpha1.json,sha256=LWBZqVLJQlt0qgc4yoRJWQfwH0oPL8aS4wHGF8LRfzk,19186 +googleapiclient/discovery_cache/documents/baremetalsolution.v2.json,sha256=hiQNSG63T3om5Hldrdn0CoUpOciITjxNmDuf9lsl11c,105593 +googleapiclient/discovery_cache/documents/batch.v1.json,sha256=nwHoC9fWFc5QxSNfIM4N3tcL4DQgvqmL1avY_A7ZB8s,86651 +googleapiclient/discovery_cache/documents/beyondcorp.v1.json,sha256=X-dzEo0czEXDzbdFZ4xbb-peetG66xdS8fAh5O1R4pw,170832 +googleapiclient/discovery_cache/documents/beyondcorp.v1alpha.json,sha256=k6DBKchf0XygfOkO_XV6d9mUPG9x3O9G5cExOTXk2ws,295421 +googleapiclient/discovery_cache/documents/biglake.v1.json,sha256=JiAvdp7lTnDjHSpeG0RZ_mZFOtNCW5lXWrok81NGECs,26554 +googleapiclient/discovery_cache/documents/bigquery.v2.json,sha256=9Y88rM3uZj_jE-kynEy64-H60BNbLiOxPUARn12YQDM,389333 +googleapiclient/discovery_cache/documents/bigqueryconnection.v1.json,sha256=uogtso7Bp7eexOLF2Syb3hapjEHyOU1Wx62pnBGwefI,43660 +googleapiclient/discovery_cache/documents/bigqueryconnection.v1beta1.json,sha256=d45MeNg37AdYm_s8hO7ZqLd8hDw3qbpif9ecV1OMYIA,32721 +googleapiclient/discovery_cache/documents/bigquerydatapolicy.v1.json,sha256=2p0Fd_kv8GXO5lhd0GOcZAcQ1jPmSxwX44z-aL3cOqo,35006 +googleapiclient/discovery_cache/documents/bigquerydatatransfer.v1.json,sha256=gEOx_g1zz5SQUQiZCa3WyR681yPGOr3hOvA4Ol23F5M,86085 +googleapiclient/discovery_cache/documents/bigqueryreservation.v1.json,sha256=lC_d23FlEL7LcARDPbOqT8Tcosliy9Z5e5gt-3EhEpU,55989 +googleapiclient/discovery_cache/documents/bigqueryreservation.v1alpha2.json,sha256=u1WvY6zx7CYg_q9OhtqB5I8Su4b1kEA7p3VwegecO8U,44509 +googleapiclient/discovery_cache/documents/bigqueryreservation.v1beta1.json,sha256=zVtHmOXJTgtYX3JK9YoFUW8uNNHwi-ZPHs4gQN-Y0cY,60957 +googleapiclient/discovery_cache/documents/bigtableadmin.v1.json,sha256=udwAbAWXjShMSJcvAaxRNI-V5QFFQdE6InVOhHT-y7Q,32684 +googleapiclient/discovery_cache/documents/bigtableadmin.v2.json,sha256=JMG5IFda7w0jyij1wtKw_rJ39GCmsFPkdG7c5Z5EW2A,178675 +googleapiclient/discovery_cache/documents/billingbudgets.v1.json,sha256=v3YOJsAFLFb0dBL3DDh40t5RYi_K8YZUszEi-IY0XQM,27319 +googleapiclient/discovery_cache/documents/billingbudgets.v1beta1.json,sha256=W1XxaGkQBjVGfxUdSJFFi2WIfppU-rP-rkFpSuTKvqk,26850 +googleapiclient/discovery_cache/documents/binaryauthorization.v1.json,sha256=DFrj4rdbosJRJW3Qcf1I97QDhN40xfXKkiLgEijQgw8,84334 +googleapiclient/discovery_cache/documents/binaryauthorization.v1beta1.json,sha256=9zlVIEnahifUYLArioJ_ycDpVZpet2mkIrclt_WkKQ4,50127 +googleapiclient/discovery_cache/documents/blockchainnodeengine.v1.json,sha256=YSkmD97vugow4WGYJevxXFVS48menuu5lCAfSg8YgGA,33199 +googleapiclient/discovery_cache/documents/blogger.v2.json,sha256=JXobWTAsHd4PoODg63CedVZHQaedheBQnvxjizGRmhc,21584 +googleapiclient/discovery_cache/documents/blogger.v3.json,sha256=y-3kCIaujgK4YBiQziOXurJPzXjtEzmGUKJwcBxL-e8,44123 +googleapiclient/discovery_cache/documents/books.v1.json,sha256=dWEew3bc3mDWbAhR-f26d9JqXKqiPtpXQ7We2VBfQVs,110062 +googleapiclient/discovery_cache/documents/businessprofileperformance.v1.json,sha256=2FQwtRgx3ObOq_qdL-a_M9evFaluSAUCpPAXZ15JpmM,24459 +googleapiclient/discovery_cache/documents/calendar.v3.json,sha256=obiH3_E4LOmalnwJs0oduIxQEteyiOz4E0RO4BUVsgw,117327 +googleapiclient/discovery_cache/documents/certificatemanager.v1.json,sha256=wVbFC_h-aH7qeCdlto-d5Sx8jvcEehWw2VeBmG2zkqM,72783 +googleapiclient/discovery_cache/documents/chat.v1.json,sha256=ytk2Q1odJ1-IKiPlet3FcDZMBeV799uZlWNwBux_5eA,265618 +googleapiclient/discovery_cache/documents/checks.v1alpha.json,sha256=_Ko7Qb9yYJxCarWZa3mlQg08Y5azaxIAj7smjiqmviA,91808 +googleapiclient/discovery_cache/documents/chromemanagement.v1.json,sha256=nmTclryXsGfcWh-SyUAZgVOdkezBfvPpu4kUONEKBEM,195311 +googleapiclient/discovery_cache/documents/chromepolicy.v1.json,sha256=av8BIAE-PVDBuAvnIqnLuIqIFM-8G57lsZHLO_-8o2g,62003 +googleapiclient/discovery_cache/documents/chromeuxreport.v1.json,sha256=airhc_AoDt3AlPYikhaq16oCnZ3SqpKfHRIZG-v_7xM,22401 +googleapiclient/discovery_cache/documents/civicinfo.v2.json,sha256=89wbEDm24KZJ7NjNtDOWjFvf9kcG4dRhjJdk4KfzkLU,38357 +googleapiclient/discovery_cache/documents/classroom.v1.json,sha256=KOyTZm1i8vKNmHK_EEtrRc3z3nSlg-pwLiBQMhbLHao,235902 +googleapiclient/discovery_cache/documents/cloudasset.v1.json,sha256=oLPhj-xH8GO6z0UBwnrdFfShakb9t3oqCTZiazXFPPI,259122 +googleapiclient/discovery_cache/documents/cloudasset.v1beta1.json,sha256=yE6WJ_RV0qR3NWkGqUA-gVQo6DMVGzP2Ef4ZMPklYoc,95251 +googleapiclient/discovery_cache/documents/cloudasset.v1p1beta1.json,sha256=H1MEC776OSu8QDjeePqukBGYmDwrgFwzTkzp7_C0EzE,83418 +googleapiclient/discovery_cache/documents/cloudasset.v1p4beta1.json,sha256=RT5pGxMQhQ_T9JKt97G6G94znnnweuqgSwEnby7Vkck,110574 +googleapiclient/discovery_cache/documents/cloudasset.v1p5beta1.json,sha256=i_hOgsgH5yPJhR9EOGBzibq1M6yjFCtA6wOBjAAePRQ,82076 +googleapiclient/discovery_cache/documents/cloudasset.v1p7beta1.json,sha256=m4-VUaiGQuHi-k1ez-nl5qxoINpJJw1bjo625f6UgM4,87731 +googleapiclient/discovery_cache/documents/cloudbilling.v1.json,sha256=jg1-B4LYm5Dp-V9jUomBsLVOd9H4uPlgCTjME9AHUrs,61261 +googleapiclient/discovery_cache/documents/cloudbilling.v1beta.json,sha256=SqLdm243EPaHYt08Zw_rblcz4XeXzo3jdFxO-A_ehno,98576 +googleapiclient/discovery_cache/documents/cloudbuild.v1.json,sha256=lMPZlZ6X_p4l6IpyPxl3f7SHNBfDzwvWUIhHafbfSwI,190972 +googleapiclient/discovery_cache/documents/cloudbuild.v1alpha1.json,sha256=tN8lxatpJZdx4mhuKWjgzYeBfbN3ZFgZOhdDI8HdMTc,103536 +googleapiclient/discovery_cache/documents/cloudbuild.v1alpha2.json,sha256=FqGePJRUozQZ3MgYXXDeeBwhKb38oRdL3RDyfn5LKOA,102139 +googleapiclient/discovery_cache/documents/cloudbuild.v1beta1.json,sha256=Z9D0XmsAbFI1Ug2BBm8i4hocf4dzGW0MP_3rprbHH2I,105268 +googleapiclient/discovery_cache/documents/cloudbuild.v2.json,sha256=hqZ4sDw_312sDjJMy6Mbdzi53ZYjjxsnwBsUzj4qF7k,112786 +googleapiclient/discovery_cache/documents/cloudchannel.v1.json,sha256=Y1T5zjX6moAfaui_zz-_6JC0-Au_XMQPgzTarPI5og0,249069 +googleapiclient/discovery_cache/documents/cloudcommerceprocurement.v1.json,sha256=NtXgLDh60lxmnNf3YKeRYfUr2suT9B1KvXUyXvfeXuU,40056 +googleapiclient/discovery_cache/documents/cloudcontrolspartner.v1.json,sha256=rMIB1jLBv1Hx5BP1XHy1P6SjL5dzpxtgQ0C2JrWdkE8,39795 +googleapiclient/discovery_cache/documents/cloudcontrolspartner.v1beta.json,sha256=WMIVTmWBALeWRidAdfvDf5UMnsjHgJ0m8LZxez5XPQc,39907 +googleapiclient/discovery_cache/documents/clouddebugger.v2.json,sha256=2IO6Cc_uKUbDzdXhyc02Xr2Y7vd8TIRq_2iWDhwGYPE,53446 +googleapiclient/discovery_cache/documents/clouddeploy.v1.json,sha256=oubO9PymCKhRmH5y2LXc1BkNxlbc4mdhvIdQuASC0YQ,259405 +googleapiclient/discovery_cache/documents/clouderrorreporting.v1beta1.json,sha256=LT3OJ_dSdWCJnai8IcrGZ5Gxr4xti3uIknpzRPbNzN8,46460 +googleapiclient/discovery_cache/documents/cloudfunctions.v1.json,sha256=d5eawdrdmmEGVnzB0XUF3yng-tScc-pjmfn9P7HBTtM,64661 +googleapiclient/discovery_cache/documents/cloudfunctions.v2.json,sha256=0XJglYg0YCJouT_-XAZL1bnVMMMbd2WfB5zzc9ubc_E,84746 +googleapiclient/discovery_cache/documents/cloudfunctions.v2alpha.json,sha256=ARHr_yHNhGiUwTWhT9zmKM114kwRJXR2NX7iqYTJgCQ,85001 +googleapiclient/discovery_cache/documents/cloudfunctions.v2beta.json,sha256=kCt1IA3ok8OgJ_SYWwUiQPagFLI_Aoigs6H7wT5td68,84950 +googleapiclient/discovery_cache/documents/cloudidentity.v1.json,sha256=lckIKcXdNU_qgxvo-6LzcZ01aMUAKL8caUg5n_A_xL8,165285 +googleapiclient/discovery_cache/documents/cloudidentity.v1beta1.json,sha256=tobyu7zftb1GWmW406G8cJBG9G7fKLn9ROhsIUKDDpU,194298 +googleapiclient/discovery_cache/documents/cloudiot.v1.json,sha256=BLM9X9po2P0rn8osda4MgRc990yAYWFVbRMnoCTjN6w,94703 +googleapiclient/discovery_cache/documents/cloudkms.v1.json,sha256=3gff_VurjeaXhFWYesDFM8IxN1mijRfDzmOAlJzYrHw,195785 +googleapiclient/discovery_cache/documents/cloudprofiler.v2.json,sha256=DKiyeC2Z010ehm-WHvDJBGMnfVFow8daU56CAP1hlkE,14920 +googleapiclient/discovery_cache/documents/cloudresourcemanager.v1.json,sha256=UDqQSYcntrGDWFUNoxTXOvAlc9gKD_lbBYBn9E6DddM,101363 +googleapiclient/discovery_cache/documents/cloudresourcemanager.v1beta1.json,sha256=Ba-DH5hvtcWloOdelBeMh6Qe-4zgQclErPHmr-hsmbc,58393 +googleapiclient/discovery_cache/documents/cloudresourcemanager.v2.json,sha256=8uhiXUbxf1fr85vhmvqNfvtp2rJ6NiptXFy8QFP7JDM,52001 +googleapiclient/discovery_cache/documents/cloudresourcemanager.v2beta1.json,sha256=1bx2QsBddoYuUmrFtlKbQ4sRpd7HGcRoKW4AoLMBu5A,52011 +googleapiclient/discovery_cache/documents/cloudresourcemanager.v3.json,sha256=zLIWRsnG4wabxo5Jppvxq4INDHXU_OazrJ4R8wZmrq8,126177 +googleapiclient/discovery_cache/documents/cloudscheduler.v1.json,sha256=5szSb7NUR7Jr0guFehnwUvUd749_1IEDU9VLQa9akXQ,46718 +googleapiclient/discovery_cache/documents/cloudscheduler.v1beta1.json,sha256=PeKbbI52f2i5z0XVndZfjrDCdmDvPtEcl3c5_W50FWw,42928 +googleapiclient/discovery_cache/documents/cloudsearch.v1.json,sha256=zioYxBar0l7onDdnYAfGKjcguMEdRkybFuuQlawHHDE,247310 +googleapiclient/discovery_cache/documents/cloudshell.v1.json,sha256=pRDGK9lNQ9_ADOmlBcTUVzBX422WFWCijv2LK--rzUA,22895 +googleapiclient/discovery_cache/documents/cloudshell.v1alpha1.json,sha256=XoIskUrEjFBy7Saxe7cApOT6ShmcKZlwzRDAiIoxgSI,25615 +googleapiclient/discovery_cache/documents/cloudsupport.v2.json,sha256=uW5kjaBY4fC_ZTPrCSKmsiT7m9h5LLCpnz2qC2kJIBE,52938 +googleapiclient/discovery_cache/documents/cloudsupport.v2beta.json,sha256=yV-RtmOBsz7Vkvob7wzXa8sdVxxKTk8zKlL39ivHr1w,58928 +googleapiclient/discovery_cache/documents/cloudtasks.v2.json,sha256=sS7OMASEA2oAj4bBCCGXeOzqWEkT-ZhGDE3H5r24xjM,96162 +googleapiclient/discovery_cache/documents/cloudtasks.v2beta2.json,sha256=OYFSBiChmrIXvCZKqqxV7TXmODreStoBzmhpV1FTmkw,113198 +googleapiclient/discovery_cache/documents/cloudtasks.v2beta3.json,sha256=NAzGvDNnJTXjz4CIj3qYNp9eOmCoOzW3r-eNVvjQAoM,103153 +googleapiclient/discovery_cache/documents/cloudtrace.v1.json,sha256=s-TVg4l2KG8Xa9LYhl4Xvar3Sl4b2Btck2esIAVx8Ag,13573 +googleapiclient/discovery_cache/documents/cloudtrace.v2.json,sha256=FYR7MlSneNHE1yqrY3FPqSw3syBJDXNaNMhid1taIX4,21080 +googleapiclient/discovery_cache/documents/cloudtrace.v2beta1.json,sha256=C-rFqBUcGEdGeys6I18EtbUb-LV2oI5FL8D7EGbrLNk,11533 +googleapiclient/discovery_cache/documents/composer.v1.json,sha256=y-7qAcZQUKDGxfPriYDlBnT9OVnxWAGnib-dOFZofVc,104729 +googleapiclient/discovery_cache/documents/composer.v1beta1.json,sha256=4ZE6Ejxo2uZPOAtDJl46riN7BV-eyIAvCKUTRLtXGis,108619 +googleapiclient/discovery_cache/documents/compute.alpha.json,sha256=Hq1w424Bnw24cJf5yqt5m_aurrCDVjdoXHJS28IE2ug,4666791 +googleapiclient/discovery_cache/documents/compute.beta.json,sha256=Tx0wSyimxY03Ftv5XRHKZjV8KgGh2QZtURGfCWDG09I,4179173 +googleapiclient/discovery_cache/documents/compute.v1.json,sha256=AThob4LZ03WjBniAug-gG3zsQkeJoay1q_u-1S3jKZw,3833242 +googleapiclient/discovery_cache/documents/config.v1.json,sha256=uqfK15vv9vJ7nzYoxqTyBbG5bO0550iKpIJvBVoDxEk,101104 +googleapiclient/discovery_cache/documents/connectors.v1.json,sha256=Qy6dCmMc6rB1624-G3FT4vGolNS_SUiGyO6YFSIgHjc,234527 +googleapiclient/discovery_cache/documents/connectors.v2.json,sha256=3oFlR5Bk3brELU4t2y52i-fPRfwrY0Lj62EUBsSVMx4,70153 +googleapiclient/discovery_cache/documents/contactcenteraiplatform.v1alpha1.json,sha256=YqDsesIVwnOMZfFQdoSWdImUhvlTBFU-Yj8MTA1BvwQ,43944 +googleapiclient/discovery_cache/documents/contactcenterinsights.v1.json,sha256=OQsjog_QlFQPlfhX6WzlR07ymJeGkCLOtaMczUAI53k,327495 +googleapiclient/discovery_cache/documents/container.v1.json,sha256=orAp0iOzD48JVay6P-b1OEAlmI9_enckWvgDDdlravI,310390 +googleapiclient/discovery_cache/documents/container.v1beta1.json,sha256=X_-hdaANczxdK1S1-Q385WBDcRIZo4Hq5hr0TlScJ5c,346072 +googleapiclient/discovery_cache/documents/containeranalysis.v1.json,sha256=UfbzOfSdF5AwiBfmEW-d-JVU7bpD28jjMsk4hgXpXaE,223841 +googleapiclient/discovery_cache/documents/containeranalysis.v1alpha1.json,sha256=b7J1ha6J1BZnZKGoAp5l1LNj73yV2Ne2gp0sidv8dH0,247765 +googleapiclient/discovery_cache/documents/containeranalysis.v1beta1.json,sha256=z9rOvR5m2M0ajXP_aCfRZFAWTTJe-wsKDgWhezu6BrQ,239061 +googleapiclient/discovery_cache/documents/content.v2.1.json,sha256=ymgq0aLEknRSoO2CavtUe3f_7GOZSVyGDwkR99vlBbU,461136 +googleapiclient/discovery_cache/documents/content.v2.json,sha256=PuV1X8AOtI9T1gaQphOf14GgA06gplme2e31cRNVSlU,389993 +googleapiclient/discovery_cache/documents/contentwarehouse.v1.json,sha256=mvMFtg4zcLO64JQheZ2qUKQCg3I69Wm9TE3TMKuVn6U,226184 +googleapiclient/discovery_cache/documents/css.v1.json,sha256=KJ2dzbEvmh5AviW8FrrVGLqjlqxFDIziUIW8RzD3k1U,39890 +googleapiclient/discovery_cache/documents/customsearch.v1.json,sha256=gkOUqUMB14JgNUySDvnSooKihyAmZWmbBYJ6Jpz0wHs,63076 +googleapiclient/discovery_cache/documents/datacatalog.v1.json,sha256=Qc9aGPU7flalDIjfmXSSAHIPeDc6RWklFWZJdnvU9Xs,190705 +googleapiclient/discovery_cache/documents/datacatalog.v1beta1.json,sha256=c2ROJXcXGwQKZRJB6c3hvIFIMxWjDxBRnF_kwAsRFuE,181608 +googleapiclient/discovery_cache/documents/dataflow.v1b3.json,sha256=sZw15obwzdR2eDprVd_yAk0S1-xOUGHgzON_539-ywo,269356 +googleapiclient/discovery_cache/documents/dataform.v1beta1.json,sha256=iROyM1iknQEzvZFAkUcl5mJadS8jks8PnGUvGZAlLOI,136023 +googleapiclient/discovery_cache/documents/datafusion.v1.json,sha256=8iHFfjfx3OPHB9FXdPGU9YvMOyr6rw5907UoESfPqWw,65057 +googleapiclient/discovery_cache/documents/datafusion.v1beta1.json,sha256=XX79TAGCboA4YoKDQO0kK4mahYMdhomkuL5sLkBbov4,73850 +googleapiclient/discovery_cache/documents/datalabeling.v1beta1.json,sha256=lns1w3E-3LgdtmfUfJyqVwbwZzQ3g6B74vHroVp6DAI,202101 +googleapiclient/discovery_cache/documents/datalineage.v1.json,sha256=6AWaukW2Ga1WYNgxlrkit7tEkBkob53s3EEuL0tn3BE,52135 +googleapiclient/discovery_cache/documents/datamigration.v1.json,sha256=nkesiVQs7Ec-ZA0-aUEejNe_C5SGp6V-vMMoVlgu6do,234856 +googleapiclient/discovery_cache/documents/datamigration.v1beta1.json,sha256=T5QcBH2cZMvW_DeBUlaA722nIv72-aFp-wgK64Xamyk,83904 +googleapiclient/discovery_cache/documents/datapipelines.v1.json,sha256=vocmGOA8i7K12Yy3kAeZ1IsUfm3fza3QWFE6taPqP6Y,38301 +googleapiclient/discovery_cache/documents/dataplex.v1.json,sha256=NhmwhtTqZEEP6YQE78o2F3FDGoQMHLWowfavDS9rkMs,450804 +googleapiclient/discovery_cache/documents/dataportability.v1.json,sha256=JIsYb0r4OeqJu3TnUjFMQsMM6RO6RLN1uSUyxB12qk4,36826 +googleapiclient/discovery_cache/documents/dataportability.v1beta.json,sha256=0unNWcA2qDzlxPxuP2kSzeAehrfQ7FFNawSOnwhioBs,36866 +googleapiclient/discovery_cache/documents/dataproc.v1.json,sha256=1vrGUIBFe13aDBIMCDfg1tEYIeroZcAjwj_GtdK2XQU,419763 +googleapiclient/discovery_cache/documents/dataproc.v1beta2.json,sha256=jfuxbI0IJ3EFmdqwKxUp_DvxU6gLnDToTAicOhZM0qo,256974 +googleapiclient/discovery_cache/documents/datastore.v1.json,sha256=xVUxCtp3Gjf2pEJpJe5QyT1JaAaEmsD_zz6a7bV70kU,100085 +googleapiclient/discovery_cache/documents/datastore.v1beta1.json,sha256=VO7aIgeLjU5MmVCPFhUE8xpT0iKgN9C3huRHRh7Gq3o,28932 +googleapiclient/discovery_cache/documents/datastore.v1beta3.json,sha256=qLI1HDg2B1L408Rxr2iJjHuxSNL1RorPuHJJ6q6BI-Y,77077 +googleapiclient/discovery_cache/documents/datastream.v1.json,sha256=n9LDes0lTcEE1TRM3-n2iuB7agQ91PszgTDgcZsyZhA,103775 +googleapiclient/discovery_cache/documents/datastream.v1alpha1.json,sha256=KmKvG9YWrllDYs58ftVhmvjhHmpOU_LrjEfuxzP6FtQ,78746 +googleapiclient/discovery_cache/documents/deploymentmanager.alpha.json,sha256=L2eiYKSq7jXnNHzCiKvHsr8iDFEOb32-vye9zb16RwE,156381 +googleapiclient/discovery_cache/documents/deploymentmanager.v2.json,sha256=AjumIeDDOEv1i8PFvUxjCq6xYMPOJ2GS_mc-WKXTX8w,110123 +googleapiclient/discovery_cache/documents/deploymentmanager.v2beta.json,sha256=ZhxaduE9vyakPZy18EkHdLWhBZ0xX_CvHlSz7LpJnqw,152471 +googleapiclient/discovery_cache/documents/developerconnect.v1.json,sha256=zCoiMEsZqTO5bRMEfWG8UEZDGYBf3WkWScTCZCtL2SU,60139 +googleapiclient/discovery_cache/documents/dfareporting.v3.3.json,sha256=AaucsfQ-9Hx6evOCWHX3E6n6llRjAlvVFEqLD2bpYXE,721039 +googleapiclient/discovery_cache/documents/dfareporting.v3.4.json,sha256=0KvRG3-gvfK5CunHotnX5uTGYwl8W_af_vg0LX0q_NU,748283 +googleapiclient/discovery_cache/documents/dfareporting.v3.5.json,sha256=E0e7bs-i0WQg8X-rLjIOn5DpeCDtiE9yDB_-J4VuCAw,17300 +googleapiclient/discovery_cache/documents/dfareporting.v4.json,sha256=23kvdMdJDjisuIu1O1MbQ9DRnU01m1tZpPpcoRtFKq8,561870 +googleapiclient/discovery_cache/documents/dialogflow.v2.json,sha256=PyLxzU9cn2TnD82R3wEyTUa7ad-JysLsWjHHoooNpVA,904937 +googleapiclient/discovery_cache/documents/dialogflow.v2beta1.json,sha256=Wg6HvfW_DURYqbmQyO2VijjOKXbSu8iRxuvPFVs0x1I,917635 +googleapiclient/discovery_cache/documents/dialogflow.v3.json,sha256=IA3ZxPl0CZpoO69KfnpMNMZnwoCWhdYlaX51Hi6dKr0,721841 +googleapiclient/discovery_cache/documents/dialogflow.v3beta1.json,sha256=wu_B0wqKmjnnzzL88PamtuVzaOsy2yfJD6gqA7uVP6Y,803132 +googleapiclient/discovery_cache/documents/digitalassetlinks.v1.json,sha256=tBdQ_zbFY3nWgAoYEu7xENFSCGOPE8fdB4DP5AR0LmU,23160 +googleapiclient/discovery_cache/documents/discovery.v1.json,sha256=OQvmfp6aUxzASZk9m__FvI0wuRXNWI_41RykyxtDkzU,18215 +googleapiclient/discovery_cache/documents/discoveryengine.v1.json,sha256=u7uCTQgoS1Rn7UI4AqAT77D6ZWOXdOJDKQFsqg6I2zo,883797 +googleapiclient/discovery_cache/documents/discoveryengine.v1alpha.json,sha256=wt8JMtvDu0XiRpb8Iri7g-Hq8jPje5xuAOE_Rr2Py2U,988792 +googleapiclient/discovery_cache/documents/discoveryengine.v1beta.json,sha256=XP3WZa-Sq99eDhAvmjzevhflqMl3jrJLLwCDwCT3BK0,938629 +googleapiclient/discovery_cache/documents/displayvideo.v1.json,sha256=RRZQfVKtGzhfs9Kx6Rp2mrBcfaRJ9p5B9I76uIcVf4c,737810 +googleapiclient/discovery_cache/documents/displayvideo.v2.json,sha256=oSWt-G3-tMflgniO0-P8CeSQdHBBof8bugoGhoOLVlo,866709 +googleapiclient/discovery_cache/documents/displayvideo.v3.json,sha256=a4ZWSkikc85rTgtrlxo0Yg9o-IwWo7kIWkLmQcoCtv0,890041 +googleapiclient/discovery_cache/documents/displayvideo.v4.json,sha256=ZawYORuamvnE2jVpVNrXpFwg7zEdTwNcHKxcGREHJ1k,9032 +googleapiclient/discovery_cache/documents/dlp.v2.json,sha256=ILofEDjRmbV_nRajCqB--gniX4jPDkatv1Dot8ac-5I,504049 +googleapiclient/discovery_cache/documents/dns.v1.json,sha256=31VX45LnFzs_GAWTePOVNpydMKIy7ZGK8Fjon5rSCsE,120991 +googleapiclient/discovery_cache/documents/dns.v1beta2.json,sha256=52oyyG4DQKMFARdbr_O7zuJr0pCiiFS6vdcCsAPXfBI,121375 +googleapiclient/discovery_cache/documents/dns.v2.json,sha256=uMSPBTHSlq6gMc-OC9ap_C9WuE34lwbub8U8wOlwoTY,136579 +googleapiclient/discovery_cache/documents/docs.v1.json,sha256=vQVgDlIF0_Q2k64NYL4moCNjX_vs6coUHA4soKIGNuE,179576 +googleapiclient/discovery_cache/documents/documentai.v1.json,sha256=VUhun0E3Y008IHLZ0eFGuV-oRZx2gI8g-OWgf0RQA14,216499 +googleapiclient/discovery_cache/documents/documentai.v1beta2.json,sha256=gOemdhcv2MjxnsArox4YiE7zXia3wVKtMsD6eqBauYw,213046 +googleapiclient/discovery_cache/documents/documentai.v1beta3.json,sha256=Mgp0oe0TMtHDrTwGa-SSCKfpTyRpBUlxzb9J-LGwVJw,242036 +googleapiclient/discovery_cache/documents/domains.v1.json,sha256=PwWqqc0xfoSTnPDD42gxDj2ImzrMVSgNsB7ZIbJuOjk,121316 +googleapiclient/discovery_cache/documents/domains.v1alpha2.json,sha256=rs8IVr2mfpDheP6stPRORTsx3_KXcnjqVoWkIMnP8z8,121969 +googleapiclient/discovery_cache/documents/domains.v1beta1.json,sha256=HUruKucE_B4mvemDN5JEM9JB75naeV7ElIHY7pMDj-s,121606 +googleapiclient/discovery_cache/documents/domainsrdap.v1.json,sha256=0kMvTxrY_-Slq8YjmDxT3Q1LZsiE4EkltzeZvzj7R3s,11381 +googleapiclient/discovery_cache/documents/doubleclickbidmanager.v1.1.json,sha256=jHYIGcAx7c3WN_5Mvr6LpEFpkB-VYgyz-rpoi41Fl-8,117347 +googleapiclient/discovery_cache/documents/doubleclickbidmanager.v1.json,sha256=c-oeCNUdKAdNJa3Bm3sqAgb6LpgiCn-s6NUUp4rsUt8,3347 +googleapiclient/discovery_cache/documents/doubleclickbidmanager.v2.json,sha256=6RTXu53BSvzuhG_XjyBAIyI8vc1Z7QWkJ_SggHTN-mI,21365 +googleapiclient/discovery_cache/documents/doubleclicksearch.v2.json,sha256=nMNNU5Wh63p6VgzxMbwKymqzMi3TmZnMbsqjzeWuT3M,34837 +googleapiclient/discovery_cache/documents/drive.v2.json,sha256=1JsmCK3ifu5GB1Pdc7PUSaEhuwzVSDuuNaXG5bjJJtI,202718 +googleapiclient/discovery_cache/documents/drive.v3.json,sha256=au-64DTcQRsh-H-9nFoN1X6URNNp2Gm1L2WLzwhqReg,172775 +googleapiclient/discovery_cache/documents/driveactivity.v2.json,sha256=8hW2V8ATuQ1vZVA-Dnvf68A76ZQ_sbrF0i_ZYC-Un84,36906 +googleapiclient/discovery_cache/documents/drivelabels.v2.json,sha256=Ht1FQobkilICz2rSzJMAStHsyHCNnK8379lRIX4Qewc,105403 +googleapiclient/discovery_cache/documents/drivelabels.v2beta.json,sha256=KllsdDh4AyvkzyqPDGNwMqPPDI7ELklHrDItFP24vNI,106827 +googleapiclient/discovery_cache/documents/essentialcontacts.v1.json,sha256=QtOLZeKQ0sPdiDEBGqfvjxWQMxQQA5GJT6bN53yTWOk,34471 +googleapiclient/discovery_cache/documents/eventarc.v1.json,sha256=nfE_eUTO4AQSL-d7jKnbcF-TLtPg7RXKYFuP11C3hgo,166722 +googleapiclient/discovery_cache/documents/eventarc.v1beta1.json,sha256=mY2ecbNBWJyhuWfnF0fndnDH0Dqr1a38N8c36huuixw,46836 +googleapiclient/discovery_cache/documents/factchecktools.v1alpha1.json,sha256=W4CxWFn3fyxidiE6iML3nT2EJg8FsBTVPgOOGjXKy6I,22449 +googleapiclient/discovery_cache/documents/fcm.v1.json,sha256=m7236CktCpc2kWNwVCKG40qiL_lxqyIqwmAvvDosjHY,31035 +googleapiclient/discovery_cache/documents/fcmdata.v1beta1.json,sha256=MyF9jSa11RwspABXDPfTYu7GVCMRxKoOOAeb6FkJiIY,15964 +googleapiclient/discovery_cache/documents/file.v1.json,sha256=peVFkE2FejtY2dS208tSulzrk7-fUb3eK6fzo9ci-7s,83404 +googleapiclient/discovery_cache/documents/file.v1beta1.json,sha256=PV7fB3cbJVOUpawLDPokrKl5o54-jj5wGvDpt51fxVQ,94032 +googleapiclient/discovery_cache/documents/firebase.v1beta1.json,sha256=SIrVTCGEiXj2AnscnEHF_KRIik5y6f0DFbbY8iaa1aM,116101 +googleapiclient/discovery_cache/documents/firebaseappcheck.v1.json,sha256=4sJ6aL6BDdt90T5PJa-4W5DbSIrHL0SvxCL4Dp5Xyxw,101098 +googleapiclient/discovery_cache/documents/firebaseappcheck.v1beta.json,sha256=ZgX4sBB3YsiZzB1iKf47-uyZKWS4hKs9F9UriqctAcI,119685 +googleapiclient/discovery_cache/documents/firebaseappdistribution.v1.json,sha256=mU5U6Iw-dcVvFKKcCWGDW9yf_gCJH3CxgYobhvNNqpU,65376 +googleapiclient/discovery_cache/documents/firebaseappdistribution.v1alpha.json,sha256=5QrB2S7cFJAnVsXnwzqZ0cayStPJe37pkQzICzDXPbY,54017 +googleapiclient/discovery_cache/documents/firebasedatabase.v1beta.json,sha256=CDrBLeHyVymSxQdwRs4RMA7M-WBCs5Jg79lnLExxtuE,15647 +googleapiclient/discovery_cache/documents/firebasedataconnect.v1beta.json,sha256=aY8mh1PEA5W1OAAes0Uv-6gTJVbp8F2CNHEaQpGVFOo,66408 +googleapiclient/discovery_cache/documents/firebasedynamiclinks.v1.json,sha256=rncuTrigAtLNh7coo5yBLG10g9E7qNRzMl7D9Lrs6Uo,33276 +googleapiclient/discovery_cache/documents/firebasehosting.v1.json,sha256=iUmcrmwQcS2COWiCL1POQJOfbbFVMfnpo9eqWWUiyN0,26742 +googleapiclient/discovery_cache/documents/firebasehosting.v1beta1.json,sha256=35WM0roy6_pJoCwTgml7DPn-xXi3QU5jaZZDMfESBfc,133655 +googleapiclient/discovery_cache/documents/firebaseml.v1.json,sha256=fshZd5QIjB4RNnuMlW2r_7Sa50JKTyE-UHjmIEwDPik,10356 +googleapiclient/discovery_cache/documents/firebaseml.v1beta2.json,sha256=1Ajl7yL81dyXtz5SQPKdh6GpKMR6W3dw-tdYjI1Sky4,16713 +googleapiclient/discovery_cache/documents/firebaseml.v2beta.json,sha256=7luCzjXobG7Dwg608hOloGuQyAiRP5MXN8ZBkacwcsY,61961 +googleapiclient/discovery_cache/documents/firebaserules.v1.json,sha256=ZJDkeRQTv28Vjex0ru4YEllbvnZbXMHPNxknLMZrUE4,34267 +googleapiclient/discovery_cache/documents/firebasestorage.v1beta.json,sha256=zOjCJ_8yjwYhvWe_8RQmeFP5aACyZe0DpTFgS5YNyS4,11253 +googleapiclient/discovery_cache/documents/firestore.v1.json,sha256=oUIEHZOtzHtn5Tr6YQAD71NmvUE7jC1NXHZrCBkO178,175534 +googleapiclient/discovery_cache/documents/firestore.v1beta1.json,sha256=llxkWENlKf5nWPvMft44-5eUZAesVQ29QE_DupcN3Kg,115173 +googleapiclient/discovery_cache/documents/firestore.v1beta2.json,sha256=JZ0NPEIf_QLVibDrdVQcj3EEKweoUqwcZR8Tqn4fa9M,42024 +googleapiclient/discovery_cache/documents/fitness.v1.json,sha256=1MKnVrlqoJrCbnSw2dXGgsnVrTpsoUvAoPtF2bObQTE,62402 +googleapiclient/discovery_cache/documents/forms.v1.json,sha256=9fTiidrmUpvtDVevcqARMFI7xR-0JI_riejral43mss,48971 +googleapiclient/discovery_cache/documents/games.v1.json,sha256=WEdJWujsSDxHD53NxbOSlEAh-qaC19SuubQcCsjJmj4,102344 +googleapiclient/discovery_cache/documents/gamesConfiguration.v1configuration.json,sha256=FwEt1QaCcASdBP9RIFgK5RaDjDrAx3SwEEvhFWZVrPE,19279 +googleapiclient/discovery_cache/documents/gamesManagement.v1management.json,sha256=Q8V6mhQAe-fVjBSx3g8C5Nyy7KwyL7OW2htLTX6j4g8,23157 +googleapiclient/discovery_cache/documents/gameservices.v1.json,sha256=E5MFFlyECvsMxSIdo45glqQhAagCDHzeRSo13RPcJ-o,54733 +googleapiclient/discovery_cache/documents/gameservices.v1beta.json,sha256=amIruWItU7wwP_wTWMXY_g2eoY-2FW24EWyPxwQtMvs,54813 +googleapiclient/discovery_cache/documents/genomics.v1.json,sha256=r75lm6eDRH0cxivOOnIQnsm-KkrMY0C8Kp1H6WirqHY,44128 +googleapiclient/discovery_cache/documents/genomics.v1alpha2.json,sha256=OBijBJvThGSucv3G0IILtC8RNGFmdxYrZeLTDFMncsM,66239 +googleapiclient/discovery_cache/documents/genomics.v2alpha1.json,sha256=80yCmc2cqlgSJfPYAxqPMCKFxDWRksFMZzsEkgcsQmI,63816 +googleapiclient/discovery_cache/documents/gkebackup.v1.json,sha256=KnOQzdzf3TigrcI05irI38RRBKxln2S4qQfAK8hjxxg,153579 +googleapiclient/discovery_cache/documents/gkehub.v1.json,sha256=vWsti_ndFL8QznyLPZJzel8_pxCSpegndQ9t6SeYonQ,229484 +googleapiclient/discovery_cache/documents/gkehub.v1alpha.json,sha256=OUtri177JQm_KW2F5vDsEKlFO8RSu86PC9D2qNAnUNU,255349 +googleapiclient/discovery_cache/documents/gkehub.v1alpha2.json,sha256=jIsXo_cDmHYf0RJNlhYTrM1ELjde46sbGHhrfbOGDc4,65373 +googleapiclient/discovery_cache/documents/gkehub.v1beta.json,sha256=PbL9STV9b_RAbhz_M3nieQT4sfWYMwnOzo_RZKQS0Yc,234386 +googleapiclient/discovery_cache/documents/gkehub.v1beta1.json,sha256=hDEstPQR1sd3b41LU5WVZnHjsGarCEzEsO6oH6WIWNE,70769 +googleapiclient/discovery_cache/documents/gkehub.v2.json,sha256=gFoyTKKO2WkBi2IFJXyPPP9gw2qjJqxjK1MhKhtSkq8,105575 +googleapiclient/discovery_cache/documents/gkehub.v2alpha.json,sha256=5cJBxMLZdxQz2rsBn-ButKasi6OSzD4vX_RW4K5JpJY,105685 +googleapiclient/discovery_cache/documents/gkehub.v2beta.json,sha256=6_FlHxaPZ9ogWxzvsKNddMZE7rh6H2RDLSGQzRdB0_g,105663 +googleapiclient/discovery_cache/documents/gkeonprem.v1.json,sha256=SB2ddnqilneRzAgLufYPcgQvEcOnGNJC3B6FjDFbgro,251420 +googleapiclient/discovery_cache/documents/gmail.v1.json,sha256=uc1urdhwNDi3D4tXAqICCeyOWvDq2rj8LfiYyNtjbys,130285 +googleapiclient/discovery_cache/documents/gmailpostmastertools.v1.json,sha256=VZ9GeZ9mWC-xOoTrHu-dGjgPbcoSf49GJjQYGaEsKz0,19146 +googleapiclient/discovery_cache/documents/gmailpostmastertools.v1beta1.json,sha256=2AzegFtjBD7FyGwpyGjlsZKSKjWpHrhjEveNQWkMLJo,19533 +googleapiclient/discovery_cache/documents/groupsmigration.v1.json,sha256=D1av9Pn3hJa_u2CCR0QiAOJzO4dnWVlxKPtcNpZzEa0,3947 +googleapiclient/discovery_cache/documents/groupssettings.v1.json,sha256=PwxhV_pC71kBNg0BHpRw9Ten-Qbst58PfAXVY6Ua5j0,24702 +googleapiclient/discovery_cache/documents/healthcare.v1.json,sha256=5S1AxogIXVgXETlNaqv3UxXgAxsATZfPh1xerwh0l7o,425514 +googleapiclient/discovery_cache/documents/healthcare.v1beta1.json,sha256=PIan6_ZWCZRuwbbOFdcTp2IXaqOOtLd9Es5HEhFt_vI,517493 +googleapiclient/discovery_cache/documents/homegraph.v1.json,sha256=qPTUCKu2hgemWV0ZD62RU5Am4q9OpZp5o-Ex-E1Cr8k,19439 +googleapiclient/discovery_cache/documents/iam.v1.json,sha256=es6jlbAQh238pUd7_q_KBGl91BvCrppWLKVNsozNp-k,225363 +googleapiclient/discovery_cache/documents/iam.v2.json,sha256=r1IFYfHw-MHMM3WAQXfsm_MPS8JHMvxaYZfy2FIb6y0,35610 +googleapiclient/discovery_cache/documents/iam.v2beta.json,sha256=xSjJ1XkIsWKAJGYvGYFwz_A-bdcG7gz_gDHT7-ZqeZo,35734 +googleapiclient/discovery_cache/documents/iamcredentials.v1.json,sha256=YY7DbQF-Oteh-PaeHyuwRCogvvRTcLo0GOWEfs76Gc0,15568 +googleapiclient/discovery_cache/documents/iap.v1.json,sha256=sHrWTDyQu5unoKWB0z4iFSfVnYv8xr8O347B1zygpEk,59990 +googleapiclient/discovery_cache/documents/iap.v1beta1.json,sha256=E-FmRVtEg5CcmV2MtJcrhp_5yYwbUnm1y0sYagRdwUQ,20190 +googleapiclient/discovery_cache/documents/ideahub.v1alpha.json,sha256=PfWK6J_Vqz1f_-mYWGBlCxLWSc8iwHrn9QRu0fl3GBY,21042 +googleapiclient/discovery_cache/documents/ideahub.v1beta.json,sha256=9N4Jk-jHWcWC-Cd5Hb_ARxh7nB6h2HVqrDNPRtei-GY,18785 +googleapiclient/discovery_cache/documents/identitytoolkit.v1.json,sha256=eTazl2iKZAjAa9mncdX4fbzaMsG7V-8xCA8fJ1KeX-I,144415 +googleapiclient/discovery_cache/documents/identitytoolkit.v2.json,sha256=2ymDalv9BVxei5zH18pCxBVp2S1XoIkKxmBuMbthXOI,135206 +googleapiclient/discovery_cache/documents/identitytoolkit.v3.json,sha256=jg5fCSI6F9Ubd6pdyjUdL3KZboo1DwLrwX_fo1SQfIY,54662 +googleapiclient/discovery_cache/documents/ids.v1.json,sha256=JPyf7bAC-g_avSj39DZ8OWEDQ4Kjtlc_DuOEL6QOdJc,45751 +googleapiclient/discovery_cache/documents/index.json,sha256=aToo8a5LH-shdyLDo4-_QRwB6s1vFYEsF2w-gmO3-eg,213200 +googleapiclient/discovery_cache/documents/indexing.v3.json,sha256=VniFOPVeV6ho_SPcRiHzZD1WuhDlrmib49WzGG6EUoU,5776 +googleapiclient/discovery_cache/documents/integrations.v1.json,sha256=xs9QD_OraCxKQg5k1Z90oL-MjjaaTaPPiFaQ6oEe6Tg,456625 +googleapiclient/discovery_cache/documents/integrations.v1alpha.json,sha256=-bp1QUgLeateldClVafXg6Z8lbasIk1B9x0Vc5UwSKk,376245 +googleapiclient/discovery_cache/documents/jobs.v2.json,sha256=L-aDKIJ86eq-tEmOrHZfKD2MmQkCP_uTwCCTQHSJcoQ,227088 +googleapiclient/discovery_cache/documents/jobs.v3.json,sha256=P-J5VqtIGG6H5Rxl2hevQTdxPTAjJO-orVKw_Fi2PfM,121084 +googleapiclient/discovery_cache/documents/jobs.v3p1beta1.json,sha256=CZjL4LfwMkUytRs_RspqoFAD8jj_daJEqCho0OPFkNg,135073 +googleapiclient/discovery_cache/documents/jobs.v4.json,sha256=-VKniKmKFhS7MMIwuCrmtz6ZDCHRuJm897uqJX0j6YU,135198 +googleapiclient/discovery_cache/documents/keep.v1.json,sha256=H6RV6WhCGGmCNNLyLnwEAADc099e1wuw0vsnnJVrx34,16988 +googleapiclient/discovery_cache/documents/kgsearch.v1.json,sha256=ZwTdiUrvYERjzOanj4JdPhI2Z9Y_A43vI79EmFRr6io,4886 +googleapiclient/discovery_cache/documents/kmsinventory.v1.json,sha256=EOJj0RNpmBO4TdluNCDMC9x4bqM12kdSBbJyopJZJOs,37255 +googleapiclient/discovery_cache/documents/language.v1.json,sha256=i9BbvkYQUc014TBtNgQIvAyOXa3AMRmHNaqL6AZTIQY,151044 +googleapiclient/discovery_cache/documents/language.v1beta1.json,sha256=-Nwsr0Ls6nKWMZSQ3ZzkW1Zdvu2gEqb6yt4r65Z1xOU,38558 +googleapiclient/discovery_cache/documents/language.v1beta2.json,sha256=2SCTKWN9wXpLK5jNvalszWpY0r5fiy3B1HCoJ-kosYY,151842 +googleapiclient/discovery_cache/documents/language.v2.json,sha256=Ux-ogcW16fvhm1RbQekLGUWrlCpeZyZmg3T4M34dgEk,137510 +googleapiclient/discovery_cache/documents/libraryagent.v1.json,sha256=DoFPi9frga11E11_DL9e8wQ6cZQfLrr1TqUJq3oNNBc,9898 +googleapiclient/discovery_cache/documents/licensing.v1.json,sha256=oOeJCfktYayhq58J7h4D_QvuULKg_vpv_7lDEySg6r4,15821 +googleapiclient/discovery_cache/documents/lifesciences.v2beta.json,sha256=4dAvNj0dYkEGW1fnfJDw50bdjXpybnuavtcv73MDNXY,52343 +googleapiclient/discovery_cache/documents/localservices.v1.json,sha256=x6aX43jOnqyA7U-kk3WhT81I9IiVEzWXvlibZ8BjARw,18779 +googleapiclient/discovery_cache/documents/logging.v2.json,sha256=BS0kjSFofWfQdY7yUmuMIfrw0i49aaSvbbhWWvxoPRk,499361 +googleapiclient/discovery_cache/documents/looker.v1.json,sha256=63oHAMoKoZnoSuqxCLW2QQyqOC-UG2uq__-qyBQzL8A,62669 +googleapiclient/discovery_cache/documents/managedidentities.v1.json,sha256=I6-Y_yAA6ziSnaSrM_l7YADwtiSuy6r16sPGmc97jck,118608 +googleapiclient/discovery_cache/documents/managedidentities.v1alpha1.json,sha256=WnjLP7j2ViTJHVOD0SEr1ZY52r4fYOZv09cye6vs17U,118634 +googleapiclient/discovery_cache/documents/managedidentities.v1beta1.json,sha256=Fo4FFL_Cay-WS4Nn5MF2_GHn5_PQpO880X-8xvzXEMU,119587 +googleapiclient/discovery_cache/documents/manufacturers.v1.json,sha256=U88bUQKj4jTRJg_6sfwe0z8J_-Il9R9TO5nAIxASzLo,41060 +googleapiclient/discovery_cache/documents/marketingplatformadmin.v1alpha.json,sha256=qTk0pcCjd6-CAIXuWIlogDucnUW-AU8Rde-UEsODlk0,12562 +googleapiclient/discovery_cache/documents/meet.v2.json,sha256=BBEIyy5XViNkOgfMe65O-rw9KBb8_yTlwgMBtD7hnuA,37589 +googleapiclient/discovery_cache/documents/memcache.v1.json,sha256=4i5N8zjO2CgySgFO_beCtfPNR43yErxoHLGjH3onSAE,71496 +googleapiclient/discovery_cache/documents/memcache.v1beta2.json,sha256=lT4Tt833MA4YeoNAOHP7l8L_oVkRtYyLqkyVSyZ3FUU,73422 +googleapiclient/discovery_cache/documents/merchantapi.accounts_v1beta.json,sha256=xpgGYUsO_vWunfZOu8rQDfaI6YEGgQ74vhZG_fZ-E0Y,124343 +googleapiclient/discovery_cache/documents/merchantapi.conversions_v1beta.json,sha256=5zcG8e8Y6TA09dl-S2Qg6ng6M2-3TIQfVc9UIFW7Ang,19642 +googleapiclient/discovery_cache/documents/merchantapi.datasources_v1beta.json,sha256=6w1rx3JrUYD4kQ5avPNTk-JFDQB8UNRYTvqRS7o3DdQ,33051 +googleapiclient/discovery_cache/documents/merchantapi.inventories_v1beta.json,sha256=Vxj_Om5hnX52OjAZuqQ95yqTi3AFBQjsr4NnM00MatM,23280 +googleapiclient/discovery_cache/documents/merchantapi.lfp_v1beta.json,sha256=VHfn9DdlpaNTifQyeuD39LjFMJ4sR0fACw3qaK4bgGU,21985 +googleapiclient/discovery_cache/documents/merchantapi.notifications_v1beta.json,sha256=3KuiqB9rXb0KRXAGSEgZkgMkKZ5U1-1q6TPUTKUd4TU,14756 +googleapiclient/discovery_cache/documents/merchantapi.products_v1beta.json,sha256=XumKJRmBkDh4PqXouuEMWt6_xyJqp_b0eKu5vK5FjL8,60221 +googleapiclient/discovery_cache/documents/merchantapi.promotions_v1beta.json,sha256=7ZjVcPPamg5_s7uPocX8SGnWF8oDqLRYRknb9CdagI4,38469 +googleapiclient/discovery_cache/documents/merchantapi.quota_v1beta.json,sha256=fHdAw0J-7XZfBpS1fGyBa6Pgbc6Sm9puiLMFu-Ok5M4,10803 +googleapiclient/discovery_cache/documents/merchantapi.reports_v1beta.json,sha256=icEEi9IIHJEKPYIcQ0GwWanIrTvRx697OcCQFGl9VRs,57674 +googleapiclient/discovery_cache/documents/merchantapi.reviews_v1beta.json,sha256=BmeYCB4nykwHEyAmdWTVhMpwPv5AgMFrgPf7t41Q508,41441 +googleapiclient/discovery_cache/documents/metastore.v1.json,sha256=q4_E6ZQsz4vu7qRkmuY35Zcio2QSPwoPHJMFRuhUlD4,138387 +googleapiclient/discovery_cache/documents/metastore.v1alpha.json,sha256=xDcn5pPHyTGM3mFo1sO8xB8iAI8Wd_aiqC07tlvs94Y,148142 +googleapiclient/discovery_cache/documents/metastore.v1beta.json,sha256=DuG0CMMSsWxWMVT_zQB8kQPv0kIHTXnbgWfHud4fKY4,148036 +googleapiclient/discovery_cache/documents/metastore.v2.json,sha256=VaZVzO_cq-76qmwfyuocvda5bbfDFgRXF3Rh5d6sDlg,65940 +googleapiclient/discovery_cache/documents/metastore.v2alpha.json,sha256=4ihtsmqBSjloKlXpeJSzEAOKxY3gVZZ0FBsXQ15TizI,87082 +googleapiclient/discovery_cache/documents/metastore.v2beta.json,sha256=mmZbhWSL0S2tI_rMSV3O9ykdcQNPhjLBxXfTw2gtANo,86926 +googleapiclient/discovery_cache/documents/migrationcenter.v1.json,sha256=Y9OsRWLjxNr-sZjnWMDo7EUTsT0nEHmeU_NdIYZUnMc,201846 +googleapiclient/discovery_cache/documents/migrationcenter.v1alpha1.json,sha256=6wzHokSknoipMJPtcWk6pEnsl-AFbzoGCdtLl-ZpPrc,260994 +googleapiclient/discovery_cache/documents/ml.v1.json,sha256=40Cfcaow97DrLVDBP6ywNVc6vJnPRr3o8aYbJa9wNvk,175519 +googleapiclient/discovery_cache/documents/monitoring.v1.json,sha256=khwaKAtHT_z_pDcpIbEnJ6oCXm72UR4e-iAYXG8Oyz0,111103 +googleapiclient/discovery_cache/documents/monitoring.v3.json,sha256=pJSIVN51vLoymsxodRL1bWsJXBh3m2Omkwnn0ebSMug,340235 +googleapiclient/discovery_cache/documents/mybusinessaccountmanagement.v1.json,sha256=U7RlX8MHt_8J5EjbR8Hd5AfnGxWixiSz8C3Bk-bkYNM,31877 +googleapiclient/discovery_cache/documents/mybusinessbusinesscalls.v1.json,sha256=w_OnSvJZU62j-5J8o3Mt3CmA2Rk7_GKu5DuhsIV574Y,16209 +googleapiclient/discovery_cache/documents/mybusinessbusinessinformation.v1.json,sha256=YM3qz1QaWvyDMqJFT3UKs9wX8Eqs06YUOnx3eLt70c4,66109 +googleapiclient/discovery_cache/documents/mybusinesslodging.v1.json,sha256=3XznJiEG44cWQkgSHQrS0u8P7mX6J6Ck9P9hGN1Sf4Q,222770 +googleapiclient/discovery_cache/documents/mybusinessnotifications.v1.json,sha256=iuA9q6GqeWcDfbXU-FrSAkrjOAXOeRBSPUXbSiIVA1I,8029 +googleapiclient/discovery_cache/documents/mybusinessplaceactions.v1.json,sha256=EnWkiYLZggSlFINF0ujYooNwRMh1qPxdVLR2n-Ikhbk,14578 +googleapiclient/discovery_cache/documents/mybusinessqanda.v1.json,sha256=MBjZalhwZNuin_Y54kxl13lctTp5ODRexqgaUK0BCBM,14263 +googleapiclient/discovery_cache/documents/mybusinessverifications.v1.json,sha256=fundzoIO-hqaxj00NFEvNpvLB2o_V-Lq0g-Z-SEPUw0,25436 +googleapiclient/discovery_cache/documents/netapp.v1.json,sha256=y4kFnBAydsWxXKol13mD84TvbAn60P744AMF-HJIwOU,119565 +googleapiclient/discovery_cache/documents/netapp.v1beta1.json,sha256=6WJTti64muknSDPEB3HRKAOefUnsOL3yw1qgsweI5yA,120185 +googleapiclient/discovery_cache/documents/networkconnectivity.v1.json,sha256=lG9TMypnujHxxrp-eeXJKw8PYkpzQxh_aT59e8tmrgU,218420 +googleapiclient/discovery_cache/documents/networkconnectivity.v1alpha1.json,sha256=6rKUWRsfJBXVkslr7A4GfMG5z68SkQOh1NKeKwc0nYw,77946 +googleapiclient/discovery_cache/documents/networkmanagement.v1.json,sha256=oL2ib2MdPcQGdEzmbMszdGFd35pibwPnb6nifuU9hqY,136309 +googleapiclient/discovery_cache/documents/networkmanagement.v1beta1.json,sha256=HCow9RtTXotqPw5u9qpVFhb2p7qDPDVXoLR0cKVUUaU,136694 +googleapiclient/discovery_cache/documents/networksecurity.v1.json,sha256=0MsKDzhUB9IEUoEf2j35J2_Vk7RX7mFw7hIM5EMCuZU,213715 +googleapiclient/discovery_cache/documents/networksecurity.v1beta1.json,sha256=N38zifIZR9ruL6HMjWLhvjbhdJ8wvruTZqRvL1K-474,297955 +googleapiclient/discovery_cache/documents/networkservices.v1.json,sha256=3F-2_pQj8BXc0X6VxOV4XkAxuASchwEVT178uQnAZX0,228821 +googleapiclient/discovery_cache/documents/networkservices.v1beta1.json,sha256=wULV7HPEgN8b9fPpTla-V3sQw0RUQ80EHwk8TGHXB0g,201237 +googleapiclient/discovery_cache/documents/notebooks.v1.json,sha256=__vD59fInJn3zUfKNttK6qgRaZyPE3VY_5s_IN02DTo,158820 +googleapiclient/discovery_cache/documents/notebooks.v2.json,sha256=9I2cY-MwbzZAK2gVfRj9vXG0OGM69G-9Ty3K-zbqbuw,73615 +googleapiclient/discovery_cache/documents/oauth2.v2.json,sha256=qbqQUKMu6g1IrIHIAfYwTAZZgIDf8j69Lr6i-rzbzLs,6601 +googleapiclient/discovery_cache/documents/ondemandscanning.v1.json,sha256=h0ZDuz_0x4m4FvPK-alwleANgGafl5OPEECbXsSp41E,89079 +googleapiclient/discovery_cache/documents/ondemandscanning.v1beta1.json,sha256=6qb8SkKIhBBRv5VMGxX3vDPO7HEL5TSb6CUbqTxEYUw,88930 +googleapiclient/discovery_cache/documents/oracledatabase.v1.json,sha256=_PpS9gAqv0eOJjsa7ViTbjOjSGskhO2QWB-48JdAF3Q,113807 +googleapiclient/discovery_cache/documents/orgpolicy.v2.json,sha256=PlrZ91XeWx8_1iPfrJWLdqeI_HfzU-O1wox_t2Wu96g,55001 +googleapiclient/discovery_cache/documents/osconfig.v1.json,sha256=jnodAzCWzshL2kpHej4PfTkLatmYZRLHjajwBd-s1oM,138166 +googleapiclient/discovery_cache/documents/osconfig.v1alpha.json,sha256=dz9rtdFFiGT0gs4tseVuQngkLKboE-40w5usXiJMvKM,102717 +googleapiclient/discovery_cache/documents/osconfig.v1beta.json,sha256=viqTggYhH-64QgnGjnL1vHmSatY6rljm4fMBc0zoJto,84213 +googleapiclient/discovery_cache/documents/osconfig.v2beta.json,sha256=QQnv2F3r7UOBh1zIsBZBdSbZpb_7OlN3baB9lAIHAjo,82834 +googleapiclient/discovery_cache/documents/oslogin.v1.json,sha256=4ET1m0EGL0LRqHrMX2sb2bPlseYbvXREX8i5281ygPs,13069 +googleapiclient/discovery_cache/documents/oslogin.v1alpha.json,sha256=mGq2JiLOp0wzSdRGyZ8jdeit9-PGW8GM2vo359H23Yk,18237 +googleapiclient/discovery_cache/documents/oslogin.v1beta.json,sha256=LKp2K_NdUiOikukkd2toj-jRemOpu7dSAghx_9r1JY4,17332 +googleapiclient/discovery_cache/documents/pagespeedonline.v5.json,sha256=HLFi19o2CGZYT-nR4oK9F-9tXBWrREeBDm9f3PKKcJY,28503 +googleapiclient/discovery_cache/documents/parallelstore.v1.json,sha256=uPQmLV0bHVI_tjbvK6t8seVP98SdxKbTVecWNT1NTRU,33852 +googleapiclient/discovery_cache/documents/parallelstore.v1beta.json,sha256=XHXxhqvfnUYlbjHUoiX9jMhv6aczOoVw71LZ3O-r30w,33747 +googleapiclient/discovery_cache/documents/paymentsresellersubscription.v1.json,sha256=GMReyufqCc1r4q61sacnTqvCNti7oRlSoRBncFKt7Jw,63907 +googleapiclient/discovery_cache/documents/people.v1.json,sha256=J4mQcN80Mg6uuLYNkT4mBIZsTRc7STO1J57hLurIhCg,112444 +googleapiclient/discovery_cache/documents/places.v1.json,sha256=Ae8Bz1mSaVA9V1svetbl3hw9mI3tt3tjciM0ZJ_zEGQ,101979 +googleapiclient/discovery_cache/documents/playablelocations.v3.json,sha256=wbkjdTm2IPCudHBKwwIOaAWwsRxfcinsOiKPR_C5G4M,25906 +googleapiclient/discovery_cache/documents/playcustomapp.v1.json,sha256=pG8LwuC9yO1R9lSyNXVskFTrZaBm_MQCl3JPegK2yck,5201 +googleapiclient/discovery_cache/documents/playdeveloperreporting.v1alpha1.json,sha256=3BaSj01eeG_qmG8QeeOp1jMzOe2jI8AEGDXkIxGVpnQ,138589 +googleapiclient/discovery_cache/documents/playdeveloperreporting.v1beta1.json,sha256=H09ESl98kgRuS7Pu5bzdQQeW0i9c20TItmxe7mTkelg,138392 +googleapiclient/discovery_cache/documents/playgrouping.v1alpha1.json,sha256=IiCHjssDE3DyDE09U8j9GumZpi2t6SZfFiy_myuQm-s,6307 +googleapiclient/discovery_cache/documents/playintegrity.v1.json,sha256=K8X2HuHOCuRoynghaOnf_DHkuqydQKJHMPMNtyp5yyY,21561 +googleapiclient/discovery_cache/documents/policyanalyzer.v1.json,sha256=UbzYQTXuhZcV_f8klEA9ps8syc4RTJ_30TpRYiLa5AE,11965 +googleapiclient/discovery_cache/documents/policyanalyzer.v1beta1.json,sha256=uc6Tw0al4RaKcVAwNyNoA2nEUNP7uzUdPxnZxnz6isQ,10922 +googleapiclient/discovery_cache/documents/policysimulator.v1.json,sha256=PYwp_Gjxv4AmP7rybR45q706VAnHXTdoMxx-AfcobRs,94572 +googleapiclient/discovery_cache/documents/policysimulator.v1alpha.json,sha256=sCxxvlvmUJpC-Wj7zQQ4FKC6WptAo6rbshCQP3LAQtk,114597 +googleapiclient/discovery_cache/documents/policysimulator.v1beta.json,sha256=e04UeFXURBxKh2YeFL8xLwwBEPOLYuOOjp9sTZ3mwTo,114473 +googleapiclient/discovery_cache/documents/policysimulator.v1beta1.json,sha256=Q1ufGYQSG04J4ufoZ0JH4hgwdeN1QeW1dhREOv-b0Pw,54107 +googleapiclient/discovery_cache/documents/policytroubleshooter.v1.json,sha256=3WWpBNOwDE2E_9qN56IAkB_QoNxbKa656bHHCbmGbB8,31718 +googleapiclient/discovery_cache/documents/policytroubleshooter.v1beta.json,sha256=k5NV64Ld0v4t3sr3ajAP0LfK9-k6OgfaR_AyZxJPQko,30156 +googleapiclient/discovery_cache/documents/pollen.v1.json,sha256=5yHEc8-STvhGa8G0J929bXjT8-YR4aUBN-U0yf4e6NA,21526 +googleapiclient/discovery_cache/documents/poly.v1.json,sha256=MI1zdlmWLLalpCwlx22n33nkszug6pksZb9_1x96K90,27091 +googleapiclient/discovery_cache/documents/privateca.v1.json,sha256=YW6nT_1nPnrEzNFZ1qLDOsMB88Y6dJIJu4qrvgITSD4,148349 +googleapiclient/discovery_cache/documents/privateca.v1beta1.json,sha256=o_tQcn7Z4-Wd2OYB7-Mv1M9vp3MrPhhJfcZAdekivtk,44003 +googleapiclient/discovery_cache/documents/prod_tt_sasportal.v1alpha1.json,sha256=96j345PnT94kQzeYPHlixWSiQk6hQ_AGYkSe7190DEs,100005 +googleapiclient/discovery_cache/documents/publicca.v1.json,sha256=msybmT2yjAQKtsnvMrA37fvWwf6i92SS9zU5RiUMne0,4849 +googleapiclient/discovery_cache/documents/publicca.v1alpha1.json,sha256=lZTujGOmJFdOVOrYFlqse4jJOM85qOAYcV_4WhALNcU,4873 +googleapiclient/discovery_cache/documents/publicca.v1beta1.json,sha256=TamTpQDX3VRyn6vt6k89HyKLcMSf9UaI8pebzVqpWSw,4869 +googleapiclient/discovery_cache/documents/pubsub.v1.json,sha256=8zk_pUWPdiaV4SRdWuBkVDkez52vGz8ihnYoJhFgO7U,129095 +googleapiclient/discovery_cache/documents/pubsub.v1beta1a.json,sha256=BANlfJnV40qmdfOkDwirxdhSc11PDRjaeV7lh2QsBuQ,26028 +googleapiclient/discovery_cache/documents/pubsub.v1beta2.json,sha256=3Y211hPblsfNRtfZ1J1PhPJy3sn5y_knPVAaxndVWPU,51138 +googleapiclient/discovery_cache/documents/pubsublite.v1.json,sha256=WMbpgrapU3kLiHERlUxjwYWKRYbm5FRYPQd4j514NI4,53052 +googleapiclient/discovery_cache/documents/rapidmigrationassessment.v1.json,sha256=oXVbBsMiTzrqLdbiKEMlLcQCJprBDtgAgESkZmO2EVI,32481 +googleapiclient/discovery_cache/documents/readerrevenuesubscriptionlinking.v1.json,sha256=JOu8DhlAMWpRZ5g8I56l7bdSkxgoj4oIb-JBxAk_rLs,8706 +googleapiclient/discovery_cache/documents/realtimebidding.v1.json,sha256=xQOSp1oYf1siTtqt6bqK4DhO_hSAYo2W_ne-fS-aays,118354 +googleapiclient/discovery_cache/documents/realtimebidding.v1alpha.json,sha256=0aZYwk8L6hmk_xVK_rr4BwT4FPEn56oiZWKVqin92rs,18365 +googleapiclient/discovery_cache/documents/recaptchaenterprise.v1.json,sha256=tQVYiks1RnSQAqbqVqWBGCdQDAdPa5ue7cZfQjI75YY,94614 +googleapiclient/discovery_cache/documents/recommendationengine.v1beta1.json,sha256=k0aW98mk8r7nsXNx8-yE_x0AIfu9uBMvo2XE14KkfeI,87898 +googleapiclient/discovery_cache/documents/recommender.v1.json,sha256=opxLJE3suZOJxlGLWEBWJCNdYVb2WAXiuuo5L42bHyE,98951 +googleapiclient/discovery_cache/documents/recommender.v1beta1.json,sha256=Omjt4eqkiA3MB0WwSQvD7NL_57N7_MXpff4xe6yC37o,110537 +googleapiclient/discovery_cache/documents/redis.v1.json,sha256=4BhewN8TBYeuSjePP4A0ppigvttIBDqaGUUaD3Ne2Gc,157176 +googleapiclient/discovery_cache/documents/redis.v1beta1.json,sha256=5KpLwVhBRYTo7OaPpdQLFWwDDATdQzbsVCpNbYyR32k,158083 +googleapiclient/discovery_cache/documents/remotebuildexecution.v1.json,sha256=7GFLt5IiI_-OYe68YsVR99EYWAZbRDdQ7MBSu2yU8n4,117609 +googleapiclient/discovery_cache/documents/remotebuildexecution.v1alpha.json,sha256=hSDoD01FhwXYIVbrOwSe7cLI19DoN1AwZm4ylBQzTgU,124864 +googleapiclient/discovery_cache/documents/remotebuildexecution.v2.json,sha256=EII61EfJ8sqYPcx-Ilk5j-XosxGTIfrN48mLKLkGfnQ,148991 +googleapiclient/discovery_cache/documents/reseller.v1.json,sha256=MMZDdYWwbKSR_7hjA60PCGweytxQc56EQN_NvbnppY4,48941 +googleapiclient/discovery_cache/documents/resourcesettings.v1.json,sha256=PBtggh9NeN9JMcNY29fETTCUnggc6vP3cjwFXEEp_jo,22442 +googleapiclient/discovery_cache/documents/retail.v2.json,sha256=ZIdkcGX2uTlxPIBeoCPsbo3vtE6oCtInqgONyoXGQC8,370710 +googleapiclient/discovery_cache/documents/retail.v2alpha.json,sha256=S0CMVwtILr7SEGyDdpATQWMDHcK74asP6cBLVgs9nfw,416459 +googleapiclient/discovery_cache/documents/retail.v2beta.json,sha256=LRww4zTaZ1xldSIQZyTn9gB1E5lrNhf4_EQs-jvMeo0,389764 +googleapiclient/discovery_cache/documents/run.v1.json,sha256=8vCM3Mi7NK_Xw8s8z4Garmaqj7Jy9nlACS_3v3CdWIs,237881 +googleapiclient/discovery_cache/documents/run.v1alpha1.json,sha256=Z0H_iXudGt7JDmcS4XZx09xCSBXPBv9MCktQqrAiayA,69965 +googleapiclient/discovery_cache/documents/run.v1beta1.json,sha256=huF9CDeN1HdW5tai_neIoAPtXOqg1t_KxstLag14WiE,40411 +googleapiclient/discovery_cache/documents/run.v2.json,sha256=H9yvAUFlq_dWaX8mrpXSh5norKubufyC9rn-ID7_jaI,222990 +googleapiclient/discovery_cache/documents/runtimeconfig.v1.json,sha256=a9eko6S6zJLWmkOlnqRstqEfiRhdFG2KKWgrFmk2NCY,10289 +googleapiclient/discovery_cache/documents/runtimeconfig.v1beta1.json,sha256=U-Eglt6hR7I2J8zmVDlWhGK3R2gs1Iy4SRZ25Z9TmD8,55381 +googleapiclient/discovery_cache/documents/safebrowsing.v4.json,sha256=swfVWEKVKLtGSoSNbRdA0LdXy48qohgK8Pnwf6yiIjE,38346 +googleapiclient/discovery_cache/documents/safebrowsing.v5.json,sha256=3oJ7cp9dBRijO_NtBpVIsfppwxMmm-L11zmWH3EQUP4,9639 +googleapiclient/discovery_cache/documents/sasportal.v1alpha1.json,sha256=MYsQcG35bK_r5XtPy_fGHq9fhWGRDCbfyftXSDP9K8c,99260 +googleapiclient/discovery_cache/documents/script.v1.json,sha256=APh7dOahCmuIJ2Qoua-EeljIP_bYn6_rsYMEfu1fOjg,52104 +googleapiclient/discovery_cache/documents/searchads360.v0.json,sha256=E6KQAKDpfsQH7li9ePnpxlMobCigsKnuUPfgSA_UVyI,330024 +googleapiclient/discovery_cache/documents/searchconsole.v1.json,sha256=z80bL4ZefQey12t3kF3j2anRBcO3Ni2-Z88iinj9u2I,40054 +googleapiclient/discovery_cache/documents/secretmanager.v1.json,sha256=4j_TOKcHQDFzRm6pVB1WQ4pzd2_Qhk87JNwWCwpVnYw,73570 +googleapiclient/discovery_cache/documents/secretmanager.v1beta1.json,sha256=PVzAlxt5L-DebguBrG-d1VkLoRiwhzGAYWN8-lwCNgg,45391 +googleapiclient/discovery_cache/documents/secretmanager.v1beta2.json,sha256=4_t6eRqdq-SnmwOGqHueUI-af1EchWfXdt1gd70xzrE,73896 +googleapiclient/discovery_cache/documents/securitycenter.v1.json,sha256=wd5rPSf5ZMYhOmPCv3zTTsqKTaroZSTtZeRs8tkk8oI,574113 +googleapiclient/discovery_cache/documents/securitycenter.v1beta1.json,sha256=em6-3a31x6-z_dTB47PBaAUdpYe3R95o_qTCT34smu0,332322 +googleapiclient/discovery_cache/documents/securitycenter.v1beta2.json,sha256=V_Xijbcu3mcGaeVlIM6EzFCIWQ26Yj79ulGpLzRzccM,355154 +googleapiclient/discovery_cache/documents/securityposture.v1.json,sha256=zgsy-R2KfQLxl6qocgr1Mv_T9iALdMSvdHk7ewz0QbU,71185 +googleapiclient/discovery_cache/documents/serviceconsumermanagement.v1.json,sha256=TIUPq5N4g6h1iU_CcqpitaLFdQe88aKdoobGW5me6mg,159824 +googleapiclient/discovery_cache/documents/serviceconsumermanagement.v1beta1.json,sha256=oucIio6BLajJAs64ywZdY2tvfhZuA3w9pYlg4GwAcq4,167066 +googleapiclient/discovery_cache/documents/servicecontrol.v1.json,sha256=53tpnS6Tolt6xpA_Q4HjT4fLJjDb_DZNjlswBMc8YRE,98114 +googleapiclient/discovery_cache/documents/servicecontrol.v2.json,sha256=ZNfLbE1Z7gIXMT3jojuGVedXslqQBVE7P6Lzm2se75g,54704 +googleapiclient/discovery_cache/documents/servicedirectory.v1.json,sha256=5MQZHzsxFcgQU4ShBy_KujENFAFlWwsrCMicG5ZHUvw,54213 +googleapiclient/discovery_cache/documents/servicedirectory.v1beta1.json,sha256=PhEcmkAf-CJYxUVRHzQDX64pN8YnTDzfUpe8dVotZPY,58715 +googleapiclient/discovery_cache/documents/servicemanagement.v1.json,sha256=pqdWtW3MyJsXLaclbJoZ06qY8t5X_nrY2R6dF7OgVjg,180450 +googleapiclient/discovery_cache/documents/servicenetworking.v1.json,sha256=udmxaNtHh9S8EmBxIkSz_SNSiDqswh-hMgrZeEEH_T8,194523 +googleapiclient/discovery_cache/documents/servicenetworking.v1beta.json,sha256=IkpBsVi9PUGCQRtwz9eEex7SROOXM1MwaJUXIWVJUOw,147234 +googleapiclient/discovery_cache/documents/serviceusage.v1.json,sha256=KNqeYY9mGch3iimfRU2HLOfSfx-OMGqJydXyzD6yONI,165913 +googleapiclient/discovery_cache/documents/serviceusage.v1beta1.json,sha256=dEe1yToSlvKH9OLcUPHsySZwvSs9lJdn5KusSJriYaw,203511 +googleapiclient/discovery_cache/documents/sheets.v4.json,sha256=cnLmZD2F-H2KmcUKsvb0iPwZWtV6Nz1KGcsp1g8wb0s,290122 +googleapiclient/discovery_cache/documents/siteVerification.v1.json,sha256=xvq_xsL575_6zsUckW9eo-jWWdxW6Ng13g8ohB2uGLA,8419 +googleapiclient/discovery_cache/documents/slides.v1.json,sha256=zJid0TPXb1gvEe8QX6RihzxQqaXLsxCMKjbBPjQiuOQ,184797 +googleapiclient/discovery_cache/documents/smartdevicemanagement.v1.json,sha256=VRNJs4ILczG5VbEi7-_jw3YjEkzGG78gRfeTwHHkUvM,12914 +googleapiclient/discovery_cache/documents/solar.v1.json,sha256=5yZ2rtmNcSGdq7R5TVBjQa5aENgGquWgty-wkdf5K5U,37771 +googleapiclient/discovery_cache/documents/sourcerepo.v1.json,sha256=PpTvPOAbVUYvw7py9gMn6bT_zn01aR939Fj9WvoSoTM,38672 +googleapiclient/discovery_cache/documents/spanner.v1.json,sha256=n5L7-KO2EvZqrjsIhJYcuhF8yZg5ystmyjlJ1A6r_k0,354544 +googleapiclient/discovery_cache/documents/speech.v1.json,sha256=GGEKtQoIBpaSPC5aUoJ2ITnTKiz6pdQDynndl3t5xvQ,64012 +googleapiclient/discovery_cache/documents/speech.v1p1beta1.json,sha256=DRXTB7k62NiHvh0Ba2x1DZKAot2Hs6svqTAuKtOhVEU,65166 +googleapiclient/discovery_cache/documents/speech.v2beta1.json,sha256=uKUcKcn6zNl_egbyhzSYNTlr_o2tpGelQ0WctfRAU54,18493 +googleapiclient/discovery_cache/documents/sqladmin.v1.json,sha256=denBcqpJWyWNSQRIJB0CjHCx8RZW1yqpPS3fNyBKJo8,202735 +googleapiclient/discovery_cache/documents/sqladmin.v1beta4.json,sha256=Ff-V3MDed7-gG8cGiujozC1706lbvq9E8XLHqBbmOqM,203992 +googleapiclient/discovery_cache/documents/storage.v1.json,sha256=2cHmcdOMLDaBE70IJdLPshboKARsCMxizBvB5x1m2Mk,192296 +googleapiclient/discovery_cache/documents/storagetransfer.v1.json,sha256=7ia7DoUsn5RPGGPoDpssg-zxVbDOJBIaFpo_ns37djw,82953 +googleapiclient/discovery_cache/documents/streetviewpublish.v1.json,sha256=6yewpIR1whuQeotQcU0Z5FM9nNBtK4KxCT2nE6ihTz0,48408 +googleapiclient/discovery_cache/documents/sts.v1.json,sha256=a6srJyiBGAygRjlDo4DYFBkfmbYetBzMZPEcGlgcvl8,27412 +googleapiclient/discovery_cache/documents/sts.v1beta.json,sha256=ehLhFjz7BpJx-iMbsbVp2uZ8ai7L55buvDelF5xHCP4,26039 +googleapiclient/discovery_cache/documents/tagmanager.v1.json,sha256=9u8cRaa2VO73YcijZyb32qCs5dSqz5pgaKCjk7aaFJQ,90841 +googleapiclient/discovery_cache/documents/tagmanager.v2.json,sha256=9J9qOoc1aNsGoKJN5vxK5VSnll5zAv3g4_bLfJXFHuY,182143 +googleapiclient/discovery_cache/documents/tasks.v1.json,sha256=QwUv_qhAMaNuPGrm_2PRb_64eqMeYD6jxwcYqS3_m1E,23350 +googleapiclient/discovery_cache/documents/testing.v1.json,sha256=ErfSznw1TuhDGknp5PpvGU0dlohFtSZL1NrvV6V2hYE,96517 +googleapiclient/discovery_cache/documents/texttospeech.v1.json,sha256=iiSSItIP3Bv8enBtFz5H1lKQQUouEqd079_zC6M_qMI,28320 +googleapiclient/discovery_cache/documents/texttospeech.v1beta1.json,sha256=Hp9IgnoLIe9FKePOjSaZmlg7TlPSSdFfGhDtuDhqPGg,27159 +googleapiclient/discovery_cache/documents/toolresults.v1beta3.json,sha256=aqB8d2Kyf1og8vOrKeqefs7FyViD8R312S-_j9dFpAw,129572 +googleapiclient/discovery_cache/documents/tpu.v1.json,sha256=igugYiMzWlYHyIf_RrAsSWSewSPU6RlX8qorT0gu6ig,32177 +googleapiclient/discovery_cache/documents/tpu.v1alpha1.json,sha256=h70ROvpgj-SJ2bx_8kubK7xTIBkx7IW-Td-WA2Im3Jw,32592 +googleapiclient/discovery_cache/documents/tpu.v2.json,sha256=xuLZjr0GZBd-cS7C7wJ8MlzouYV8Kyt3ZFWDW-KlbIE,55790 +googleapiclient/discovery_cache/documents/tpu.v2alpha1.json,sha256=zwh2sg5b1SCYRlPNWswxBF1x8CyAkxm8YOD5hz_epXI,65788 +googleapiclient/discovery_cache/documents/trafficdirector.v2.json,sha256=Mwr5Rl2tgkRXEwvkVxuYeYBMR1TlkznDoMZvp4TobFQ,32631 +googleapiclient/discovery_cache/documents/trafficdirector.v3.json,sha256=XEqVGLMARZhwrhcJgkbaht9RXTxFch6OspyqggWJZNg,49583 +googleapiclient/discovery_cache/documents/transcoder.v1.json,sha256=exIC2RnxSzTHHPhw_hOpsdv8ttiBQKiRys2ZfF7q4xA,68230 +googleapiclient/discovery_cache/documents/transcoder.v1beta1.json,sha256=nj-9u_qqRm_1H3tlaTpWZ2AEkXlgI6uIzwTqj7DZQYg,60514 +googleapiclient/discovery_cache/documents/translate.v2.json,sha256=uIVhWVyniBow5R6_BxTdei59xpeUcu2hPy_fLyX8Jig,11984 +googleapiclient/discovery_cache/documents/translate.v3.json,sha256=HrtBCxOpNG7Hri8Lfujl8wbLiB6QqObKW9I5pAw7Eaw,119552 +googleapiclient/discovery_cache/documents/translate.v3beta1.json,sha256=aasVPL7Ane3L5JyALevOO-QaOx1gxQ-yFV7klIXf8sU,68811 +googleapiclient/discovery_cache/documents/travelimpactmodel.v1.json,sha256=eDKYeRQqM2TzYd6p5wtwmApjKvJxBBmcsC_YFSxfhNY,9794 +googleapiclient/discovery_cache/documents/vault.v1.json,sha256=1SkrgZdLQzz1GPlHNTBK0r1j5yIeXZ_821BasBvKzgQ,74876 +googleapiclient/discovery_cache/documents/vectortile.v1.json,sha256=ox05W0ZLG7Yn-6uUQOP3yeXgibQfaQ6tqA6iv865DQg,47652 +googleapiclient/discovery_cache/documents/verifiedaccess.v1.json,sha256=ZkV9tEhH50MT-olunVV3JgrrUGBAOEjvqGlYD4bEaOg,6762 +googleapiclient/discovery_cache/documents/verifiedaccess.v2.json,sha256=s6ul_w8_t_hWmapmY9HTbfOqobscqQgFDXLfgKkyFNY,17399 +googleapiclient/discovery_cache/documents/versionhistory.v1.json,sha256=BwYMLgoRxklbjB4vPgJAmVcxeuFcfxuNqmU2UpzwFaI,17208 +googleapiclient/discovery_cache/documents/videointelligence.v1.json,sha256=bS5RJTw0RmnzN_959Lcswnr1EkBjZKFyJr6D3PkCouU,177769 +googleapiclient/discovery_cache/documents/videointelligence.v1beta2.json,sha256=VXXJIfS-HTi3NerPmbwo8PG8AxyG34eQkNu4SDs6NGw,169935 +googleapiclient/discovery_cache/documents/videointelligence.v1p1beta1.json,sha256=5NmWrlOSASJCUOxHjg2PdMNt74ZK6o3oF4P9HtbJE4M,170011 +googleapiclient/discovery_cache/documents/videointelligence.v1p2beta1.json,sha256=su-l5Hevu5penBXa4CjRjekw3PrQaEhkhahC3IveJRk,170011 +googleapiclient/discovery_cache/documents/videointelligence.v1p3beta1.json,sha256=NdbSeRYC9drqIMZ-ksNv-_n5sR7bHvYeaUTCJswhJ4o,170062 +googleapiclient/discovery_cache/documents/vision.v1.json,sha256=h4oQU338yEcP8F9Ux5L9GRuUklohw0-BwS_nrzxbeLI,313435 +googleapiclient/discovery_cache/documents/vision.v1p1beta1.json,sha256=WLUhg3ah5PTFA_cV0gc1tuVy_mcwhTE7muurC0mKHeM,278159 +googleapiclient/discovery_cache/documents/vision.v1p2beta1.json,sha256=FRMYZPzEEih3EmJB48C0S0BaWaF8qy2JRvG58QC2gt8,278159 +googleapiclient/discovery_cache/documents/vmmigration.v1.json,sha256=6Szpeh1WgqwJ-Cq2hwq1bNfGGLJeVidOpGt2AHNG8cQ,186747 +googleapiclient/discovery_cache/documents/vmmigration.v1alpha1.json,sha256=LAJYXqVxGkmMJo-1EMmX9UsHSWJ3PWFqg2_YXHBXqFA,194350 +googleapiclient/discovery_cache/documents/vmwareengine.v1.json,sha256=A53DnykSnFlSWRcsgrOfJ50htMLYiM4pKjwR_V_qy4w,256218 +googleapiclient/discovery_cache/documents/vpcaccess.v1.json,sha256=7qrUllrWKdu7F0H1JpVsr2eYCQEul_w3PW_ECPnFzrA,21178 +googleapiclient/discovery_cache/documents/vpcaccess.v1beta1.json,sha256=r3FqAwy19cLLhb8Hkp7FiLIVpH0syQt7oUUX4TM7CcU,21577 +googleapiclient/discovery_cache/documents/walletobjects.v1.json,sha256=1CDFgjzSC4SlYakgTY5AM0ZgPCkIqK7vgSnDncUT7kA,352754 +googleapiclient/discovery_cache/documents/webfonts.v1.json,sha256=2rRNnUYk-ciNDxKVHxdZ0oDav0fJK9weyEZuxy895SM,6307 +googleapiclient/discovery_cache/documents/webmasters.v3.json,sha256=TOTCcpZyUk-dbmnjFVFoQam0hq2wFQg2UV9x-4fuVLY,20097 +googleapiclient/discovery_cache/documents/webrisk.v1.json,sha256=so8K_q7m91psDyyd3aZ_7fKz3CqxLChoz2_El0Pbaig,26588 +googleapiclient/discovery_cache/documents/websecurityscanner.v1.json,sha256=l7GYs5aNeyH3_bUuVGdUWxFHfk49VIKcXLl7kRJkZEI,44164 +googleapiclient/discovery_cache/documents/websecurityscanner.v1alpha.json,sha256=QDkWVUfvc0d8Bw95U-_IKqyD3aJ1xXcIcsun53gHdaU,36660 +googleapiclient/discovery_cache/documents/websecurityscanner.v1beta.json,sha256=Cy-vHk51aVECUYd1QFrnow39K0KwVF_RVD9qPdUS9l8,42878 +googleapiclient/discovery_cache/documents/workflowexecutions.v1.json,sha256=MePGvzvFlN5rjJgBIvRwERWUOXhBtENYO_mR8Er-7eA,39209 +googleapiclient/discovery_cache/documents/workflowexecutions.v1beta.json,sha256=tERDKKmiGTK-l3wOrCAhVaA1SjA9d2Wp2fcbqaScb4E,14318 +googleapiclient/discovery_cache/documents/workflows.v1.json,sha256=CAw0DCnGVvqZL6PzCfQ5j3Zs_0O5nClVjmsOReI1jgM,28611 +googleapiclient/discovery_cache/documents/workflows.v1beta.json,sha256=uzRGy5XU0VF1X9yjB5cVgfnnUDAOWrSoNYIvdAeYHsk,22461 +googleapiclient/discovery_cache/documents/workloadmanager.v1.json,sha256=DgV3tXKBcX-wECgZ7s0svBuWNIlVh3VqsnYl3qpROc8,67964 +googleapiclient/discovery_cache/documents/workspaceevents.v1.json,sha256=xyHj_UBJyQrs7Soj4IqyvOq5OsqQlWHMuXSS2pk1btA,30827 +googleapiclient/discovery_cache/documents/workstations.v1.json,sha256=IXjqVydLESbVTMBE5f5_Ic4nxLb4ch2gctD8l3yXnWY,104610 +googleapiclient/discovery_cache/documents/workstations.v1beta.json,sha256=XWSL6Hd1rZV3GnqUw2ME0WBH16jQSZN_DkCnMnn7jC4,104108 +googleapiclient/discovery_cache/documents/youtube.v3.json,sha256=W0gQY8maCA1W8mYW6aTa3mFEYT_oDLT_ty9LgLvB8S0,372790 +googleapiclient/discovery_cache/documents/youtubeAnalytics.v1.json,sha256=KLPoKHO_XT4-LyIZjwSGh44f_htMwjePD2PXR8MH-YY,3220 +googleapiclient/discovery_cache/documents/youtubeAnalytics.v2.json,sha256=Ixl98t7bVU4LpG2rai6RgzSeW-TUmk4abOVGzzttGKQ,28888 +googleapiclient/discovery_cache/documents/youtubereporting.v1.json,sha256=fez-pYUk5gzTFG4W4mPJ7crQV1F1msk1Zdxf2DzpLl0,23094 +googleapiclient/discovery_cache/file_cache.py,sha256=sim3Mg4HgRYo3vX75jvcKy_aV568EvIrtBfvfbw-044,4774 +googleapiclient/errors.py,sha256=9h3uimcMcczBHZJFWAX_YDABzJeJugWB0jmj11rp-LI,5460 +googleapiclient/http.py,sha256=ITE51oqDBqN1-AA5D-Tnlj3egGc_5O0V5xSzBw3UTKI,68241 +googleapiclient/mimeparse.py,sha256=wwouQMCjppTocJtiQhkkTa27kocYwlFRALL2z11Xo1Y,6530 +googleapiclient/model.py,sha256=NQDO1GhOGNVCJlSSCLOecdA11yf8RDXfSLFxYb3R7EE,14085 +googleapiclient/sample_tools.py,sha256=hDBLJNwLluN6AGhaBR-3GXWDrOwRNPvXyseLOyz9l30,4315 +googleapiclient/schema.py,sha256=rR3u8WPQ_V8a7GCUsNuvtf6GxzwuMO0HaqsTBp3tnyM,10414 +googleapiclient/version.py,sha256=i41lsW2z1sl9n_bGRT7un47ma9dWmwY4ESL1bEHiHmI,599 diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/WHEEL b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ee810efb31c37815a70827942976bfcba5b5d2bc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.3.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..f907e7e175c8bc3cc7226dbe019836a82cefb1ea --- /dev/null +++ b/.venv/lib/python3.11/site-packages/google_api_python_client-2.159.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +apiclient +googleapiclient +googleapiclient/discovery_cache diff --git a/.venv/lib/python3.11/site-packages/networkx/__init__.py b/.venv/lib/python3.11/site-packages/networkx/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5710827b61d5f5fa25dde778489c2d56677b9b37 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/__init__.py @@ -0,0 +1,53 @@ +""" +NetworkX +======== + +NetworkX is a Python package for the creation, manipulation, and study of the +structure, dynamics, and functions of complex networks. + +See https://networkx.org for complete documentation. +""" + +__version__ = "3.4.2" + + +# These are imported in order as listed +from networkx.lazy_imports import _lazy_import + +from networkx.exception import * + +from networkx import utils +from networkx.utils import _clear_cache, _dispatchable + +# load_and_call entry_points, set configs +config = utils.backends._set_configs_from_environment() +utils.config = utils.configs.config = config # type: ignore[attr-defined] + +from networkx import classes +from networkx.classes import filters +from networkx.classes import * + +from networkx import convert +from networkx.convert import * + +from networkx import convert_matrix +from networkx.convert_matrix import * + +from networkx import relabel +from networkx.relabel import * + +from networkx import generators +from networkx.generators import * + +from networkx import readwrite +from networkx.readwrite import * + +# Need to test with SciPy, when available +from networkx import algorithms +from networkx.algorithms import * + +from networkx import linalg +from networkx.linalg import * + +from networkx import drawing +from networkx.drawing import * diff --git a/.venv/lib/python3.11/site-packages/networkx/convert.py b/.venv/lib/python3.11/site-packages/networkx/convert.py new file mode 100644 index 0000000000000000000000000000000000000000..3f89c35d8067bf06768109f73caa3436d3719662 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/convert.py @@ -0,0 +1,502 @@ +"""Functions to convert NetworkX graphs to and from other formats. + +The preferred way of converting data to a NetworkX graph is through the +graph constructor. The constructor calls the to_networkx_graph() function +which attempts to guess the input type and convert it automatically. + +Examples +-------- +Create a graph with a single edge from a dictionary of dictionaries + +>>> d = {0: {1: 1}} # dict-of-dicts single edge (0,1) +>>> G = nx.Graph(d) + +See Also +-------- +nx_agraph, nx_pydot +""" + +import warnings +from collections.abc import Collection, Generator, Iterator + +import networkx as nx + +__all__ = [ + "to_networkx_graph", + "from_dict_of_dicts", + "to_dict_of_dicts", + "from_dict_of_lists", + "to_dict_of_lists", + "from_edgelist", + "to_edgelist", +] + + +def to_networkx_graph(data, create_using=None, multigraph_input=False): + """Make a NetworkX graph from a known data structure. + + The preferred way to call this is automatically + from the class constructor + + >>> d = {0: {1: {"weight": 1}}} # dict-of-dicts single edge (0,1) + >>> G = nx.Graph(d) + + instead of the equivalent + + >>> G = nx.from_dict_of_dicts(d) + + Parameters + ---------- + data : object to be converted + + Current known types are: + any NetworkX graph + dict-of-dicts + dict-of-lists + container (e.g. set, list, tuple) of edges + iterator (e.g. itertools.chain) that produces edges + generator of edges + Pandas DataFrame (row per edge) + 2D numpy array + scipy sparse array + pygraphviz agraph + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + multigraph_input : bool (default False) + If True and data is a dict_of_dicts, + try to create a multigraph assuming dict_of_dict_of_lists. + If data and create_using are both multigraphs then create + a multigraph from a multigraph. + + """ + # NX graph + if hasattr(data, "adj"): + try: + result = from_dict_of_dicts( + data.adj, + create_using=create_using, + multigraph_input=data.is_multigraph(), + ) + # data.graph should be dict-like + result.graph.update(data.graph) + # data.nodes should be dict-like + # result.add_node_from(data.nodes.items()) possible but + # for custom node_attr_dict_factory which may be hashable + # will be unexpected behavior + for n, dd in data.nodes.items(): + result._node[n].update(dd) + return result + except Exception as err: + raise nx.NetworkXError("Input is not a correct NetworkX graph.") from err + + # dict of dicts/lists + if isinstance(data, dict): + try: + return from_dict_of_dicts( + data, create_using=create_using, multigraph_input=multigraph_input + ) + except Exception as err1: + if multigraph_input is True: + raise nx.NetworkXError( + f"converting multigraph_input raised:\n{type(err1)}: {err1}" + ) + try: + return from_dict_of_lists(data, create_using=create_using) + except Exception as err2: + raise TypeError("Input is not known type.") from err2 + + # edgelists + if isinstance(data, list | tuple | nx.reportviews.EdgeViewABC | Iterator): + try: + return from_edgelist(data, create_using=create_using) + except: + pass + + # pygraphviz agraph + if hasattr(data, "is_strict"): + try: + return nx.nx_agraph.from_agraph(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError("Input is not a correct pygraphviz graph.") from err + + # Pandas DataFrame + try: + import pandas as pd + + if isinstance(data, pd.DataFrame): + if data.shape[0] == data.shape[1]: + try: + return nx.from_pandas_adjacency(data, create_using=create_using) + except Exception as err: + msg = "Input is not a correct Pandas DataFrame adjacency matrix." + raise nx.NetworkXError(msg) from err + else: + try: + return nx.from_pandas_edgelist( + data, edge_attr=True, create_using=create_using + ) + except Exception as err: + msg = "Input is not a correct Pandas DataFrame edge-list." + raise nx.NetworkXError(msg) from err + except ImportError: + pass + + # numpy array + try: + import numpy as np + + if isinstance(data, np.ndarray): + try: + return nx.from_numpy_array(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError( + f"Failed to interpret array as an adjacency matrix." + ) from err + except ImportError: + pass + + # scipy sparse array - any format + try: + import scipy + + if hasattr(data, "format"): + try: + return nx.from_scipy_sparse_array(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError( + "Input is not a correct scipy sparse array type." + ) from err + except ImportError: + pass + + # Note: most general check - should remain last in order of execution + # Includes containers (e.g. list, set, dict, etc.), generators, and + # iterators (e.g. itertools.chain) of edges + + if isinstance(data, Collection | Generator | Iterator): + try: + return from_edgelist(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError("Input is not a valid edge list") from err + + raise nx.NetworkXError("Input is not a known data type for conversion.") + + +@nx._dispatchable +def to_dict_of_lists(G, nodelist=None): + """Returns adjacency representation of graph as a dictionary of lists. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist + + Notes + ----- + Completely ignores edge data for MultiGraph and MultiDiGraph. + + """ + if nodelist is None: + nodelist = G + + d = {} + for n in nodelist: + d[n] = [nbr for nbr in G.neighbors(n) if nbr in nodelist] + return d + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_dict_of_lists(d, create_using=None): + """Returns a graph from a dictionary of lists. + + Parameters + ---------- + d : dictionary of lists + A dictionary of lists adjacency representation. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + Examples + -------- + >>> dol = {0: [1]} # single edge (0,1) + >>> G = nx.from_dict_of_lists(dol) + + or + + >>> G = nx.Graph(dol) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_nodes_from(d) + if G.is_multigraph() and not G.is_directed(): + # a dict_of_lists can't show multiedges. BUT for undirected graphs, + # each edge shows up twice in the dict_of_lists. + # So we need to treat this case separately. + seen = {} + for node, nbrlist in d.items(): + for nbr in nbrlist: + if nbr not in seen: + G.add_edge(node, nbr) + seen[node] = 1 # don't allow reverse edge to show up + else: + G.add_edges_from( + ((node, nbr) for node, nbrlist in d.items() for nbr in nbrlist) + ) + return G + + +def to_dict_of_dicts(G, nodelist=None, edge_data=None): + """Returns adjacency representation of graph as a dictionary of dictionaries. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist + + edge_data : scalar, optional + If provided, the value of the dictionary will be set to `edge_data` for + all edges. Usual values could be `1` or `True`. If `edge_data` is + `None` (the default), the edgedata in `G` is used, resulting in a + dict-of-dict-of-dicts. If `G` is a MultiGraph, the result will be a + dict-of-dict-of-dict-of-dicts. See Notes for an approach to customize + handling edge data. `edge_data` should *not* be a container. + + Returns + ------- + dod : dict + A nested dictionary representation of `G`. Note that the level of + nesting depends on the type of `G` and the value of `edge_data` + (see Examples). + + See Also + -------- + from_dict_of_dicts, to_dict_of_lists + + Notes + ----- + For a more custom approach to handling edge data, try:: + + dod = { + n: {nbr: custom(n, nbr, dd) for nbr, dd in nbrdict.items()} + for n, nbrdict in G.adj.items() + } + + where `custom` returns the desired edge data for each edge between `n` and + `nbr`, given existing edge data `dd`. + + Examples + -------- + >>> G = nx.path_graph(3) + >>> nx.to_dict_of_dicts(G) + {0: {1: {}}, 1: {0: {}, 2: {}}, 2: {1: {}}} + + Edge data is preserved by default (``edge_data=None``), resulting + in dict-of-dict-of-dicts where the innermost dictionary contains the + edge data: + + >>> G = nx.Graph() + >>> G.add_edges_from( + ... [ + ... (0, 1, {"weight": 1.0}), + ... (1, 2, {"weight": 2.0}), + ... (2, 0, {"weight": 1.0}), + ... ] + ... ) + >>> d = nx.to_dict_of_dicts(G) + >>> d # doctest: +SKIP + {0: {1: {'weight': 1.0}, 2: {'weight': 1.0}}, + 1: {0: {'weight': 1.0}, 2: {'weight': 2.0}}, + 2: {1: {'weight': 2.0}, 0: {'weight': 1.0}}} + >>> d[1][2]["weight"] + 2.0 + + If `edge_data` is not `None`, edge data in the original graph (if any) is + replaced: + + >>> d = nx.to_dict_of_dicts(G, edge_data=1) + >>> d + {0: {1: 1, 2: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 0: 1}} + >>> d[1][2] + 1 + + This also applies to MultiGraphs: edge data is preserved by default: + + >>> G = nx.MultiGraph() + >>> G.add_edge(0, 1, key="a", weight=1.0) + 'a' + >>> G.add_edge(0, 1, key="b", weight=5.0) + 'b' + >>> d = nx.to_dict_of_dicts(G) + >>> d # doctest: +SKIP + {0: {1: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}, + 1: {0: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}} + >>> d[0][1]["b"]["weight"] + 5.0 + + But multi edge data is lost if `edge_data` is not `None`: + + >>> d = nx.to_dict_of_dicts(G, edge_data=10) + >>> d + {0: {1: 10}, 1: {0: 10}} + """ + dod = {} + if nodelist is None: + if edge_data is None: + for u, nbrdict in G.adjacency(): + dod[u] = nbrdict.copy() + else: # edge_data is not None + for u, nbrdict in G.adjacency(): + dod[u] = dod.fromkeys(nbrdict, edge_data) + else: # nodelist is not None + if edge_data is None: + for u in nodelist: + dod[u] = {} + for v, data in ((v, data) for v, data in G[u].items() if v in nodelist): + dod[u][v] = data + else: # nodelist and edge_data are not None + for u in nodelist: + dod[u] = {} + for v in (v for v in G[u] if v in nodelist): + dod[u][v] = edge_data + return dod + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_dict_of_dicts(d, create_using=None, multigraph_input=False): + """Returns a graph from a dictionary of dictionaries. + + Parameters + ---------- + d : dictionary of dictionaries + A dictionary of dictionaries adjacency representation. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + multigraph_input : bool (default False) + When True, the dict `d` is assumed + to be a dict-of-dict-of-dict-of-dict structure keyed by + node to neighbor to edge keys to edge data for multi-edges. + Otherwise this routine assumes dict-of-dict-of-dict keyed by + node to neighbor to edge data. + + Examples + -------- + >>> dod = {0: {1: {"weight": 1}}} # single edge (0,1) + >>> G = nx.from_dict_of_dicts(dod) + + or + + >>> G = nx.Graph(dod) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_nodes_from(d) + # does dict d represent a MultiGraph or MultiDiGraph? + if multigraph_input: + if G.is_directed(): + if G.is_multigraph(): + G.add_edges_from( + (u, v, key, data) + for u, nbrs in d.items() + for v, datadict in nbrs.items() + for key, data in datadict.items() + ) + else: + G.add_edges_from( + (u, v, data) + for u, nbrs in d.items() + for v, datadict in nbrs.items() + for key, data in datadict.items() + ) + else: # Undirected + if G.is_multigraph(): + seen = set() # don't add both directions of undirected graph + for u, nbrs in d.items(): + for v, datadict in nbrs.items(): + if (u, v) not in seen: + G.add_edges_from( + (u, v, key, data) for key, data in datadict.items() + ) + seen.add((v, u)) + else: + seen = set() # don't add both directions of undirected graph + for u, nbrs in d.items(): + for v, datadict in nbrs.items(): + if (u, v) not in seen: + G.add_edges_from( + (u, v, data) for key, data in datadict.items() + ) + seen.add((v, u)) + + else: # not a multigraph to multigraph transfer + if G.is_multigraph() and not G.is_directed(): + # d can have both representations u-v, v-u in dict. Only add one. + # We don't need this check for digraphs since we add both directions, + # or for Graph() since it is done implicitly (parallel edges not allowed) + seen = set() + for u, nbrs in d.items(): + for v, data in nbrs.items(): + if (u, v) not in seen: + G.add_edge(u, v, key=0) + G[u][v][0].update(data) + seen.add((v, u)) + else: + G.add_edges_from( + ((u, v, data) for u, nbrs in d.items() for v, data in nbrs.items()) + ) + return G + + +@nx._dispatchable(preserve_edge_attrs=True) +def to_edgelist(G, nodelist=None): + """Returns a list of edges in the graph. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist + + """ + if nodelist is None: + return G.edges(data=True) + return G.edges(nodelist, data=True) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_edgelist(edgelist, create_using=None): + """Returns a graph from a list of edges. + + Parameters + ---------- + edgelist : list or iterator + Edge tuples + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + Examples + -------- + >>> edgelist = [(0, 1)] # single edge (0,1) + >>> G = nx.from_edgelist(edgelist) + + or + + >>> G = nx.Graph(edgelist) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_edges_from(edgelist) + return G diff --git a/.venv/lib/python3.11/site-packages/networkx/exception.py b/.venv/lib/python3.11/site-packages/networkx/exception.py new file mode 100644 index 0000000000000000000000000000000000000000..c960cf13fd5a8e4da0ca68c66350b8baa1728c34 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/exception.py @@ -0,0 +1,131 @@ +""" +********** +Exceptions +********** + +Base exceptions and errors for NetworkX. +""" + +__all__ = [ + "HasACycle", + "NodeNotFound", + "PowerIterationFailedConvergence", + "ExceededMaxIterations", + "AmbiguousSolution", + "NetworkXAlgorithmError", + "NetworkXException", + "NetworkXError", + "NetworkXNoCycle", + "NetworkXNoPath", + "NetworkXNotImplemented", + "NetworkXPointlessConcept", + "NetworkXUnbounded", + "NetworkXUnfeasible", +] + + +class NetworkXException(Exception): + """Base class for exceptions in NetworkX.""" + + +class NetworkXError(NetworkXException): + """Exception for a serious error in NetworkX""" + + +class NetworkXPointlessConcept(NetworkXException): + """Raised when a null graph is provided as input to an algorithm + that cannot use it. + + The null graph is sometimes considered a pointless concept [1]_, + thus the name of the exception. + + Notes + ----- + Null graphs and empty graphs are often used interchangeably but they + are well defined in NetworkX. An ``empty_graph`` is a graph with ``n`` nodes + and 0 edges, and a ``null_graph`` is a graph with 0 nodes and 0 edges. + + References + ---------- + .. [1] Harary, F. and Read, R. "Is the Null Graph a Pointless + Concept?" In Graphs and Combinatorics Conference, George + Washington University. New York: Springer-Verlag, 1973. + + """ + + +class NetworkXAlgorithmError(NetworkXException): + """Exception for unexpected termination of algorithms.""" + + +class NetworkXUnfeasible(NetworkXAlgorithmError): + """Exception raised by algorithms trying to solve a problem + instance that has no feasible solution.""" + + +class NetworkXNoPath(NetworkXUnfeasible): + """Exception for algorithms that should return a path when running + on graphs where such a path does not exist.""" + + +class NetworkXNoCycle(NetworkXUnfeasible): + """Exception for algorithms that should return a cycle when running + on graphs where such a cycle does not exist.""" + + +class HasACycle(NetworkXException): + """Raised if a graph has a cycle when an algorithm expects that it + will have no cycles. + + """ + + +class NetworkXUnbounded(NetworkXAlgorithmError): + """Exception raised by algorithms trying to solve a maximization + or a minimization problem instance that is unbounded.""" + + +class NetworkXNotImplemented(NetworkXException): + """Exception raised by algorithms not implemented for a type of graph.""" + + +class NodeNotFound(NetworkXException): + """Exception raised if requested node is not present in the graph""" + + +class AmbiguousSolution(NetworkXException): + """Raised if more than one valid solution exists for an intermediary step + of an algorithm. + + In the face of ambiguity, refuse the temptation to guess. + This may occur, for example, when trying to determine the + bipartite node sets in a disconnected bipartite graph when + computing bipartite matchings. + + """ + + +class ExceededMaxIterations(NetworkXException): + """Raised if a loop iterates too many times without breaking. + + This may occur, for example, in an algorithm that computes + progressively better approximations to a value but exceeds an + iteration bound specified by the user. + + """ + + +class PowerIterationFailedConvergence(ExceededMaxIterations): + """Raised when the power iteration method fails to converge within a + specified iteration limit. + + `num_iterations` is the number of iterations that have been + completed when this exception was raised. + + """ + + def __init__(self, num_iterations, *args, **kw): + msg = f"power iteration failed to converge within {num_iterations} iterations" + exception_message = msg + superinit = super().__init__ + superinit(self, exception_message, *args, **kw) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__init__.py b/.venv/lib/python3.11/site-packages/networkx/linalg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..119db185a1ae440fd2cdb6c7f531331642313c34 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/__init__.py @@ -0,0 +1,13 @@ +from networkx.linalg.attrmatrix import * +from networkx.linalg import attrmatrix +from networkx.linalg.spectrum import * +from networkx.linalg import spectrum +from networkx.linalg.graphmatrix import * +from networkx.linalg import graphmatrix +from networkx.linalg.laplacianmatrix import * +from networkx.linalg import laplacianmatrix +from networkx.linalg.algebraicconnectivity import * +from networkx.linalg.modularitymatrix import * +from networkx.linalg import modularitymatrix +from networkx.linalg.bethehessianmatrix import * +from networkx.linalg import bethehessianmatrix diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00931f37b5b987c91e404636290921bdd23decd0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/algebraicconnectivity.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/algebraicconnectivity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2cb24f3a85b957d50430f07806bb04e0c259b9c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/algebraicconnectivity.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/attrmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/attrmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac1fd1478134d9ad0cef5d56bafe4d2cccae88e0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/attrmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/bethehessianmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/bethehessianmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1da300fc49da3d7a80c4cf426ae98b9ccd99f197 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/bethehessianmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/graphmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/graphmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..076164dec7059fe9da3321df49d5d5745e02935b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/graphmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/laplacianmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/laplacianmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06f2f93cf076e5dd57a72c8837afb42afc7a61c3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/laplacianmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/modularitymatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/modularitymatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed8316cff23e196247d0150cf8ef22b01dd06dd9 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/modularitymatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/spectrum.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/spectrum.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f715ea0e40e5de3d9430fe726184acd5fe7684c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/__pycache__/spectrum.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/algebraicconnectivity.py b/.venv/lib/python3.11/site-packages/networkx/linalg/algebraicconnectivity.py new file mode 100644 index 0000000000000000000000000000000000000000..c94972a5c335569992f1cf19be5f55097cbdffd9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/algebraicconnectivity.py @@ -0,0 +1,657 @@ +""" +Algebraic connectivity and Fiedler vectors of undirected graphs. +""" + +from functools import partial + +import networkx as nx +from networkx.utils import ( + not_implemented_for, + np_random_state, + reverse_cuthill_mckee_ordering, +) + +__all__ = [ + "algebraic_connectivity", + "fiedler_vector", + "spectral_ordering", + "spectral_bisection", +] + + +class _PCGSolver: + """Preconditioned conjugate gradient method. + + To solve Ax = b: + M = A.diagonal() # or some other preconditioner + solver = _PCGSolver(lambda x: A * x, lambda x: M * x) + x = solver.solve(b) + + The inputs A and M are functions which compute + matrix multiplication on the argument. + A - multiply by the matrix A in Ax=b + M - multiply by M, the preconditioner surrogate for A + + Warning: There is no limit on number of iterations. + """ + + def __init__(self, A, M): + self._A = A + self._M = M + + def solve(self, B, tol): + import numpy as np + + # Densifying step - can this be kept sparse? + B = np.asarray(B) + X = np.ndarray(B.shape, order="F") + for j in range(B.shape[1]): + X[:, j] = self._solve(B[:, j], tol) + return X + + def _solve(self, b, tol): + import numpy as np + import scipy as sp + + A = self._A + M = self._M + tol *= sp.linalg.blas.dasum(b) + # Initialize. + x = np.zeros(b.shape) + r = b.copy() + z = M(r) + rz = sp.linalg.blas.ddot(r, z) + p = z.copy() + # Iterate. + while True: + Ap = A(p) + alpha = rz / sp.linalg.blas.ddot(p, Ap) + x = sp.linalg.blas.daxpy(p, x, a=alpha) + r = sp.linalg.blas.daxpy(Ap, r, a=-alpha) + if sp.linalg.blas.dasum(r) < tol: + return x + z = M(r) + beta = sp.linalg.blas.ddot(r, z) + beta, rz = beta / rz, beta + p = sp.linalg.blas.daxpy(p, z, a=beta) + + +class _LUSolver: + """LU factorization. + + To solve Ax = b: + solver = _LUSolver(A) + x = solver.solve(b) + + optional argument `tol` on solve method is ignored but included + to match _PCGsolver API. + """ + + def __init__(self, A): + import scipy as sp + + self._LU = sp.sparse.linalg.splu( + A, + permc_spec="MMD_AT_PLUS_A", + diag_pivot_thresh=0.0, + options={"Equil": True, "SymmetricMode": True}, + ) + + def solve(self, B, tol=None): + import numpy as np + + B = np.asarray(B) + X = np.ndarray(B.shape, order="F") + for j in range(B.shape[1]): + X[:, j] = self._LU.solve(B[:, j]) + return X + + +def _preprocess_graph(G, weight): + """Compute edge weights and eliminate zero-weight edges.""" + if G.is_directed(): + H = nx.MultiGraph() + H.add_nodes_from(G) + H.add_weighted_edges_from( + ((u, v, e.get(weight, 1.0)) for u, v, e in G.edges(data=True) if u != v), + weight=weight, + ) + G = H + if not G.is_multigraph(): + edges = ( + (u, v, abs(e.get(weight, 1.0))) for u, v, e in G.edges(data=True) if u != v + ) + else: + edges = ( + (u, v, sum(abs(e.get(weight, 1.0)) for e in G[u][v].values())) + for u, v in G.edges() + if u != v + ) + H = nx.Graph() + H.add_nodes_from(G) + H.add_weighted_edges_from((u, v, e) for u, v, e in edges if e != 0) + return H + + +def _rcm_estimate(G, nodelist): + """Estimate the Fiedler vector using the reverse Cuthill-McKee ordering.""" + import numpy as np + + G = G.subgraph(nodelist) + order = reverse_cuthill_mckee_ordering(G) + n = len(nodelist) + index = dict(zip(nodelist, range(n))) + x = np.ndarray(n, dtype=float) + for i, u in enumerate(order): + x[index[u]] = i + x -= (n - 1) / 2.0 + return x + + +def _tracemin_fiedler(L, X, normalized, tol, method): + """Compute the Fiedler vector of L using the TraceMIN-Fiedler algorithm. + + The Fiedler vector of a connected undirected graph is the eigenvector + corresponding to the second smallest eigenvalue of the Laplacian matrix + of the graph. This function starts with the Laplacian L, not the Graph. + + Parameters + ---------- + L : Laplacian of a possibly weighted or normalized, but undirected graph + + X : Initial guess for a solution. Usually a matrix of random numbers. + This function allows more than one column in X to identify more than + one eigenvector if desired. + + normalized : bool + Whether the normalized Laplacian matrix is used. + + tol : float + Tolerance of relative residual in eigenvalue computation. + Warning: There is no limit on number of iterations. + + method : string + Should be 'tracemin_pcg' or 'tracemin_lu'. + Otherwise exception is raised. + + Returns + ------- + sigma, X : Two NumPy arrays of floats. + The lowest eigenvalues and corresponding eigenvectors of L. + The size of input X determines the size of these outputs. + As this is for Fiedler vectors, the zero eigenvalue (and + constant eigenvector) are avoided. + """ + import numpy as np + import scipy as sp + + n = X.shape[0] + + if normalized: + # Form the normalized Laplacian matrix and determine the eigenvector of + # its nullspace. + e = np.sqrt(L.diagonal()) + # TODO: rm csr_array wrapper when spdiags array creation becomes available + D = sp.sparse.csr_array(sp.sparse.spdiags(1 / e, 0, n, n, format="csr")) + L = D @ L @ D + e *= 1.0 / np.linalg.norm(e, 2) + + if normalized: + + def project(X): + """Make X orthogonal to the nullspace of L.""" + X = np.asarray(X) + for j in range(X.shape[1]): + X[:, j] -= (X[:, j] @ e) * e + + else: + + def project(X): + """Make X orthogonal to the nullspace of L.""" + X = np.asarray(X) + for j in range(X.shape[1]): + X[:, j] -= X[:, j].sum() / n + + if method == "tracemin_pcg": + D = L.diagonal().astype(float) + solver = _PCGSolver(lambda x: L @ x, lambda x: D * x) + elif method == "tracemin_lu": + # Convert A to CSC to suppress SparseEfficiencyWarning. + A = sp.sparse.csc_array(L, dtype=float, copy=True) + # Force A to be nonsingular. Since A is the Laplacian matrix of a + # connected graph, its rank deficiency is one, and thus one diagonal + # element needs to modified. Changing to infinity forces a zero in the + # corresponding element in the solution. + i = (A.indptr[1:] - A.indptr[:-1]).argmax() + A[i, i] = np.inf + solver = _LUSolver(A) + else: + raise nx.NetworkXError(f"Unknown linear system solver: {method}") + + # Initialize. + Lnorm = abs(L).sum(axis=1).flatten().max() + project(X) + W = np.ndarray(X.shape, order="F") + + while True: + # Orthonormalize X. + X = np.linalg.qr(X)[0] + # Compute iteration matrix H. + W[:, :] = L @ X + H = X.T @ W + sigma, Y = sp.linalg.eigh(H, overwrite_a=True) + # Compute the Ritz vectors. + X = X @ Y + # Test for convergence exploiting the fact that L * X == W * Y. + res = sp.linalg.blas.dasum(W @ Y[:, 0] - sigma[0] * X[:, 0]) / Lnorm + if res < tol: + break + # Compute X = L \ X / (X' * (L \ X)). + # L \ X can have an arbitrary projection on the nullspace of L, + # which will be eliminated. + W[:, :] = solver.solve(X, tol) + X = (sp.linalg.inv(W.T @ X) @ W.T).T # Preserves Fortran storage order. + project(X) + + return sigma, np.asarray(X) + + +def _get_fiedler_func(method): + """Returns a function that solves the Fiedler eigenvalue problem.""" + import numpy as np + + if method == "tracemin": # old style keyword `. + + Returns + ------- + algebraic_connectivity : float + Algebraic connectivity. + + Raises + ------ + NetworkXNotImplemented + If G is directed. + + NetworkXError + If G has less than two nodes. + + Notes + ----- + Edge weights are interpreted by their absolute values. For MultiGraph's, + weights of parallel edges are summed. Zero-weighted edges are ignored. + + See Also + -------- + laplacian_matrix + + Examples + -------- + For undirected graphs algebraic connectivity can tell us if a graph is connected or not + `G` is connected iff ``algebraic_connectivity(G) > 0``: + + >>> G = nx.complete_graph(5) + >>> nx.algebraic_connectivity(G) > 0 + True + >>> G.add_node(10) # G is no longer connected + >>> nx.algebraic_connectivity(G) > 0 + False + + """ + if len(G) < 2: + raise nx.NetworkXError("graph has less than two nodes.") + G = _preprocess_graph(G, weight) + if not nx.is_connected(G): + return 0.0 + + L = nx.laplacian_matrix(G) + if L.shape[0] == 2: + return 2.0 * float(L[0, 0]) if not normalized else 2.0 + + find_fiedler = _get_fiedler_func(method) + x = None if method != "lobpcg" else _rcm_estimate(G, G) + sigma, fiedler = find_fiedler(L, x, normalized, tol, seed) + return float(sigma) + + +@not_implemented_for("directed") +@np_random_state(5) +@nx._dispatchable(edge_attrs="weight") +def fiedler_vector( + G, weight="weight", normalized=False, tol=1e-8, method="tracemin_pcg", seed=None +): + """Returns the Fiedler vector of a connected undirected graph. + + The Fiedler vector of a connected undirected graph is the eigenvector + corresponding to the second smallest eigenvalue of the Laplacian matrix + of the graph. + + Parameters + ---------- + G : NetworkX graph + An undirected graph. + + weight : object, optional (default: None) + The data key used to determine the weight of each edge. If None, then + each edge has unit weight. + + normalized : bool, optional (default: False) + Whether the normalized Laplacian matrix is used. + + tol : float, optional (default: 1e-8) + Tolerance of relative residual in eigenvalue computation. + + method : string, optional (default: 'tracemin_pcg') + Method of eigenvalue computation. It must be one of the tracemin + options shown below (TraceMIN), 'lanczos' (Lanczos iteration) + or 'lobpcg' (LOBPCG). + + The TraceMIN algorithm uses a linear system solver. The following + values allow specifying the solver to be used. + + =============== ======================================== + Value Solver + =============== ======================================== + 'tracemin_pcg' Preconditioned conjugate gradient method + 'tracemin_lu' LU factorization + =============== ======================================== + + seed : integer, random_state, or None (default) + Indicator of random number generation state. + See :ref:`Randomness`. + + Returns + ------- + fiedler_vector : NumPy array of floats. + Fiedler vector. + + Raises + ------ + NetworkXNotImplemented + If G is directed. + + NetworkXError + If G has less than two nodes or is not connected. + + Notes + ----- + Edge weights are interpreted by their absolute values. For MultiGraph's, + weights of parallel edges are summed. Zero-weighted edges are ignored. + + See Also + -------- + laplacian_matrix + + Examples + -------- + Given a connected graph the signs of the values in the Fiedler vector can be + used to partition the graph into two components. + + >>> G = nx.barbell_graph(5, 0) + >>> nx.fiedler_vector(G, normalized=True, seed=1) + array([-0.32864129, -0.32864129, -0.32864129, -0.32864129, -0.26072899, + 0.26072899, 0.32864129, 0.32864129, 0.32864129, 0.32864129]) + + The connected components are the two 5-node cliques of the barbell graph. + """ + import numpy as np + + if len(G) < 2: + raise nx.NetworkXError("graph has less than two nodes.") + G = _preprocess_graph(G, weight) + if not nx.is_connected(G): + raise nx.NetworkXError("graph is not connected.") + + if len(G) == 2: + return np.array([1.0, -1.0]) + + find_fiedler = _get_fiedler_func(method) + L = nx.laplacian_matrix(G) + x = None if method != "lobpcg" else _rcm_estimate(G, G) + sigma, fiedler = find_fiedler(L, x, normalized, tol, seed) + return fiedler + + +@np_random_state(5) +@nx._dispatchable(edge_attrs="weight") +def spectral_ordering( + G, weight="weight", normalized=False, tol=1e-8, method="tracemin_pcg", seed=None +): + """Compute the spectral_ordering of a graph. + + The spectral ordering of a graph is an ordering of its nodes where nodes + in the same weakly connected components appear contiguous and ordered by + their corresponding elements in the Fiedler vector of the component. + + Parameters + ---------- + G : NetworkX graph + A graph. + + weight : object, optional (default: None) + The data key used to determine the weight of each edge. If None, then + each edge has unit weight. + + normalized : bool, optional (default: False) + Whether the normalized Laplacian matrix is used. + + tol : float, optional (default: 1e-8) + Tolerance of relative residual in eigenvalue computation. + + method : string, optional (default: 'tracemin_pcg') + Method of eigenvalue computation. It must be one of the tracemin + options shown below (TraceMIN), 'lanczos' (Lanczos iteration) + or 'lobpcg' (LOBPCG). + + The TraceMIN algorithm uses a linear system solver. The following + values allow specifying the solver to be used. + + =============== ======================================== + Value Solver + =============== ======================================== + 'tracemin_pcg' Preconditioned conjugate gradient method + 'tracemin_lu' LU factorization + =============== ======================================== + + seed : integer, random_state, or None (default) + Indicator of random number generation state. + See :ref:`Randomness`. + + Returns + ------- + spectral_ordering : NumPy array of floats. + Spectral ordering of nodes. + + Raises + ------ + NetworkXError + If G is empty. + + Notes + ----- + Edge weights are interpreted by their absolute values. For MultiGraph's, + weights of parallel edges are summed. Zero-weighted edges are ignored. + + See Also + -------- + laplacian_matrix + """ + if len(G) == 0: + raise nx.NetworkXError("graph is empty.") + G = _preprocess_graph(G, weight) + + find_fiedler = _get_fiedler_func(method) + order = [] + for component in nx.connected_components(G): + size = len(component) + if size > 2: + L = nx.laplacian_matrix(G, component) + x = None if method != "lobpcg" else _rcm_estimate(G, component) + sigma, fiedler = find_fiedler(L, x, normalized, tol, seed) + sort_info = zip(fiedler, range(size), component) + order.extend(u for x, c, u in sorted(sort_info)) + else: + order.extend(component) + + return order + + +@nx._dispatchable(edge_attrs="weight") +def spectral_bisection( + G, weight="weight", normalized=False, tol=1e-8, method="tracemin_pcg", seed=None +): + """Bisect the graph using the Fiedler vector. + + This method uses the Fiedler vector to bisect a graph. + The partition is defined by the nodes which are associated with + either positive or negative values in the vector. + + Parameters + ---------- + G : NetworkX Graph + + weight : str, optional (default: weight) + The data key used to determine the weight of each edge. If None, then + each edge has unit weight. + + normalized : bool, optional (default: False) + Whether the normalized Laplacian matrix is used. + + tol : float, optional (default: 1e-8) + Tolerance of relative residual in eigenvalue computation. + + method : string, optional (default: 'tracemin_pcg') + Method of eigenvalue computation. It must be one of the tracemin + options shown below (TraceMIN), 'lanczos' (Lanczos iteration) + or 'lobpcg' (LOBPCG). + + The TraceMIN algorithm uses a linear system solver. The following + values allow specifying the solver to be used. + + =============== ======================================== + Value Solver + =============== ======================================== + 'tracemin_pcg' Preconditioned conjugate gradient method + 'tracemin_lu' LU factorization + =============== ======================================== + + seed : integer, random_state, or None (default) + Indicator of random number generation state. + See :ref:`Randomness`. + + Returns + ------- + bisection : tuple of sets + Sets with the bisection of nodes + + Examples + -------- + >>> G = nx.barbell_graph(3, 0) + >>> nx.spectral_bisection(G) + ({0, 1, 2}, {3, 4, 5}) + + References + ---------- + .. [1] M. E. J Newman 'Networks: An Introduction', pages 364-370 + Oxford University Press 2011. + """ + import numpy as np + + v = nx.fiedler_vector(G, weight, normalized, tol, method, seed) + nodes = np.array(list(G)) + pos_vals = v >= 0 + + return set(nodes[~pos_vals].tolist()), set(nodes[pos_vals].tolist()) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/attrmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/attrmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..b5a70492807bd5e5ea8571c66b3a12af41ca4edd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/attrmatrix.py @@ -0,0 +1,465 @@ +""" +Functions for constructing matrix-like objects from graph attributes. +""" + +import networkx as nx + +__all__ = ["attr_matrix", "attr_sparse_matrix"] + + +def _node_value(G, node_attr): + """Returns a function that returns a value from G.nodes[u]. + + We return a function expecting a node as its sole argument. Then, in the + simplest scenario, the returned function will return G.nodes[u][node_attr]. + However, we also handle the case when `node_attr` is None or when it is a + function itself. + + Parameters + ---------- + G : graph + A NetworkX graph + + node_attr : {None, str, callable} + Specification of how the value of the node attribute should be obtained + from the node attribute dictionary. + + Returns + ------- + value : function + A function expecting a node as its sole argument. The function will + returns a value from G.nodes[u] that depends on `edge_attr`. + + """ + if node_attr is None: + + def value(u): + return u + + elif not callable(node_attr): + # assume it is a key for the node attribute dictionary + def value(u): + return G.nodes[u][node_attr] + + else: + # Advanced: Allow users to specify something else. + # + # For example, + # node_attr = lambda u: G.nodes[u].get('size', .5) * 3 + # + value = node_attr + + return value + + +def _edge_value(G, edge_attr): + """Returns a function that returns a value from G[u][v]. + + Suppose there exists an edge between u and v. Then we return a function + expecting u and v as arguments. For Graph and DiGraph, G[u][v] is + the edge attribute dictionary, and the function (essentially) returns + G[u][v][edge_attr]. However, we also handle cases when `edge_attr` is None + and when it is a function itself. For MultiGraph and MultiDiGraph, G[u][v] + is a dictionary of all edges between u and v. In this case, the returned + function sums the value of `edge_attr` for every edge between u and v. + + Parameters + ---------- + G : graph + A NetworkX graph + + edge_attr : {None, str, callable} + Specification of how the value of the edge attribute should be obtained + from the edge attribute dictionary, G[u][v]. For multigraphs, G[u][v] + is a dictionary of all the edges between u and v. This allows for + special treatment of multiedges. + + Returns + ------- + value : function + A function expecting two nodes as parameters. The nodes should + represent the from- and to- node of an edge. The function will + return a value from G[u][v] that depends on `edge_attr`. + + """ + + if edge_attr is None: + # topological count of edges + + if G.is_multigraph(): + + def value(u, v): + return len(G[u][v]) + + else: + + def value(u, v): + return 1 + + elif not callable(edge_attr): + # assume it is a key for the edge attribute dictionary + + if edge_attr == "weight": + # provide a default value + if G.is_multigraph(): + + def value(u, v): + return sum(d.get(edge_attr, 1) for d in G[u][v].values()) + + else: + + def value(u, v): + return G[u][v].get(edge_attr, 1) + + else: + # otherwise, the edge attribute MUST exist for each edge + if G.is_multigraph(): + + def value(u, v): + return sum(d[edge_attr] for d in G[u][v].values()) + + else: + + def value(u, v): + return G[u][v][edge_attr] + + else: + # Advanced: Allow users to specify something else. + # + # Alternative default value: + # edge_attr = lambda u,v: G[u][v].get('thickness', .5) + # + # Function on an attribute: + # edge_attr = lambda u,v: abs(G[u][v]['weight']) + # + # Handle Multi(Di)Graphs differently: + # edge_attr = lambda u,v: numpy.prod([d['size'] for d in G[u][v].values()]) + # + # Ignore multiple edges + # edge_attr = lambda u,v: 1 if len(G[u][v]) else 0 + # + value = edge_attr + + return value + + +@nx._dispatchable(edge_attrs={"edge_attr": None}, node_attrs="node_attr") +def attr_matrix( + G, + edge_attr=None, + node_attr=None, + normalized=False, + rc_order=None, + dtype=None, + order=None, +): + """Returns the attribute matrix using attributes from `G` as a numpy array. + + If only `G` is passed in, then the adjacency matrix is constructed. + + Let A be a discrete set of values for the node attribute `node_attr`. Then + the elements of A represent the rows and columns of the constructed matrix. + Now, iterate through every edge e=(u,v) in `G` and consider the value + of the edge attribute `edge_attr`. If ua and va are the values of the + node attribute `node_attr` for u and v, respectively, then the value of + the edge attribute is added to the matrix element at (ua, va). + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the attribute matrix. + + edge_attr : str, optional + Each element of the matrix represents a running total of the + specified edge attribute for edges whose node attributes correspond + to the rows/cols of the matrix. The attribute must be present for + all edges in the graph. If no attribute is specified, then we + just count the number of edges whose node attributes correspond + to the matrix element. + + node_attr : str, optional + Each row and column in the matrix represents a particular value + of the node attribute. The attribute must be present for all nodes + in the graph. Note, the values of this attribute should be reliably + hashable. So, float values are not recommended. If no attribute is + specified, then the rows and columns will be the nodes of the graph. + + normalized : bool, optional + If True, then each row is normalized by the summation of its values. + + rc_order : list, optional + A list of the node attribute values. This list specifies the ordering + of rows and columns of the array. If no ordering is provided, then + the ordering will be random (and also, a return value). + + Other Parameters + ---------------- + dtype : NumPy data-type, optional + A valid NumPy dtype used to initialize the array. Keep in mind certain + dtypes can yield unexpected results if the array is to be normalized. + The parameter is passed to numpy.zeros(). If unspecified, the NumPy + default is used. + + order : {'C', 'F'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. This parameter is passed to + numpy.zeros(). If unspecified, the NumPy default is used. + + Returns + ------- + M : 2D NumPy ndarray + The attribute matrix. + + ordering : list + If `rc_order` was specified, then only the attribute matrix is returned. + However, if `rc_order` was None, then the ordering used to construct + the matrix is returned as well. + + Examples + -------- + Construct an adjacency matrix: + + >>> G = nx.Graph() + >>> G.add_edge(0, 1, thickness=1, weight=3) + >>> G.add_edge(0, 2, thickness=2) + >>> G.add_edge(1, 2, thickness=3) + >>> nx.attr_matrix(G, rc_order=[0, 1, 2]) + array([[0., 1., 1.], + [1., 0., 1.], + [1., 1., 0.]]) + + Alternatively, we can obtain the matrix describing edge thickness. + + >>> nx.attr_matrix(G, edge_attr="thickness", rc_order=[0, 1, 2]) + array([[0., 1., 2.], + [1., 0., 3.], + [2., 3., 0.]]) + + We can also color the nodes and ask for the probability distribution over + all edges (u,v) describing: + + Pr(v has color Y | u has color X) + + >>> G.nodes[0]["color"] = "red" + >>> G.nodes[1]["color"] = "red" + >>> G.nodes[2]["color"] = "blue" + >>> rc = ["red", "blue"] + >>> nx.attr_matrix(G, node_attr="color", normalized=True, rc_order=rc) + array([[0.33333333, 0.66666667], + [1. , 0. ]]) + + For example, the above tells us that for all edges (u,v): + + Pr( v is red | u is red) = 1/3 + Pr( v is blue | u is red) = 2/3 + + Pr( v is red | u is blue) = 1 + Pr( v is blue | u is blue) = 0 + + Finally, we can obtain the total weights listed by the node colors. + + >>> nx.attr_matrix(G, edge_attr="weight", node_attr="color", rc_order=rc) + array([[3., 2.], + [2., 0.]]) + + Thus, the total weight over all edges (u,v) with u and v having colors: + + (red, red) is 3 # the sole contribution is from edge (0,1) + (red, blue) is 2 # contributions from edges (0,2) and (1,2) + (blue, red) is 2 # same as (red, blue) since graph is undirected + (blue, blue) is 0 # there are no edges with blue endpoints + + """ + import numpy as np + + edge_value = _edge_value(G, edge_attr) + node_value = _node_value(G, node_attr) + + if rc_order is None: + ordering = list({node_value(n) for n in G}) + else: + ordering = rc_order + + N = len(ordering) + undirected = not G.is_directed() + index = dict(zip(ordering, range(N))) + M = np.zeros((N, N), dtype=dtype, order=order) + + seen = set() + for u, nbrdict in G.adjacency(): + for v in nbrdict: + # Obtain the node attribute values. + i, j = index[node_value(u)], index[node_value(v)] + if v not in seen: + M[i, j] += edge_value(u, v) + if undirected: + M[j, i] = M[i, j] + + if undirected: + seen.add(u) + + if normalized: + M /= M.sum(axis=1).reshape((N, 1)) + + if rc_order is None: + return M, ordering + else: + return M + + +@nx._dispatchable(edge_attrs={"edge_attr": None}, node_attrs="node_attr") +def attr_sparse_matrix( + G, edge_attr=None, node_attr=None, normalized=False, rc_order=None, dtype=None +): + """Returns a SciPy sparse array using attributes from G. + + If only `G` is passed in, then the adjacency matrix is constructed. + + Let A be a discrete set of values for the node attribute `node_attr`. Then + the elements of A represent the rows and columns of the constructed matrix. + Now, iterate through every edge e=(u,v) in `G` and consider the value + of the edge attribute `edge_attr`. If ua and va are the values of the + node attribute `node_attr` for u and v, respectively, then the value of + the edge attribute is added to the matrix element at (ua, va). + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the NumPy matrix. + + edge_attr : str, optional + Each element of the matrix represents a running total of the + specified edge attribute for edges whose node attributes correspond + to the rows/cols of the matrix. The attribute must be present for + all edges in the graph. If no attribute is specified, then we + just count the number of edges whose node attributes correspond + to the matrix element. + + node_attr : str, optional + Each row and column in the matrix represents a particular value + of the node attribute. The attribute must be present for all nodes + in the graph. Note, the values of this attribute should be reliably + hashable. So, float values are not recommended. If no attribute is + specified, then the rows and columns will be the nodes of the graph. + + normalized : bool, optional + If True, then each row is normalized by the summation of its values. + + rc_order : list, optional + A list of the node attribute values. This list specifies the ordering + of rows and columns of the array. If no ordering is provided, then + the ordering will be random (and also, a return value). + + Other Parameters + ---------------- + dtype : NumPy data-type, optional + A valid NumPy dtype used to initialize the array. Keep in mind certain + dtypes can yield unexpected results if the array is to be normalized. + The parameter is passed to numpy.zeros(). If unspecified, the NumPy + default is used. + + Returns + ------- + M : SciPy sparse array + The attribute matrix. + + ordering : list + If `rc_order` was specified, then only the matrix is returned. + However, if `rc_order` was None, then the ordering used to construct + the matrix is returned as well. + + Examples + -------- + Construct an adjacency matrix: + + >>> G = nx.Graph() + >>> G.add_edge(0, 1, thickness=1, weight=3) + >>> G.add_edge(0, 2, thickness=2) + >>> G.add_edge(1, 2, thickness=3) + >>> M = nx.attr_sparse_matrix(G, rc_order=[0, 1, 2]) + >>> M.toarray() + array([[0., 1., 1.], + [1., 0., 1.], + [1., 1., 0.]]) + + Alternatively, we can obtain the matrix describing edge thickness. + + >>> M = nx.attr_sparse_matrix(G, edge_attr="thickness", rc_order=[0, 1, 2]) + >>> M.toarray() + array([[0., 1., 2.], + [1., 0., 3.], + [2., 3., 0.]]) + + We can also color the nodes and ask for the probability distribution over + all edges (u,v) describing: + + Pr(v has color Y | u has color X) + + >>> G.nodes[0]["color"] = "red" + >>> G.nodes[1]["color"] = "red" + >>> G.nodes[2]["color"] = "blue" + >>> rc = ["red", "blue"] + >>> M = nx.attr_sparse_matrix(G, node_attr="color", normalized=True, rc_order=rc) + >>> M.toarray() + array([[0.33333333, 0.66666667], + [1. , 0. ]]) + + For example, the above tells us that for all edges (u,v): + + Pr( v is red | u is red) = 1/3 + Pr( v is blue | u is red) = 2/3 + + Pr( v is red | u is blue) = 1 + Pr( v is blue | u is blue) = 0 + + Finally, we can obtain the total weights listed by the node colors. + + >>> M = nx.attr_sparse_matrix(G, edge_attr="weight", node_attr="color", rc_order=rc) + >>> M.toarray() + array([[3., 2.], + [2., 0.]]) + + Thus, the total weight over all edges (u,v) with u and v having colors: + + (red, red) is 3 # the sole contribution is from edge (0,1) + (red, blue) is 2 # contributions from edges (0,2) and (1,2) + (blue, red) is 2 # same as (red, blue) since graph is undirected + (blue, blue) is 0 # there are no edges with blue endpoints + + """ + import numpy as np + import scipy as sp + + edge_value = _edge_value(G, edge_attr) + node_value = _node_value(G, node_attr) + + if rc_order is None: + ordering = list({node_value(n) for n in G}) + else: + ordering = rc_order + + N = len(ordering) + undirected = not G.is_directed() + index = dict(zip(ordering, range(N))) + M = sp.sparse.lil_array((N, N), dtype=dtype) + + seen = set() + for u, nbrdict in G.adjacency(): + for v in nbrdict: + # Obtain the node attribute values. + i, j = index[node_value(u)], index[node_value(v)] + if v not in seen: + M[i, j] += edge_value(u, v) + if undirected: + M[j, i] = M[i, j] + + if undirected: + seen.add(u) + + if normalized: + M *= 1 / M.sum(axis=1)[:, np.newaxis] # in-place mult preserves sparse + + if rc_order is None: + return M, ordering + else: + return M diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/bethehessianmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/bethehessianmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..3d42fc6d37f033f058338d9a7a8eb718bd6dc14e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/bethehessianmatrix.py @@ -0,0 +1,79 @@ +"""Bethe Hessian or deformed Laplacian matrix of graphs.""" + +import networkx as nx +from networkx.utils import not_implemented_for + +__all__ = ["bethe_hessian_matrix"] + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +@nx._dispatchable +def bethe_hessian_matrix(G, r=None, nodelist=None): + r"""Returns the Bethe Hessian matrix of G. + + The Bethe Hessian is a family of matrices parametrized by r, defined as + H(r) = (r^2 - 1) I - r A + D where A is the adjacency matrix, D is the + diagonal matrix of node degrees, and I is the identify matrix. It is equal + to the graph laplacian when the regularizer r = 1. + + The default choice of regularizer should be the ratio [2]_ + + .. math:: + r_m = \left(\sum k_i \right)^{-1}\left(\sum k_i^2 \right) - 1 + + Parameters + ---------- + G : Graph + A NetworkX graph + r : float + Regularizer parameter + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by ``G.nodes()``. + + Returns + ------- + H : scipy.sparse.csr_array + The Bethe Hessian matrix of `G`, with parameter `r`. + + Examples + -------- + >>> k = [3, 2, 2, 1, 0] + >>> G = nx.havel_hakimi_graph(k) + >>> H = nx.bethe_hessian_matrix(G) + >>> H.toarray() + array([[ 3.5625, -1.25 , -1.25 , -1.25 , 0. ], + [-1.25 , 2.5625, -1.25 , 0. , 0. ], + [-1.25 , -1.25 , 2.5625, 0. , 0. ], + [-1.25 , 0. , 0. , 1.5625, 0. ], + [ 0. , 0. , 0. , 0. , 0.5625]]) + + See Also + -------- + bethe_hessian_spectrum + adjacency_matrix + laplacian_matrix + + References + ---------- + .. [1] A. Saade, F. Krzakala and L. Zdeborová + "Spectral Clustering of Graphs with the Bethe Hessian", + Advances in Neural Information Processing Systems, 2014. + .. [2] C. M. Le, E. Levina + "Estimating the number of communities in networks by spectral methods" + arXiv:1507.00827, 2015. + """ + import scipy as sp + + if nodelist is None: + nodelist = list(G) + if r is None: + r = sum(d**2 for v, d in nx.degree(G)) / sum(d for v, d in nx.degree(G)) - 1 + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, format="csr") + n, m = A.shape + # TODO: Rm csr_array wrapper when spdiags array creation becomes available + D = sp.sparse.csr_array(sp.sparse.spdiags(A.sum(axis=1), 0, m, n, format="csr")) + # TODO: Rm csr_array wrapper when eye array creation becomes available + I = sp.sparse.csr_array(sp.sparse.eye(m, n, format="csr")) + return (r**2 - 1) * I - r * A + D diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/graphmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/graphmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..9f477bcccc5b6a077b8c7f1807299d71664ebede --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/graphmatrix.py @@ -0,0 +1,168 @@ +""" +Adjacency matrix and incidence matrix of graphs. +""" + +import networkx as nx + +__all__ = ["incidence_matrix", "adjacency_matrix"] + + +@nx._dispatchable(edge_attrs="weight") +def incidence_matrix( + G, nodelist=None, edgelist=None, oriented=False, weight=None, *, dtype=None +): + """Returns incidence matrix of G. + + The incidence matrix assigns each row to a node and each column to an edge. + For a standard incidence matrix a 1 appears wherever a row's node is + incident on the column's edge. For an oriented incidence matrix each + edge is assigned an orientation (arbitrarily for undirected and aligning to + direction for directed). A -1 appears for the source (tail) of an edge and + 1 for the destination (head) of the edge. The elements are zero otherwise. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list, optional (default= all nodes in G) + The rows are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + edgelist : list, optional (default= all edges in G) + The columns are ordered according to the edges in edgelist. + If edgelist is None, then the ordering is produced by G.edges(). + + oriented: bool, optional (default=False) + If True, matrix elements are +1 or -1 for the head or tail node + respectively of each edge. If False, +1 occurs at both nodes. + + weight : string or None, optional (default=None) + The edge data key used to provide each value in the matrix. + If None, then each edge has weight 1. Edge weights, if used, + should be positive so that the orientation can provide the sign. + + dtype : a NumPy dtype or None (default=None) + The dtype of the output sparse array. This type should be a compatible + type of the weight argument, eg. if weight would return a float this + argument should also be a float. + If None, then the default for SciPy is used. + + Returns + ------- + A : SciPy sparse array + The incidence matrix of G. + + Notes + ----- + For MultiGraph/MultiDiGraph, the edges in edgelist should be + (u,v,key) 3-tuples. + + "Networks are the best discrete model for so many problems in + applied mathematics" [1]_. + + References + ---------- + .. [1] Gil Strang, Network applications: A = incidence matrix, + http://videolectures.net/mit18085f07_strang_lec03/ + """ + import scipy as sp + + if nodelist is None: + nodelist = list(G) + if edgelist is None: + if G.is_multigraph(): + edgelist = list(G.edges(keys=True)) + else: + edgelist = list(G.edges()) + A = sp.sparse.lil_array((len(nodelist), len(edgelist)), dtype=dtype) + node_index = {node: i for i, node in enumerate(nodelist)} + for ei, e in enumerate(edgelist): + (u, v) = e[:2] + if u == v: + continue # self loops give zero column + try: + ui = node_index[u] + vi = node_index[v] + except KeyError as err: + raise nx.NetworkXError( + f"node {u} or {v} in edgelist but not in nodelist" + ) from err + if weight is None: + wt = 1 + else: + if G.is_multigraph(): + ekey = e[2] + wt = G[u][v][ekey].get(weight, 1) + else: + wt = G[u][v].get(weight, 1) + if oriented: + A[ui, ei] = -wt + A[vi, ei] = wt + else: + A[ui, ei] = wt + A[vi, ei] = wt + return A.asformat("csc") + + +@nx._dispatchable(edge_attrs="weight") +def adjacency_matrix(G, nodelist=None, dtype=None, weight="weight"): + """Returns adjacency matrix of `G`. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in `nodelist`. + If ``nodelist=None`` (the default), then the ordering is produced by + ``G.nodes()``. + + dtype : NumPy data-type, optional + The desired data-type for the array. + If `None`, then the NumPy default is used. + + weight : string or None, optional (default='weight') + The edge data key used to provide each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + A : SciPy sparse array + Adjacency matrix representation of G. + + Notes + ----- + For directed graphs, entry ``i, j`` corresponds to an edge from ``i`` to ``j``. + + If you want a pure Python adjacency matrix representation try + :func:`~networkx.convert.to_dict_of_dicts` which will return a + dictionary-of-dictionaries format that can be addressed as a + sparse matrix. + + For multigraphs with parallel edges the weights are summed. + See :func:`networkx.convert_matrix.to_numpy_array` for other options. + + The convention used for self-loop edges in graphs is to assign the + diagonal matrix entry value to the edge weight attribute + (or the number 1 if the edge has no weight attribute). If the + alternate convention of doubling the edge weight is desired the + resulting SciPy sparse array can be modified as follows:: + + >>> G = nx.Graph([(1, 1)]) + >>> A = nx.adjacency_matrix(G) + >>> A.toarray() + array([[1]]) + >>> A.setdiag(A.diagonal() * 2) + >>> A.toarray() + array([[2]]) + + See Also + -------- + to_numpy_array + to_scipy_sparse_array + to_dict_of_dicts + adjacency_spectrum + """ + return nx.to_scipy_sparse_array(G, nodelist=nodelist, dtype=dtype, weight=weight) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/laplacianmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/laplacianmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..d49ae4910e7047a8ee0a585745209cfea92c2c12 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/laplacianmatrix.py @@ -0,0 +1,617 @@ +"""Laplacian matrix of graphs. + +All calculations here are done using the out-degree. For Laplacians using +in-degree, use `G.reverse(copy=False)` instead of `G` and take the transpose. + +The `laplacian_matrix` function provides an unnormalized matrix, +while `normalized_laplacian_matrix`, `directed_laplacian_matrix`, +and `directed_combinatorial_laplacian_matrix` are all normalized. +""" + +import networkx as nx +from networkx.utils import not_implemented_for + +__all__ = [ + "laplacian_matrix", + "normalized_laplacian_matrix", + "total_spanning_tree_weight", + "directed_laplacian_matrix", + "directed_combinatorial_laplacian_matrix", +] + + +@nx._dispatchable(edge_attrs="weight") +def laplacian_matrix(G, nodelist=None, weight="weight"): + """Returns the Laplacian matrix of G. + + The graph Laplacian is the matrix L = D - A, where + A is the adjacency matrix and D is the diagonal matrix of node degrees. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + L : SciPy sparse array + The Laplacian matrix of G. + + Notes + ----- + For MultiGraph, the edges weights are summed. + + This returns an unnormalized matrix. For a normalized output, + use `normalized_laplacian_matrix`, `directed_laplacian_matrix`, + or `directed_combinatorial_laplacian_matrix`. + + This calculation uses the out-degree of the graph `G`. To use the + in-degree for calculations instead, use `G.reverse(copy=False)` and + take the transpose. + + See Also + -------- + :func:`~networkx.convert_matrix.to_numpy_array` + normalized_laplacian_matrix + directed_laplacian_matrix + directed_combinatorial_laplacian_matrix + :func:`~networkx.linalg.spectrum.laplacian_spectrum` + + Examples + -------- + For graphs with multiple connected components, L is permutation-similar + to a block diagonal matrix where each block is the respective Laplacian + matrix for each component. + + >>> G = nx.Graph([(1, 2), (2, 3), (4, 5)]) + >>> print(nx.laplacian_matrix(G).toarray()) + [[ 1 -1 0 0 0] + [-1 2 -1 0 0] + [ 0 -1 1 0 0] + [ 0 0 0 1 -1] + [ 0 0 0 -1 1]] + + >>> edges = [ + ... (1, 2), + ... (2, 1), + ... (2, 4), + ... (4, 3), + ... (3, 4), + ... ] + >>> DiG = nx.DiGraph(edges) + >>> print(nx.laplacian_matrix(DiG).toarray()) + [[ 1 -1 0 0] + [-1 2 -1 0] + [ 0 0 1 -1] + [ 0 0 -1 1]] + + Notice that node 4 is represented by the third column and row. This is because + by default the row/column order is the order of `G.nodes` (i.e. the node added + order -- in the edgelist, 4 first appears in (2, 4), before node 3 in edge (4, 3).) + To control the node order of the matrix, use the `nodelist` argument. + + >>> print(nx.laplacian_matrix(DiG, nodelist=[1, 2, 3, 4]).toarray()) + [[ 1 -1 0 0] + [-1 2 0 -1] + [ 0 0 1 -1] + [ 0 0 -1 1]] + + This calculation uses the out-degree of the graph `G`. To use the + in-degree for calculations instead, use `G.reverse(copy=False)` and + take the transpose. + + >>> print(nx.laplacian_matrix(DiG.reverse(copy=False)).toarray().T) + [[ 1 -1 0 0] + [-1 1 -1 0] + [ 0 0 2 -1] + [ 0 0 -1 1]] + + References + ---------- + .. [1] Langville, Amy N., and Carl D. Meyer. Google’s PageRank and Beyond: + The Science of Search Engine Rankings. Princeton University Press, 2006. + + """ + import scipy as sp + + if nodelist is None: + nodelist = list(G) + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, format="csr") + n, m = A.shape + # TODO: rm csr_array wrapper when spdiags can produce arrays + D = sp.sparse.csr_array(sp.sparse.spdiags(A.sum(axis=1), 0, m, n, format="csr")) + return D - A + + +@nx._dispatchable(edge_attrs="weight") +def normalized_laplacian_matrix(G, nodelist=None, weight="weight"): + r"""Returns the normalized Laplacian matrix of G. + + The normalized graph Laplacian is the matrix + + .. math:: + + N = D^{-1/2} L D^{-1/2} + + where `L` is the graph Laplacian and `D` is the diagonal matrix of + node degrees [1]_. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + N : SciPy sparse array + The normalized Laplacian matrix of G. + + Notes + ----- + For MultiGraph, the edges weights are summed. + See :func:`to_numpy_array` for other options. + + If the Graph contains selfloops, D is defined as ``diag(sum(A, 1))``, where A is + the adjacency matrix [2]_. + + This calculation uses the out-degree of the graph `G`. To use the + in-degree for calculations instead, use `G.reverse(copy=False)` and + take the transpose. + + For an unnormalized output, use `laplacian_matrix`. + + Examples + -------- + + >>> import numpy as np + >>> edges = [ + ... (1, 2), + ... (2, 1), + ... (2, 4), + ... (4, 3), + ... (3, 4), + ... ] + >>> DiG = nx.DiGraph(edges) + >>> print(nx.normalized_laplacian_matrix(DiG).toarray()) + [[ 1. -0.70710678 0. 0. ] + [-0.70710678 1. -0.70710678 0. ] + [ 0. 0. 1. -1. ] + [ 0. 0. -1. 1. ]] + + Notice that node 4 is represented by the third column and row. This is because + by default the row/column order is the order of `G.nodes` (i.e. the node added + order -- in the edgelist, 4 first appears in (2, 4), before node 3 in edge (4, 3).) + To control the node order of the matrix, use the `nodelist` argument. + + >>> print(nx.normalized_laplacian_matrix(DiG, nodelist=[1, 2, 3, 4]).toarray()) + [[ 1. -0.70710678 0. 0. ] + [-0.70710678 1. 0. -0.70710678] + [ 0. 0. 1. -1. ] + [ 0. 0. -1. 1. ]] + >>> G = nx.Graph(edges) + >>> print(nx.normalized_laplacian_matrix(G).toarray()) + [[ 1. -0.70710678 0. 0. ] + [-0.70710678 1. -0.5 0. ] + [ 0. -0.5 1. -0.70710678] + [ 0. 0. -0.70710678 1. ]] + + See Also + -------- + laplacian_matrix + normalized_laplacian_spectrum + directed_laplacian_matrix + directed_combinatorial_laplacian_matrix + + References + ---------- + .. [1] Fan Chung-Graham, Spectral Graph Theory, + CBMS Regional Conference Series in Mathematics, Number 92, 1997. + .. [2] Steve Butler, Interlacing For Weighted Graphs Using The Normalized + Laplacian, Electronic Journal of Linear Algebra, Volume 16, pp. 90-98, + March 2007. + .. [3] Langville, Amy N., and Carl D. Meyer. Google’s PageRank and Beyond: + The Science of Search Engine Rankings. Princeton University Press, 2006. + """ + import numpy as np + import scipy as sp + + if nodelist is None: + nodelist = list(G) + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, format="csr") + n, _ = A.shape + diags = A.sum(axis=1) + # TODO: rm csr_array wrapper when spdiags can produce arrays + D = sp.sparse.csr_array(sp.sparse.spdiags(diags, 0, n, n, format="csr")) + L = D - A + with np.errstate(divide="ignore"): + diags_sqrt = 1.0 / np.sqrt(diags) + diags_sqrt[np.isinf(diags_sqrt)] = 0 + # TODO: rm csr_array wrapper when spdiags can produce arrays + DH = sp.sparse.csr_array(sp.sparse.spdiags(diags_sqrt, 0, n, n, format="csr")) + return DH @ (L @ DH) + + +@nx._dispatchable(edge_attrs="weight") +def total_spanning_tree_weight(G, weight=None, root=None): + """ + Returns the total weight of all spanning trees of `G`. + + Kirchoff's Tree Matrix Theorem [1]_, [2]_ states that the determinant of any + cofactor of the Laplacian matrix of a graph is the number of spanning trees + in the graph. For a weighted Laplacian matrix, it is the sum across all + spanning trees of the multiplicative weight of each tree. That is, the + weight of each tree is the product of its edge weights. + + For unweighted graphs, the total weight equals the number of spanning trees in `G`. + + For directed graphs, the total weight follows by summing over all directed + spanning trees in `G` that start in the `root` node [3]_. + + .. deprecated:: 3.3 + + ``total_spanning_tree_weight`` is deprecated and will be removed in v3.5. + Use ``nx.number_of_spanning_trees(G)`` instead. + + Parameters + ---------- + G : NetworkX Graph + + weight : string or None, optional (default=None) + The key for the edge attribute holding the edge weight. + If None, then each edge has weight 1. + + root : node (only required for directed graphs) + A node in the directed graph `G`. + + Returns + ------- + total_weight : float + Undirected graphs: + The sum of the total multiplicative weights for all spanning trees in `G`. + Directed graphs: + The sum of the total multiplicative weights for all spanning trees of `G`, + rooted at node `root`. + + Raises + ------ + NetworkXPointlessConcept + If `G` does not contain any nodes. + + NetworkXError + If the graph `G` is not (weakly) connected, + or if `G` is directed and the root node is not specified or not in G. + + Examples + -------- + >>> G = nx.complete_graph(5) + >>> round(nx.total_spanning_tree_weight(G)) + 125 + + >>> G = nx.Graph() + >>> G.add_edge(1, 2, weight=2) + >>> G.add_edge(1, 3, weight=1) + >>> G.add_edge(2, 3, weight=1) + >>> round(nx.total_spanning_tree_weight(G, "weight")) + 5 + + Notes + ----- + Self-loops are excluded. Multi-edges are contracted in one edge + equal to the sum of the weights. + + References + ---------- + .. [1] Wikipedia + "Kirchhoff's theorem." + https://en.wikipedia.org/wiki/Kirchhoff%27s_theorem + .. [2] Kirchhoff, G. R. + Über die Auflösung der Gleichungen, auf welche man + bei der Untersuchung der linearen Vertheilung + Galvanischer Ströme geführt wird + Annalen der Physik und Chemie, vol. 72, pp. 497-508, 1847. + .. [3] Margoliash, J. + "Matrix-Tree Theorem for Directed Graphs" + https://www.math.uchicago.edu/~may/VIGRE/VIGRE2010/REUPapers/Margoliash.pdf + """ + import warnings + + warnings.warn( + ( + "\n\ntotal_spanning_tree_weight is deprecated and will be removed in v3.5.\n" + "Use `nx.number_of_spanning_trees(G)` instead." + ), + category=DeprecationWarning, + stacklevel=3, + ) + + return nx.number_of_spanning_trees(G, weight=weight, root=root) + + +############################################################################### +# Code based on work from https://github.com/bjedwards + + +@not_implemented_for("undirected") +@not_implemented_for("multigraph") +@nx._dispatchable(edge_attrs="weight") +def directed_laplacian_matrix( + G, nodelist=None, weight="weight", walk_type=None, alpha=0.95 +): + r"""Returns the directed Laplacian matrix of G. + + The graph directed Laplacian is the matrix + + .. math:: + + L = I - \frac{1}{2} \left (\Phi^{1/2} P \Phi^{-1/2} + \Phi^{-1/2} P^T \Phi^{1/2} \right ) + + where `I` is the identity matrix, `P` is the transition matrix of the + graph, and `\Phi` a matrix with the Perron vector of `P` in the diagonal and + zeros elsewhere [1]_. + + Depending on the value of walk_type, `P` can be the transition matrix + induced by a random walk, a lazy random walk, or a random walk with + teleportation (PageRank). + + Parameters + ---------- + G : DiGraph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + walk_type : string or None, optional (default=None) + One of ``"random"``, ``"lazy"``, or ``"pagerank"``. If ``walk_type=None`` + (the default), then a value is selected according to the properties of `G`: + - ``walk_type="random"`` if `G` is strongly connected and aperiodic + - ``walk_type="lazy"`` if `G` is strongly connected but not aperiodic + - ``walk_type="pagerank"`` for all other cases. + + alpha : real + (1 - alpha) is the teleportation probability used with pagerank + + Returns + ------- + L : NumPy matrix + Normalized Laplacian of G. + + Notes + ----- + Only implemented for DiGraphs + + The result is always a symmetric matrix. + + This calculation uses the out-degree of the graph `G`. To use the + in-degree for calculations instead, use `G.reverse(copy=False)` and + take the transpose. + + See Also + -------- + laplacian_matrix + normalized_laplacian_matrix + directed_combinatorial_laplacian_matrix + + References + ---------- + .. [1] Fan Chung (2005). + Laplacians and the Cheeger inequality for directed graphs. + Annals of Combinatorics, 9(1), 2005 + """ + import numpy as np + import scipy as sp + + # NOTE: P has type ndarray if walk_type=="pagerank", else csr_array + P = _transition_matrix( + G, nodelist=nodelist, weight=weight, walk_type=walk_type, alpha=alpha + ) + + n, m = P.shape + + evals, evecs = sp.sparse.linalg.eigs(P.T, k=1) + v = evecs.flatten().real + p = v / v.sum() + # p>=0 by Perron-Frobenius Thm. Use abs() to fix roundoff across zero gh-6865 + sqrtp = np.sqrt(np.abs(p)) + Q = ( + # TODO: rm csr_array wrapper when spdiags creates arrays + sp.sparse.csr_array(sp.sparse.spdiags(sqrtp, 0, n, n)) + @ P + # TODO: rm csr_array wrapper when spdiags creates arrays + @ sp.sparse.csr_array(sp.sparse.spdiags(1.0 / sqrtp, 0, n, n)) + ) + # NOTE: This could be sparsified for the non-pagerank cases + I = np.identity(len(G)) + + return I - (Q + Q.T) / 2.0 + + +@not_implemented_for("undirected") +@not_implemented_for("multigraph") +@nx._dispatchable(edge_attrs="weight") +def directed_combinatorial_laplacian_matrix( + G, nodelist=None, weight="weight", walk_type=None, alpha=0.95 +): + r"""Return the directed combinatorial Laplacian matrix of G. + + The graph directed combinatorial Laplacian is the matrix + + .. math:: + + L = \Phi - \frac{1}{2} \left (\Phi P + P^T \Phi \right) + + where `P` is the transition matrix of the graph and `\Phi` a matrix + with the Perron vector of `P` in the diagonal and zeros elsewhere [1]_. + + Depending on the value of walk_type, `P` can be the transition matrix + induced by a random walk, a lazy random walk, or a random walk with + teleportation (PageRank). + + Parameters + ---------- + G : DiGraph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + walk_type : string or None, optional (default=None) + One of ``"random"``, ``"lazy"``, or ``"pagerank"``. If ``walk_type=None`` + (the default), then a value is selected according to the properties of `G`: + - ``walk_type="random"`` if `G` is strongly connected and aperiodic + - ``walk_type="lazy"`` if `G` is strongly connected but not aperiodic + - ``walk_type="pagerank"`` for all other cases. + + alpha : real + (1 - alpha) is the teleportation probability used with pagerank + + Returns + ------- + L : NumPy matrix + Combinatorial Laplacian of G. + + Notes + ----- + Only implemented for DiGraphs + + The result is always a symmetric matrix. + + This calculation uses the out-degree of the graph `G`. To use the + in-degree for calculations instead, use `G.reverse(copy=False)` and + take the transpose. + + See Also + -------- + laplacian_matrix + normalized_laplacian_matrix + directed_laplacian_matrix + + References + ---------- + .. [1] Fan Chung (2005). + Laplacians and the Cheeger inequality for directed graphs. + Annals of Combinatorics, 9(1), 2005 + """ + import scipy as sp + + P = _transition_matrix( + G, nodelist=nodelist, weight=weight, walk_type=walk_type, alpha=alpha + ) + + n, m = P.shape + + evals, evecs = sp.sparse.linalg.eigs(P.T, k=1) + v = evecs.flatten().real + p = v / v.sum() + # NOTE: could be improved by not densifying + # TODO: Rm csr_array wrapper when spdiags array creation becomes available + Phi = sp.sparse.csr_array(sp.sparse.spdiags(p, 0, n, n)).toarray() + + return Phi - (Phi @ P + P.T @ Phi) / 2.0 + + +def _transition_matrix(G, nodelist=None, weight="weight", walk_type=None, alpha=0.95): + """Returns the transition matrix of G. + + This is a row stochastic giving the transition probabilities while + performing a random walk on the graph. Depending on the value of walk_type, + P can be the transition matrix induced by a random walk, a lazy random walk, + or a random walk with teleportation (PageRank). + + Parameters + ---------- + G : DiGraph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + walk_type : string or None, optional (default=None) + One of ``"random"``, ``"lazy"``, or ``"pagerank"``. If ``walk_type=None`` + (the default), then a value is selected according to the properties of `G`: + - ``walk_type="random"`` if `G` is strongly connected and aperiodic + - ``walk_type="lazy"`` if `G` is strongly connected but not aperiodic + - ``walk_type="pagerank"`` for all other cases. + + alpha : real + (1 - alpha) is the teleportation probability used with pagerank + + Returns + ------- + P : numpy.ndarray + transition matrix of G. + + Raises + ------ + NetworkXError + If walk_type not specified or alpha not in valid range + """ + import numpy as np + import scipy as sp + + if walk_type is None: + if nx.is_strongly_connected(G): + if nx.is_aperiodic(G): + walk_type = "random" + else: + walk_type = "lazy" + else: + walk_type = "pagerank" + + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, dtype=float) + n, m = A.shape + if walk_type in ["random", "lazy"]: + # TODO: Rm csr_array wrapper when spdiags array creation becomes available + DI = sp.sparse.csr_array(sp.sparse.spdiags(1.0 / A.sum(axis=1), 0, n, n)) + if walk_type == "random": + P = DI @ A + else: + # TODO: Rm csr_array wrapper when identity array creation becomes available + I = sp.sparse.csr_array(sp.sparse.identity(n)) + P = (I + DI @ A) / 2.0 + + elif walk_type == "pagerank": + if not (0 < alpha < 1): + raise nx.NetworkXError("alpha must be between 0 and 1") + # this is using a dense representation. NOTE: This should be sparsified! + A = A.toarray() + # add constant to dangling nodes' row + A[A.sum(axis=1) == 0, :] = 1 / n + # normalize + A = A / A.sum(axis=1)[np.newaxis, :].T + P = alpha * A + (1 - alpha) / n + else: + raise nx.NetworkXError("walk_type must be random, lazy, or pagerank") + + return P diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/modularitymatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/modularitymatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..0287910bdccc61263afa4d6adfa2b0d78afc4daa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/modularitymatrix.py @@ -0,0 +1,166 @@ +"""Modularity matrix of graphs.""" + +import networkx as nx +from networkx.utils import not_implemented_for + +__all__ = ["modularity_matrix", "directed_modularity_matrix"] + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +@nx._dispatchable(edge_attrs="weight") +def modularity_matrix(G, nodelist=None, weight=None): + r"""Returns the modularity matrix of G. + + The modularity matrix is the matrix B = A - , where A is the adjacency + matrix and is the average adjacency matrix, assuming that the graph + is described by the configuration model. + + More specifically, the element B_ij of B is defined as + + .. math:: + A_{ij} - {k_i k_j \over 2 m} + + where k_i is the degree of node i, and where m is the number of edges + in the graph. When weight is set to a name of an attribute edge, Aij, k_i, + k_j and m are computed using its value. + + Parameters + ---------- + G : Graph + A NetworkX graph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default=None) + The edge attribute that holds the numerical value used for + the edge weight. If None then all edge weights are 1. + + Returns + ------- + B : Numpy array + The modularity matrix of G. + + Examples + -------- + >>> k = [3, 2, 2, 1, 0] + >>> G = nx.havel_hakimi_graph(k) + >>> B = nx.modularity_matrix(G) + + + See Also + -------- + to_numpy_array + modularity_spectrum + adjacency_matrix + directed_modularity_matrix + + References + ---------- + .. [1] M. E. J. Newman, "Modularity and community structure in networks", + Proc. Natl. Acad. Sci. USA, vol. 103, pp. 8577-8582, 2006. + """ + import numpy as np + + if nodelist is None: + nodelist = list(G) + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, format="csr") + k = A.sum(axis=1) + m = k.sum() * 0.5 + # Expected adjacency matrix + X = np.outer(k, k) / (2 * m) + + return A - X + + +@not_implemented_for("undirected") +@not_implemented_for("multigraph") +@nx._dispatchable(edge_attrs="weight") +def directed_modularity_matrix(G, nodelist=None, weight=None): + """Returns the directed modularity matrix of G. + + The modularity matrix is the matrix B = A - , where A is the adjacency + matrix and is the expected adjacency matrix, assuming that the graph + is described by the configuration model. + + More specifically, the element B_ij of B is defined as + + .. math:: + B_{ij} = A_{ij} - k_i^{out} k_j^{in} / m + + where :math:`k_i^{in}` is the in degree of node i, and :math:`k_j^{out}` is the out degree + of node j, with m the number of edges in the graph. When weight is set + to a name of an attribute edge, Aij, k_i, k_j and m are computed using + its value. + + Parameters + ---------- + G : DiGraph + A NetworkX DiGraph + + nodelist : list, optional + The rows and columns are ordered according to the nodes in nodelist. + If nodelist is None, then the ordering is produced by G.nodes(). + + weight : string or None, optional (default=None) + The edge attribute that holds the numerical value used for + the edge weight. If None then all edge weights are 1. + + Returns + ------- + B : Numpy array + The modularity matrix of G. + + Examples + -------- + >>> G = nx.DiGraph() + >>> G.add_edges_from( + ... ( + ... (1, 2), + ... (1, 3), + ... (3, 1), + ... (3, 2), + ... (3, 5), + ... (4, 5), + ... (4, 6), + ... (5, 4), + ... (5, 6), + ... (6, 4), + ... ) + ... ) + >>> B = nx.directed_modularity_matrix(G) + + + Notes + ----- + NetworkX defines the element A_ij of the adjacency matrix as 1 if there + is a link going from node i to node j. Leicht and Newman use the opposite + definition. This explains the different expression for B_ij. + + See Also + -------- + to_numpy_array + modularity_spectrum + adjacency_matrix + modularity_matrix + + References + ---------- + .. [1] E. A. Leicht, M. E. J. Newman, + "Community structure in directed networks", + Phys. Rev Lett., vol. 100, no. 11, p. 118703, 2008. + """ + import numpy as np + + if nodelist is None: + nodelist = list(G) + A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, format="csr") + k_in = A.sum(axis=0) + k_out = A.sum(axis=1) + m = k_in.sum() + # Expected adjacency matrix + X = np.outer(k_out, k_in) / m + + return A - X diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/spectrum.py b/.venv/lib/python3.11/site-packages/networkx/linalg/spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..079b18550ca5b1478ec3f4b12ee40e5451627355 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/spectrum.py @@ -0,0 +1,186 @@ +""" +Eigenvalue spectrum of graphs. +""" + +import networkx as nx + +__all__ = [ + "laplacian_spectrum", + "adjacency_spectrum", + "modularity_spectrum", + "normalized_laplacian_spectrum", + "bethe_hessian_spectrum", +] + + +@nx._dispatchable(edge_attrs="weight") +def laplacian_spectrum(G, weight="weight"): + """Returns eigenvalues of the Laplacian of G + + Parameters + ---------- + G : graph + A NetworkX graph + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + evals : NumPy array + Eigenvalues + + Notes + ----- + For MultiGraph/MultiDiGraph, the edges weights are summed. + See :func:`~networkx.convert_matrix.to_numpy_array` for other options. + + See Also + -------- + laplacian_matrix + + Examples + -------- + The multiplicity of 0 as an eigenvalue of the laplacian matrix is equal + to the number of connected components of G. + + >>> G = nx.Graph() # Create a graph with 5 nodes and 3 connected components + >>> G.add_nodes_from(range(5)) + >>> G.add_edges_from([(0, 2), (3, 4)]) + >>> nx.laplacian_spectrum(G) + array([0., 0., 0., 2., 2.]) + + """ + import scipy as sp + + return sp.linalg.eigvalsh(nx.laplacian_matrix(G, weight=weight).todense()) + + +@nx._dispatchable(edge_attrs="weight") +def normalized_laplacian_spectrum(G, weight="weight"): + """Return eigenvalues of the normalized Laplacian of G + + Parameters + ---------- + G : graph + A NetworkX graph + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + evals : NumPy array + Eigenvalues + + Notes + ----- + For MultiGraph/MultiDiGraph, the edges weights are summed. + See to_numpy_array for other options. + + See Also + -------- + normalized_laplacian_matrix + """ + import scipy as sp + + return sp.linalg.eigvalsh( + nx.normalized_laplacian_matrix(G, weight=weight).todense() + ) + + +@nx._dispatchable(edge_attrs="weight") +def adjacency_spectrum(G, weight="weight"): + """Returns eigenvalues of the adjacency matrix of G. + + Parameters + ---------- + G : graph + A NetworkX graph + + weight : string or None, optional (default='weight') + The edge data key used to compute each value in the matrix. + If None, then each edge has weight 1. + + Returns + ------- + evals : NumPy array + Eigenvalues + + Notes + ----- + For MultiGraph/MultiDiGraph, the edges weights are summed. + See to_numpy_array for other options. + + See Also + -------- + adjacency_matrix + """ + import scipy as sp + + return sp.linalg.eigvals(nx.adjacency_matrix(G, weight=weight).todense()) + + +@nx._dispatchable +def modularity_spectrum(G): + """Returns eigenvalues of the modularity matrix of G. + + Parameters + ---------- + G : Graph + A NetworkX Graph or DiGraph + + Returns + ------- + evals : NumPy array + Eigenvalues + + See Also + -------- + modularity_matrix + + References + ---------- + .. [1] M. E. J. Newman, "Modularity and community structure in networks", + Proc. Natl. Acad. Sci. USA, vol. 103, pp. 8577-8582, 2006. + """ + import scipy as sp + + if G.is_directed(): + return sp.linalg.eigvals(nx.directed_modularity_matrix(G)) + else: + return sp.linalg.eigvals(nx.modularity_matrix(G)) + + +@nx._dispatchable +def bethe_hessian_spectrum(G, r=None): + """Returns eigenvalues of the Bethe Hessian matrix of G. + + Parameters + ---------- + G : Graph + A NetworkX Graph or DiGraph + + r : float + Regularizer parameter + + Returns + ------- + evals : NumPy array + Eigenvalues + + See Also + -------- + bethe_hessian_matrix + + References + ---------- + .. [1] A. Saade, F. Krzakala and L. Zdeborová + "Spectral clustering of graphs with the bethe hessian", + Advances in Neural Information Processing Systems. 2014. + """ + import scipy as sp + + return sp.linalg.eigvalsh(nx.bethe_hessian_matrix(G, r).todense()) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__init__.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd0facad999383c5dab1079882e45a61b71ccb1a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_algebraic_connectivity.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_algebraic_connectivity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..647ad6b001124c190ff3f217a7f9eb4dbc04232a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_algebraic_connectivity.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_attrmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_attrmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d48f6561cce74f61528aff0cb7aafa5e0e2c23e Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_attrmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_bethehessian.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_bethehessian.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..570ab1542e1f9f44fff9ae1787027be6fe5acf75 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_bethehessian.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_graphmatrix.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_graphmatrix.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..254ab1bfd4dba67a059e7d408b6c7608ba8151b7 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_graphmatrix.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_laplacian.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_laplacian.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1e9d2f3b7906d1a2a5925ffbc0904db1a71c742 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_laplacian.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_modularity.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_modularity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..495b8d2442f7d543e984a60c08c95f81d3f2e958 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_modularity.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_spectrum.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_spectrum.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bb031a8da8f93688ed82541913e40702f6990c6 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/__pycache__/test_spectrum.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_algebraic_connectivity.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_algebraic_connectivity.py new file mode 100644 index 0000000000000000000000000000000000000000..089d917a6832e1ab211eb3ced08344b84ddb285a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_algebraic_connectivity.py @@ -0,0 +1,402 @@ +from math import sqrt + +import pytest + +np = pytest.importorskip("numpy") + + +import networkx as nx + +methods = ("tracemin_pcg", "tracemin_lu", "lanczos", "lobpcg") + + +def test_algebraic_connectivity_tracemin_chol(): + """Test that "tracemin_chol" raises an exception.""" + pytest.importorskip("scipy") + G = nx.barbell_graph(5, 4) + with pytest.raises(nx.NetworkXError): + nx.algebraic_connectivity(G, method="tracemin_chol") + + +def test_fiedler_vector_tracemin_chol(): + """Test that "tracemin_chol" raises an exception.""" + pytest.importorskip("scipy") + G = nx.barbell_graph(5, 4) + with pytest.raises(nx.NetworkXError): + nx.fiedler_vector(G, method="tracemin_chol") + + +def test_spectral_ordering_tracemin_chol(): + """Test that "tracemin_chol" raises an exception.""" + pytest.importorskip("scipy") + G = nx.barbell_graph(5, 4) + with pytest.raises(nx.NetworkXError): + nx.spectral_ordering(G, method="tracemin_chol") + + +def test_fiedler_vector_tracemin_unknown(): + """Test that "tracemin_unknown" raises an exception.""" + pytest.importorskip("scipy") + G = nx.barbell_graph(5, 4) + L = nx.laplacian_matrix(G) + X = np.asarray(np.random.normal(size=(1, L.shape[0]))).T + with pytest.raises(nx.NetworkXError, match="Unknown linear system solver"): + nx.linalg.algebraicconnectivity._tracemin_fiedler( + L, X, normalized=False, tol=1e-8, method="tracemin_unknown" + ) + + +def test_spectral_bisection(): + pytest.importorskip("scipy") + G = nx.barbell_graph(3, 0) + C = nx.spectral_bisection(G) + assert C == ({0, 1, 2}, {3, 4, 5}) + + mapping = dict(enumerate("badfec")) + G = nx.relabel_nodes(G, mapping) + C = nx.spectral_bisection(G) + assert C == ( + {mapping[0], mapping[1], mapping[2]}, + {mapping[3], mapping[4], mapping[5]}, + ) + + +def check_eigenvector(A, l, x): + nx = np.linalg.norm(x) + # Check zeroness. + assert nx != pytest.approx(0, abs=1e-07) + y = A @ x + ny = np.linalg.norm(y) + # Check collinearity. + assert x @ y == pytest.approx(nx * ny, abs=1e-7) + # Check eigenvalue. + assert ny == pytest.approx(l * nx, abs=1e-7) + + +class TestAlgebraicConnectivity: + @pytest.mark.parametrize("method", methods) + def test_directed(self, method): + G = nx.DiGraph() + pytest.raises( + nx.NetworkXNotImplemented, nx.algebraic_connectivity, G, method=method + ) + pytest.raises(nx.NetworkXNotImplemented, nx.fiedler_vector, G, method=method) + + @pytest.mark.parametrize("method", methods) + def test_null_and_singleton(self, method): + G = nx.Graph() + pytest.raises(nx.NetworkXError, nx.algebraic_connectivity, G, method=method) + pytest.raises(nx.NetworkXError, nx.fiedler_vector, G, method=method) + G.add_edge(0, 0) + pytest.raises(nx.NetworkXError, nx.algebraic_connectivity, G, method=method) + pytest.raises(nx.NetworkXError, nx.fiedler_vector, G, method=method) + + @pytest.mark.parametrize("method", methods) + def test_disconnected(self, method): + G = nx.Graph() + G.add_nodes_from(range(2)) + assert nx.algebraic_connectivity(G) == 0 + pytest.raises(nx.NetworkXError, nx.fiedler_vector, G, method=method) + G.add_edge(0, 1, weight=0) + assert nx.algebraic_connectivity(G) == 0 + pytest.raises(nx.NetworkXError, nx.fiedler_vector, G, method=method) + + def test_unrecognized_method(self): + pytest.importorskip("scipy") + G = nx.path_graph(4) + pytest.raises(nx.NetworkXError, nx.algebraic_connectivity, G, method="unknown") + pytest.raises(nx.NetworkXError, nx.fiedler_vector, G, method="unknown") + + @pytest.mark.parametrize("method", methods) + def test_two_nodes(self, method): + pytest.importorskip("scipy") + G = nx.Graph() + G.add_edge(0, 1, weight=1) + A = nx.laplacian_matrix(G) + assert nx.algebraic_connectivity(G, tol=1e-12, method=method) == pytest.approx( + 2, abs=1e-7 + ) + x = nx.fiedler_vector(G, tol=1e-12, method=method) + check_eigenvector(A, 2, x) + + @pytest.mark.parametrize("method", methods) + def test_two_nodes_multigraph(self, method): + pytest.importorskip("scipy") + G = nx.MultiGraph() + G.add_edge(0, 0, spam=1e8) + G.add_edge(0, 1, spam=1) + G.add_edge(0, 1, spam=-2) + A = -3 * nx.laplacian_matrix(G, weight="spam") + assert nx.algebraic_connectivity( + G, weight="spam", tol=1e-12, method=method + ) == pytest.approx(6, abs=1e-7) + x = nx.fiedler_vector(G, weight="spam", tol=1e-12, method=method) + check_eigenvector(A, 6, x) + + def test_abbreviation_of_method(self): + pytest.importorskip("scipy") + G = nx.path_graph(8) + A = nx.laplacian_matrix(G) + sigma = 2 - sqrt(2 + sqrt(2)) + ac = nx.algebraic_connectivity(G, tol=1e-12, method="tracemin") + assert ac == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, tol=1e-12, method="tracemin") + check_eigenvector(A, sigma, x) + + @pytest.mark.parametrize("method", methods) + def test_path(self, method): + pytest.importorskip("scipy") + G = nx.path_graph(8) + A = nx.laplacian_matrix(G) + sigma = 2 - sqrt(2 + sqrt(2)) + ac = nx.algebraic_connectivity(G, tol=1e-12, method=method) + assert ac == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, tol=1e-12, method=method) + check_eigenvector(A, sigma, x) + + @pytest.mark.parametrize("method", methods) + def test_problematic_graph_issue_2381(self, method): + pytest.importorskip("scipy") + G = nx.path_graph(4) + G.add_edges_from([(4, 2), (5, 1)]) + A = nx.laplacian_matrix(G) + sigma = 0.438447187191 + ac = nx.algebraic_connectivity(G, tol=1e-12, method=method) + assert ac == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, tol=1e-12, method=method) + check_eigenvector(A, sigma, x) + + @pytest.mark.parametrize("method", methods) + def test_cycle(self, method): + pytest.importorskip("scipy") + G = nx.cycle_graph(8) + A = nx.laplacian_matrix(G) + sigma = 2 - sqrt(2) + ac = nx.algebraic_connectivity(G, tol=1e-12, method=method) + assert ac == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, tol=1e-12, method=method) + check_eigenvector(A, sigma, x) + + @pytest.mark.parametrize("method", methods) + def test_seed_argument(self, method): + pytest.importorskip("scipy") + G = nx.cycle_graph(8) + A = nx.laplacian_matrix(G) + sigma = 2 - sqrt(2) + ac = nx.algebraic_connectivity(G, tol=1e-12, method=method, seed=1) + assert ac == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, tol=1e-12, method=method, seed=1) + check_eigenvector(A, sigma, x) + + @pytest.mark.parametrize( + ("normalized", "sigma", "laplacian_fn"), + ( + (False, 0.2434017461399311, nx.laplacian_matrix), + (True, 0.08113391537997749, nx.normalized_laplacian_matrix), + ), + ) + @pytest.mark.parametrize("method", methods) + def test_buckminsterfullerene(self, normalized, sigma, laplacian_fn, method): + pytest.importorskip("scipy") + G = nx.Graph( + [ + (1, 10), + (1, 41), + (1, 59), + (2, 12), + (2, 42), + (2, 60), + (3, 6), + (3, 43), + (3, 57), + (4, 8), + (4, 44), + (4, 58), + (5, 13), + (5, 56), + (5, 57), + (6, 10), + (6, 31), + (7, 14), + (7, 56), + (7, 58), + (8, 12), + (8, 32), + (9, 23), + (9, 53), + (9, 59), + (10, 15), + (11, 24), + (11, 53), + (11, 60), + (12, 16), + (13, 14), + (13, 25), + (14, 26), + (15, 27), + (15, 49), + (16, 28), + (16, 50), + (17, 18), + (17, 19), + (17, 54), + (18, 20), + (18, 55), + (19, 23), + (19, 41), + (20, 24), + (20, 42), + (21, 31), + (21, 33), + (21, 57), + (22, 32), + (22, 34), + (22, 58), + (23, 24), + (25, 35), + (25, 43), + (26, 36), + (26, 44), + (27, 51), + (27, 59), + (28, 52), + (28, 60), + (29, 33), + (29, 34), + (29, 56), + (30, 51), + (30, 52), + (30, 53), + (31, 47), + (32, 48), + (33, 45), + (34, 46), + (35, 36), + (35, 37), + (36, 38), + (37, 39), + (37, 49), + (38, 40), + (38, 50), + (39, 40), + (39, 51), + (40, 52), + (41, 47), + (42, 48), + (43, 49), + (44, 50), + (45, 46), + (45, 54), + (46, 55), + (47, 54), + (48, 55), + ] + ) + A = laplacian_fn(G) + try: + assert nx.algebraic_connectivity( + G, normalized=normalized, tol=1e-12, method=method + ) == pytest.approx(sigma, abs=1e-7) + x = nx.fiedler_vector(G, normalized=normalized, tol=1e-12, method=method) + check_eigenvector(A, sigma, x) + except nx.NetworkXError as err: + if err.args not in ( + ("Cholesky solver unavailable.",), + ("LU solver unavailable.",), + ): + raise + + +class TestSpectralOrdering: + _graphs = (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph) + + @pytest.mark.parametrize("graph", _graphs) + def test_nullgraph(self, graph): + G = graph() + pytest.raises(nx.NetworkXError, nx.spectral_ordering, G) + + @pytest.mark.parametrize("graph", _graphs) + def test_singleton(self, graph): + G = graph() + G.add_node("x") + assert nx.spectral_ordering(G) == ["x"] + G.add_edge("x", "x", weight=33) + G.add_edge("x", "x", weight=33) + assert nx.spectral_ordering(G) == ["x"] + + def test_unrecognized_method(self): + G = nx.path_graph(4) + pytest.raises(nx.NetworkXError, nx.spectral_ordering, G, method="unknown") + + @pytest.mark.parametrize("method", methods) + def test_three_nodes(self, method): + pytest.importorskip("scipy") + G = nx.Graph() + G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2), (2, 3, 1)], weight="spam") + order = nx.spectral_ordering(G, weight="spam", method=method) + assert set(order) == set(G) + assert {1, 3} in (set(order[:-1]), set(order[1:])) + + @pytest.mark.parametrize("method", methods) + def test_three_nodes_multigraph(self, method): + pytest.importorskip("scipy") + G = nx.MultiDiGraph() + G.add_weighted_edges_from([(1, 2, 1), (1, 3, 2), (2, 3, 1), (2, 3, 2)]) + order = nx.spectral_ordering(G, method=method) + assert set(order) == set(G) + assert {2, 3} in (set(order[:-1]), set(order[1:])) + + @pytest.mark.parametrize("method", methods) + def test_path(self, method): + pytest.importorskip("scipy") + path = list(range(10)) + np.random.shuffle(path) + G = nx.Graph() + nx.add_path(G, path) + order = nx.spectral_ordering(G, method=method) + assert order in [path, list(reversed(path))] + + @pytest.mark.parametrize("method", methods) + def test_seed_argument(self, method): + pytest.importorskip("scipy") + path = list(range(10)) + np.random.shuffle(path) + G = nx.Graph() + nx.add_path(G, path) + order = nx.spectral_ordering(G, method=method, seed=1) + assert order in [path, list(reversed(path))] + + @pytest.mark.parametrize("method", methods) + def test_disconnected(self, method): + pytest.importorskip("scipy") + G = nx.Graph() + nx.add_path(G, range(0, 10, 2)) + nx.add_path(G, range(1, 10, 2)) + order = nx.spectral_ordering(G, method=method) + assert set(order) == set(G) + seqs = [ + list(range(0, 10, 2)), + list(range(8, -1, -2)), + list(range(1, 10, 2)), + list(range(9, -1, -2)), + ] + assert order[:5] in seqs + assert order[5:] in seqs + + @pytest.mark.parametrize( + ("normalized", "expected_order"), + ( + (False, [[1, 2, 0, 3, 4, 5, 6, 9, 7, 8], [8, 7, 9, 6, 5, 4, 3, 0, 2, 1]]), + (True, [[1, 2, 3, 0, 4, 5, 9, 6, 7, 8], [8, 7, 6, 9, 5, 4, 0, 3, 2, 1]]), + ), + ) + @pytest.mark.parametrize("method", methods) + def test_cycle(self, normalized, expected_order, method): + pytest.importorskip("scipy") + path = list(range(10)) + G = nx.Graph() + nx.add_path(G, path, weight=5) + G.add_edge(path[-1], path[0], weight=1) + A = nx.laplacian_matrix(G).todense() + order = nx.spectral_ordering(G, normalized=normalized, method=method) + assert order in expected_order diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_attrmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_attrmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..01574bb3b8f284edef6c7f92fe1c7e7a239e0610 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_attrmatrix.py @@ -0,0 +1,108 @@ +import pytest + +np = pytest.importorskip("numpy") + +import networkx as nx + + +def test_attr_matrix(): + G = nx.Graph() + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 2, thickness=2) + G.add_edge(1, 2, thickness=3) + + def node_attr(u): + return G.nodes[u].get("size", 0.5) * 3 + + def edge_attr(u, v): + return G[u][v].get("thickness", 0.5) + + M = nx.attr_matrix(G, edge_attr=edge_attr, node_attr=node_attr) + np.testing.assert_equal(M[0], np.array([[6.0]])) + assert M[1] == [1.5] + + +def test_attr_matrix_directed(): + G = nx.DiGraph() + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 2, thickness=2) + G.add_edge(1, 2, thickness=3) + M = nx.attr_matrix(G, rc_order=[0, 1, 2]) + # fmt: off + data = np.array( + [[0., 1., 1.], + [0., 0., 1.], + [0., 0., 0.]] + ) + # fmt: on + np.testing.assert_equal(M, np.array(data)) + + +def test_attr_matrix_multigraph(): + G = nx.MultiGraph() + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 2, thickness=2) + G.add_edge(1, 2, thickness=3) + M = nx.attr_matrix(G, rc_order=[0, 1, 2]) + # fmt: off + data = np.array( + [[0., 3., 1.], + [3., 0., 1.], + [1., 1., 0.]] + ) + # fmt: on + np.testing.assert_equal(M, np.array(data)) + M = nx.attr_matrix(G, edge_attr="weight", rc_order=[0, 1, 2]) + # fmt: off + data = np.array( + [[0., 9., 1.], + [9., 0., 1.], + [1., 1., 0.]] + ) + # fmt: on + np.testing.assert_equal(M, np.array(data)) + M = nx.attr_matrix(G, edge_attr="thickness", rc_order=[0, 1, 2]) + # fmt: off + data = np.array( + [[0., 3., 2.], + [3., 0., 3.], + [2., 3., 0.]] + ) + # fmt: on + np.testing.assert_equal(M, np.array(data)) + + +def test_attr_sparse_matrix(): + pytest.importorskip("scipy") + G = nx.Graph() + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 2, thickness=2) + G.add_edge(1, 2, thickness=3) + M = nx.attr_sparse_matrix(G) + mtx = M[0] + data = np.ones((3, 3), float) + np.fill_diagonal(data, 0) + np.testing.assert_equal(mtx.todense(), np.array(data)) + assert M[1] == [0, 1, 2] + + +def test_attr_sparse_matrix_directed(): + pytest.importorskip("scipy") + G = nx.DiGraph() + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 1, thickness=1, weight=3) + G.add_edge(0, 2, thickness=2) + G.add_edge(1, 2, thickness=3) + M = nx.attr_sparse_matrix(G, rc_order=[0, 1, 2]) + # fmt: off + data = np.array( + [[0., 1., 1.], + [0., 0., 1.], + [0., 0., 0.]] + ) + # fmt: on + np.testing.assert_equal(M.todense(), np.array(data)) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_bethehessian.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_bethehessian.py new file mode 100644 index 0000000000000000000000000000000000000000..339fe1be390b40083efdd61f1cae4ff62838fc93 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_bethehessian.py @@ -0,0 +1,41 @@ +import pytest + +np = pytest.importorskip("numpy") +pytest.importorskip("scipy") + +import networkx as nx +from networkx.generators.degree_seq import havel_hakimi_graph + + +class TestBetheHessian: + @classmethod + def setup_class(cls): + deg = [3, 2, 2, 1, 0] + cls.G = havel_hakimi_graph(deg) + cls.P = nx.path_graph(3) + + def test_bethe_hessian(self): + "Bethe Hessian matrix" + # fmt: off + H = np.array([[4, -2, 0], + [-2, 5, -2], + [0, -2, 4]]) + # fmt: on + permutation = [2, 0, 1] + # Bethe Hessian gives expected form + np.testing.assert_equal(nx.bethe_hessian_matrix(self.P, r=2).todense(), H) + # nodelist is correctly implemented + np.testing.assert_equal( + nx.bethe_hessian_matrix(self.P, r=2, nodelist=permutation).todense(), + H[np.ix_(permutation, permutation)], + ) + # Equal to Laplacian matrix when r=1 + np.testing.assert_equal( + nx.bethe_hessian_matrix(self.G, r=1).todense(), + nx.laplacian_matrix(self.G).todense(), + ) + # Correct default for the regularizer r + np.testing.assert_equal( + nx.bethe_hessian_matrix(self.G).todense(), + nx.bethe_hessian_matrix(self.G, r=1.25).todense(), + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_graphmatrix.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_graphmatrix.py new file mode 100644 index 0000000000000000000000000000000000000000..519198bc07b32f16c1c0ae0cd9b8bbe6b81bce62 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_graphmatrix.py @@ -0,0 +1,276 @@ +import pytest + +np = pytest.importorskip("numpy") +pytest.importorskip("scipy") + +import networkx as nx +from networkx.exception import NetworkXError +from networkx.generators.degree_seq import havel_hakimi_graph + + +def test_incidence_matrix_simple(): + deg = [3, 2, 2, 1, 0] + G = havel_hakimi_graph(deg) + deg = [(1, 0), (1, 0), (1, 0), (2, 0), (1, 0), (2, 1), (0, 1), (0, 1)] + MG = nx.random_clustered_graph(deg, seed=42) + + I = nx.incidence_matrix(G, dtype=int).todense() + # fmt: off + expected = np.array( + [[1, 1, 1, 0], + [0, 1, 0, 1], + [1, 0, 0, 1], + [0, 0, 1, 0], + [0, 0, 0, 0]] + ) + # fmt: on + np.testing.assert_equal(I, expected) + + I = nx.incidence_matrix(MG, dtype=int).todense() + # fmt: off + expected = np.array( + [[1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 1, 0, 1]] + ) + # fmt: on + np.testing.assert_equal(I, expected) + + with pytest.raises(NetworkXError): + nx.incidence_matrix(G, nodelist=[0, 1]) + + +class TestGraphMatrix: + @classmethod + def setup_class(cls): + deg = [3, 2, 2, 1, 0] + cls.G = havel_hakimi_graph(deg) + # fmt: off + cls.OI = np.array( + [[-1, -1, -1, 0], + [1, 0, 0, -1], + [0, 1, 0, 1], + [0, 0, 1, 0], + [0, 0, 0, 0]] + ) + cls.A = np.array( + [[0, 1, 1, 1, 0], + [1, 0, 1, 0, 0], + [1, 1, 0, 0, 0], + [1, 0, 0, 0, 0], + [0, 0, 0, 0, 0]] + ) + # fmt: on + cls.WG = havel_hakimi_graph(deg) + cls.WG.add_edges_from( + (u, v, {"weight": 0.5, "other": 0.3}) for (u, v) in cls.G.edges() + ) + # fmt: off + cls.WA = np.array( + [[0, 0.5, 0.5, 0.5, 0], + [0.5, 0, 0.5, 0, 0], + [0.5, 0.5, 0, 0, 0], + [0.5, 0, 0, 0, 0], + [0, 0, 0, 0, 0]] + ) + # fmt: on + cls.MG = nx.MultiGraph(cls.G) + cls.MG2 = cls.MG.copy() + cls.MG2.add_edge(0, 1) + # fmt: off + cls.MG2A = np.array( + [[0, 2, 1, 1, 0], + [2, 0, 1, 0, 0], + [1, 1, 0, 0, 0], + [1, 0, 0, 0, 0], + [0, 0, 0, 0, 0]] + ) + cls.MGOI = np.array( + [[-1, -1, -1, -1, 0], + [1, 1, 0, 0, -1], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 0]] + ) + # fmt: on + cls.no_edges_G = nx.Graph([(1, 2), (3, 2, {"weight": 8})]) + cls.no_edges_A = np.array([[0, 0], [0, 0]]) + + def test_incidence_matrix(self): + "Conversion to incidence matrix" + I = nx.incidence_matrix( + self.G, + nodelist=sorted(self.G), + edgelist=sorted(self.G.edges()), + oriented=True, + dtype=int, + ).todense() + np.testing.assert_equal(I, self.OI) + + I = nx.incidence_matrix( + self.G, + nodelist=sorted(self.G), + edgelist=sorted(self.G.edges()), + oriented=False, + dtype=int, + ).todense() + np.testing.assert_equal(I, np.abs(self.OI)) + + I = nx.incidence_matrix( + self.MG, + nodelist=sorted(self.MG), + edgelist=sorted(self.MG.edges()), + oriented=True, + dtype=int, + ).todense() + np.testing.assert_equal(I, self.OI) + + I = nx.incidence_matrix( + self.MG, + nodelist=sorted(self.MG), + edgelist=sorted(self.MG.edges()), + oriented=False, + dtype=int, + ).todense() + np.testing.assert_equal(I, np.abs(self.OI)) + + I = nx.incidence_matrix( + self.MG2, + nodelist=sorted(self.MG2), + edgelist=sorted(self.MG2.edges()), + oriented=True, + dtype=int, + ).todense() + np.testing.assert_equal(I, self.MGOI) + + I = nx.incidence_matrix( + self.MG2, + nodelist=sorted(self.MG), + edgelist=sorted(self.MG2.edges()), + oriented=False, + dtype=int, + ).todense() + np.testing.assert_equal(I, np.abs(self.MGOI)) + + I = nx.incidence_matrix(self.G, dtype=np.uint8) + assert I.dtype == np.uint8 + + def test_weighted_incidence_matrix(self): + I = nx.incidence_matrix( + self.WG, + nodelist=sorted(self.WG), + edgelist=sorted(self.WG.edges()), + oriented=True, + dtype=int, + ).todense() + np.testing.assert_equal(I, self.OI) + + I = nx.incidence_matrix( + self.WG, + nodelist=sorted(self.WG), + edgelist=sorted(self.WG.edges()), + oriented=False, + dtype=int, + ).todense() + np.testing.assert_equal(I, np.abs(self.OI)) + + # np.testing.assert_equal(nx.incidence_matrix(self.WG,oriented=True, + # weight='weight').todense(),0.5*self.OI) + # np.testing.assert_equal(nx.incidence_matrix(self.WG,weight='weight').todense(), + # np.abs(0.5*self.OI)) + # np.testing.assert_equal(nx.incidence_matrix(self.WG,oriented=True,weight='other').todense(), + # 0.3*self.OI) + + I = nx.incidence_matrix( + self.WG, + nodelist=sorted(self.WG), + edgelist=sorted(self.WG.edges()), + oriented=True, + weight="weight", + ).todense() + np.testing.assert_equal(I, 0.5 * self.OI) + + I = nx.incidence_matrix( + self.WG, + nodelist=sorted(self.WG), + edgelist=sorted(self.WG.edges()), + oriented=False, + weight="weight", + ).todense() + np.testing.assert_equal(I, np.abs(0.5 * self.OI)) + + I = nx.incidence_matrix( + self.WG, + nodelist=sorted(self.WG), + edgelist=sorted(self.WG.edges()), + oriented=True, + weight="other", + ).todense() + np.testing.assert_equal(I, 0.3 * self.OI) + + # WMG=nx.MultiGraph(self.WG) + # WMG.add_edge(0,1,weight=0.5,other=0.3) + # np.testing.assert_equal(nx.incidence_matrix(WMG,weight='weight').todense(), + # np.abs(0.5*self.MGOI)) + # np.testing.assert_equal(nx.incidence_matrix(WMG,weight='weight',oriented=True).todense(), + # 0.5*self.MGOI) + # np.testing.assert_equal(nx.incidence_matrix(WMG,weight='other',oriented=True).todense(), + # 0.3*self.MGOI) + + WMG = nx.MultiGraph(self.WG) + WMG.add_edge(0, 1, weight=0.5, other=0.3) + + I = nx.incidence_matrix( + WMG, + nodelist=sorted(WMG), + edgelist=sorted(WMG.edges(keys=True)), + oriented=True, + weight="weight", + ).todense() + np.testing.assert_equal(I, 0.5 * self.MGOI) + + I = nx.incidence_matrix( + WMG, + nodelist=sorted(WMG), + edgelist=sorted(WMG.edges(keys=True)), + oriented=False, + weight="weight", + ).todense() + np.testing.assert_equal(I, np.abs(0.5 * self.MGOI)) + + I = nx.incidence_matrix( + WMG, + nodelist=sorted(WMG), + edgelist=sorted(WMG.edges(keys=True)), + oriented=True, + weight="other", + ).todense() + np.testing.assert_equal(I, 0.3 * self.MGOI) + + def test_adjacency_matrix(self): + "Conversion to adjacency matrix" + np.testing.assert_equal(nx.adjacency_matrix(self.G).todense(), self.A) + np.testing.assert_equal(nx.adjacency_matrix(self.MG).todense(), self.A) + np.testing.assert_equal(nx.adjacency_matrix(self.MG2).todense(), self.MG2A) + np.testing.assert_equal( + nx.adjacency_matrix(self.G, nodelist=[0, 1]).todense(), self.A[:2, :2] + ) + np.testing.assert_equal(nx.adjacency_matrix(self.WG).todense(), self.WA) + np.testing.assert_equal( + nx.adjacency_matrix(self.WG, weight=None).todense(), self.A + ) + np.testing.assert_equal( + nx.adjacency_matrix(self.MG2, weight=None).todense(), self.MG2A + ) + np.testing.assert_equal( + nx.adjacency_matrix(self.WG, weight="other").todense(), 0.6 * self.WA + ) + np.testing.assert_equal( + nx.adjacency_matrix(self.no_edges_G, nodelist=[1, 3]).todense(), + self.no_edges_A, + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_laplacian.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_laplacian.py new file mode 100644 index 0000000000000000000000000000000000000000..23f1b28e19f1af4097ae3e99501a45439a6f1598 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_laplacian.py @@ -0,0 +1,336 @@ +import pytest + +np = pytest.importorskip("numpy") +pytest.importorskip("scipy") + +import networkx as nx +from networkx.generators.degree_seq import havel_hakimi_graph +from networkx.generators.expanders import margulis_gabber_galil_graph + + +class TestLaplacian: + @classmethod + def setup_class(cls): + deg = [3, 2, 2, 1, 0] + cls.G = havel_hakimi_graph(deg) + cls.WG = nx.Graph( + (u, v, {"weight": 0.5, "other": 0.3}) for (u, v) in cls.G.edges() + ) + cls.WG.add_node(4) + cls.MG = nx.MultiGraph(cls.G) + + # Graph with clsloops + cls.Gsl = cls.G.copy() + for node in cls.Gsl.nodes(): + cls.Gsl.add_edge(node, node) + + # Graph used as an example in Sec. 4.1 of Langville and Meyer, + # "Google's PageRank and Beyond". + cls.DiG = nx.DiGraph() + cls.DiG.add_edges_from( + ( + (1, 2), + (1, 3), + (3, 1), + (3, 2), + (3, 5), + (4, 5), + (4, 6), + (5, 4), + (5, 6), + (6, 4), + ) + ) + cls.DiMG = nx.MultiDiGraph(cls.DiG) + cls.DiWG = nx.DiGraph( + (u, v, {"weight": 0.5, "other": 0.3}) for (u, v) in cls.DiG.edges() + ) + cls.DiGsl = cls.DiG.copy() + for node in cls.DiGsl.nodes(): + cls.DiGsl.add_edge(node, node) + + def test_laplacian(self): + "Graph Laplacian" + # fmt: off + NL = np.array([[ 3, -1, -1, -1, 0], + [-1, 2, -1, 0, 0], + [-1, -1, 2, 0, 0], + [-1, 0, 0, 1, 0], + [ 0, 0, 0, 0, 0]]) + # fmt: on + WL = 0.5 * NL + OL = 0.3 * NL + # fmt: off + DiNL = np.array([[ 2, -1, -1, 0, 0, 0], + [ 0, 0, 0, 0, 0, 0], + [-1, -1, 3, -1, 0, 0], + [ 0, 0, 0, 2, -1, -1], + [ 0, 0, 0, -1, 2, -1], + [ 0, 0, 0, 0, -1, 1]]) + # fmt: on + DiWL = 0.5 * DiNL + DiOL = 0.3 * DiNL + np.testing.assert_equal(nx.laplacian_matrix(self.G).todense(), NL) + np.testing.assert_equal(nx.laplacian_matrix(self.MG).todense(), NL) + np.testing.assert_equal( + nx.laplacian_matrix(self.G, nodelist=[0, 1]).todense(), + np.array([[1, -1], [-1, 1]]), + ) + np.testing.assert_equal(nx.laplacian_matrix(self.WG).todense(), WL) + np.testing.assert_equal(nx.laplacian_matrix(self.WG, weight=None).todense(), NL) + np.testing.assert_equal( + nx.laplacian_matrix(self.WG, weight="other").todense(), OL + ) + + np.testing.assert_equal(nx.laplacian_matrix(self.DiG).todense(), DiNL) + np.testing.assert_equal(nx.laplacian_matrix(self.DiMG).todense(), DiNL) + np.testing.assert_equal( + nx.laplacian_matrix(self.DiG, nodelist=[1, 2]).todense(), + np.array([[1, -1], [0, 0]]), + ) + np.testing.assert_equal(nx.laplacian_matrix(self.DiWG).todense(), DiWL) + np.testing.assert_equal( + nx.laplacian_matrix(self.DiWG, weight=None).todense(), DiNL + ) + np.testing.assert_equal( + nx.laplacian_matrix(self.DiWG, weight="other").todense(), DiOL + ) + + def test_normalized_laplacian(self): + "Generalized Graph Laplacian" + # fmt: off + G = np.array([[ 1. , -0.408, -0.408, -0.577, 0.], + [-0.408, 1. , -0.5 , 0. , 0.], + [-0.408, -0.5 , 1. , 0. , 0.], + [-0.577, 0. , 0. , 1. , 0.], + [ 0. , 0. , 0. , 0. , 0.]]) + GL = np.array([[ 1. , -0.408, -0.408, -0.577, 0. ], + [-0.408, 1. , -0.5 , 0. , 0. ], + [-0.408, -0.5 , 1. , 0. , 0. ], + [-0.577, 0. , 0. , 1. , 0. ], + [ 0. , 0. , 0. , 0. , 0. ]]) + Lsl = np.array([[ 0.75 , -0.2887, -0.2887, -0.3536, 0. ], + [-0.2887, 0.6667, -0.3333, 0. , 0. ], + [-0.2887, -0.3333, 0.6667, 0. , 0. ], + [-0.3536, 0. , 0. , 0.5 , 0. ], + [ 0. , 0. , 0. , 0. , 0. ]]) + + DiG = np.array([[ 1. , 0. , -0.4082, 0. , 0. , 0. ], + [ 0. , 0. , 0. , 0. , 0. , 0. ], + [-0.4082, 0. , 1. , 0. , -0.4082, 0. ], + [ 0. , 0. , 0. , 1. , -0.5 , -0.7071], + [ 0. , 0. , 0. , -0.5 , 1. , -0.7071], + [ 0. , 0. , 0. , -0.7071, 0. , 1. ]]) + DiGL = np.array([[ 1. , 0. , -0.4082, 0. , 0. , 0. ], + [ 0. , 0. , 0. , 0. , 0. , 0. ], + [-0.4082, 0. , 1. , -0.4082, 0. , 0. ], + [ 0. , 0. , 0. , 1. , -0.5 , -0.7071], + [ 0. , 0. , 0. , -0.5 , 1. , -0.7071], + [ 0. , 0. , 0. , 0. , -0.7071, 1. ]]) + DiLsl = np.array([[ 0.6667, -0.5774, -0.2887, 0. , 0. , 0. ], + [ 0. , 0. , 0. , 0. , 0. , 0. ], + [-0.2887, -0.5 , 0.75 , -0.2887, 0. , 0. ], + [ 0. , 0. , 0. , 0.6667, -0.3333, -0.4082], + [ 0. , 0. , 0. , -0.3333, 0.6667, -0.4082], + [ 0. , 0. , 0. , 0. , -0.4082, 0.5 ]]) + # fmt: on + + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.G, nodelist=range(5)).todense(), + G, + decimal=3, + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.G).todense(), GL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.MG).todense(), GL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.WG).todense(), GL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.WG, weight="other").todense(), + GL, + decimal=3, + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.Gsl).todense(), Lsl, decimal=3 + ) + + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix( + self.DiG, + nodelist=range(1, 1 + 6), + ).todense(), + DiG, + decimal=3, + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.DiG).todense(), DiGL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.DiMG).todense(), DiGL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.DiWG).todense(), DiGL, decimal=3 + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.DiWG, weight="other").todense(), + DiGL, + decimal=3, + ) + np.testing.assert_almost_equal( + nx.normalized_laplacian_matrix(self.DiGsl).todense(), DiLsl, decimal=3 + ) + + +def test_directed_laplacian(): + "Directed Laplacian" + # Graph used as an example in Sec. 4.1 of Langville and Meyer, + # "Google's PageRank and Beyond". The graph contains dangling nodes, so + # the pagerank random walk is selected by directed_laplacian + G = nx.DiGraph() + G.add_edges_from( + ( + (1, 2), + (1, 3), + (3, 1), + (3, 2), + (3, 5), + (4, 5), + (4, 6), + (5, 4), + (5, 6), + (6, 4), + ) + ) + # fmt: off + GL = np.array([[ 0.9833, -0.2941, -0.3882, -0.0291, -0.0231, -0.0261], + [-0.2941, 0.8333, -0.2339, -0.0536, -0.0589, -0.0554], + [-0.3882, -0.2339, 0.9833, -0.0278, -0.0896, -0.0251], + [-0.0291, -0.0536, -0.0278, 0.9833, -0.4878, -0.6675], + [-0.0231, -0.0589, -0.0896, -0.4878, 0.9833, -0.2078], + [-0.0261, -0.0554, -0.0251, -0.6675, -0.2078, 0.9833]]) + # fmt: on + L = nx.directed_laplacian_matrix(G, alpha=0.9, nodelist=sorted(G)) + np.testing.assert_almost_equal(L, GL, decimal=3) + + # Make the graph strongly connected, so we can use a random and lazy walk + G.add_edges_from(((2, 5), (6, 1))) + # fmt: off + GL = np.array([[ 1. , -0.3062, -0.4714, 0. , 0. , -0.3227], + [-0.3062, 1. , -0.1443, 0. , -0.3162, 0. ], + [-0.4714, -0.1443, 1. , 0. , -0.0913, 0. ], + [ 0. , 0. , 0. , 1. , -0.5 , -0.5 ], + [ 0. , -0.3162, -0.0913, -0.5 , 1. , -0.25 ], + [-0.3227, 0. , 0. , -0.5 , -0.25 , 1. ]]) + # fmt: on + L = nx.directed_laplacian_matrix( + G, alpha=0.9, nodelist=sorted(G), walk_type="random" + ) + np.testing.assert_almost_equal(L, GL, decimal=3) + + # fmt: off + GL = np.array([[ 0.5 , -0.1531, -0.2357, 0. , 0. , -0.1614], + [-0.1531, 0.5 , -0.0722, 0. , -0.1581, 0. ], + [-0.2357, -0.0722, 0.5 , 0. , -0.0456, 0. ], + [ 0. , 0. , 0. , 0.5 , -0.25 , -0.25 ], + [ 0. , -0.1581, -0.0456, -0.25 , 0.5 , -0.125 ], + [-0.1614, 0. , 0. , -0.25 , -0.125 , 0.5 ]]) + # fmt: on + L = nx.directed_laplacian_matrix(G, alpha=0.9, nodelist=sorted(G), walk_type="lazy") + np.testing.assert_almost_equal(L, GL, decimal=3) + + # Make a strongly connected periodic graph + G = nx.DiGraph() + G.add_edges_from(((1, 2), (2, 4), (4, 1), (1, 3), (3, 4))) + # fmt: off + GL = np.array([[ 0.5 , -0.176, -0.176, -0.25 ], + [-0.176, 0.5 , 0. , -0.176], + [-0.176, 0. , 0.5 , -0.176], + [-0.25 , -0.176, -0.176, 0.5 ]]) + # fmt: on + L = nx.directed_laplacian_matrix(G, alpha=0.9, nodelist=sorted(G)) + np.testing.assert_almost_equal(L, GL, decimal=3) + + +def test_directed_combinatorial_laplacian(): + "Directed combinatorial Laplacian" + # Graph used as an example in Sec. 4.1 of Langville and Meyer, + # "Google's PageRank and Beyond". The graph contains dangling nodes, so + # the pagerank random walk is selected by directed_laplacian + G = nx.DiGraph() + G.add_edges_from( + ( + (1, 2), + (1, 3), + (3, 1), + (3, 2), + (3, 5), + (4, 5), + (4, 6), + (5, 4), + (5, 6), + (6, 4), + ) + ) + # fmt: off + GL = np.array([[ 0.0366, -0.0132, -0.0153, -0.0034, -0.0020, -0.0027], + [-0.0132, 0.0450, -0.0111, -0.0076, -0.0062, -0.0069], + [-0.0153, -0.0111, 0.0408, -0.0035, -0.0083, -0.0027], + [-0.0034, -0.0076, -0.0035, 0.3688, -0.1356, -0.2187], + [-0.0020, -0.0062, -0.0083, -0.1356, 0.2026, -0.0505], + [-0.0027, -0.0069, -0.0027, -0.2187, -0.0505, 0.2815]]) + # fmt: on + + L = nx.directed_combinatorial_laplacian_matrix(G, alpha=0.9, nodelist=sorted(G)) + np.testing.assert_almost_equal(L, GL, decimal=3) + + # Make the graph strongly connected, so we can use a random and lazy walk + G.add_edges_from(((2, 5), (6, 1))) + + # fmt: off + GL = np.array([[ 0.1395, -0.0349, -0.0465, 0. , 0. , -0.0581], + [-0.0349, 0.093 , -0.0116, 0. , -0.0465, 0. ], + [-0.0465, -0.0116, 0.0698, 0. , -0.0116, 0. ], + [ 0. , 0. , 0. , 0.2326, -0.1163, -0.1163], + [ 0. , -0.0465, -0.0116, -0.1163, 0.2326, -0.0581], + [-0.0581, 0. , 0. , -0.1163, -0.0581, 0.2326]]) + # fmt: on + + L = nx.directed_combinatorial_laplacian_matrix( + G, alpha=0.9, nodelist=sorted(G), walk_type="random" + ) + np.testing.assert_almost_equal(L, GL, decimal=3) + + # fmt: off + GL = np.array([[ 0.0698, -0.0174, -0.0233, 0. , 0. , -0.0291], + [-0.0174, 0.0465, -0.0058, 0. , -0.0233, 0. ], + [-0.0233, -0.0058, 0.0349, 0. , -0.0058, 0. ], + [ 0. , 0. , 0. , 0.1163, -0.0581, -0.0581], + [ 0. , -0.0233, -0.0058, -0.0581, 0.1163, -0.0291], + [-0.0291, 0. , 0. , -0.0581, -0.0291, 0.1163]]) + # fmt: on + + L = nx.directed_combinatorial_laplacian_matrix( + G, alpha=0.9, nodelist=sorted(G), walk_type="lazy" + ) + np.testing.assert_almost_equal(L, GL, decimal=3) + + E = nx.DiGraph(margulis_gabber_galil_graph(2)) + L = nx.directed_combinatorial_laplacian_matrix(E) + # fmt: off + expected = np.array( + [[ 0.16666667, -0.08333333, -0.08333333, 0. ], + [-0.08333333, 0.16666667, 0. , -0.08333333], + [-0.08333333, 0. , 0.16666667, -0.08333333], + [ 0. , -0.08333333, -0.08333333, 0.16666667]] + ) + # fmt: on + np.testing.assert_almost_equal(L, expected, decimal=6) + + with pytest.raises(nx.NetworkXError): + nx.directed_combinatorial_laplacian_matrix(G, walk_type="pagerank", alpha=100) + with pytest.raises(nx.NetworkXError): + nx.directed_combinatorial_laplacian_matrix(G, walk_type="silly") diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_modularity.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_modularity.py new file mode 100644 index 0000000000000000000000000000000000000000..9f94ff4db33a427fa2f0ef51470bc1c57c8b8682 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_modularity.py @@ -0,0 +1,87 @@ +import pytest + +np = pytest.importorskip("numpy") +pytest.importorskip("scipy") + +import networkx as nx +from networkx.generators.degree_seq import havel_hakimi_graph + + +class TestModularity: + @classmethod + def setup_class(cls): + deg = [3, 2, 2, 1, 0] + cls.G = havel_hakimi_graph(deg) + # Graph used as an example in Sec. 4.1 of Langville and Meyer, + # "Google's PageRank and Beyond". (Used for test_directed_laplacian) + cls.DG = nx.DiGraph() + cls.DG.add_edges_from( + ( + (1, 2), + (1, 3), + (3, 1), + (3, 2), + (3, 5), + (4, 5), + (4, 6), + (5, 4), + (5, 6), + (6, 4), + ) + ) + + def test_modularity(self): + "Modularity matrix" + # fmt: off + B = np.array([[-1.125, 0.25, 0.25, 0.625, 0.], + [0.25, -0.5, 0.5, -0.25, 0.], + [0.25, 0.5, -0.5, -0.25, 0.], + [0.625, -0.25, -0.25, -0.125, 0.], + [0., 0., 0., 0., 0.]]) + # fmt: on + + permutation = [4, 0, 1, 2, 3] + np.testing.assert_equal(nx.modularity_matrix(self.G), B) + np.testing.assert_equal( + nx.modularity_matrix(self.G, nodelist=permutation), + B[np.ix_(permutation, permutation)], + ) + + def test_modularity_weight(self): + "Modularity matrix with weights" + # fmt: off + B = np.array([[-1.125, 0.25, 0.25, 0.625, 0.], + [0.25, -0.5, 0.5, -0.25, 0.], + [0.25, 0.5, -0.5, -0.25, 0.], + [0.625, -0.25, -0.25, -0.125, 0.], + [0., 0., 0., 0., 0.]]) + # fmt: on + + G_weighted = self.G.copy() + for n1, n2 in G_weighted.edges(): + G_weighted.edges[n1, n2]["weight"] = 0.5 + # The following test would fail in networkx 1.1 + np.testing.assert_equal(nx.modularity_matrix(G_weighted), B) + # The following test that the modularity matrix get rescaled accordingly + np.testing.assert_equal( + nx.modularity_matrix(G_weighted, weight="weight"), 0.5 * B + ) + + def test_directed_modularity(self): + "Directed Modularity matrix" + # fmt: off + B = np.array([[-0.2, 0.6, 0.8, -0.4, -0.4, -0.4], + [0., 0., 0., 0., 0., 0.], + [0.7, 0.4, -0.3, -0.6, 0.4, -0.6], + [-0.2, -0.4, -0.2, -0.4, 0.6, 0.6], + [-0.2, -0.4, -0.2, 0.6, -0.4, 0.6], + [-0.1, -0.2, -0.1, 0.8, -0.2, -0.2]]) + # fmt: on + node_permutation = [5, 1, 2, 3, 4, 6] + idx_permutation = [4, 0, 1, 2, 3, 5] + mm = nx.directed_modularity_matrix(self.DG, nodelist=sorted(self.DG)) + np.testing.assert_equal(mm, B) + np.testing.assert_equal( + nx.directed_modularity_matrix(self.DG, nodelist=node_permutation), + B[np.ix_(idx_permutation, idx_permutation)], + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_spectrum.py b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..e9101303cba60c56825101fa5762b56a3083e7af --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/linalg/tests/test_spectrum.py @@ -0,0 +1,71 @@ +import pytest + +np = pytest.importorskip("numpy") +pytest.importorskip("scipy") + +import networkx as nx +from networkx.generators.degree_seq import havel_hakimi_graph + + +class TestSpectrum: + @classmethod + def setup_class(cls): + deg = [3, 2, 2, 1, 0] + cls.G = havel_hakimi_graph(deg) + cls.P = nx.path_graph(3) + cls.WG = nx.Graph( + (u, v, {"weight": 0.5, "other": 0.3}) for (u, v) in cls.G.edges() + ) + cls.WG.add_node(4) + cls.DG = nx.DiGraph() + nx.add_path(cls.DG, [0, 1, 2]) + + def test_laplacian_spectrum(self): + "Laplacian eigenvalues" + evals = np.array([0, 0, 1, 3, 4]) + e = sorted(nx.laplacian_spectrum(self.G)) + np.testing.assert_almost_equal(e, evals) + e = sorted(nx.laplacian_spectrum(self.WG, weight=None)) + np.testing.assert_almost_equal(e, evals) + e = sorted(nx.laplacian_spectrum(self.WG)) + np.testing.assert_almost_equal(e, 0.5 * evals) + e = sorted(nx.laplacian_spectrum(self.WG, weight="other")) + np.testing.assert_almost_equal(e, 0.3 * evals) + + def test_normalized_laplacian_spectrum(self): + "Normalized Laplacian eigenvalues" + evals = np.array([0, 0, 0.7712864461218, 1.5, 1.7287135538781]) + e = sorted(nx.normalized_laplacian_spectrum(self.G)) + np.testing.assert_almost_equal(e, evals) + e = sorted(nx.normalized_laplacian_spectrum(self.WG, weight=None)) + np.testing.assert_almost_equal(e, evals) + e = sorted(nx.normalized_laplacian_spectrum(self.WG)) + np.testing.assert_almost_equal(e, evals) + e = sorted(nx.normalized_laplacian_spectrum(self.WG, weight="other")) + np.testing.assert_almost_equal(e, evals) + + def test_adjacency_spectrum(self): + "Adjacency eigenvalues" + evals = np.array([-np.sqrt(2), 0, np.sqrt(2)]) + e = sorted(nx.adjacency_spectrum(self.P)) + np.testing.assert_almost_equal(e, evals) + + def test_modularity_spectrum(self): + "Modularity eigenvalues" + evals = np.array([-1.5, 0.0, 0.0]) + e = sorted(nx.modularity_spectrum(self.P)) + np.testing.assert_almost_equal(e, evals) + # Directed modularity eigenvalues + evals = np.array([-0.5, 0.0, 0.0]) + e = sorted(nx.modularity_spectrum(self.DG)) + np.testing.assert_almost_equal(e, evals) + + def test_bethe_hessian_spectrum(self): + "Bethe Hessian eigenvalues" + evals = np.array([0.5 * (9 - np.sqrt(33)), 4, 0.5 * (9 + np.sqrt(33))]) + e = sorted(nx.bethe_hessian_spectrum(self.P, r=2)) + np.testing.assert_almost_equal(e, evals) + # Collapses back to Laplacian: + e1 = sorted(nx.bethe_hessian_spectrum(self.P, r=1)) + e2 = sorted(nx.laplacian_spectrum(self.P)) + np.testing.assert_almost_equal(e1, e2) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/adjlist.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/adjlist.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca45d85673c9c52f56887baf599e8d0976eea7c0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/adjlist.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/edgelist.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/edgelist.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69b1f07299c2d51229376fbc7f69ef8e1b39e760 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/edgelist.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/graphml.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/graphml.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4992f710bdff06da8f224f00012c1d777b5ab61a Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/graphml.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/leda.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/leda.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51761c3e28e676d9eb2d386d1551ce03754df0c3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/leda.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/p2g.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/p2g.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7577383ad2c2dd653ce3dbe095b3892cf3de3860 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/p2g.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/pajek.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/pajek.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce92f8439ea18ae7d72c613add49edf340647a06 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/pajek.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/sparse6.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/sparse6.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..008f962d116d039102051f9e745c0727a479ce68 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/__pycache__/sparse6.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/adjlist.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/adjlist.py new file mode 100644 index 0000000000000000000000000000000000000000..768af5ad73c148dc6300e66ecc4c440af6e231e8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/adjlist.py @@ -0,0 +1,310 @@ +""" +************** +Adjacency List +************** +Read and write NetworkX graphs as adjacency lists. + +Adjacency list format is useful for graphs without data associated +with nodes or edges and for nodes that can be meaningfully represented +as strings. + +Format +------ +The adjacency list format consists of lines with node labels. The +first label in a line is the source node. Further labels in the line +are considered target nodes and are added to the graph along with an edge +between the source node and target node. + +The graph with edges a-b, a-c, d-e can be represented as the following +adjacency list (anything following the # in a line is a comment):: + + a b c # source target target + d e +""" + +__all__ = ["generate_adjlist", "write_adjlist", "parse_adjlist", "read_adjlist"] + +import networkx as nx +from networkx.utils import open_file + + +def generate_adjlist(G, delimiter=" "): + """Generate a single line of the graph G in adjacency list format. + + Parameters + ---------- + G : NetworkX graph + + delimiter : string, optional + Separator for node labels + + Returns + ------- + lines : string + Lines of data in adjlist format. + + Examples + -------- + >>> G = nx.lollipop_graph(4, 3) + >>> for line in nx.generate_adjlist(G): + ... print(line) + 0 1 2 3 + 1 2 3 + 2 3 + 3 4 + 4 5 + 5 6 + 6 + + See Also + -------- + write_adjlist, read_adjlist + + Notes + ----- + The default `delimiter=" "` will result in unexpected results if node names contain + whitespace characters. To avoid this problem, specify an alternate delimiter when spaces are + valid in node names. + + NB: This option is not available for data that isn't user-generated. + + """ + directed = G.is_directed() + seen = set() + for s, nbrs in G.adjacency(): + line = str(s) + delimiter + for t, data in nbrs.items(): + if not directed and t in seen: + continue + if G.is_multigraph(): + for d in data.values(): + line += str(t) + delimiter + else: + line += str(t) + delimiter + if not directed: + seen.add(s) + yield line[: -len(delimiter)] + + +@open_file(1, mode="wb") +def write_adjlist(G, path, comments="#", delimiter=" ", encoding="utf-8"): + """Write graph G in single-line adjacency-list format to path. + + + Parameters + ---------- + G : NetworkX graph + + path : string or file + Filename or file handle for data output. + Filenames ending in .gz or .bz2 will be compressed. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels + + encoding : string, optional + Text encoding. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_adjlist(G, "test.adjlist") + + The path can be a filehandle or a string with the name of the file. If a + filehandle is provided, it has to be opened in 'wb' mode. + + >>> fh = open("test.adjlist", "wb") + >>> nx.write_adjlist(G, fh) + + Notes + ----- + The default `delimiter=" "` will result in unexpected results if node names contain + whitespace characters. To avoid this problem, specify an alternate delimiter when spaces are + valid in node names. + NB: This option is not available for data that isn't user-generated. + + This format does not store graph, node, or edge data. + + See Also + -------- + read_adjlist, generate_adjlist + """ + import sys + import time + + pargs = comments + " ".join(sys.argv) + "\n" + header = ( + pargs + + comments + + f" GMT {time.asctime(time.gmtime())}\n" + + comments + + f" {G.name}\n" + ) + path.write(header.encode(encoding)) + + for line in generate_adjlist(G, delimiter): + line += "\n" + path.write(line.encode(encoding)) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def parse_adjlist( + lines, comments="#", delimiter=None, create_using=None, nodetype=None +): + """Parse lines of a graph adjacency list representation. + + Parameters + ---------- + lines : list or iterator of strings + Input data in adjlist format + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + nodetype : Python type, optional + Convert nodes to this type. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels. The default is whitespace. + + Returns + ------- + G: NetworkX graph + The graph corresponding to the lines in adjacency list format. + + Examples + -------- + >>> lines = ["1 2 5", "2 3 4", "3 5", "4", "5"] + >>> G = nx.parse_adjlist(lines, nodetype=int) + >>> nodes = [1, 2, 3, 4, 5] + >>> all(node in G for node in nodes) + True + >>> edges = [(1, 2), (1, 5), (2, 3), (2, 4), (3, 5)] + >>> all((u, v) in G.edges() or (v, u) in G.edges() for (u, v) in edges) + True + + See Also + -------- + read_adjlist + + """ + G = nx.empty_graph(0, create_using) + for line in lines: + p = line.find(comments) + if p >= 0: + line = line[:p] + if not len(line): + continue + vlist = line.rstrip("\n").split(delimiter) + u = vlist.pop(0) + # convert types + if nodetype is not None: + try: + u = nodetype(u) + except BaseException as err: + raise TypeError( + f"Failed to convert node ({u}) to type {nodetype}" + ) from err + G.add_node(u) + if nodetype is not None: + try: + vlist = list(map(nodetype, vlist)) + except BaseException as err: + raise TypeError( + f"Failed to convert nodes ({','.join(vlist)}) to type {nodetype}" + ) from err + G.add_edges_from([(u, v) for v in vlist]) + return G + + +@open_file(0, mode="rb") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_adjlist( + path, + comments="#", + delimiter=None, + create_using=None, + nodetype=None, + encoding="utf-8", +): + """Read graph in adjacency list format from path. + + Parameters + ---------- + path : string or file + Filename or file handle to read. + Filenames ending in .gz or .bz2 will be uncompressed. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + nodetype : Python type, optional + Convert nodes to this type. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels. The default is whitespace. + + Returns + ------- + G: NetworkX graph + The graph corresponding to the lines in adjacency list format. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_adjlist(G, "test.adjlist") + >>> G = nx.read_adjlist("test.adjlist") + + The path can be a filehandle or a string with the name of the file. If a + filehandle is provided, it has to be opened in 'rb' mode. + + >>> fh = open("test.adjlist", "rb") + >>> G = nx.read_adjlist(fh) + + Filenames ending in .gz or .bz2 will be compressed. + + >>> nx.write_adjlist(G, "test.adjlist.gz") + >>> G = nx.read_adjlist("test.adjlist.gz") + + The optional nodetype is a function to convert node strings to nodetype. + + For example + + >>> G = nx.read_adjlist("test.adjlist", nodetype=int) + + will attempt to convert all nodes to integer type. + + Since nodes must be hashable, the function nodetype must return hashable + types (e.g. int, float, str, frozenset - or tuples of those, etc.) + + The optional create_using parameter indicates the type of NetworkX graph + created. The default is `nx.Graph`, an undirected graph. + To read the data as a directed graph use + + >>> G = nx.read_adjlist("test.adjlist", create_using=nx.DiGraph) + + Notes + ----- + This format does not store graph or node data. + + See Also + -------- + write_adjlist + """ + lines = (line.decode(encoding) for line in path) + return parse_adjlist( + lines, + comments=comments, + delimiter=delimiter, + create_using=create_using, + nodetype=nodetype, + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/edgelist.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/edgelist.py new file mode 100644 index 0000000000000000000000000000000000000000..393b64ed7fec4a632fcaded6cb25fd7a6393a0bf --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/edgelist.py @@ -0,0 +1,489 @@ +""" +********** +Edge Lists +********** +Read and write NetworkX graphs as edge lists. + +The multi-line adjacency list format is useful for graphs with nodes +that can be meaningfully represented as strings. With the edgelist +format simple edge data can be stored but node or graph data is not. +There is no way of representing isolated nodes unless the node has a +self-loop edge. + +Format +------ +You can read or write three formats of edge lists with these functions. + +Node pairs with no data:: + + 1 2 + +Python dictionary as data:: + + 1 2 {'weight':7, 'color':'green'} + +Arbitrary data:: + + 1 2 7 green +""" + +__all__ = [ + "generate_edgelist", + "write_edgelist", + "parse_edgelist", + "read_edgelist", + "read_weighted_edgelist", + "write_weighted_edgelist", +] + +import networkx as nx +from networkx.utils import open_file + + +def generate_edgelist(G, delimiter=" ", data=True): + """Generate a single line of the graph G in edge list format. + + Parameters + ---------- + G : NetworkX graph + + delimiter : string, optional + Separator for node labels + + data : bool or list of keys + If False generate no edge data. If True use a dictionary + representation of edge data. If a list of keys use a list of data + values corresponding to the keys. + + Returns + ------- + lines : string + Lines of data in adjlist format. + + Examples + -------- + >>> G = nx.lollipop_graph(4, 3) + >>> G[1][2]["weight"] = 3 + >>> G[3][4]["capacity"] = 12 + >>> for line in nx.generate_edgelist(G, data=False): + ... print(line) + 0 1 + 0 2 + 0 3 + 1 2 + 1 3 + 2 3 + 3 4 + 4 5 + 5 6 + + >>> for line in nx.generate_edgelist(G): + ... print(line) + 0 1 {} + 0 2 {} + 0 3 {} + 1 2 {'weight': 3} + 1 3 {} + 2 3 {} + 3 4 {'capacity': 12} + 4 5 {} + 5 6 {} + + >>> for line in nx.generate_edgelist(G, data=["weight"]): + ... print(line) + 0 1 + 0 2 + 0 3 + 1 2 3 + 1 3 + 2 3 + 3 4 + 4 5 + 5 6 + + See Also + -------- + write_adjlist, read_adjlist + """ + if data is True: + for u, v, d in G.edges(data=True): + e = u, v, dict(d) + yield delimiter.join(map(str, e)) + elif data is False: + for u, v in G.edges(data=False): + e = u, v + yield delimiter.join(map(str, e)) + else: + for u, v, d in G.edges(data=True): + e = [u, v] + try: + e.extend(d[k] for k in data) + except KeyError: + pass # missing data for this edge, should warn? + yield delimiter.join(map(str, e)) + + +@open_file(1, mode="wb") +def write_edgelist(G, path, comments="#", delimiter=" ", data=True, encoding="utf-8"): + """Write graph as a list of edges. + + Parameters + ---------- + G : graph + A NetworkX graph + path : file or string + File or filename to write. If a file is provided, it must be + opened in 'wb' mode. Filenames ending in .gz or .bz2 will be compressed. + comments : string, optional + The character used to indicate the start of a comment + delimiter : string, optional + The string used to separate values. The default is whitespace. + data : bool or list, optional + If False write no edge data. + If True write a string representation of the edge data dictionary.. + If a list (or other iterable) is provided, write the keys specified + in the list. + encoding: string, optional + Specify which encoding to use when writing file. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_edgelist(G, "test.edgelist") + >>> G = nx.path_graph(4) + >>> fh = open("test.edgelist", "wb") + >>> nx.write_edgelist(G, fh) + >>> nx.write_edgelist(G, "test.edgelist.gz") + >>> nx.write_edgelist(G, "test.edgelist.gz", data=False) + + >>> G = nx.Graph() + >>> G.add_edge(1, 2, weight=7, color="red") + >>> nx.write_edgelist(G, "test.edgelist", data=False) + >>> nx.write_edgelist(G, "test.edgelist", data=["color"]) + >>> nx.write_edgelist(G, "test.edgelist", data=["color", "weight"]) + + See Also + -------- + read_edgelist + write_weighted_edgelist + """ + + for line in generate_edgelist(G, delimiter, data): + line += "\n" + path.write(line.encode(encoding)) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def parse_edgelist( + lines, comments="#", delimiter=None, create_using=None, nodetype=None, data=True +): + """Parse lines of an edge list representation of a graph. + + Parameters + ---------- + lines : list or iterator of strings + Input data in edgelist format + comments : string, optional + Marker for comment lines. Default is `'#'`. To specify that no character + should be treated as a comment, use ``comments=None``. + delimiter : string, optional + Separator for node labels. Default is `None`, meaning any whitespace. + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + nodetype : Python type, optional + Convert nodes to this type. Default is `None`, meaning no conversion is + performed. + data : bool or list of (label,type) tuples + If `False` generate no edge data or if `True` use a dictionary + representation of edge data or a list tuples specifying dictionary + key names and types for edge data. + + Returns + ------- + G: NetworkX Graph + The graph corresponding to lines + + Examples + -------- + Edgelist with no data: + + >>> lines = ["1 2", "2 3", "3 4"] + >>> G = nx.parse_edgelist(lines, nodetype=int) + >>> list(G) + [1, 2, 3, 4] + >>> list(G.edges()) + [(1, 2), (2, 3), (3, 4)] + + Edgelist with data in Python dictionary representation: + + >>> lines = ["1 2 {'weight': 3}", "2 3 {'weight': 27}", "3 4 {'weight': 3.0}"] + >>> G = nx.parse_edgelist(lines, nodetype=int) + >>> list(G) + [1, 2, 3, 4] + >>> list(G.edges(data=True)) + [(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})] + + Edgelist with data in a list: + + >>> lines = ["1 2 3", "2 3 27", "3 4 3.0"] + >>> G = nx.parse_edgelist(lines, nodetype=int, data=(("weight", float),)) + >>> list(G) + [1, 2, 3, 4] + >>> list(G.edges(data=True)) + [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})] + + See Also + -------- + read_weighted_edgelist + """ + from ast import literal_eval + + G = nx.empty_graph(0, create_using) + for line in lines: + if comments is not None: + p = line.find(comments) + if p >= 0: + line = line[:p] + if not line: + continue + # split line, should have 2 or more + s = line.rstrip("\n").split(delimiter) + if len(s) < 2: + continue + u = s.pop(0) + v = s.pop(0) + d = s + if nodetype is not None: + try: + u = nodetype(u) + v = nodetype(v) + except Exception as err: + raise TypeError( + f"Failed to convert nodes {u},{v} to type {nodetype}." + ) from err + + if len(d) == 0 or data is False: + # no data or data type specified + edgedata = {} + elif data is True: + # no edge types specified + try: # try to evaluate as dictionary + if delimiter == ",": + edgedata_str = ",".join(d) + else: + edgedata_str = " ".join(d) + edgedata = dict(literal_eval(edgedata_str.strip())) + except Exception as err: + raise TypeError( + f"Failed to convert edge data ({d}) to dictionary." + ) from err + else: + # convert edge data to dictionary with specified keys and type + if len(d) != len(data): + raise IndexError( + f"Edge data {d} and data_keys {data} are not the same length" + ) + edgedata = {} + for (edge_key, edge_type), edge_value in zip(data, d): + try: + edge_value = edge_type(edge_value) + except Exception as err: + raise TypeError( + f"Failed to convert {edge_key} data {edge_value} " + f"to type {edge_type}." + ) from err + edgedata.update({edge_key: edge_value}) + G.add_edge(u, v, **edgedata) + return G + + +@open_file(0, mode="rb") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_edgelist( + path, + comments="#", + delimiter=None, + create_using=None, + nodetype=None, + data=True, + edgetype=None, + encoding="utf-8", +): + """Read a graph from a list of edges. + + Parameters + ---------- + path : file or string + File or filename to read. If a file is provided, it must be + opened in 'rb' mode. + Filenames ending in .gz or .bz2 will be uncompressed. + comments : string, optional + The character used to indicate the start of a comment. To specify that + no character should be treated as a comment, use ``comments=None``. + delimiter : string, optional + The string used to separate values. The default is whitespace. + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + nodetype : int, float, str, Python type, optional + Convert node data from strings to specified type + data : bool or list of (label,type) tuples + Tuples specifying dictionary key names and types for edge data + edgetype : int, float, str, Python type, optional OBSOLETE + Convert edge data from strings to specified type and use as 'weight' + encoding: string, optional + Specify which encoding to use when reading file. + + Returns + ------- + G : graph + A networkx Graph or other type specified with create_using + + Examples + -------- + >>> nx.write_edgelist(nx.path_graph(4), "test.edgelist") + >>> G = nx.read_edgelist("test.edgelist") + + >>> fh = open("test.edgelist", "rb") + >>> G = nx.read_edgelist(fh) + >>> fh.close() + + >>> G = nx.read_edgelist("test.edgelist", nodetype=int) + >>> G = nx.read_edgelist("test.edgelist", create_using=nx.DiGraph) + + Edgelist with data in a list: + + >>> textline = "1 2 3" + >>> fh = open("test.edgelist", "w") + >>> d = fh.write(textline) + >>> fh.close() + >>> G = nx.read_edgelist("test.edgelist", nodetype=int, data=(("weight", float),)) + >>> list(G) + [1, 2] + >>> list(G.edges(data=True)) + [(1, 2, {'weight': 3.0})] + + See parse_edgelist() for more examples of formatting. + + See Also + -------- + parse_edgelist + write_edgelist + + Notes + ----- + Since nodes must be hashable, the function nodetype must return hashable + types (e.g. int, float, str, frozenset - or tuples of those, etc.) + """ + lines = (line if isinstance(line, str) else line.decode(encoding) for line in path) + return parse_edgelist( + lines, + comments=comments, + delimiter=delimiter, + create_using=create_using, + nodetype=nodetype, + data=data, + ) + + +def write_weighted_edgelist(G, path, comments="#", delimiter=" ", encoding="utf-8"): + """Write graph G as a list of edges with numeric weights. + + Parameters + ---------- + G : graph + A NetworkX graph + path : file or string + File or filename to write. If a file is provided, it must be + opened in 'wb' mode. + Filenames ending in .gz or .bz2 will be compressed. + comments : string, optional + The character used to indicate the start of a comment + delimiter : string, optional + The string used to separate values. The default is whitespace. + encoding: string, optional + Specify which encoding to use when writing file. + + Examples + -------- + >>> G = nx.Graph() + >>> G.add_edge(1, 2, weight=7) + >>> nx.write_weighted_edgelist(G, "test.weighted.edgelist") + + See Also + -------- + read_edgelist + write_edgelist + read_weighted_edgelist + """ + write_edgelist( + G, + path, + comments=comments, + delimiter=delimiter, + data=("weight",), + encoding=encoding, + ) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def read_weighted_edgelist( + path, + comments="#", + delimiter=None, + create_using=None, + nodetype=None, + encoding="utf-8", +): + """Read a graph as list of edges with numeric weights. + + Parameters + ---------- + path : file or string + File or filename to read. If a file is provided, it must be + opened in 'rb' mode. + Filenames ending in .gz or .bz2 will be uncompressed. + comments : string, optional + The character used to indicate the start of a comment. + delimiter : string, optional + The string used to separate values. The default is whitespace. + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + nodetype : int, float, str, Python type, optional + Convert node data from strings to specified type + encoding: string, optional + Specify which encoding to use when reading file. + + Returns + ------- + G : graph + A networkx Graph or other type specified with create_using + + Notes + ----- + Since nodes must be hashable, the function nodetype must return hashable + types (e.g. int, float, str, frozenset - or tuples of those, etc.) + + Example edgelist file format. + + With numeric edge data:: + + # read with + # >>> G=nx.read_weighted_edgelist(fh) + # source target data + a b 1 + a c 3.14159 + d e 42 + + See Also + -------- + write_weighted_edgelist + """ + return read_edgelist( + path, + comments=comments, + delimiter=delimiter, + create_using=create_using, + nodetype=nodetype, + data=(("weight", float),), + encoding=encoding, + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/gml.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/gml.py new file mode 100644 index 0000000000000000000000000000000000000000..891d709685e8b119abb1db0d5489fc14e657a579 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/gml.py @@ -0,0 +1,879 @@ +""" +Read graphs in GML format. + +"GML, the Graph Modelling Language, is our proposal for a portable +file format for graphs. GML's key features are portability, simple +syntax, extensibility and flexibility. A GML file consists of a +hierarchical key-value lists. Graphs can be annotated with arbitrary +data structures. The idea for a common file format was born at the +GD'95; this proposal is the outcome of many discussions. GML is the +standard file format in the Graphlet graph editor system. It has been +overtaken and adapted by several other systems for drawing graphs." + +GML files are stored using a 7-bit ASCII encoding with any extended +ASCII characters (iso8859-1) appearing as HTML character entities. +You will need to give some thought into how the exported data should +interact with different languages and even different Python versions. +Re-importing from gml is also a concern. + +Without specifying a `stringizer`/`destringizer`, the code is capable of +writing `int`/`float`/`str`/`dict`/`list` data as required by the GML +specification. For writing other data types, and for reading data other +than `str` you need to explicitly supply a `stringizer`/`destringizer`. + +For additional documentation on the GML file format, please see the +`GML website `_. + +Several example graphs in GML format may be found on Mark Newman's +`Network data page `_. +""" + +import html.entities as htmlentitydefs +import re +import warnings +from ast import literal_eval +from collections import defaultdict +from enum import Enum +from io import StringIO +from typing import Any, NamedTuple + +import networkx as nx +from networkx.exception import NetworkXError +from networkx.utils import open_file + +__all__ = ["read_gml", "parse_gml", "generate_gml", "write_gml"] + + +def escape(text): + """Use XML character references to escape characters. + + Use XML character references for unprintable or non-ASCII + characters, double quotes and ampersands in a string + """ + + def fixup(m): + ch = m.group(0) + return "&#" + str(ord(ch)) + ";" + + text = re.sub('[^ -~]|[&"]', fixup, text) + return text if isinstance(text, str) else str(text) + + +def unescape(text): + """Replace XML character references with the referenced characters""" + + def fixup(m): + text = m.group(0) + if text[1] == "#": + # Character reference + if text[2] == "x": + code = int(text[3:-1], 16) + else: + code = int(text[2:-1]) + else: + # Named entity + try: + code = htmlentitydefs.name2codepoint[text[1:-1]] + except KeyError: + return text # leave unchanged + try: + return chr(code) + except (ValueError, OverflowError): + return text # leave unchanged + + return re.sub("&(?:[0-9A-Za-z]+|#(?:[0-9]+|x[0-9A-Fa-f]+));", fixup, text) + + +def literal_destringizer(rep): + """Convert a Python literal to the value it represents. + + Parameters + ---------- + rep : string + A Python literal. + + Returns + ------- + value : object + The value of the Python literal. + + Raises + ------ + ValueError + If `rep` is not a Python literal. + """ + if isinstance(rep, str): + orig_rep = rep + try: + return literal_eval(rep) + except SyntaxError as err: + raise ValueError(f"{orig_rep!r} is not a valid Python literal") from err + else: + raise ValueError(f"{rep!r} is not a string") + + +@open_file(0, mode="rb") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_gml(path, label="label", destringizer=None): + """Read graph in GML format from `path`. + + Parameters + ---------- + path : filename or filehandle + The filename or filehandle to read from. + + label : string, optional + If not None, the parsed nodes will be renamed according to node + attributes indicated by `label`. Default value: 'label'. + + destringizer : callable, optional + A `destringizer` that recovers values stored as strings in GML. If it + cannot convert a string to a value, a `ValueError` is raised. Default + value : None. + + Returns + ------- + G : NetworkX graph + The parsed graph. + + Raises + ------ + NetworkXError + If the input cannot be parsed. + + See Also + -------- + write_gml, parse_gml + literal_destringizer + + Notes + ----- + GML files are stored using a 7-bit ASCII encoding with any extended + ASCII characters (iso8859-1) appearing as HTML character entities. + Without specifying a `stringizer`/`destringizer`, the code is capable of + writing `int`/`float`/`str`/`dict`/`list` data as required by the GML + specification. For writing other data types, and for reading data other + than `str` you need to explicitly supply a `stringizer`/`destringizer`. + + For additional documentation on the GML file format, please see the + `GML url `_. + + See the module docstring :mod:`networkx.readwrite.gml` for more details. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_gml(G, "test.gml") + + GML values are interpreted as strings by default: + + >>> H = nx.read_gml("test.gml") + >>> H.nodes + NodeView(('0', '1', '2', '3')) + + When a `destringizer` is provided, GML values are converted to the provided type. + For example, integer nodes can be recovered as shown below: + + >>> J = nx.read_gml("test.gml", destringizer=int) + >>> J.nodes + NodeView((0, 1, 2, 3)) + + """ + + def filter_lines(lines): + for line in lines: + try: + line = line.decode("ascii") + except UnicodeDecodeError as err: + raise NetworkXError("input is not ASCII-encoded") from err + if not isinstance(line, str): + lines = str(lines) + if line and line[-1] == "\n": + line = line[:-1] + yield line + + G = parse_gml_lines(filter_lines(path), label, destringizer) + return G + + +@nx._dispatchable(graphs=None, returns_graph=True) +def parse_gml(lines, label="label", destringizer=None): + """Parse GML graph from a string or iterable. + + Parameters + ---------- + lines : string or iterable of strings + Data in GML format. + + label : string, optional + If not None, the parsed nodes will be renamed according to node + attributes indicated by `label`. Default value: 'label'. + + destringizer : callable, optional + A `destringizer` that recovers values stored as strings in GML. If it + cannot convert a string to a value, a `ValueError` is raised. Default + value : None. + + Returns + ------- + G : NetworkX graph + The parsed graph. + + Raises + ------ + NetworkXError + If the input cannot be parsed. + + See Also + -------- + write_gml, read_gml + + Notes + ----- + This stores nested GML attributes as dictionaries in the NetworkX graph, + node, and edge attribute structures. + + GML files are stored using a 7-bit ASCII encoding with any extended + ASCII characters (iso8859-1) appearing as HTML character entities. + Without specifying a `stringizer`/`destringizer`, the code is capable of + writing `int`/`float`/`str`/`dict`/`list` data as required by the GML + specification. For writing other data types, and for reading data other + than `str` you need to explicitly supply a `stringizer`/`destringizer`. + + For additional documentation on the GML file format, please see the + `GML url `_. + + See the module docstring :mod:`networkx.readwrite.gml` for more details. + """ + + def decode_line(line): + if isinstance(line, bytes): + try: + line.decode("ascii") + except UnicodeDecodeError as err: + raise NetworkXError("input is not ASCII-encoded") from err + if not isinstance(line, str): + line = str(line) + return line + + def filter_lines(lines): + if isinstance(lines, str): + lines = decode_line(lines) + lines = lines.splitlines() + yield from lines + else: + for line in lines: + line = decode_line(line) + if line and line[-1] == "\n": + line = line[:-1] + if line.find("\n") != -1: + raise NetworkXError("input line contains newline") + yield line + + G = parse_gml_lines(filter_lines(lines), label, destringizer) + return G + + +class Pattern(Enum): + """encodes the index of each token-matching pattern in `tokenize`.""" + + KEYS = 0 + REALS = 1 + INTS = 2 + STRINGS = 3 + DICT_START = 4 + DICT_END = 5 + COMMENT_WHITESPACE = 6 + + +class Token(NamedTuple): + category: Pattern + value: Any + line: int + position: int + + +LIST_START_VALUE = "_networkx_list_start" + + +def parse_gml_lines(lines, label, destringizer): + """Parse GML `lines` into a graph.""" + + def tokenize(): + patterns = [ + r"[A-Za-z][0-9A-Za-z_]*\b", # keys + # reals + r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|INF)(?:[Ee][+-]?[0-9]+)?", + r"[+-]?[0-9]+", # ints + r'".*?"', # strings + r"\[", # dict start + r"\]", # dict end + r"#.*$|\s+", # comments and whitespaces + ] + tokens = re.compile("|".join(f"({pattern})" for pattern in patterns)) + lineno = 0 + multilines = [] # entries spread across multiple lines + for line in lines: + pos = 0 + + # deal with entries spread across multiple lines + # + # should we actually have to deal with escaped "s then do it here + if multilines: + multilines.append(line.strip()) + if line[-1] == '"': # closing multiline entry + # multiline entries will be joined by space. cannot + # reintroduce newlines as this will break the tokenizer + line = " ".join(multilines) + multilines = [] + else: # continued multiline entry + lineno += 1 + continue + else: + if line.count('"') == 1: # opening multiline entry + if line.strip()[0] != '"' and line.strip()[-1] != '"': + # since we expect something like key "value", the " should not be found at ends + # otherwise tokenizer will pick up the formatting mistake. + multilines = [line.rstrip()] + lineno += 1 + continue + + length = len(line) + + while pos < length: + match = tokens.match(line, pos) + if match is None: + m = f"cannot tokenize {line[pos:]} at ({lineno + 1}, {pos + 1})" + raise NetworkXError(m) + for i in range(len(patterns)): + group = match.group(i + 1) + if group is not None: + if i == 0: # keys + value = group.rstrip() + elif i == 1: # reals + value = float(group) + elif i == 2: # ints + value = int(group) + else: + value = group + if i != 6: # comments and whitespaces + yield Token(Pattern(i), value, lineno + 1, pos + 1) + pos += len(group) + break + lineno += 1 + yield Token(None, None, lineno + 1, 1) # EOF + + def unexpected(curr_token, expected): + category, value, lineno, pos = curr_token + value = repr(value) if value is not None else "EOF" + raise NetworkXError(f"expected {expected}, found {value} at ({lineno}, {pos})") + + def consume(curr_token, category, expected): + if curr_token.category == category: + return next(tokens) + unexpected(curr_token, expected) + + def parse_kv(curr_token): + dct = defaultdict(list) + while curr_token.category == Pattern.KEYS: + key = curr_token.value + curr_token = next(tokens) + category = curr_token.category + if category == Pattern.REALS or category == Pattern.INTS: + value = curr_token.value + curr_token = next(tokens) + elif category == Pattern.STRINGS: + value = unescape(curr_token.value[1:-1]) + if destringizer: + try: + value = destringizer(value) + except ValueError: + pass + # Special handling for empty lists and tuples + if value == "()": + value = () + if value == "[]": + value = [] + curr_token = next(tokens) + elif category == Pattern.DICT_START: + curr_token, value = parse_dict(curr_token) + else: + # Allow for string convertible id and label values + if key in ("id", "label", "source", "target"): + try: + # String convert the token value + value = unescape(str(curr_token.value)) + if destringizer: + try: + value = destringizer(value) + except ValueError: + pass + curr_token = next(tokens) + except Exception: + msg = ( + "an int, float, string, '[' or string" + + " convertible ASCII value for node id or label" + ) + unexpected(curr_token, msg) + # Special handling for nan and infinity. Since the gml language + # defines unquoted strings as keys, the numeric and string branches + # are skipped and we end up in this special branch, so we need to + # convert the current token value to a float for NAN and plain INF. + # +/-INF are handled in the pattern for 'reals' in tokenize(). This + # allows labels and values to be nan or infinity, but not keys. + elif curr_token.value in {"NAN", "INF"}: + value = float(curr_token.value) + curr_token = next(tokens) + else: # Otherwise error out + unexpected(curr_token, "an int, float, string or '['") + dct[key].append(value) + + def clean_dict_value(value): + if not isinstance(value, list): + return value + if len(value) == 1: + return value[0] + if value[0] == LIST_START_VALUE: + return value[1:] + return value + + dct = {key: clean_dict_value(value) for key, value in dct.items()} + return curr_token, dct + + def parse_dict(curr_token): + # dict start + curr_token = consume(curr_token, Pattern.DICT_START, "'['") + # dict contents + curr_token, dct = parse_kv(curr_token) + # dict end + curr_token = consume(curr_token, Pattern.DICT_END, "']'") + return curr_token, dct + + def parse_graph(): + curr_token, dct = parse_kv(next(tokens)) + if curr_token.category is not None: # EOF + unexpected(curr_token, "EOF") + if "graph" not in dct: + raise NetworkXError("input contains no graph") + graph = dct["graph"] + if isinstance(graph, list): + raise NetworkXError("input contains more than one graph") + return graph + + tokens = tokenize() + graph = parse_graph() + + directed = graph.pop("directed", False) + multigraph = graph.pop("multigraph", False) + if not multigraph: + G = nx.DiGraph() if directed else nx.Graph() + else: + G = nx.MultiDiGraph() if directed else nx.MultiGraph() + graph_attr = {k: v for k, v in graph.items() if k not in ("node", "edge")} + G.graph.update(graph_attr) + + def pop_attr(dct, category, attr, i): + try: + return dct.pop(attr) + except KeyError as err: + raise NetworkXError(f"{category} #{i} has no {attr!r} attribute") from err + + nodes = graph.get("node", []) + mapping = {} + node_labels = set() + for i, node in enumerate(nodes if isinstance(nodes, list) else [nodes]): + id = pop_attr(node, "node", "id", i) + if id in G: + raise NetworkXError(f"node id {id!r} is duplicated") + if label is not None and label != "id": + node_label = pop_attr(node, "node", label, i) + if node_label in node_labels: + raise NetworkXError(f"node label {node_label!r} is duplicated") + node_labels.add(node_label) + mapping[id] = node_label + G.add_node(id, **node) + + edges = graph.get("edge", []) + for i, edge in enumerate(edges if isinstance(edges, list) else [edges]): + source = pop_attr(edge, "edge", "source", i) + target = pop_attr(edge, "edge", "target", i) + if source not in G: + raise NetworkXError(f"edge #{i} has undefined source {source!r}") + if target not in G: + raise NetworkXError(f"edge #{i} has undefined target {target!r}") + if not multigraph: + if not G.has_edge(source, target): + G.add_edge(source, target, **edge) + else: + arrow = "->" if directed else "--" + msg = f"edge #{i} ({source!r}{arrow}{target!r}) is duplicated" + raise nx.NetworkXError(msg) + else: + key = edge.pop("key", None) + if key is not None and G.has_edge(source, target, key): + arrow = "->" if directed else "--" + msg = f"edge #{i} ({source!r}{arrow}{target!r}, {key!r})" + msg2 = 'Hint: If multigraph add "multigraph 1" to file header.' + raise nx.NetworkXError(msg + " is duplicated\n" + msg2) + G.add_edge(source, target, key, **edge) + + if label is not None and label != "id": + G = nx.relabel_nodes(G, mapping) + return G + + +def literal_stringizer(value): + """Convert a `value` to a Python literal in GML representation. + + Parameters + ---------- + value : object + The `value` to be converted to GML representation. + + Returns + ------- + rep : string + A double-quoted Python literal representing value. Unprintable + characters are replaced by XML character references. + + Raises + ------ + ValueError + If `value` cannot be converted to GML. + + Notes + ----- + The original value can be recovered using the + :func:`networkx.readwrite.gml.literal_destringizer` function. + """ + + def stringize(value): + if isinstance(value, int | bool) or value is None: + if value is True: # GML uses 1/0 for boolean values. + buf.write(str(1)) + elif value is False: + buf.write(str(0)) + else: + buf.write(str(value)) + elif isinstance(value, str): + text = repr(value) + if text[0] != "u": + try: + value.encode("latin1") + except UnicodeEncodeError: + text = "u" + text + buf.write(text) + elif isinstance(value, float | complex | str | bytes): + buf.write(repr(value)) + elif isinstance(value, list): + buf.write("[") + first = True + for item in value: + if not first: + buf.write(",") + else: + first = False + stringize(item) + buf.write("]") + elif isinstance(value, tuple): + if len(value) > 1: + buf.write("(") + first = True + for item in value: + if not first: + buf.write(",") + else: + first = False + stringize(item) + buf.write(")") + elif value: + buf.write("(") + stringize(value[0]) + buf.write(",)") + else: + buf.write("()") + elif isinstance(value, dict): + buf.write("{") + first = True + for key, value in value.items(): + if not first: + buf.write(",") + else: + first = False + stringize(key) + buf.write(":") + stringize(value) + buf.write("}") + elif isinstance(value, set): + buf.write("{") + first = True + for item in value: + if not first: + buf.write(",") + else: + first = False + stringize(item) + buf.write("}") + else: + msg = f"{value!r} cannot be converted into a Python literal" + raise ValueError(msg) + + buf = StringIO() + stringize(value) + return buf.getvalue() + + +def generate_gml(G, stringizer=None): + r"""Generate a single entry of the graph `G` in GML format. + + Parameters + ---------- + G : NetworkX graph + The graph to be converted to GML. + + stringizer : callable, optional + A `stringizer` which converts non-int/non-float/non-dict values into + strings. If it cannot convert a value into a string, it should raise a + `ValueError` to indicate that. Default value: None. + + Returns + ------- + lines: generator of strings + Lines of GML data. Newlines are not appended. + + Raises + ------ + NetworkXError + If `stringizer` cannot convert a value into a string, or the value to + convert is not a string while `stringizer` is None. + + See Also + -------- + literal_stringizer + + Notes + ----- + Graph attributes named 'directed', 'multigraph', 'node' or + 'edge', node attributes named 'id' or 'label', edge attributes + named 'source' or 'target' (or 'key' if `G` is a multigraph) + are ignored because these attribute names are used to encode the graph + structure. + + GML files are stored using a 7-bit ASCII encoding with any extended + ASCII characters (iso8859-1) appearing as HTML character entities. + Without specifying a `stringizer`/`destringizer`, the code is capable of + writing `int`/`float`/`str`/`dict`/`list` data as required by the GML + specification. For writing other data types, and for reading data other + than `str` you need to explicitly supply a `stringizer`/`destringizer`. + + For additional documentation on the GML file format, please see the + `GML url `_. + + See the module docstring :mod:`networkx.readwrite.gml` for more details. + + Examples + -------- + >>> G = nx.Graph() + >>> G.add_node("1") + >>> print("\n".join(nx.generate_gml(G))) + graph [ + node [ + id 0 + label "1" + ] + ] + >>> G = nx.MultiGraph([("a", "b"), ("a", "b")]) + >>> print("\n".join(nx.generate_gml(G))) + graph [ + multigraph 1 + node [ + id 0 + label "a" + ] + node [ + id 1 + label "b" + ] + edge [ + source 0 + target 1 + key 0 + ] + edge [ + source 0 + target 1 + key 1 + ] + ] + """ + valid_keys = re.compile("^[A-Za-z][0-9A-Za-z_]*$") + + def stringize(key, value, ignored_keys, indent, in_list=False): + if not isinstance(key, str): + raise NetworkXError(f"{key!r} is not a string") + if not valid_keys.match(key): + raise NetworkXError(f"{key!r} is not a valid key") + if not isinstance(key, str): + key = str(key) + if key not in ignored_keys: + if isinstance(value, int | bool): + if key == "label": + yield indent + key + ' "' + str(value) + '"' + elif value is True: + # python bool is an instance of int + yield indent + key + " 1" + elif value is False: + yield indent + key + " 0" + # GML only supports signed 32-bit integers + elif value < -(2**31) or value >= 2**31: + yield indent + key + ' "' + str(value) + '"' + else: + yield indent + key + " " + str(value) + elif isinstance(value, float): + text = repr(value).upper() + # GML matches INF to keys, so prepend + to INF. Use repr(float(*)) + # instead of string literal to future proof against changes to repr. + if text == repr(float("inf")).upper(): + text = "+" + text + else: + # GML requires that a real literal contain a decimal point, but + # repr may not output a decimal point when the mantissa is + # integral and hence needs fixing. + epos = text.rfind("E") + if epos != -1 and text.find(".", 0, epos) == -1: + text = text[:epos] + "." + text[epos:] + if key == "label": + yield indent + key + ' "' + text + '"' + else: + yield indent + key + " " + text + elif isinstance(value, dict): + yield indent + key + " [" + next_indent = indent + " " + for key, value in value.items(): + yield from stringize(key, value, (), next_indent) + yield indent + "]" + elif isinstance(value, tuple) and key == "label": + yield indent + key + f" \"({','.join(repr(v) for v in value)})\"" + elif isinstance(value, list | tuple) and key != "label" and not in_list: + if len(value) == 0: + yield indent + key + " " + f'"{value!r}"' + if len(value) == 1: + yield indent + key + " " + f'"{LIST_START_VALUE}"' + for val in value: + yield from stringize(key, val, (), indent, True) + else: + if stringizer: + try: + value = stringizer(value) + except ValueError as err: + raise NetworkXError( + f"{value!r} cannot be converted into a string" + ) from err + if not isinstance(value, str): + raise NetworkXError(f"{value!r} is not a string") + yield indent + key + ' "' + escape(value) + '"' + + multigraph = G.is_multigraph() + yield "graph [" + + # Output graph attributes + if G.is_directed(): + yield " directed 1" + if multigraph: + yield " multigraph 1" + ignored_keys = {"directed", "multigraph", "node", "edge"} + for attr, value in G.graph.items(): + yield from stringize(attr, value, ignored_keys, " ") + + # Output node data + node_id = dict(zip(G, range(len(G)))) + ignored_keys = {"id", "label"} + for node, attrs in G.nodes.items(): + yield " node [" + yield " id " + str(node_id[node]) + yield from stringize("label", node, (), " ") + for attr, value in attrs.items(): + yield from stringize(attr, value, ignored_keys, " ") + yield " ]" + + # Output edge data + ignored_keys = {"source", "target"} + kwargs = {"data": True} + if multigraph: + ignored_keys.add("key") + kwargs["keys"] = True + for e in G.edges(**kwargs): + yield " edge [" + yield " source " + str(node_id[e[0]]) + yield " target " + str(node_id[e[1]]) + if multigraph: + yield from stringize("key", e[2], (), " ") + for attr, value in e[-1].items(): + yield from stringize(attr, value, ignored_keys, " ") + yield " ]" + yield "]" + + +@open_file(1, mode="wb") +def write_gml(G, path, stringizer=None): + """Write a graph `G` in GML format to the file or file handle `path`. + + Parameters + ---------- + G : NetworkX graph + The graph to be converted to GML. + + path : filename or filehandle + The filename or filehandle to write. Files whose names end with .gz or + .bz2 will be compressed. + + stringizer : callable, optional + A `stringizer` which converts non-int/non-float/non-dict values into + strings. If it cannot convert a value into a string, it should raise a + `ValueError` to indicate that. Default value: None. + + Raises + ------ + NetworkXError + If `stringizer` cannot convert a value into a string, or the value to + convert is not a string while `stringizer` is None. + + See Also + -------- + read_gml, generate_gml + literal_stringizer + + Notes + ----- + Graph attributes named 'directed', 'multigraph', 'node' or + 'edge', node attributes named 'id' or 'label', edge attributes + named 'source' or 'target' (or 'key' if `G` is a multigraph) + are ignored because these attribute names are used to encode the graph + structure. + + GML files are stored using a 7-bit ASCII encoding with any extended + ASCII characters (iso8859-1) appearing as HTML character entities. + Without specifying a `stringizer`/`destringizer`, the code is capable of + writing `int`/`float`/`str`/`dict`/`list` data as required by the GML + specification. For writing other data types, and for reading data other + than `str` you need to explicitly supply a `stringizer`/`destringizer`. + + Note that while we allow non-standard GML to be read from a file, we make + sure to write GML format. In particular, underscores are not allowed in + attribute names. + For additional documentation on the GML file format, please see the + `GML url `_. + + See the module docstring :mod:`networkx.readwrite.gml` for more details. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_gml(G, "test.gml") + + Filenames ending in .gz or .bz2 will be compressed. + + >>> nx.write_gml(G, "test.gml.gz") + """ + for line in generate_gml(G, stringizer): + path.write((line + "\n").encode("ascii")) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/graph6.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/graph6.py new file mode 100644 index 0000000000000000000000000000000000000000..4ff2f93c43c1cacb3bd1a9d85b500a410269c5a2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/graph6.py @@ -0,0 +1,417 @@ +# Original author: D. Eppstein, UC Irvine, August 12, 2003. +# The original code at http://www.ics.uci.edu/~eppstein/PADS/ is public domain. +"""Functions for reading and writing graphs in the *graph6* format. + +The *graph6* file format is suitable for small graphs or large dense +graphs. For large sparse graphs, use the *sparse6* format. + +For more information, see the `graph6`_ homepage. + +.. _graph6: http://users.cecs.anu.edu.au/~bdm/data/formats.html + +""" + +from itertools import islice + +import networkx as nx +from networkx.exception import NetworkXError +from networkx.utils import not_implemented_for, open_file + +__all__ = ["from_graph6_bytes", "read_graph6", "to_graph6_bytes", "write_graph6"] + + +def _generate_graph6_bytes(G, nodes, header): + """Yield bytes in the graph6 encoding of a graph. + + `G` is an undirected simple graph. `nodes` is the list of nodes for + which the node-induced subgraph will be encoded; if `nodes` is the + list of all nodes in the graph, the entire graph will be + encoded. `header` is a Boolean that specifies whether to generate + the header ``b'>>graph6<<'`` before the remaining data. + + This function generates `bytes` objects in the following order: + + 1. the header (if requested), + 2. the encoding of the number of nodes, + 3. each character, one-at-a-time, in the encoding of the requested + node-induced subgraph, + 4. a newline character. + + This function raises :exc:`ValueError` if the graph is too large for + the graph6 format (that is, greater than ``2 ** 36`` nodes). + + """ + n = len(G) + if n >= 2**36: + raise ValueError( + "graph6 is only defined if number of nodes is less than 2 ** 36" + ) + if header: + yield b">>graph6<<" + for d in n_to_data(n): + yield str.encode(chr(d + 63)) + # This generates the same as `(v in G[u] for u, v in combinations(G, 2))`, + # but in "column-major" order instead of "row-major" order. + bits = (nodes[j] in G[nodes[i]] for j in range(1, n) for i in range(j)) + chunk = list(islice(bits, 6)) + while chunk: + d = sum(b << 5 - i for i, b in enumerate(chunk)) + yield str.encode(chr(d + 63)) + chunk = list(islice(bits, 6)) + yield b"\n" + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_graph6_bytes(bytes_in): + """Read a simple undirected graph in graph6 format from bytes. + + Parameters + ---------- + bytes_in : bytes + Data in graph6 format, without a trailing newline. + + Returns + ------- + G : Graph + + Raises + ------ + NetworkXError + If bytes_in is unable to be parsed in graph6 format + + ValueError + If any character ``c`` in bytes_in does not satisfy + ``63 <= ord(c) < 127``. + + Examples + -------- + >>> G = nx.from_graph6_bytes(b"A_") + >>> sorted(G.edges()) + [(0, 1)] + + See Also + -------- + read_graph6, write_graph6 + + References + ---------- + .. [1] Graph6 specification + + + """ + + def bits(): + """Returns sequence of individual bits from 6-bit-per-value + list of data values.""" + for d in data: + for i in [5, 4, 3, 2, 1, 0]: + yield (d >> i) & 1 + + if bytes_in.startswith(b">>graph6<<"): + bytes_in = bytes_in[10:] + + data = [c - 63 for c in bytes_in] + if any(c > 63 for c in data): + raise ValueError("each input character must be in range(63, 127)") + + n, data = data_to_n(data) + nd = (n * (n - 1) // 2 + 5) // 6 + if len(data) != nd: + raise NetworkXError( + f"Expected {n * (n - 1) // 2} bits but got {len(data) * 6} in graph6" + ) + + G = nx.Graph() + G.add_nodes_from(range(n)) + for (i, j), b in zip(((i, j) for j in range(1, n) for i in range(j)), bits()): + if b: + G.add_edge(i, j) + + return G + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +def to_graph6_bytes(G, nodes=None, header=True): + """Convert a simple undirected graph to bytes in graph6 format. + + Parameters + ---------- + G : Graph (undirected) + + nodes: list or iterable + Nodes are labeled 0...n-1 in the order provided. If None the ordering + given by ``G.nodes()`` is used. + + header: bool + If True add '>>graph6<<' bytes to head of data. + + Raises + ------ + NetworkXNotImplemented + If the graph is directed or is a multigraph. + + ValueError + If the graph has at least ``2 ** 36`` nodes; the graph6 format + is only defined for graphs of order less than ``2 ** 36``. + + Examples + -------- + >>> nx.to_graph6_bytes(nx.path_graph(2)) + b'>>graph6< + + """ + if nodes is not None: + G = G.subgraph(nodes) + H = nx.convert_node_labels_to_integers(G) + nodes = sorted(H.nodes()) + return b"".join(_generate_graph6_bytes(H, nodes, header)) + + +@open_file(0, mode="rb") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_graph6(path): + """Read simple undirected graphs in graph6 format from path. + + Parameters + ---------- + path : file or string + File or filename to write. + + Returns + ------- + G : Graph or list of Graphs + If the file contains multiple lines then a list of graphs is returned + + Raises + ------ + NetworkXError + If the string is unable to be parsed in graph6 format + + Examples + -------- + You can read a graph6 file by giving the path to the file:: + + >>> import tempfile + >>> with tempfile.NamedTemporaryFile(delete=False) as f: + ... _ = f.write(b">>graph6<>> list(G.edges()) + [(0, 1)] + + You can also read a graph6 file by giving an open file-like object:: + + >>> import tempfile + >>> with tempfile.NamedTemporaryFile() as f: + ... _ = f.write(b">>graph6<>> list(G.edges()) + [(0, 1)] + + See Also + -------- + from_graph6_bytes, write_graph6 + + References + ---------- + .. [1] Graph6 specification + + + """ + glist = [] + for line in path: + line = line.strip() + if not len(line): + continue + glist.append(from_graph6_bytes(line)) + if len(glist) == 1: + return glist[0] + else: + return glist + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +@open_file(1, mode="wb") +def write_graph6(G, path, nodes=None, header=True): + """Write a simple undirected graph to a path in graph6 format. + + Parameters + ---------- + G : Graph (undirected) + + path : str + The path naming the file to which to write the graph. + + nodes: list or iterable + Nodes are labeled 0...n-1 in the order provided. If None the ordering + given by ``G.nodes()`` is used. + + header: bool + If True add '>>graph6<<' string to head of data + + Raises + ------ + NetworkXNotImplemented + If the graph is directed or is a multigraph. + + ValueError + If the graph has at least ``2 ** 36`` nodes; the graph6 format + is only defined for graphs of order less than ``2 ** 36``. + + Examples + -------- + You can write a graph6 file by giving the path to a file:: + + >>> import tempfile + >>> with tempfile.NamedTemporaryFile(delete=False) as f: + ... nx.write_graph6(nx.path_graph(2), f.name) + ... _ = f.seek(0) + ... print(f.read()) + b'>>graph6< + + """ + return write_graph6_file(G, path, nodes=nodes, header=header) + + +@not_implemented_for("directed") +@not_implemented_for("multigraph") +def write_graph6_file(G, f, nodes=None, header=True): + """Write a simple undirected graph to a file-like object in graph6 format. + + Parameters + ---------- + G : Graph (undirected) + + f : file-like object + The file to write. + + nodes: list or iterable + Nodes are labeled 0...n-1 in the order provided. If None the ordering + given by ``G.nodes()`` is used. + + header: bool + If True add '>>graph6<<' string to head of data + + Raises + ------ + NetworkXNotImplemented + If the graph is directed or is a multigraph. + + ValueError + If the graph has at least ``2 ** 36`` nodes; the graph6 format + is only defined for graphs of order less than ``2 ** 36``. + + Examples + -------- + You can write a graph6 file by giving an open file-like object:: + + >>> import tempfile + >>> with tempfile.NamedTemporaryFile() as f: + ... nx.write_graph6(nx.path_graph(2), f) + ... _ = f.seek(0) + ... print(f.read()) + b'>>graph6< + + """ + if nodes is not None: + G = G.subgraph(nodes) + H = nx.convert_node_labels_to_integers(G) + nodes = sorted(H.nodes()) + for b in _generate_graph6_bytes(H, nodes, header): + f.write(b) + + +def data_to_n(data): + """Read initial one-, four- or eight-unit value from graph6 + integer sequence. + + Return (value, rest of seq.)""" + if data[0] <= 62: + return data[0], data[1:] + if data[1] <= 62: + return (data[1] << 12) + (data[2] << 6) + data[3], data[4:] + return ( + (data[2] << 30) + + (data[3] << 24) + + (data[4] << 18) + + (data[5] << 12) + + (data[6] << 6) + + data[7], + data[8:], + ) + + +def n_to_data(n): + """Convert an integer to one-, four- or eight-unit graph6 sequence. + + This function is undefined if `n` is not in ``range(2 ** 36)``. + + """ + if n <= 62: + return [n] + elif n <= 258047: + return [63, (n >> 12) & 0x3F, (n >> 6) & 0x3F, n & 0x3F] + else: # if n <= 68719476735: + return [ + 63, + 63, + (n >> 30) & 0x3F, + (n >> 24) & 0x3F, + (n >> 18) & 0x3F, + (n >> 12) & 0x3F, + (n >> 6) & 0x3F, + n & 0x3F, + ] diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__init__.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..532c71d79b7b8936481be8db0defaedf9a96b3e3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__init__.py @@ -0,0 +1,19 @@ +""" +********* +JSON data +********* +Generate and parse JSON serializable data for NetworkX graphs. + +These formats are suitable for use with the d3.js examples https://d3js.org/ + +The three formats that you can generate with NetworkX are: + + - node-link like in the d3.js example https://bl.ocks.org/mbostock/4062045 + - tree like in the d3.js example https://bl.ocks.org/mbostock/4063550 + - adjacency like in the d3.js example https://bost.ocks.org/mike/miserables/ +""" + +from networkx.readwrite.json_graph.node_link import * +from networkx.readwrite.json_graph.adjacency import * +from networkx.readwrite.json_graph.tree import * +from networkx.readwrite.json_graph.cytoscape import * diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c915362a8fa8b9108a05c09141d8de890a2babc4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/adjacency.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/adjacency.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c40c897f2bb7d43e7a835632380be36204f9002 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/adjacency.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/cytoscape.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/cytoscape.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17cef118789b2450b77b56144328571969f1e258 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/cytoscape.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/node_link.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/node_link.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2134fec24204476c3060e2bb80faad851cffebb2 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/node_link.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/tree.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/tree.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddfa6285151fae90b7a8ade9b11da99ef8da36d0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/__pycache__/tree.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/adjacency.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/adjacency.py new file mode 100644 index 0000000000000000000000000000000000000000..3b05747565e73388b0871fbb7daf0f85ad2ce98b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/adjacency.py @@ -0,0 +1,156 @@ +import networkx as nx + +__all__ = ["adjacency_data", "adjacency_graph"] + +_attrs = {"id": "id", "key": "key"} + + +def adjacency_data(G, attrs=_attrs): + """Returns data in adjacency format that is suitable for JSON serialization + and use in JavaScript documents. + + Parameters + ---------- + G : NetworkX graph + + attrs : dict + A dictionary that contains two keys 'id' and 'key'. The corresponding + values provide the attribute names for storing NetworkX-internal graph + data. The values should be unique. Default value: + :samp:`dict(id='id', key='key')`. + + If some user-defined graph data use these attribute names as data keys, + they may be silently dropped. + + Returns + ------- + data : dict + A dictionary with adjacency formatted data. + + Raises + ------ + NetworkXError + If values in attrs are not unique. + + Examples + -------- + >>> from networkx.readwrite import json_graph + >>> G = nx.Graph([(1, 2)]) + >>> data = json_graph.adjacency_data(G) + + To serialize with json + + >>> import json + >>> s = json.dumps(data) + + Notes + ----- + Graph, node, and link attributes will be written when using this format + but attribute keys must be strings if you want to serialize the resulting + data with JSON. + + The default value of attrs will be changed in a future release of NetworkX. + + See Also + -------- + adjacency_graph, node_link_data, tree_data + """ + multigraph = G.is_multigraph() + id_ = attrs["id"] + # Allow 'key' to be omitted from attrs if the graph is not a multigraph. + key = None if not multigraph else attrs["key"] + if id_ == key: + raise nx.NetworkXError("Attribute names are not unique.") + data = {} + data["directed"] = G.is_directed() + data["multigraph"] = multigraph + data["graph"] = list(G.graph.items()) + data["nodes"] = [] + data["adjacency"] = [] + for n, nbrdict in G.adjacency(): + data["nodes"].append({**G.nodes[n], id_: n}) + adj = [] + if multigraph: + for nbr, keys in nbrdict.items(): + for k, d in keys.items(): + adj.append({**d, id_: nbr, key: k}) + else: + for nbr, d in nbrdict.items(): + adj.append({**d, id_: nbr}) + data["adjacency"].append(adj) + return data + + +@nx._dispatchable(graphs=None, returns_graph=True) +def adjacency_graph(data, directed=False, multigraph=True, attrs=_attrs): + """Returns graph from adjacency data format. + + Parameters + ---------- + data : dict + Adjacency list formatted graph data + + directed : bool + If True, and direction not specified in data, return a directed graph. + + multigraph : bool + If True, and multigraph not specified in data, return a multigraph. + + attrs : dict + A dictionary that contains two keys 'id' and 'key'. The corresponding + values provide the attribute names for storing NetworkX-internal graph + data. The values should be unique. Default value: + :samp:`dict(id='id', key='key')`. + + Returns + ------- + G : NetworkX graph + A NetworkX graph object + + Examples + -------- + >>> from networkx.readwrite import json_graph + >>> G = nx.Graph([(1, 2)]) + >>> data = json_graph.adjacency_data(G) + >>> H = json_graph.adjacency_graph(data) + + Notes + ----- + The default value of attrs will be changed in a future release of NetworkX. + + See Also + -------- + adjacency_graph, node_link_data, tree_data + """ + multigraph = data.get("multigraph", multigraph) + directed = data.get("directed", directed) + if multigraph: + graph = nx.MultiGraph() + else: + graph = nx.Graph() + if directed: + graph = graph.to_directed() + id_ = attrs["id"] + # Allow 'key' to be omitted from attrs if the graph is not a multigraph. + key = None if not multigraph else attrs["key"] + graph.graph = dict(data.get("graph", [])) + mapping = [] + for d in data["nodes"]: + node_data = d.copy() + node = node_data.pop(id_) + mapping.append(node) + graph.add_node(node) + graph.nodes[node].update(node_data) + for i, d in enumerate(data["adjacency"]): + source = mapping[i] + for tdata in d: + target_data = tdata.copy() + target = target_data.pop(id_) + if not multigraph: + graph.add_edge(source, target) + graph[source][target].update(target_data) + else: + ky = target_data.pop(key, None) + graph.add_edge(source, target, key=ky) + graph[source][target][ky].update(target_data) + return graph diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__init__.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff4c053868ba12046839d53b06870db1c87b9f00 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_adjacency.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_adjacency.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd293ac67b1bcc9efc4ae9c2102fcca78cd0da70 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_adjacency.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_cytoscape.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_cytoscape.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9797f459338c25b5aca713616896b8695c5aed66 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_cytoscape.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_node_link.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_node_link.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12e4f643e38a004346f6a6432a117718b2c1b1de Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_node_link.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_tree.cpython-311.pyc b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_tree.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1611c7e27bfa50d83278c036112d8e3bde110720 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/__pycache__/test_tree.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py new file mode 100644 index 0000000000000000000000000000000000000000..37506382c55a110b26fdba32a268545d23f4474b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py @@ -0,0 +1,78 @@ +import copy +import json + +import pytest + +import networkx as nx +from networkx.readwrite.json_graph import adjacency_data, adjacency_graph +from networkx.utils import graphs_equal + + +class TestAdjacency: + def test_graph(self): + G = nx.path_graph(4) + H = adjacency_graph(adjacency_data(G)) + assert graphs_equal(G, H) + + def test_graph_attributes(self): + G = nx.path_graph(4) + G.add_node(1, color="red") + G.add_edge(1, 2, width=7) + G.graph["foo"] = "bar" + G.graph[1] = "one" + + H = adjacency_graph(adjacency_data(G)) + assert graphs_equal(G, H) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + d = json.dumps(adjacency_data(G)) + H = adjacency_graph(json.loads(d)) + assert graphs_equal(G, H) + assert H.graph["foo"] == "bar" + assert H.graph[1] == "one" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + def test_digraph(self): + G = nx.DiGraph() + nx.add_path(G, [1, 2, 3]) + H = adjacency_graph(adjacency_data(G)) + assert H.is_directed() + assert graphs_equal(G, H) + + def test_multidigraph(self): + G = nx.MultiDiGraph() + nx.add_path(G, [1, 2, 3]) + H = adjacency_graph(adjacency_data(G)) + assert H.is_directed() + assert H.is_multigraph() + assert graphs_equal(G, H) + + def test_multigraph(self): + G = nx.MultiGraph() + G.add_edge(1, 2, key="first") + G.add_edge(1, 2, key="second", color="blue") + H = adjacency_graph(adjacency_data(G)) + assert graphs_equal(G, H) + assert H[1][2]["second"]["color"] == "blue" + + def test_input_data_is_not_modified_when_building_graph(self): + G = nx.path_graph(4) + input_data = adjacency_data(G) + orig_data = copy.deepcopy(input_data) + # Ensure input is unmodified by deserialisation + assert graphs_equal(G, adjacency_graph(input_data)) + assert input_data == orig_data + + def test_adjacency_form_json_serialisable(self): + G = nx.path_graph(4) + H = adjacency_graph(json.loads(json.dumps(adjacency_data(G)))) + assert graphs_equal(G, H) + + def test_exception(self): + with pytest.raises(nx.NetworkXError): + G = nx.MultiDiGraph() + attrs = {"id": "node", "key": "node"} + adjacency_data(G, attrs) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py new file mode 100644 index 0000000000000000000000000000000000000000..5d47f21f4217d1997165c4f19feb67d283d2dab2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py @@ -0,0 +1,78 @@ +import copy +import json + +import pytest + +import networkx as nx +from networkx.readwrite.json_graph import cytoscape_data, cytoscape_graph + + +def test_graph(): + G = nx.path_graph(4) + H = cytoscape_graph(cytoscape_data(G)) + assert nx.is_isomorphic(G, H) + + +def test_input_data_is_not_modified_when_building_graph(): + G = nx.path_graph(4) + input_data = cytoscape_data(G) + orig_data = copy.deepcopy(input_data) + # Ensure input is unmodified by cytoscape_graph (gh-4173) + cytoscape_graph(input_data) + assert input_data == orig_data + + +def test_graph_attributes(): + G = nx.path_graph(4) + G.add_node(1, color="red") + G.add_edge(1, 2, width=7) + G.graph["foo"] = "bar" + G.graph[1] = "one" + G.add_node(3, name="node", id="123") + + H = cytoscape_graph(cytoscape_data(G)) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + assert H.nodes[3]["name"] == "node" + assert H.nodes[3]["id"] == "123" + + d = json.dumps(cytoscape_data(G)) + H = cytoscape_graph(json.loads(d)) + assert H.graph["foo"] == "bar" + assert H.graph[1] == "one" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + assert H.nodes[3]["name"] == "node" + assert H.nodes[3]["id"] == "123" + + +def test_digraph(): + G = nx.DiGraph() + nx.add_path(G, [1, 2, 3]) + H = cytoscape_graph(cytoscape_data(G)) + assert H.is_directed() + assert nx.is_isomorphic(G, H) + + +def test_multidigraph(): + G = nx.MultiDiGraph() + nx.add_path(G, [1, 2, 3]) + H = cytoscape_graph(cytoscape_data(G)) + assert H.is_directed() + assert H.is_multigraph() + + +def test_multigraph(): + G = nx.MultiGraph() + G.add_edge(1, 2, key="first") + G.add_edge(1, 2, key="second", color="blue") + H = cytoscape_graph(cytoscape_data(G)) + assert nx.is_isomorphic(G, H) + assert H[1][2]["second"]["color"] == "blue" + + +def test_exception(): + with pytest.raises(nx.NetworkXError): + G = nx.MultiDiGraph() + cytoscape_data(G, name="foo", ident="foo") diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py new file mode 100644 index 0000000000000000000000000000000000000000..f903f6066a6e7be922136275ba8fa6df0da41caa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py @@ -0,0 +1,175 @@ +import json + +import pytest + +import networkx as nx +from networkx.readwrite.json_graph import node_link_data, node_link_graph + + +def test_node_link_edges_default_future_warning(): + "Test FutureWarning is raised when `edges=None` in node_link_data and node_link_graph" + G = nx.Graph([(1, 2)]) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + data = nx.node_link_data(G) # edges=None, the default + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = nx.node_link_graph(data) # edges=None, the default + + +def test_node_link_deprecated_link_param(): + G = nx.Graph([(1, 2)]) + with pytest.warns(DeprecationWarning, match="Keyword argument 'link'"): + data = nx.node_link_data(G, link="links") + with pytest.warns(DeprecationWarning, match="Keyword argument 'link'"): + H = nx.node_link_graph(data, link="links") + + +class TestNodeLink: + # TODO: To be removed when signature change complete + def test_custom_attrs_dep(self): + G = nx.path_graph(4) + G.add_node(1, color="red") + G.add_edge(1, 2, width=7) + G.graph[1] = "one" + G.graph["foo"] = "bar" + + attrs = { + "source": "c_source", + "target": "c_target", + "name": "c_id", + "key": "c_key", + "link": "c_links", + } + + H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs) + assert nx.is_isomorphic(G, H) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + # provide only a partial dictionary of keywords. + # This is similar to an example in the doc string + attrs = { + "link": "c_links", + "source": "c_source", + "target": "c_target", + } + H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs) + assert nx.is_isomorphic(G, H) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + def test_exception_dep(self): + G = nx.MultiDiGraph() + with pytest.raises(nx.NetworkXError): + with pytest.warns(FutureWarning, match="\nThe default value will be"): + node_link_data(G, name="node", source="node", target="node", key="node") + + def test_graph(self): + G = nx.path_graph(4) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(node_link_data(G)) + assert nx.is_isomorphic(G, H) + + def test_graph_attributes(self): + G = nx.path_graph(4) + G.add_node(1, color="red") + G.add_edge(1, 2, width=7) + G.graph[1] = "one" + G.graph["foo"] = "bar" + + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(node_link_data(G)) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + with pytest.warns(FutureWarning, match="\nThe default value will be"): + d = json.dumps(node_link_data(G)) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(json.loads(d)) + assert H.graph["foo"] == "bar" + assert H.graph["1"] == "one" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 + + def test_digraph(self): + G = nx.DiGraph() + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(node_link_data(G)) + assert H.is_directed() + + def test_multigraph(self): + G = nx.MultiGraph() + G.add_edge(1, 2, key="first") + G.add_edge(1, 2, key="second", color="blue") + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(node_link_data(G)) + assert nx.is_isomorphic(G, H) + assert H[1][2]["second"]["color"] == "blue" + + def test_graph_with_tuple_nodes(self): + G = nx.Graph() + G.add_edge((0, 0), (1, 0), color=[255, 255, 0]) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + d = node_link_data(G) + dumped_d = json.dumps(d) + dd = json.loads(dumped_d) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(dd) + assert H.nodes[(0, 0)] == G.nodes[(0, 0)] + assert H[(0, 0)][(1, 0)]["color"] == [255, 255, 0] + + def test_unicode_keys(self): + q = "qualité" + G = nx.Graph() + G.add_node(1, **{q: q}) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + s = node_link_data(G) + output = json.dumps(s, ensure_ascii=False) + data = json.loads(output) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(data) + assert H.nodes[1][q] == q + + def test_exception(self): + G = nx.MultiDiGraph() + attrs = {"name": "node", "source": "node", "target": "node", "key": "node"} + with pytest.raises(nx.NetworkXError): + with pytest.warns(FutureWarning, match="\nThe default value will be"): + node_link_data(G, **attrs) + + def test_string_ids(self): + q = "qualité" + G = nx.DiGraph() + G.add_node("A") + G.add_node(q) + G.add_edge("A", q) + with pytest.warns(FutureWarning, match="\nThe default value will be"): + data = node_link_data(G) + assert data["links"][0]["source"] == "A" + assert data["links"][0]["target"] == q + with pytest.warns(FutureWarning, match="\nThe default value will be"): + H = node_link_graph(data) + assert nx.is_isomorphic(G, H) + + def test_custom_attrs(self): + G = nx.path_graph(4) + G.add_node(1, color="red") + G.add_edge(1, 2, width=7) + G.graph[1] = "one" + G.graph["foo"] = "bar" + + attrs = { + "source": "c_source", + "target": "c_target", + "name": "c_id", + "key": "c_key", + "link": "c_links", + } + + H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs) + assert nx.is_isomorphic(G, H) + assert H.graph["foo"] == "bar" + assert H.nodes[1]["color"] == "red" + assert H[1][2]["width"] == 7 diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_tree.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_tree.py new file mode 100644 index 0000000000000000000000000000000000000000..643a14d89b5211f2d97b98f2e227e68361781b97 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/json_graph/tests/test_tree.py @@ -0,0 +1,48 @@ +import json + +import pytest + +import networkx as nx +from networkx.readwrite.json_graph import tree_data, tree_graph + + +def test_graph(): + G = nx.DiGraph() + G.add_nodes_from([1, 2, 3], color="red") + G.add_edge(1, 2, foo=7) + G.add_edge(1, 3, foo=10) + G.add_edge(3, 4, foo=10) + H = tree_graph(tree_data(G, 1)) + assert nx.is_isomorphic(G, H) + + +def test_graph_attributes(): + G = nx.DiGraph() + G.add_nodes_from([1, 2, 3], color="red") + G.add_edge(1, 2, foo=7) + G.add_edge(1, 3, foo=10) + G.add_edge(3, 4, foo=10) + H = tree_graph(tree_data(G, 1)) + assert H.nodes[1]["color"] == "red" + + d = json.dumps(tree_data(G, 1)) + H = tree_graph(json.loads(d)) + assert H.nodes[1]["color"] == "red" + + +def test_exceptions(): + with pytest.raises(TypeError, match="is not a tree."): + G = nx.complete_graph(3) + tree_data(G, 0) + with pytest.raises(TypeError, match="is not directed."): + G = nx.path_graph(3) + tree_data(G, 0) + with pytest.raises(TypeError, match="is not weakly connected."): + G = nx.path_graph(3, create_using=nx.DiGraph) + G.add_edge(2, 0) + G.add_node(3) + tree_data(G, 0) + with pytest.raises(nx.NetworkXError, match="must be different."): + G = nx.MultiDiGraph() + G.add_node(0) + tree_data(G, 0, ident="node", children="node") diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/multiline_adjlist.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/multiline_adjlist.py new file mode 100644 index 0000000000000000000000000000000000000000..808445dbfececca748bb62b28f23417c109df335 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/multiline_adjlist.py @@ -0,0 +1,393 @@ +""" +************************* +Multi-line Adjacency List +************************* +Read and write NetworkX graphs as multi-line adjacency lists. + +The multi-line adjacency list format is useful for graphs with +nodes that can be meaningfully represented as strings. With this format +simple edge data can be stored but node or graph data is not. + +Format +------ +The first label in a line is the source node label followed by the node degree +d. The next d lines are target node labels and optional edge data. +That pattern repeats for all nodes in the graph. + +The graph with edges a-b, a-c, d-e can be represented as the following +adjacency list (anything following the # in a line is a comment):: + + # example.multiline-adjlist + a 2 + b + c + d 1 + e +""" + +__all__ = [ + "generate_multiline_adjlist", + "write_multiline_adjlist", + "parse_multiline_adjlist", + "read_multiline_adjlist", +] + +import networkx as nx +from networkx.utils import open_file + + +def generate_multiline_adjlist(G, delimiter=" "): + """Generate a single line of the graph G in multiline adjacency list format. + + Parameters + ---------- + G : NetworkX graph + + delimiter : string, optional + Separator for node labels + + Returns + ------- + lines : string + Lines of data in multiline adjlist format. + + Examples + -------- + >>> G = nx.lollipop_graph(4, 3) + >>> for line in nx.generate_multiline_adjlist(G): + ... print(line) + 0 3 + 1 {} + 2 {} + 3 {} + 1 2 + 2 {} + 3 {} + 2 1 + 3 {} + 3 1 + 4 {} + 4 1 + 5 {} + 5 1 + 6 {} + 6 0 + + See Also + -------- + write_multiline_adjlist, read_multiline_adjlist + """ + if G.is_directed(): + if G.is_multigraph(): + for s, nbrs in G.adjacency(): + nbr_edges = [ + (u, data) + for u, datadict in nbrs.items() + for key, data in datadict.items() + ] + deg = len(nbr_edges) + yield str(s) + delimiter + str(deg) + for u, d in nbr_edges: + if d is None: + yield str(u) + else: + yield str(u) + delimiter + str(d) + else: # directed single edges + for s, nbrs in G.adjacency(): + deg = len(nbrs) + yield str(s) + delimiter + str(deg) + for u, d in nbrs.items(): + if d is None: + yield str(u) + else: + yield str(u) + delimiter + str(d) + else: # undirected + if G.is_multigraph(): + seen = set() # helper dict used to avoid duplicate edges + for s, nbrs in G.adjacency(): + nbr_edges = [ + (u, data) + for u, datadict in nbrs.items() + if u not in seen + for key, data in datadict.items() + ] + deg = len(nbr_edges) + yield str(s) + delimiter + str(deg) + for u, d in nbr_edges: + if d is None: + yield str(u) + else: + yield str(u) + delimiter + str(d) + seen.add(s) + else: # undirected single edges + seen = set() # helper dict used to avoid duplicate edges + for s, nbrs in G.adjacency(): + nbr_edges = [(u, d) for u, d in nbrs.items() if u not in seen] + deg = len(nbr_edges) + yield str(s) + delimiter + str(deg) + for u, d in nbr_edges: + if d is None: + yield str(u) + else: + yield str(u) + delimiter + str(d) + seen.add(s) + + +@open_file(1, mode="wb") +def write_multiline_adjlist(G, path, delimiter=" ", comments="#", encoding="utf-8"): + """Write the graph G in multiline adjacency list format to path + + Parameters + ---------- + G : NetworkX graph + + path : string or file + Filename or file handle to write to. + Filenames ending in .gz or .bz2 will be compressed. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels + + encoding : string, optional + Text encoding. + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_multiline_adjlist(G, "test.adjlist") + + The path can be a file handle or a string with the name of the file. If a + file handle is provided, it has to be opened in 'wb' mode. + + >>> fh = open("test.adjlist", "wb") + >>> nx.write_multiline_adjlist(G, fh) + + Filenames ending in .gz or .bz2 will be compressed. + + >>> nx.write_multiline_adjlist(G, "test.adjlist.gz") + + See Also + -------- + read_multiline_adjlist + """ + import sys + import time + + pargs = comments + " ".join(sys.argv) + header = ( + f"{pargs}\n" + + comments + + f" GMT {time.asctime(time.gmtime())}\n" + + comments + + f" {G.name}\n" + ) + path.write(header.encode(encoding)) + + for multiline in generate_multiline_adjlist(G, delimiter): + multiline += "\n" + path.write(multiline.encode(encoding)) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def parse_multiline_adjlist( + lines, comments="#", delimiter=None, create_using=None, nodetype=None, edgetype=None +): + """Parse lines of a multiline adjacency list representation of a graph. + + Parameters + ---------- + lines : list or iterator of strings + Input data in multiline adjlist format + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + nodetype : Python type, optional + Convert nodes to this type. + + edgetype : Python type, optional + Convert edges to this type. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels. The default is whitespace. + + Returns + ------- + G: NetworkX graph + The graph corresponding to the lines in multiline adjacency list format. + + Examples + -------- + >>> lines = [ + ... "1 2", + ... "2 {'weight':3, 'name': 'Frodo'}", + ... "3 {}", + ... "2 1", + ... "5 {'weight':6, 'name': 'Saruman'}", + ... ] + >>> G = nx.parse_multiline_adjlist(iter(lines), nodetype=int) + >>> list(G) + [1, 2, 3, 5] + + """ + from ast import literal_eval + + G = nx.empty_graph(0, create_using) + for line in lines: + p = line.find(comments) + if p >= 0: + line = line[:p] + if not line: + continue + try: + (u, deg) = line.rstrip("\n").split(delimiter) + deg = int(deg) + except BaseException as err: + raise TypeError(f"Failed to read node and degree on line ({line})") from err + if nodetype is not None: + try: + u = nodetype(u) + except BaseException as err: + raise TypeError( + f"Failed to convert node ({u}) to type {nodetype}" + ) from err + G.add_node(u) + for i in range(deg): + while True: + try: + line = next(lines) + except StopIteration as err: + msg = f"Failed to find neighbor for node ({u})" + raise TypeError(msg) from err + p = line.find(comments) + if p >= 0: + line = line[:p] + if line: + break + vlist = line.rstrip("\n").split(delimiter) + numb = len(vlist) + if numb < 1: + continue # isolated node + v = vlist.pop(0) + data = "".join(vlist) + if nodetype is not None: + try: + v = nodetype(v) + except BaseException as err: + raise TypeError( + f"Failed to convert node ({v}) to type {nodetype}" + ) from err + if edgetype is not None: + try: + edgedata = {"weight": edgetype(data)} + except BaseException as err: + raise TypeError( + f"Failed to convert edge data ({data}) to type {edgetype}" + ) from err + else: + try: # try to evaluate + edgedata = literal_eval(data) + except: + edgedata = {} + G.add_edge(u, v, **edgedata) + + return G + + +@open_file(0, mode="rb") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_multiline_adjlist( + path, + comments="#", + delimiter=None, + create_using=None, + nodetype=None, + edgetype=None, + encoding="utf-8", +): + """Read graph in multi-line adjacency list format from path. + + Parameters + ---------- + path : string or file + Filename or file handle to read. + Filenames ending in .gz or .bz2 will be uncompressed. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + nodetype : Python type, optional + Convert nodes to this type. + + edgetype : Python type, optional + Convert edge data to this type. + + comments : string, optional + Marker for comment lines + + delimiter : string, optional + Separator for node labels. The default is whitespace. + + Returns + ------- + G: NetworkX graph + + Examples + -------- + >>> G = nx.path_graph(4) + >>> nx.write_multiline_adjlist(G, "test.adjlist") + >>> G = nx.read_multiline_adjlist("test.adjlist") + + The path can be a file or a string with the name of the file. If a + file s provided, it has to be opened in 'rb' mode. + + >>> fh = open("test.adjlist", "rb") + >>> G = nx.read_multiline_adjlist(fh) + + Filenames ending in .gz or .bz2 will be compressed. + + >>> nx.write_multiline_adjlist(G, "test.adjlist.gz") + >>> G = nx.read_multiline_adjlist("test.adjlist.gz") + + The optional nodetype is a function to convert node strings to nodetype. + + For example + + >>> G = nx.read_multiline_adjlist("test.adjlist", nodetype=int) + + will attempt to convert all nodes to integer type. + + The optional edgetype is a function to convert edge data strings to + edgetype. + + >>> G = nx.read_multiline_adjlist("test.adjlist") + + The optional create_using parameter is a NetworkX graph container. + The default is Graph(), an undirected graph. To read the data as + a directed graph use + + >>> G = nx.read_multiline_adjlist("test.adjlist", create_using=nx.DiGraph) + + Notes + ----- + This format does not store graph, node, or edge data. + + See Also + -------- + write_multiline_adjlist + """ + lines = (line.decode(encoding) for line in path) + return parse_multiline_adjlist( + lines, + comments=comments, + delimiter=delimiter, + create_using=create_using, + nodetype=nodetype, + edgetype=edgetype, + ) diff --git a/.venv/lib/python3.11/site-packages/networkx/readwrite/p2g.py b/.venv/lib/python3.11/site-packages/networkx/readwrite/p2g.py new file mode 100644 index 0000000000000000000000000000000000000000..804adb23e5296032020e7f90626ca74c9f9398f0 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/networkx/readwrite/p2g.py @@ -0,0 +1,105 @@ +""" +This module provides the following: read and write of p2g format +used in metabolic pathway studies. + +See https://web.archive.org/web/20080626113807/http://www.cs.purdue.edu/homes/koyuturk/pathway/ for a description. + +The summary is included here: + +A file that describes a uniquely labeled graph (with extension ".gr") +format looks like the following: + + +name +3 4 +a +1 2 +b + +c +0 2 + +"name" is simply a description of what the graph corresponds to. The +second line displays the number of nodes and number of edges, +respectively. This sample graph contains three nodes labeled "a", "b", +and "c". The rest of the graph contains two lines for each node. The +first line for a node contains the node label. After the declaration +of the node label, the out-edges of that node in the graph are +provided. For instance, "a" is linked to nodes 1 and 2, which are +labeled "b" and "c", while the node labeled "b" has no outgoing +edges. Observe that node labeled "c" has an outgoing edge to +itself. Indeed, self-loops are allowed. Node index starts from 0. + +""" + +import networkx as nx +from networkx.utils import open_file + + +@open_file(1, mode="w") +def write_p2g(G, path, encoding="utf-8"): + """Write NetworkX graph in p2g format. + + Notes + ----- + This format is meant to be used with directed graphs with + possible self loops. + """ + path.write((f"{G.name}\n").encode(encoding)) + path.write((f"{G.order()} {G.size()}\n").encode(encoding)) + nodes = list(G) + # make dictionary mapping nodes to integers + nodenumber = dict(zip(nodes, range(len(nodes)))) + for n in nodes: + path.write((f"{n}\n").encode(encoding)) + for nbr in G.neighbors(n): + path.write((f"{nodenumber[nbr]} ").encode(encoding)) + path.write("\n".encode(encoding)) + + +@open_file(0, mode="r") +@nx._dispatchable(graphs=None, returns_graph=True) +def read_p2g(path, encoding="utf-8"): + """Read graph in p2g format from path. + + Returns + ------- + MultiDiGraph + + Notes + ----- + If you want a DiGraph (with no self loops allowed and no edge data) + use D=nx.DiGraph(read_p2g(path)) + """ + lines = (line.decode(encoding) for line in path) + G = parse_p2g(lines) + return G + + +@nx._dispatchable(graphs=None, returns_graph=True) +def parse_p2g(lines): + """Parse p2g format graph from string or iterable. + + Returns + ------- + MultiDiGraph + """ + description = next(lines).strip() + # are multiedges (parallel edges) allowed? + G = nx.MultiDiGraph(name=description, selfloops=True) + nnodes, nedges = map(int, next(lines).split()) + nodelabel = {} + nbrs = {} + # loop over the nodes keeping track of node labels and out neighbors + # defer adding edges until all node labels are known + for i in range(nnodes): + n = next(lines).strip() + nodelabel[i] = n + G.add_node(n) + nbrs[n] = map(int, next(lines).split()) + # now we know all of the node labels so we can add the edges + # with the correct labels + for n in G: + for nbr in nbrs[n]: + G.add_edge(n, nodelabel[nbr]) + return G diff --git a/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/INSTALLER b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..a1b589e38a32041e49332e5e81c2d363dc418d68 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT new file mode 100644 index 0000000000000000000000000000000000000000..b8bb97185926d7daed314609753173945ed4ff1a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/top_level.txt b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..01c650244d0ccb6043c603b736fcf8d9e622bc71 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/sniffio-1.3.1.dist-info/top_level.txt @@ -0,0 +1 @@ +sniffio diff --git a/.venv/lib/python3.11/site-packages/torchaudio/lib/_torchaudio.so b/.venv/lib/python3.11/site-packages/torchaudio/lib/_torchaudio.so new file mode 100644 index 0000000000000000000000000000000000000000..14922610d8cb88c5988bbce8b586756a67450957 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/torchaudio/lib/_torchaudio.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c20a328fc1108057a6263d055546885bc69839e146ac1a10523663011868c599 +size 133816 diff --git a/.venv/lib/python3.11/site-packages/torchaudio/lib/libtorchaudio.so b/.venv/lib/python3.11/site-packages/torchaudio/lib/libtorchaudio.so new file mode 100644 index 0000000000000000000000000000000000000000..cfb0f4b74b3182b7a73279ded5b0307a8bcd496c --- /dev/null +++ b/.venv/lib/python3.11/site-packages/torchaudio/lib/libtorchaudio.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d101b1b9c1e20f607b2e89c2323602f096bc4dd96b8ede1e3f60e66482d4dc1f +size 2452168 diff --git a/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_en-ja_3M-pairs_3.5e-5/iter_0000698/model-00004-of-00004.safetensors b/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_en-ja_3M-pairs_3.5e-5/iter_0000698/model-00004-of-00004.safetensors new file mode 100644 index 0000000000000000000000000000000000000000..9021d4930ff995ae8cab99404878fed831452926 --- /dev/null +++ b/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_en-ja_3M-pairs_3.5e-5/iter_0000698/model-00004-of-00004.safetensors @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a40017220dabf327a7f62c5c6a5ea720627ec78ce72c3211cb41df1d8750711 +size 1223688320 diff --git a/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_zh_1G-tokens/iter_0003873/model-00004-of-00004.safetensors b/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_zh_1G-tokens/iter_0003873/model-00004-of-00004.safetensors new file mode 100644 index 0000000000000000000000000000000000000000..658f529fe1e8c6426b2e1274ebd120db6fbbb6bb --- /dev/null +++ b/llm_tutorial/llm_recipes/models/hf-model-eval/llm-jp-v3-3.7b_zh_1G-tokens/iter_0003873/model-00004-of-00004.safetensors @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe48e1bd483cb86166b9d9bda02adbf9b55366115d98a021d6dc2a8e50ed3119 +size 1223688320