diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f8fdc4f..9afaad4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,49 +1,29 @@ -# This is a basic workflow to help you get started with Actions - name: Check code style -# Controls when the workflow will run on: - # Triggers the workflow on push or pull request events [ push, pull_request ] -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "Check_code_style" Check_code_style: - - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # A strategy creates a build matrix for your jobs + runs-on: ubuntu-18.04 strategy: - - # You can define a matrix of different job configurations matrix: - - # Each option you define in the matrix has a key and value python-version: [ 3.8 ] - - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - name: Set up Git repository uses: actions/checkout@v2 - # Setup Python with version from matrix - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - # Install pre-commit - name: Install pre-commit run: | python -m pip install pre-commit==2.15.0 pre-commit install - # Run pre-commit on all the files in the repo - name: Run pre-commit run: | pre-commit run --all-files --color always --verbose --show-diff-on-failure diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ca13504 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Test + +on: [ push, pull_request ] + +jobs: + tests: + runs-on: ubuntu-18.04 + strategy: + matrix: + python-version: [ "3.8" ] + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install packages + run: | + python -m pip install --upgrade pip wheel setuptools + python -m pip install . + python -m pip install -r requirements/test.txt + python -m pip list + + - name: Test Genegram + run: | + pytest -vv -s tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74514e6..39591c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,6 +6,6 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 051ba01..0000000 --- a/Pipfile +++ /dev/null @@ -1,18 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -tensorflow = "*" -pillow = "*" -cfpq-data = "==2.0.0" -pyformlang = "*" -pygraphblas = "*" - -[dev-packages] -pre-commit = "*" -black = "*" - -[requires] -python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 40887d2..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,1107 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "64395db63eff600d49e3acc49e9108f8c1beb9d99924c83b5f235432b6ec5487" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "absl-py": { - "hashes": [ - "sha256:84e6dcdc69c947d0c13e5457d056bd43cade4c2393dce00d684aedea77ddc2a3", - "sha256:ac511215c01ee9ae47b19716599e8ccfa746f2e18de72bdf641b79b22afa27ea" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "astunparse": { - "hashes": [ - "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872", - "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8" - ], - "version": "==1.6.3" - }, - "cachetools": { - "hashes": [ - "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693", - "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1" - ], - "markers": "python_version ~= '3.5'", - "version": "==4.2.4" - }, - "certifi": { - "hashes": [ - "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", - "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" - ], - "version": "==2021.10.8" - }, - "cffi": { - "hashes": [ - "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3", - "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2", - "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636", - "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20", - "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728", - "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27", - "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66", - "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443", - "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0", - "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7", - "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39", - "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605", - "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a", - "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37", - "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029", - "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139", - "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc", - "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df", - "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14", - "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880", - "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2", - "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a", - "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e", - "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474", - "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024", - "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8", - "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0", - "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e", - "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a", - "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e", - "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032", - "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6", - "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e", - "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b", - "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e", - "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954", - "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962", - "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c", - "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4", - "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55", - "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962", - "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023", - "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c", - "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6", - "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8", - "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382", - "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7", - "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc", - "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997", - "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796" - ], - "version": "==1.15.0" - }, - "cfpq-data": { - "hashes": [ - "sha256:404e9f22244ecb92deec122f8d00210542b252723d07ed70617a7e7a2f4d88d0", - "sha256:c655e34172fb9a851a4927d7ca24327807e24e283731624c956179c58ffc5483" - ], - "index": "pypi", - "version": "==2.0.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", - "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" - ], - "markers": "python_version >= '3'", - "version": "==2.0.7" - }, - "contextvars": { - "hashes": [ - "sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e" - ], - "version": "==2.4" - }, - "flatbuffers": { - "hashes": [ - "sha256:12158ab0272375eab8db2d663ae97370c33f152b27801fa6024e1d6105fd4dd2", - "sha256:3751954f0604580d3219ae49a85fafec9d85eec599c0b96226e1bc0b48e57474" - ], - "version": "==2.0" - }, - "gast": { - "hashes": [ - "sha256:40feb7b8b8434785585ab224d1568b857edb18297e5a3047f1ba012bc83b42c1", - "sha256:b7adcdd5adbebf1adf17378da5ba3f543684dbec47b1cda1f3997e573cd542c4" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.4.0" - }, - "google-auth": { - "hashes": [ - "sha256:a348a50b027679cb7dae98043ac8dbcc1d7951f06d8387496071a1e05a2465c0", - "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.3.3" - }, - "google-auth-oauthlib": { - "hashes": [ - "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73", - "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a" - ], - "markers": "python_version >= '3.6'", - "version": "==0.4.6" - }, - "google-pasta": { - "hashes": [ - "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954", - "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed", - "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e" - ], - "version": "==0.2.0" - }, - "grpcio": { - "hashes": [ - "sha256:0aa1af3e1480b6dd3092ee67c4b67b1ea88d638fcdc4d1a611ae11e311800b34", - "sha256:0c075616d5e86fb65fd4784d5a87d6e5a1882d277dce5c33d9b67cfc71d79899", - "sha256:133fb9a3cf4519543e4e41eb18b5dac0da26941aeabca8122dbcf3decbad2d21", - "sha256:23a3f03e1d9ac429ff78d23d2ab07756d3728ee1a68b5f244d8435006608b6aa", - "sha256:2a34c8979de10b04a44d2cad07d41d83643e65e49f84a05b1adf150aeb41c95f", - "sha256:2eb8180a6d9e47fc865a4e92a2678f3202145021ef2c1bccf165fa5744f6ec95", - "sha256:2f2ee78a6ae88d668ceda56fa4a18d8a38b34c2f2e1332083dd1da1a92870703", - "sha256:31a47af7356fb5ed3120636dd75c5efb571ecf15737484119e31286687f0e52a", - "sha256:3213dfe3abfc3fda7f30e86aa5967dce0c2eb4cc90a0504f95434091bf6b219a", - "sha256:32b7ca83f1a6929217098aaaac89fc49879ae714c95501d40df41a0e7506164c", - "sha256:3713e3918da6ae10812a64e75620a172f01af2ff0a1c99d6481c910e1d4a9053", - "sha256:3b4b7c1ab18283eb64af5648d20eabef9237a2aec09e30a805f18adc9497258d", - "sha256:3f0b70cf8632028714a8341b841b011a47900b1c163bf5fababb4ab3888c9b6c", - "sha256:61aa02f4505c5bbbaeba80fef1bd6871d1aef05a8778a707ab91303ee0865ad0", - "sha256:6ca497ccecaa8727f14c4ccc9ffb70a19c6413fe1d4650500c90a7febd662860", - "sha256:71d9ed5a732a54b9c87764609f2fd2bc4ae72fa85e271038eb132ea723222209", - "sha256:72d0bdc3605dc8f4187b302e1180643963896e3f2917a52becb51afb54448e3e", - "sha256:734690b3f35468f8ed4003ec7622d2d47567f1881f5fcdca34f1e52551c2ef55", - "sha256:740f5b21a7108a8c08bf522434752dc1d306274d47ca8b4d51af5588a16b6113", - "sha256:766f1b943abc3e27842b72fba6e28fb9f57c9b84029fd7e91146e4c37034d937", - "sha256:788154b32bf712e9711d001df024af5f7b2522117876c129bb27b9ad6e5461fb", - "sha256:7a22a7378ea59ad1e6f2e79f9da6862eb9e1f6586253aee784d419a49e3f4bd9", - "sha256:8487fb0649ebebc9c5dca1a6dc4eb7fddf701183426b3eefeb3584639d223d43", - "sha256:8824b36e6b0e45fefe0b4eac5ad460830e0cbc856a0c794f711289b4b8933d53", - "sha256:888d8519709652dd39415de5f79abd50257201b345dd4f40151feffc3dad3232", - "sha256:9170b5d2082fc00c057c6ccd6b893033c1ade05717fcec1d63557c3bc7afdb1b", - "sha256:9b751271b029432a526a4970dc9b70d93eb6f0963b6a841b574f780b72651969", - "sha256:9d1be99f216b18f8a9dbdfbdbcc9a6caee504d0d27295fdbb5c8da35f5254a69", - "sha256:9e403d07d77ed4495ad3c18994191525b11274693e72e464241c9139e2f9cd7c", - "sha256:a3bb4302389b23f2006ecaaea5eb4a39cc80ea98d1964159e59c1c20ef39a483", - "sha256:a5ac91db3c588296366554b2d91116fc3a9f05bae516cafae07220e1f05bfef7", - "sha256:b1232c5efc8a9e4b7a13db235c51135412beb9e62e618a2a89dd0463edb3d929", - "sha256:b8dd1b6456c6fb3681affe0f81dff4b3bc46f825fc05e086d64216545da9ad92", - "sha256:c32c470e077b34a52e87e7de26644ad0f9e9ff89a785ff7e6466870869659e05", - "sha256:c35b847bc6bd3c3a118a13277d91a772e7dd163ce7dd2791239f9941b6eaafe3", - "sha256:c3a446b6a1f8077cc03d0d496fc1cecdd3d0b66860c0c5b65cc92d0549117840", - "sha256:d1461672b2eaef9affb60a71014ebd2f789deea7c9acb1d4bd163de92dd8e044", - "sha256:e156ea12adb7a7ca8d8280c9df850c15510b790c785fc26c9a3fb928cd221fd4", - "sha256:ead9885b53777bed4b0694ff0baea9d2c519ff774b17b177bde43d73e2b4aa38", - "sha256:ebbe9582ef06559a2358827a588ab4b92a2639517de8fe428288772820ab03b5", - "sha256:f68aa98f5970eccb6c94456f3447a99916c42fbddae1971256bc4e7c40a6593b", - "sha256:fc2eadfb5ec956c556c138fab0dfc1d2395c57ae0bfea047edae1976a26b250c", - "sha256:fd11995e3402af0f838844194707da8b3235f1719bcac961493f0138f1325893", - "sha256:fd570720871dc84d2adc8430ce287319c9238d1e2f70c140f9bc54c690fabd1b" - ], - "version": "==1.41.1" - }, - "h5py": { - "hashes": [ - "sha256:320f5810e058dcea73529dc98ae42263d34004bcc9209d7bf44eac5136716c3f", - "sha256:3f9518c37a8b9cd067927a8cbc6fe96846d5d28c32e10baf49c8a1d012c9b0a6", - "sha256:40fe8572511c317ec7598271b5dce9c25957cc373af733e53ea246bbf0244958", - "sha256:77c7be4001ac7d3ed80477de5b6942501d782de1bbe4886597bdfec2a7ab821f", - "sha256:834a53178fc558c9832081fd18bc22f6f2585edcc06c802adfa4afb07ff884b4", - "sha256:c99329ebe346a73bd13bf3fc3d54ee9421f6ee00de6ea085c64a856a9acee7b3", - "sha256:cdff848353e2d20d5f66535957595c79932851f51e496623a96186d02ba0cf30", - "sha256:d15e1556ff9591b3f3d8c7b3b66085f3867e5500528663bf11c499ab71c9c6b6", - "sha256:f75d7bdaba8d3537490b0f53adcd043991ada3597f88e772969172c45145a8f6", - "sha256:fc763e707aa631fdc200878c7b5c13ea5b5d1f9772696871dcd6f0f5932fcd35" - ], - "markers": "python_version >= '3.7'", - "version": "==3.5.0" - }, - "idna": { - "hashes": [ - "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", - "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" - ], - "markers": "python_version >= '3'", - "version": "==3.3" - }, - "immutables": { - "hashes": [ - "sha256:0020aaa4010b136056c20a46ce53204e1407a9e4464246cb2cf95b90808d9161", - "sha256:064001638ab5d36f6aa05b6101446f4a5793fb71e522bc81b8fc65a1894266ff", - "sha256:0a396314b9024fa55bf83a27813fd76cf9f27dce51f53b0f19b51de035146251", - "sha256:19bdede174847c2ef1292df0f23868ab3918b560febb09fcac6eec621bd4812b", - "sha256:1de393f1b188740ca7b38f946f2bbc7edf3910d2048f03bbb8d01f17a038d67c", - "sha256:2505d93395d3f8ae4223e21465994c3bc6952015a38dc4f03cb3e07a2b8d8325", - "sha256:298a301f85f307b4c056a0825eb30f060e64d73605e783289f3df37dd762bab8", - "sha256:29c9ed003eacb92e630ef200e31f47236c2139b39476894f7963b32bd39bafa3", - "sha256:4a2a71678348fb95b13ca108d447f559a754c41b47bd1e7e4fb23974e735682d", - "sha256:50793a44ba0d228ed8cad4d0925e00dfd62ea32f44ddee8854f8066447272d05", - "sha256:511c93d8b1bbbf103ff3f1f120c5a68a9866ce03dea6ac406537f93ca9b19139", - "sha256:734c269e82e5f307fb6e17945953b67659d1731e65309787b8f7ba267d1468f2", - "sha256:798b095381eb42cf40db6876339e7bed84093e5868018a9e73d8e1f7ab4bb21e", - "sha256:799621dcdcdcbb2516546a40123b87bf88de75fe7459f7bd8144f079ace6ec3e", - "sha256:7a58825ff2254e2612c5a932174398a4ea8fbddd8a64a02c880cc32ee28b8820", - "sha256:7bcf52aeb983bd803b7c6106eae1b2d9a0c7ab1241bc6b45e2174ba2b7283031", - "sha256:9ccf4c0e3e2e3237012b516c74c49de8872ccdf9129739f7a0b9d7444a8c4862", - "sha256:a307eb0984eb43e815dcacea3ac50c11d00a936ecf694c46991cd5a23bcb0ec0", - "sha256:a454d5d3fee4b7cc627345791eb2ca4b27fa3bbb062ccf362ecaaa51679a07ed", - "sha256:aa7bf572ae1e006104c584be70dc634849cf0dc62f42f4ee194774f97e7fd17d", - "sha256:acbfa79d44228d96296279068441f980dc63dbed52522d9227ff9f4d96c6627e", - "sha256:b651b61c1af6cda2ee201450f2ffe048a5959bc88e43e6c312f4c93e69c9e929", - "sha256:b779617f5b94486bfd0f22162cd72eb5f2beb0214a14b75fdafb7b2c908ed0cb", - "sha256:d59beef203a3765db72b1d0943547425c8318ecf7d64c451fd1e130b653c2fbb", - "sha256:d67e86859598eed0d926562da33325dac7767b7b1eff84e232c22abea19f4360", - "sha256:edd9f67671555af1eb99ad3c7550238487dd7ac0ac5205b40204ed61c9a922ac", - "sha256:fcf678a3074613119385a02a07c469ec5130559f5ea843c85a0840c80b5b71c6" - ], - "markers": "python_version >= '3.6'", - "version": "==0.16" - }, - "isodate": { - "hashes": [ - "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8", - "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81" - ], - "version": "==0.6.0" - }, - "keras": { - "hashes": [ - "sha256:0c33ae1f728064ca0d35dfba999e9c316f03623bf5688c82fb83cc74a80ea248" - ], - "version": "==2.7.0" - }, - "keras-preprocessing": { - "hashes": [ - "sha256:7b82029b130ff61cc99b55f3bd27427df4838576838c5b2f65940e4fcec99a7b", - "sha256:add82567c50c8bc648c14195bf544a5ce7c1f76761536956c3d2978970179ef3" - ], - "version": "==1.1.2" - }, - "lazy-property": { - "hashes": [ - "sha256:3a1cd36949b323468731ba569e169415a609d5904d53fffe982055fbe67b1180", - "sha256:907b3a9e653771f4d0bcafd0fbfcdac8aaade85b31ed7eccd1cbfe59c59b149f" - ], - "version": "==0.0.1" - }, - "libclang": { - "hashes": [ - "sha256:275126823c60ab5c9fae6a433cbb6a47e4d1b5f668a985fbd6065553dbc7efcc", - "sha256:3b0585dbdc3f3b372340f1efe7c28bf4a5b658d2fb8dc0544a7ef0d2bef40618", - "sha256:6df2f8a2cb75181e3c1e99e5dfca2fd44bb7f84ed12d5112541e03c10384f306", - "sha256:b828cb52cf3f02fb0e0a8fddb9dece7e2ed006f8b4d54ee811cef0471d414367", - "sha256:fadad3bf5fbab50c996eb151adc58c3a7cbee45a9135060c416be7b640372112" - ], - "version": "==12.0.0" - }, - "llvmlite": { - "hashes": [ - "sha256:14030a1c0f9aee0185db069163240c51d4e8a3eec0daf02468e057281dee612b", - "sha256:15b8ac7a489e31b7d5c482193edaa44374e3c18e409bea494224e31eb60e38e5", - "sha256:30431fe9a9b7b1c3585b71149cc11dc79b9d62dc86d3db15c3dcca33d274b5be", - "sha256:447b01c25d18921c4179f2eccba218d7c82b65cfe3952b0d018d569945427bf9", - "sha256:4616e17914fcc7c5bfb7d1014acbd4fca478949820e86218a29d9473d0aa221b", - "sha256:4c1e91fd4ba2764161e9a05b6fff46a52d26170186bad99629777e8c7246f0ef", - "sha256:57c1dae337863b497c141d40736041d4acb7769226b44fe05959fce3c3570d5d", - "sha256:6392b870cd018ec0c645d6bbb918d6aa0eeca8c62674baaee30862d6b6865b15", - "sha256:7449acca596f45e9e12b20c0b72d184f83025341cc2d44d7ccf5fe31356dcd08", - "sha256:74b6c3d2eb8cef32a09e8fd7637d0d37628c74f4deedf9361e0c0ebecc239208", - "sha256:794191922ac6414c55d66058eaba8b88a630c6e9f2cf0db7e8e661e74d71fa14", - "sha256:995c1a2c8b6a11a7f2c66e52576de6a28292d37842d383aae5be7b965b56d10f", - "sha256:ab00b7996e5ef795f59d95d3125850f3af28d19e43bdc08473947cb8045ce098", - "sha256:b6466d6369051e5c083b15cf285c00595ddb7f828be1ebecb1dfb97f3fab0bff", - "sha256:c92a209439fd0b8a41f6e2aba1d3afa260357028a29ed7db8c602c4d67c21540", - "sha256:cf7d623d33d24df51adc4e9e9f5734df752330661793d1662425ad2e926cb2d4", - "sha256:d31a3bd69894b31bbc68df00e0b37b0980a0cf025f9dbea9cdd37988230c33a3", - "sha256:df1d1b162a426480b37d6c4adeddff49e2fb9f71b307c7facac67bdce4767746", - "sha256:f9e84d683943c2f636b08db9b2d182d4b40b83e1a3e31e100af3bb9ed8d94bcd" - ], - "markers": "python_version < '3.10' and python_version >= '3.7'", - "version": "==0.37.0" - }, - "markdown": { - "hashes": [ - "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49", - "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c" - ], - "markers": "python_version >= '3.6'", - "version": "==3.3.4" - }, - "mmparse": { - "hashes": [ - "sha256:8db89bde4ae8f85a297dcb6cc83830072f12d929fb2285266d01a0118bb99243" - ], - "version": "==0.6" - }, - "networkx": { - "hashes": [ - "sha256:2306f1950ce772c5a59a57f5486d59bb9cab98497c45fc49cbc45ac0dec119bb", - "sha256:5fcb7004be69e8fbdf07dcb502efa5c77cadcaad6982164134eeb9721f826c2e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.6.2" - }, - "numba": { - "hashes": [ - "sha256:0354df1fcfa9d9d8df3b63780fae408c8f23c474d71a4e929f4c5b44f2c9ce5a", - "sha256:0f1c2c23c4e05cbed19f7a15710a25e71ab818ba7cd0bf66572bacd221721f22", - "sha256:1380429f4a3f73440aae093a058713c780fdc14930b3070c883bc1737e8711b0", - "sha256:5239bf413a9d3c7fad839400d5082032635511c3b7058e17835c7c4090f223ed", - "sha256:5492ffa42425b7dc783e4376dfc07617c751d7d087d64fe8c2e7944038e35261", - "sha256:606ebf5b0474d89f96a2e1354f0349e985c3897c2989b78e47b095d67434cf4c", - "sha256:64451b4fd2437ebb7bbcff72133b28575cb8464eb3f10ccd88c70a3792e6de0a", - "sha256:77479e79b6840e3eb5e0613bbdbb4be8f4b9c4130bafdf6ac39b9507ea742f15", - "sha256:7da918aed4790a4ce6682061971e6248e7422dd5618dcac8054d4a47955182dc", - "sha256:884ad2cdebb6f8bcc7b5ec70e56c9acdb8456482c49cea12273d34709dfc2c9c", - "sha256:b385451355a9023c9611400c7c6d4088f5781ed11b104b5d690f0ad65b142860", - "sha256:b657cece0b069cd4361a6d25aaae2e9e9df9e65abfa63f09345352fbb1069a11", - "sha256:c2e877a33f6920365e96ad088023f786a4b1ce44a7e772763cc02c55f49614dd", - "sha256:c36e50271146c3c33f10111488307a6aa75416aa53384709b037599426a967ea", - "sha256:d0799e7e8640a31d9567a032a6e046d797356afb3e812e0a0f97e6e74ded7e35", - "sha256:ec7033409e66158e9f2b83c22d887fda7949bf2ac652bbbdcbc006b590c37339", - "sha256:ef4d27ee039007510c3de9c42fd6bb57051661ceeca4a9a6244b642a742632a0", - "sha256:f9dfc803c864edcc2381219b800abf366793400aea55e26d4d5b7d953e14f43f", - "sha256:fe4f0c881dbaac0c818dafc80e348edf8d8f1022278c368390ca20e92ed381cc" - ], - "markers": "python_version < '3.10' and python_version >= '3.7'", - "version": "==0.54.1" - }, - "numpy": { - "hashes": [ - "sha256:1676b0a292dd3c99e49305a16d7a9f42a4ab60ec522eac0d3dd20cdf362ac010", - "sha256:16f221035e8bd19b9dc9a57159e38d2dd060b48e93e1d843c49cb370b0f415fd", - "sha256:43909c8bb289c382170e0282158a38cf306a8ad2ff6dfadc447e90f9961bef43", - "sha256:4e465afc3b96dbc80cf4a5273e5e2b1e3451286361b4af70ce1adb2984d392f9", - "sha256:55b745fca0a5ab738647d0e4db099bd0a23279c32b31a783ad2ccea729e632df", - "sha256:5d050e1e4bc9ddb8656d7b4f414557720ddcca23a5b88dd7cff65e847864c400", - "sha256:637d827248f447e63585ca3f4a7d2dfaa882e094df6cfa177cc9cf9cd6cdf6d2", - "sha256:6690080810f77485667bfbff4f69d717c3be25e5b11bb2073e76bb3f578d99b4", - "sha256:66fbc6fed94a13b9801fb70b96ff30605ab0a123e775a5e7a26938b717c5d71a", - "sha256:67d44acb72c31a97a3d5d33d103ab06d8ac20770e1c5ad81bdb3f0c086a56cf6", - "sha256:6ca2b85a5997dabc38301a22ee43c82adcb53ff660b89ee88dded6b33687e1d8", - "sha256:6e51534e78d14b4a009a062641f465cfaba4fdcb046c3ac0b1f61dd97c861b1b", - "sha256:70eb5808127284c4e5c9e836208e09d685a7978b6a216db85960b1a112eeace8", - "sha256:830b044f4e64a76ba71448fce6e604c0fc47a0e54d8f6467be23749ac2cbd2fb", - "sha256:8b7bb4b9280da3b2856cb1fc425932f46fba609819ee1c62256f61799e6a51d2", - "sha256:a9c65473ebc342715cb2d7926ff1e202c26376c0dcaaee85a1fd4b8d8c1d3b2f", - "sha256:c1c09247ccea742525bdb5f4b5ceeacb34f95731647fe55774aa36557dbb5fa4", - "sha256:c5bf0e132acf7557fc9bb8ded8b53bbbbea8892f3c9a1738205878ca9434206a", - "sha256:db250fd3e90117e0312b611574cd1b3f78bec046783195075cbd7ba9c3d73f16", - "sha256:e515c9a93aebe27166ec9593411c58494fa98e5fcc219e47260d9ab8a1cc7f9f", - "sha256:e55185e51b18d788e49fe8305fd73ef4470596b33fc2c1ceb304566b99c71a69", - "sha256:ea9cff01e75a956dbee133fa8e5b68f2f92175233de2f88de3a682dd94deda65", - "sha256:f1452578d0516283c87608a5a5548b0cdde15b99650efdfd85182102ef7a7c17", - "sha256:f39a995e47cb8649673cfa0579fbdd1cdd33ea497d1728a6cb194d6252268e48" - ], - "markers": "python_version >= '3.7'", - "version": "==1.20.3" - }, - "oauthlib": { - "hashes": [ - "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc", - "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3" - ], - "markers": "python_version >= '3.6'", - "version": "==3.1.1" - }, - "opt-einsum": { - "hashes": [ - "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147", - "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549" - ], - "markers": "python_version >= '3.5'", - "version": "==3.3.0" - }, - "pandas": { - "hashes": [ - "sha256:003ba92db58b71a5f8add604a17a059f3068ef4e8c0c365b088468d0d64935fd", - "sha256:10e10a2527db79af6e830c3d5842a4d60383b162885270f8cffc15abca4ba4a9", - "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f", - "sha256:2d1dc09c0013d8faa7474574d61b575f9af6257ab95c93dcf33a14fd8d2c1bab", - "sha256:35c77609acd2e4d517da41bae0c11c70d31c87aae8dd1aabd2670906c6d2c143", - "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a", - "sha256:42493f8ae67918bf129869abea8204df899902287a7f5eaf596c8e54e0ac7ff4", - "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac", - "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f", - "sha256:5ba0aac1397e1d7b654fccf263a4798a9e84ef749866060d19e577e927d66e1b", - "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b", - "sha256:a2aa18d3f0b7d538e21932f637fbfe8518d085238b429e4790a35e1e44a96ffc", - "sha256:a388960f979665b447f0847626e40f99af8cf191bce9dc571d716433130cb3a7", - "sha256:a51528192755f7429c5bcc9e80832c517340317c861318fea9cea081b57c9afd", - "sha256:b528e126c13816a4374e56b7b18bfe91f7a7f6576d1aadba5dee6a87a7f479ae", - "sha256:c1aa4de4919358c5ef119f6377bc5964b3a7023c23e845d9db7d9016fa0c5b1c", - "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5", - "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e", - "sha256:d47750cf07dee6b55d8423471be70d627314277976ff2edd1381f02d52dbadf9", - "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63", - "sha256:dd324f8ee05925ee85de0ea3f0d66e1362e8c80799eb4eb04927d32335a3e44a", - "sha256:eaca36a80acaacb8183930e2e5ad7f71539a66805d6204ea88736570b2876a7b", - "sha256:f567e972dce3bbc3a8076e0b675273b4a9e8576ac629149cf8286ee13c259ae5", - "sha256:fe48e4925455c964db914b958f6e7032d285848b7538a5e1b19aeb26ffaea3ec" - ], - "markers": "python_full_version >= '3.7.1'", - "version": "==1.3.4" - }, - "pillow": { - "hashes": [ - "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76", - "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585", - "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b", - "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8", - "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55", - "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc", - "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645", - "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff", - "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc", - "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b", - "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6", - "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20", - "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e", - "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a", - "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779", - "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02", - "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39", - "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f", - "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a", - "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409", - "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c", - "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488", - "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b", - "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d", - "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09", - "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b", - "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153", - "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9", - "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad", - "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df", - "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df", - "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed", - "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed", - "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698", - "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29", - "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649", - "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49", - "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b", - "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2", - "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a", - "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78" - ], - "index": "pypi", - "version": "==8.4.0" - }, - "protobuf": { - "hashes": [ - "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942", - "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f", - "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560", - "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089", - "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6", - "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04", - "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7", - "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e", - "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d", - "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7", - "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c", - "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002", - "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6", - "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853", - "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d", - "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3", - "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8", - "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995", - "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea", - "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6", - "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2", - "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b", - "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17", - "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d" - ], - "markers": "python_version >= '3.5'", - "version": "==3.19.1" - }, - "pyasn1": { - "hashes": [ - "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", - "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", - "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", - "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", - "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", - "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", - "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", - "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", - "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", - "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", - "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", - "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", - "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" - ], - "version": "==0.4.8" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8", - "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199", - "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811", - "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed", - "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4", - "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", - "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74", - "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb", - "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45", - "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd", - "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0", - "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d", - "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405" - ], - "version": "==0.2.8" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pydot": { - "hashes": [ - "sha256:248081a39bcb56784deb018977e428605c1c758f10897a339fce1dd728ff007d", - "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.4.2" - }, - "pyformlang": { - "hashes": [ - "sha256:ba8a1932ce93801a812881a0f718a8e96cc31e8fdd2df846f8fab27d63125f76", - "sha256:d7b46a25ad73f5e2d1cf59145fb746de2b5168971e876400d773f3e2010ddcca" - ], - "index": "pypi", - "version": "==0.1.26" - }, - "pygraphblas": { - "hashes": [ - "sha256:ecd763cf54394ca1d5e37474dcf36b8727ae001df3e3485180e0d94be81b858b" - ], - "index": "pypi", - "version": "==5.1.8.0" - }, - "pyparsing": { - "hashes": [ - "sha256:4881e3d2979f27b41a3a2421b10be9cbfa7ce2baa6c7117952222f8bbea6650c", - "sha256:9329d1c1b51f0f76371c4ded42c5ec4cc0be18456b22193e0570c2da98ed288b" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.5" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pytz": { - "hashes": [ - "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c", - "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326" - ], - "version": "==2021.3" - }, - "rdflib": { - "hashes": [ - "sha256:7ce4d757eb26f4dd43205ec340d8c097f29e5adfe45d6ea20238c731dc679879", - "sha256:bb24f0058070d5843503e15b37c597bc3858d328d11acd9476efad3aa62f555d" - ], - "markers": "python_version >= '3.7'", - "version": "==6.0.0" - }, - "requests": { - "hashes": [ - "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", - "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.26.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", - "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a", - "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc" - ], - "version": "==1.3.0" - }, - "rsa": { - "hashes": [ - "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2", - "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9" - ], - "markers": "python_version >= '3.6'", - "version": "==4.7.2" - }, - "scipy": { - "hashes": [ - "sha256:1437073f1d4664990879aa8f9547524764372e0fef84a077be4b19e82bba7a8d", - "sha256:17fd991a275e4283453f89d404209aa92059ac68d76d804b4bc1716a3742e1b5", - "sha256:1ea6233f5a365cb7945b4304bd06323ece3ece85d6a3fa8598d2f53e513467c9", - "sha256:2d25272c03ee3c0fe5e0dff1bb7889280bb6c9e1766fa9c7bde81ad8a5f78694", - "sha256:30bdda199667e74b50208a793eb1ba47a04e5e3fa16f5ff06c6f7969ae78e4da", - "sha256:359b60a0cccd17723b9d5e329a5212a710e771a3ddde800e472fb93732756c46", - "sha256:39f838ea5ce8da868785193d88d05cf5a6d5c390804ec99de29a28e1dcdd53e6", - "sha256:4d175ba93e00d8eef8f7cd70d4d88a9106a86800c82ea03cf2268c36d6545483", - "sha256:5273d832fb9cd5724ee0d335c16a903b923441107dd973d27fc4293075a9f4e3", - "sha256:54951f51d731c832b1b8885e0a92e89f33d087de7e40d02078bf0d49c7cbdbb5", - "sha256:74f518ce542533054695f743e4271cb8986b63f95bb51d70fcee4f3929cbff7d", - "sha256:7b1d0f5f524518f1a86f288443528e4ff4a739c0966db663af4129b7ac7849f8", - "sha256:82c5befebf54d799d77e5f0205c03030f57f69ba2541baa44d2e6ad138c28cd3", - "sha256:8482c8e45857ab0a5446eb7460d2307a27cbbe659d6d2257820c6d6eb950fd0f", - "sha256:87cf3964db0f1cce17aeed5bfc1b89a6b4b07dbfc48e50d21fa3549e00456803", - "sha256:8b5726a0fedeaa6beb1095e4466998bdd1d1e960b28db9b5a16c89cbd7b2ebf1", - "sha256:97eb573e361a73a553b915dc195c6f72a08249964b1a33f157f9659f3b6210d1", - "sha256:a80eb01c43fd98257ec7a49ff5cec0edba32031b5f86503f55399a48cb2c5379", - "sha256:cac71d5476a6f56b50459da21f6221707e0051ebd428b2137db32ef4a43bb15e", - "sha256:d86abd1ddf421dea5e9cebfeb4de0d205b3dc04e78249afedba9c6c3b2227ff2", - "sha256:dc2d1bf41294e63c7302bf499973ac0c7f73c93c01763db43055f6525234bf11", - "sha256:e08b81fcd9bf98740b58dc6fdd7879e33a64dcb682201c1135f7d4a75216bb05", - "sha256:e3efe7ef75dfe627b354ab0af0dbc918eadee97cc80ff1aabea6d3e01114ebdd", - "sha256:fa2dbabaaecdb502641b0b3c00dec05fb475ae48655c66da16c9ed24eda1e711" - ], - "markers": "python_version < '3.11' and python_version >= '3.7'", - "version": "==1.7.2" - }, - "setuptools": { - "hashes": [ - "sha256:a481fbc56b33f5d8f6b33dce41482e64c68b668be44ff42922903b03872590bf", - "sha256:dae6b934a965c8a59d6d230d3867ec408bb95e73bd538ff77e71fedf1eaca729" - ], - "markers": "python_version >= '3.6'", - "version": "==58.5.3" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "ssgetpy": { - "hashes": [ - "sha256:3860bce10bc4ee3cb5ed93c08388bafadfcaac24175a7c42fcbb187c033ddc0d", - "sha256:bf298e173f1545d451877220b1912b2c94af79314e9c4812a5398a393c1010b5" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0rc2" - }, - "suitesparse-graphblas": { - "hashes": [ - "sha256:129a4661501c4d8c00b2370180804669b832ca36ddd749617622295c40b7888c", - "sha256:15810893b43217ee0299e2e3e11c1c194270b285d279e6dc8d1bda1a729a3f3e", - "sha256:267f52bd75e16c09dd08524ee41868c9eb528f5de44e70a287734e4df6510f7e", - "sha256:e07520e3cbb09de0b17235c5c02fc21fdfea62f602545116c14386ff6104d09a" - ], - "markers": "python_version >= '3.7'", - "version": "==5.1.10.1" - }, - "tensorboard": { - "hashes": [ - "sha256:239f78a4a8dff200ce585a030c787773a8c1184d5c159252f5f85bac4e3c3b38" - ], - "markers": "python_version >= '3.6'", - "version": "==2.7.0" - }, - "tensorboard-data-server": { - "hashes": [ - "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7", - "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a", - "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee" - ], - "markers": "python_version >= '3.6'", - "version": "==0.6.1" - }, - "tensorboard-plugin-wit": { - "hashes": [ - "sha256:2a80d1c551d741e99b2f197bb915d8a133e24adb8da1732b840041860f91183a" - ], - "version": "==1.8.0" - }, - "tensorflow": { - "hashes": [ - "sha256:12f19d6c7161ffef21e69a14e60aaaf47dc25bff133ac954839f1308262b29bf", - "sha256:62db938367757c9fb54c56fa2fbbe3cd3bf05c046ee1da81667257fea554dc70", - "sha256:7c7572de9f80547dcf3bcc2f94cd7d5dc14395d494c3b9d223e7b3e579a6b24f", - "sha256:8f6066b1cbd33e47beb5a35df0f06c60d59d1e0b02ac420789b7679e8d879c70", - "sha256:a4539796c4ff8e7a3baaa6337cea237e49ec8f56a9c45f618a9642b474c0c8bd", - "sha256:ae17148d608a280c8d82d18404883367015e36cb1f4e02d34395d70a96912432", - "sha256:be0a2e3460dacc4214eedd6484b11e50583cb790ed37c6872378100fcaff1090", - "sha256:e24b83eecf70d0467adbd5c5507a4f1a448b6df8cc586471d3decce8c50dd25f", - "sha256:eb653c85bdd00ca2dba6104b1a04c37eaffccec47e5357a0f57d7b9ee9a678c0" - ], - "index": "pypi", - "version": "==2.7.0" - }, - "tensorflow-estimator": { - "hashes": [ - "sha256:325b5a224864379242b7b76c6987ca544239be82579d33e68ec7c2bda57abc9d" - ], - "version": "==2.7.0" - }, - "tensorflow-io-gcs-filesystem": { - "hashes": [ - "sha256:14b80d87c3876a1915d5b45ee46291c82f577113de88a852fa5918f0ac1ba949", - "sha256:32b9f368486ffd748801e3d8602de1868334406ef47d63292f7fceee1f491d4d", - "sha256:4345771944f1bbc4bd38ed35de9248b8cec10af78ce107c96f0a45bd20b152d5", - "sha256:a4d2a30c3fb966f8d74ec41380ab2a101dad46aa73a3f0e9f8e5b1dee6bc4cf7", - "sha256:ab3967679a360650609fef533c1fe9157f83264a542584a95407329b45dfd514", - "sha256:c5de3d42baf0bf0c7fad173dcae2e93b79176f701b1083f28fa698bd507785ec", - "sha256:def85b562e53d540c8ed620780b3a7f6c925a69bc4534014c29100bd4a014f9c", - "sha256:fd002b7eb9d0e4d7c188825ce3f73be5a63c19eac67ef3441f5448d3b024405b", - "sha256:ff734f70c80709263b633839f29edc15669f13fa213084ce4e85a3d0215ca7fd" - ], - "markers": "python_version < '3.10' and python_version >= '3.6'", - "version": "==0.22.0" - }, - "termcolor": { - "hashes": [ - "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b" - ], - "version": "==1.1.0" - }, - "tqdm": { - "hashes": [ - "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c", - "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.62.3" - }, - "typing-extensions": { - "hashes": [ - "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", - "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", - "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" - ], - "version": "==3.10.0.2" - }, - "urllib3": { - "hashes": [ - "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", - "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.26.7" - }, - "werkzeug": { - "hashes": [ - "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", - "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" - ], - "markers": "python_version >= '3.6'", - "version": "==2.0.2" - }, - "wheel": { - "hashes": [ - "sha256:21014b2bd93c6d0034b6ba5d35e4eb284340e09d63c59aef6fc14b0f346146fd", - "sha256:e2ef7239991699e3355d54f8e968a21bb940a1dbf34a4d226741e64462516fad" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.37.0" - }, - "wrapt": { - "hashes": [ - "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179", - "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096", - "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374", - "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df", - "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185", - "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785", - "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7", - "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909", - "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918", - "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33", - "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068", - "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829", - "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af", - "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79", - "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce", - "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc", - "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36", - "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade", - "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca", - "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32", - "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125", - "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e", - "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709", - "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f", - "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b", - "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb", - "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb", - "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489", - "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640", - "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb", - "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851", - "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d", - "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44", - "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13", - "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2", - "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb", - "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b", - "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9", - "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755", - "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c", - "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a", - "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf", - "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3", - "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229", - "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e", - "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de", - "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554", - "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10", - "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80", - "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056", - "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.13.3" - } - }, - "develop": { - "backports.entry-points-selectable": { - "hashes": [ - "sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b", - "sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386" - ], - "markers": "python_version >= '2.7'", - "version": "==1.1.1" - }, - "black": { - "hashes": [ - "sha256:6eb7448da9143ee65b856a5f3676b7dda98ad9abe0f87fce8c59291f15e82a5b", - "sha256:a9952229092e325fe5f3dae56d81f639b23f7131eb840781947e4b2886030f33" - ], - "index": "pypi", - "version": "==21.10b0" - }, - "cfgv": { - "hashes": [ - "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426", - "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.3.1" - }, - "click": { - "hashes": [ - "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", - "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" - ], - "markers": "python_version >= '3.6'", - "version": "==8.0.3" - }, - "distlib": { - "hashes": [ - "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31", - "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05" - ], - "version": "==0.3.3" - }, - "filelock": { - "hashes": [ - "sha256:7afc856f74fa7006a289fd10fa840e1eebd8bbff6bffb69c26c54a0512ea8cf8", - "sha256:bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b" - ], - "markers": "python_version >= '3.6'", - "version": "==3.3.2" - }, - "identify": { - "hashes": [ - "sha256:6f0368ba0f21c199645a331beb7425d5374376e71bc149e9cb55e45cb45f832d", - "sha256:ba945bddb4322394afcf3f703fa68eda08a6acc0f99d9573eb2be940aa7b9bba" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==2.3.5" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, - "nodeenv": { - "hashes": [ - "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", - "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" - ], - "version": "==1.6.0" - }, - "pathspec": { - "hashes": [ - "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", - "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" - ], - "version": "==0.9.0" - }, - "platformdirs": { - "hashes": [ - "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2", - "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d" - ], - "markers": "python_version >= '3.6'", - "version": "==2.4.0" - }, - "pre-commit": { - "hashes": [ - "sha256:3c25add78dbdfb6a28a651780d5c311ac40dd17f160eb3954a0c59da40a505a7", - "sha256:a4ed01000afcb484d9eb8d504272e642c4c4099bbad3a6b27e519bd6a3e928a6" - ], - "index": "pypi", - "version": "==2.15.0" - }, - "pyyaml": { - "hashes": [ - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_version >= '3.6'", - "version": "==6.0" - }, - "regex": { - "hashes": [ - "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f", - "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc", - "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4", - "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4", - "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8", - "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f", - "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a", - "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef", - "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f", - "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc", - "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50", - "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d", - "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d", - "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733", - "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36", - "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345", - "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0", - "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12", - "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646", - "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667", - "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244", - "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29", - "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec", - "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf", - "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4", - "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449", - "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a", - "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d", - "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb", - "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e", - "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83", - "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e", - "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a", - "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94", - "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc", - "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e", - "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965", - "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0", - "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36", - "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec", - "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23", - "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7", - "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe", - "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6", - "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b", - "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb", - "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b", - "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30", - "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e" - ], - "version": "==2021.11.10" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tomli": { - "hashes": [ - "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee", - "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade" - ], - "markers": "python_version >= '3.6'", - "version": "==1.2.2" - }, - "typing-extensions": { - "hashes": [ - "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e", - "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7", - "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34" - ], - "version": "==3.10.0.2" - }, - "virtualenv": { - "hashes": [ - "sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814", - "sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==20.10.0" - } - } -} diff --git a/README.md b/README.md index 8c3c834..1c16e40 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,131 @@ -[![Check code style](https://github.com/JetBrains-Research/Genegram/actions/workflows/check_code_style.yml/badge.svg)](https://github.com/JetBrains-Research/Genegram/actions/workflows/check_code_style.yml) +[![Check code style](https://github.com/JetBrains-Research/Genegram/actions/workflows/lint.yml/badge.svg)](https://github.com/JetBrains-Research/Genegram/actions/workflows/lint.yml) +[![Check code style](https://github.com/JetBrains-Research/Genegram/actions/workflows/test.yml/badge.svg)](https://github.com/JetBrains-Research/Genegram/actions/workflows/test.yml) --- -# Genegram + +# Genegram: RNA Secondary Structure Prediction by Combination of Formal Grammars and Neural Networks ## Description -[comment]: <> (TODO) +Command Line Tool for Predicting RNA Secondary Structure Connectivity Table -## Install +## FASTA format -We use [`Pipenv`](https://pipenv.pypa.io/en/latest/) to manage dependencies. +**⚠️ We use a slightly more strict FASTA format than the [standard](https://en.wikipedia.org/wiki/FASTA_format) ⚠️** -### [Install Pipenv](https://pipenv.pypa.io/en/latest/#install-pipenv-today) +### Our format -```shell -pip install --user pipenv +```text +>RNA description +RNA sequence +... +>RNA description +RNA sequence ``` -### Install Genegram (from sources) +### Example + +```text +>34551 +GGCCUCCAAGCUGUGCCUUGGGUGGCC +>34552 +CCUCCCUUACAAGGAGG +>34553 +GGAGUGGCCGAAAGGCAUCUCC +>34735 +GGCUCUCAGUGAGCC +``` -```shell -git clone https://github.com/JetBrains-Research/Genegram -cd Genegram -pipenv install --ignore-pipfile +## Requirements + +### Hardware + +* Genegram requires only a standard computer with around 16 GB RAM to support the in-memory operations for RNAs sequence length less than 500 + +### OS + +* [`Ubuntu 18.04`](https://releases.ubuntu.com/18.04/) + +### Software + +* [`Python 3.8`](https://www.python.org/downloads/release/python-380/) +* [`Virtualenv`](https://virtualenv.pypa.io/en/latest/installation/) +* [`CUDA 11.2`](https://developer.nvidia.com/cuda-11.2.0-download-archive) *(Optional If using GPU)* +* [`cuDNN 8.1`](https://developer.nvidia.com/cudnn) *(Optional If using GPU)* + +### Python packages + +```text +tensorflow==2.7.0 +pygraphblas==4.2.2 +pyformlang==0.1.26 ``` +## Installation + +### From PyPI + +To install **Gengram** from PyPI following commands can be used in terminal: + +1. `virtualenv -p python3.8 venv` +2. `source ./venv/bin/activate` +3. `pip install genegram` + +### From sources + +To install **Gengram** from sources following commands can be used in terminal: + +1. `git clone https://github.com/JetBrains-Research/Genegram.git` +2. `cd Genegram` + +Either follow `virtualenv` column steps or `conda` column steps to create virtual environment +and to install **Genegram** dependencies given in table below: + +| | virtualenv | conda | +| --- |--------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 3. | `virtualenv -p python3.8 venv` | `conda create -n venv python=3.8` | +| 4. | `source ./venv/bin/activate` | `conda activate venv` | +| 5. | To run Genegram on **CPU:**
`pip install tensorflow-cpu==2.7.0`
or
To run Genegram on **GPU:**
`pip install tensorflow-gpu==2.7.0` | To run Genegram on **CPU:**
`conda install tensorflow-cpu==2.7.0 --channel conda-forge`
or
To run Genegram on **GPU:**
`conda install tensorflow-gpu==2.7.0 --channel conda-forge` | +| 6. | `pip install .` | `pip install .` | + ## Usage -Run the following command with arguments. +After successfully installing the package, you have three options to use it: -```bash -python -m genegram -``` +1. `python -m genegram ` +2. `genegram ` +3. ```Python + from genegram import process_fasta_group + process_fasta_group() + ``` -### **Arguments** +### Arguments Argument | Required | Description :--- | :---: | :--- -i, --inp | True | Path to the [`FASTA`](http://genetics.bwh.harvard.edu/pph/FASTA.html) file -o, --out | True | Path to the folder where the [`Connectivity Tables`](http://rna.urmc.rochester.edu/Text/File_Formats.html#CT) will be saved -m, --model | False | Type of the model to be used:
`main` -- The default model, the best on average
`mps` -- Multiplet prediction model
`pks` -- Pseudoknots prediction model --l, --log | False | Type of the logging level to be used:
`INFO` -- Confirmation that things are working as expected
`WARNING` -- An indication that something unexpected happened, the software is still working as expected
`ERROR` -- Due to a more serious problem, the software has not been able to perform some function
`CRITICAL` -- A serious error, indicating that the program itself may be unable to continue running
`DEBUG` -- Detailed information, typically of interest only when diagnosing problems -## Code style +## Examples + +If you have installed Genegram from sources, you can run the following example (in the Genegram folder) + +`genegram -i tests/data/seq.fasta -o EXAMPLE_FOLDER -m main` + +## Information for Developers + +### Code Style We recommend you use a [pre-commit](https://pre-commit.com/#install) hook, which runs [black](https://github.com/psf/black) when you type git commit. -### Install pre-commit +#### Install pre-commit ```shell pipenv install --dev pre-commit install ``` -### Use pre-commit +#### Use pre-commit ```shell pre-commit run --all-files --color always --verbose diff --git a/genegram/__init__.py b/genegram/__init__.py index 1a925cb..9335c4d 100644 --- a/genegram/__init__.py +++ b/genegram/__init__.py @@ -1,6 +1,11 @@ +__version__ = "1.0.0" + import genegram.shared from genegram.shared import * +import genegram.utils +from genegram.utils import * + import genegram.cfpq_pyalgo from genegram.cfpq_pyalgo import * @@ -9,3 +14,6 @@ import genegram.predict from genegram.predict import * + +import genegram.main +from genegram.main import * diff --git a/genegram/__main__.py b/genegram/__main__.py index 1f42c4f..9738b24 100644 --- a/genegram/__main__.py +++ b/genegram/__main__.py @@ -1,12 +1,11 @@ -import logging +"""Genegram CLI""" from argparse import ArgumentParser, RawTextHelpFormatter from pathlib import Path -from genegram.parsing import read_fasta -from genegram.predict import rna_predict, setup_model, clear_session -from genegram.shared import ROOT +from genegram.main import process_fasta_group -if __name__ == "__main__": + +def main(): parser = ArgumentParser( description="Genegram", formatter_class=RawTextHelpFormatter ) @@ -34,49 +33,15 @@ "\npks -- Pseudoknots prediction model" ), ) - parser.add_argument( - "-l", - "--log", - required=False, - type=str, - choices=["INFO", "WARNING", "ERROR", "CRITICAL", "DEBUG"], - default="INFO", - help=( - "Type of the logging level to be used:" - "\nINFO -- Confirmation that things are working as expected" - "\nWARNING -- An indication that something unexpected happened, the software is still working as expected" - "\nERROR -- Due to a more serious problem, the software has not been able to perform some function" - "\nCRITICAL -- A serious error, indicating that the program itself may be unable to continue running" - "\nDEBUG -- Detailed information, typically of interest only when diagnosing problems" - ), - ) args = parser.parse_args() - logging.basicConfig( - level=args.log, - format="[%(asctime)s]>%(levelname)s>%(message)s", - datefmt="%Y-%m-%d %H:%M:%S", + process_fasta_group( + fasta=Path(args.inp).resolve(), + out=Path(args.out).resolve(), + weights=args.model, ) - logging.info(f"Parse {args=}") - - out = Path(args.out).resolve() - if not out.exists(): - out.mkdir(parents=True, exist_ok=True) - logging.info(f"Create {out} dir") - - model = setup_model(ROOT / "weights" / f"{args.model}.h5") - - for rna in read_fasta(Path(args.inp).resolve()): - pred = rna_predict(rna, model) - target_path = out / f"{rna.description}.ct" - - with open(target_path, "w") as fout: - fout.write(pred.ct) - logging.info( - f"Save {rna=} secondary structure connectivity table to {target_path=}" - ) - - clear_session() +if __name__ == "__main__": + main() diff --git a/genegram/cfpq_pyalgo.py b/genegram/cfpq_pyalgo.py index 674de45..700d86b 100644 --- a/genegram/cfpq_pyalgo.py +++ b/genegram/cfpq_pyalgo.py @@ -1,121 +1,238 @@ -from typing import AbstractSet, Iterable, Tuple +"""The All-Pairs CFL-reachability module""" +from collections import defaultdict +from typing import Dict, List -from cfpq_data import cnf_from_cfg -from networkx import MultiDiGraph -from pyformlang.cfg import CFG, Variable, Terminal -from pygraphblas import Matrix, BOOL +import pygraphblas as gb +from pyformlang.cfg import CFG, Production, Variable, Terminal, Epsilon __all__ = [ - "CNF", - "BooleanMatrixGraph", "all_pairs_reachability_matrix", + "WCNF", + "BooleanMatrixGraph", ] -class CNF: - def __init__( - self, - start_symbol: Variable, - variables: AbstractSet[Variable], - terminals: AbstractSet[Terminal], - unary_productions: Iterable[Tuple[Variable, Terminal]], - double_productions: Iterable[Tuple[Variable, Variable, Variable]], - ): - self.start_symbol = start_symbol - self.variables = variables - self.terminals = terminals - self.unary_productions = unary_productions - self.double_productions = double_productions - - @classmethod - def from_cfg(cls, cfg: CFG): - base_cnf = cnf_from_cfg(cfg) - - unary_productions = list() - double_productions = list() - - for p in base_cnf.productions: - if len(p.body) == 0: - unary_productions.append((p.head, Terminal("$"))) - elif len(p.body) == 1: - unary_productions.append((p.head, Terminal(p.body[0].value))) - elif len(p.body) == 2: - double_productions.append( - (p.head, Variable(p.body[0].value), Variable(p.body[1].value)) - ) - - cnf = CNF( - base_cnf.start_symbol, - base_cnf.variables, - base_cnf.terminals, - unary_productions, - double_productions, - ) - - return cnf - - @classmethod - def from_text(cls, text, start_symbol: Variable = Variable("S")): - return CNF.from_cfg(CFG.from_text(text, start_symbol)) - - class BooleanMatrixGraph: - def __init__(self, matrices_size: int): - self.matrices_size = matrices_size - self.matrices = dict() - - def __getitem__(self, item) -> Matrix: - if item not in self.matrices: - self.matrices[item] = Matrix.sparse( - BOOL, self.matrices_size, self.matrices_size + """A Labeled Graph decomposed into Boolean Matrices""" + + def __init__(self, number_of_nodes: int = 0): + self._matrices: Dict[str, gb.Matrix] = dict() + self._number_of_nodes: int = number_of_nodes + + def __getitem__(self, label: str) -> gb.Matrix: + if label not in self._matrices: + self._matrices[label] = gb.Matrix.sparse( + typ=gb.BOOL, + nrows=self._number_of_nodes, + ncols=self._number_of_nodes, ) - return self.matrices[item] + return self._matrices[label] - def __setitem__(self, key, value): - self.matrices[key] = value - - def __iter__(self): - return self.matrices.__iter__() + def __setitem__(self, label: str, matrix: gb.Matrix) -> None: + self._matrices[label] = matrix @property - def labels(self): - return list(self.matrices.keys()) - - @classmethod - def from_multidigraph(cls, g: MultiDiGraph): - bmg = BooleanMatrixGraph(g.number_of_nodes()) + def number_of_nodes(self) -> int: + """The number of nodes in the graph + Returns + ------- + number_of_nodes: int + Number of nodes in the graph + """ + return self._number_of_nodes + + def add_edge(self, u: int, v: int, label: str) -> None: + """Add an edge between `u` and `v` with label `label`. + The nodes `u` and `v` will be automatically added if they are + not already in the graph. + Parameters + ---------- + u: int + The tail of the edge + v: int + The head of the edge + label: str + The label of the edge + """ + if max(u, v) > self._number_of_nodes - 1: + self._number_of_nodes = max(u, v) + 1 + for key in self._matrices: + self._matrices[key].resize(self._number_of_nodes, self._number_of_nodes) + + if label not in self._matrices: + self._matrices[label] = gb.Matrix.sparse( + typ=gb.BOOL, nrows=self._number_of_nodes, ncols=self._number_of_nodes + ) - for u, v, edge_labels in g.edges(data=True): - bmg[Terminal(edge_labels["label"])][u, v] = 1 + self._matrices[label][u, v] = True + + +class WCNF: + """A Weak Chomsky Normal Form of Context-Free Grammar + in which products take the following form: + - A -> B C + - A -> a + - A -> epsilon + where `A`, `B` and `C` are variables; `a` is an arbitrary terminal + Also known as Weak Chomsky Normal Form + + Parameters + ---------- + cfg: CFG + Context-Free Grammar + """ + + def __init__(self, cfg: CFG): + self._cfg: CFG = cfg + self.start_variable: Variable = cfg.start_symbol + + if not _is_in_wcnf(cfg): + cnf = cfg.to_normal_form() + else: + cnf = cfg + + self.epsilon_productions: List[Production] = [] + self.unary_productions: List[Production] = [] + self.binary_productions: List[Production] = [] + + for production in self._cfg.productions: + if production.body in ([], Epsilon): + if production not in self.epsilon_productions: + self.epsilon_productions.append(production) + + for production in cnf.productions: + if len(production.body) == 1: + if production not in self.unary_productions: + self.unary_productions.append(production) + elif len(production.body) == 2: + if production not in self.binary_productions: + self.binary_productions.append(production) + + self.productions = ( + self.epsilon_productions + self.unary_productions + self.binary_productions + ) - return bmg + self.variables: List[Variable] = [] + self.terminals: List[Terminal] = [] - @classmethod - def from_triples(cls, triples): - number_of_nodes = max({max(u, v) for u, label, v in triples}) + for production in self.productions: + if production.head not in self.variables: + self.variables.append(production.head) - bmg = BooleanMatrixGraph(number_of_nodes + 1) + for term in production.body: + if isinstance(term, Terminal): + if term not in self.terminals: + self.terminals.append(term) + elif isinstance(term, Variable): + if term not in self.variables: + self.variables.append(term) - for u, label, v in triples: - bmg[Terminal(label)][u, v] = 1 + def contains(self, word: str) -> bool: + """Gives the membership of a word to the grammar - return bmg + Parameters + ---------- + word : str + The word to check + Returns + ---------- + contains : bool + Whether word if in the grammar's language or not + """ + return self._cfg.contains(word) -def all_pairs_reachability_matrix(graph: BooleanMatrixGraph, grammar: CNF): - m = BooleanMatrixGraph(graph.matrices_size) - for l, r in grammar.unary_productions: - m[l] += graph[r] + @classmethod + def from_text(cls, text, start_symbol=Variable("S")): + """ + Read a Weak Chomsky Normal Form Context-Free Grammar from a text. + The text contains one rule per line. + The structure of a production is: + head -> body1 | body2 | ... | bodyn + where | separates the bodies. + A variable (or non terminal) begins by a capital letter. + A terminal begins by a non-capital character + Terminals and Variables are separated by spaces. + An epsilon symbol can be represented by epsilon, $, ε, ϵ or Є. + If you want to have a variable name starting with a non-capital \ + letter or a terminal starting with a capital letter, you can \ + explicitly give the type of your symbol with "VAR:yourVariableName" \ + or "TER:yourTerminalName" (with the quotation marks). For example: + S -> "TER:John" "VAR:d" a b + + Parameters + ---------- + text : str + The text of transform + start_symbol : str, optional + The start symbol, S by default + + Returns + ------- + wcnf : WCNF + A Weak Chomsky Normal Form Context-Free Grammar + """ + return cls(CFG.from_text(text, start_symbol)) + + +def _is_in_wcnf(cfg: CFG) -> bool: + for production in cfg.productions: + if len(production.body) > 2: + return False + elif len(production.body) == 2: + if not ( + isinstance(production.body[0], Variable) + and isinstance(production.body[1], Variable) + ): + return False + elif len(production.body) == 1: + if not isinstance(production.body[0], Terminal): + return False + return True + + +def all_pairs_reachability_matrix( + graph: BooleanMatrixGraph, grammar: WCNF +) -> gb.Matrix: + """Determines the pairs of vertices (`u`, `v`) + where there exists a path from `u` to `v` + in `graph` and its word is in the language of `grammar` + + Parameters + ---------- + graph: BooleanMatrixGraph + grammar: WCNF + + Returns + ------- + pairs: pygraphblas.Matrix + Reachability matrix + """ + bmg = BooleanMatrixGraph(graph.number_of_nodes) + + for production in grammar.unary_productions: + # production :: l -> r + l = production.head.value + r = production.body[0].value + + bmg[l] += graph[r] changed = True + nvals = defaultdict(int) while changed: changed = False - for l, r1, r2 in grammar.double_productions: - old_nnz = m[l].nvals - m[l] += m[r1].mxm(m[r2], semiring=BOOL.ANY_PAIR) - new_nnz = m[l].nvals + for production in grammar.binary_productions: + # production :: l -> r1 r2 + l = production.head.value + r1 = production.body[0].value + r2 = production.body[1].value + + bmg[l] += bmg[r1].mxm(bmg[r2], semiring=gb.BOOL.ANY_PAIR) + + nnz = bmg[l].nvals + + changed |= nvals[l] != nnz - if old_nnz != new_nnz: - changed = True + nvals[l] = nnz - return m[grammar.start_symbol] + return bmg[grammar.start_variable.value] diff --git a/genegram/grammar.txt b/genegram/grammar.txt deleted file mode 100644 index 61a102b..0000000 --- a/genegram/grammar.txt +++ /dev/null @@ -1,27 +0,0 @@ -S -> S1 -S0 -> Any_str | Any_str S1 S0 -S2 -> a S0 u | g S0 c | u S0 a | c S0 g -S3 -> a S2 u | g S2 c | u S2 a | c S2 g -S4 -> a S3 u | g S3 c | u S3 a | c S3 g -S1 -> a S1 u | u S1 a | c S1 g | g S1 c | S4 -Any -> a | u | c | g -Any_str -> Any -Any_str -> Any Any -Any_str -> Any Any Any -Any_str -> Any Any Any Any -Any_str -> Any Any Any Any Any -Any_str -> Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any -Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any diff --git a/genegram/main.py b/genegram/main.py new file mode 100644 index 0000000..78c40fd --- /dev/null +++ b/genegram/main.py @@ -0,0 +1,107 @@ +"""FASTA processing module""" +from pathlib import Path + +from genegram.parsing import ( + read_fasta_group, + parse_rna_group, + read_fasta_single, + parse_rna_single, +) +from genegram.predict import setup_model, clear_session, predict +from genegram.shared import ROOT +from genegram.utils import binarize_image, create_connectivity_table, remove_multiplets + +__all__ = [ + "process_fasta_single", + "process_fasta_group", +] + + +def process_fasta_single( + fasta: Path, + out: Path, + weights: str = "main", + bin_coeff: float = 0.6, +) -> Path: + """Process Secondary Structure for each RNA from `fasta` file using single method + + Parameters + ---------- + fasta: Path + The path to FASTA input file + out: + The path where the resulting connectivity tables will be saved + weights: + Type of predictive model weights + bin_coeff: + Binarization coefficient + + Returns + ------- + out: Path + The path where the resulting connectivity tables will be saved + """ + if not out.exists(): + out.mkdir(parents=True, exist_ok=True) + + model = setup_model(ROOT / "weights" / f"{weights}.h5") + + for rna in read_fasta_single(fasta): + image = parse_rna_single(rna) + prediction = predict(image, model) + prediction_cleaned = remove_multiplets(prediction) + pred_bin = binarize_image(prediction_cleaned, bin_coeff) + ct = create_connectivity_table(pred_bin, *rna) + + target_path = out / f"{rna.description}.ct" + with open(target_path, "w") as fout: + fout.write(ct) + + clear_session() + + return Path(out).resolve() + + +def process_fasta_group( + fasta: Path, + out: Path, + weights: str = "main", + bin_coeff: float = 0.6, +) -> Path: + """Process Secondary Structure for each RNA from `fasta` file using group method + + Parameters + ---------- + fasta: Path + The path to FASTA input file + out: + The path where the resulting connectivity tables will be saved + weights: + Type of predictive model weights + bin_coeff: + Binarization coefficient + + Returns + ------- + out: Path + The path where the resulting connectivity tables will be saved + """ + if not out.exists(): + out.mkdir(parents=True, exist_ok=True) + + model = setup_model(ROOT / "weights" / f"{weights}.h5") + + for rna_group in read_fasta_group(fasta): + for index, image in parse_rna_group(rna_group): + prediction = predict(image, model) + prediction_cleaned = remove_multiplets(prediction) + pred_bin = binarize_image(prediction_cleaned, bin_coeff) + ct = create_connectivity_table(pred_bin, *rna_group[index]) + + target_path = out / f"{rna_group[index].description}.ct" + with open(target_path, "w") as fout: + fout.write(ct) + + clear_session() + + return Path(out).resolve() diff --git a/genegram/parsing.py b/genegram/parsing.py index ab7a8c8..58d591b 100644 --- a/genegram/parsing.py +++ b/genegram/parsing.py @@ -1,71 +1,172 @@ -import logging -from collections import namedtuple +"""FASTA parsing module""" from pathlib import Path -from typing import Iterator +from typing import List, Tuple -from PIL import Image, ImageDraw -from cfpq_data import cfg_from_txt -from pyformlang.cfg import Terminal +import numpy as np -from genegram.cfpq_pyalgo import BooleanMatrixGraph, CNF, all_pairs_reachability_matrix -from genegram.shared import ROOT - -GRAMMAR = CNF.from_cfg(cfg_from_txt(ROOT / "grammar.txt")) -NUCLEOTIDE_TO_COLOR = {"a": 32, "c": 64, "g": 96, "u": 128} - -RNA = namedtuple("RNA", ["description", "sequence"]) +from genegram.cfpq_pyalgo import BooleanMatrixGraph, all_pairs_reachability_matrix +from genegram.shared import GROUP_LEN, GRAMMAR, RNA, NUCLEOTIDE_TO_COLOR __all__ = [ - "RNA", - "read_fasta", - "rna_to_img", + "read_fasta_single", + "read_fasta_group", + "parse_rna_single", + "parse_rna_group", ] -def read_fasta(fasta: Path) -> Iterator[RNA]: - logging.info(f"Read {fasta=}") +def read_fasta_single(fasta: Path) -> List[RNA]: + """Read FASTA file from `fasta` path. + The FASTA file must be in the following format: + >`RNA description` + `RNA sequence` + >`RNA description` + `RNA sequence` + ... + >`RNA description` + `RNA sequence` + + Parameters + ---------- + fasta: Path + Path to the FASTA file + + Returns + ------- + rna: List[RNA] + RNA sequences from a FASTA file + """ with open(fasta, "r") as fin: - while True: - desc = fin.readline().strip() + data = fin.readlines() + return [ + RNA(data[i].strip()[1:], data[i + 1].strip()) for i in range(0, len(data), 2) + ] + + +def read_fasta_group(fasta: Path, limit: int = GROUP_LEN) -> List[List[RNA]]: + """Read FASTA file from `fasta` path. + The FASTA file must be in the following format: + >`RNA description` + `RNA sequence` + >`RNA description` + `RNA sequence` + ... + >`RNA description` + `RNA sequence` + + Parameters + ---------- + fasta: Path + Path to the FASTA file + limit: int + Limit on the total size of a group of RNA sequences + + Returns + ------- + rna: List[List[RNA]] + Grouped RNA sequences from a FASTA file + """ + with open(fasta, "r") as fin: + data = fin.readlines() + result = [] + cur_group = [] + cur_len = 0 - # EOF - if not desc: - break + for i in range(0, len(data), 2): + rna = RNA(data[i].strip()[1:], data[i + 1].strip()) + cur_group.append(rna) + cur_len += 1 + len(rna.sequence) - seq = fin.readline().strip() + if abs(limit - cur_len) < abs(limit - (cur_len + 1 + len(rna.sequence))): + result.append(cur_group) + cur_group = [] + cur_len = 0 - logging.debug(f"read_fasta():\n{desc=} \n{seq=}") + if cur_group: + result.append(cur_group) - yield RNA(desc[1:], seq.lower()) + return result -def rna_to_img(rna: RNA) -> Image: - logging.info(f"{rna=} to image") +def parse_rna_single(rna: RNA) -> np.ndarray: + """Parse RNA `rna` with grammar `GRAMMAR` - bmg = BooleanMatrixGraph(matrices_size=len(rna.sequence) + 1) + Parameters + ---------- + rna: RNA - for i, nucleotide in enumerate(rna.sequence): - bmg[Terminal(nucleotide)][i, i + 1] = True + Returns + ------- + image: np.ndarray + The result of parsing presented as an image + """ + n = len(rna.sequence) + bmg = BooleanMatrixGraph(n + 1) + # in grammar, nucleotides must be lowercase + for i, nucleotide in enumerate(rna.sequence.lower()): + bmg[nucleotide][i, i + 1] = True - reachabilities = all_pairs_reachability_matrix( - graph=bmg, - grammar=GRAMMAR, - ) + reachabilities = all_pairs_reachability_matrix(bmg, GRAMMAR) - # create white&black 8-bit image - img = Image.new(mode="L", size=(bmg.matrices_size - 1, bmg.matrices_size - 1)) - im_draw = ImageDraw.Draw(img) + image = np.zeros((n, n), dtype=np.uint8) - # draw reachabilities + # draw pairings I, J, _ = reachabilities.to_lists() - for k, i in enumerate(I): - j = J[k] - im_draw.line(xy=[(j - 3, i + 2), (j - 1, i)], fill=255) + for i, j in zip(I, J): + image[i + 2, j - 3] = 255 + image[i + 1, j - 2] = 255 + image[i, j - 1] = 255 - # draw letters + # draw nucleotides for i, nucleotide in enumerate(rna.sequence): - im_draw.point(xy=(i, i), fill=NUCLEOTIDE_TO_COLOR[nucleotide]) + image[i, i] = NUCLEOTIDE_TO_COLOR[nucleotide] + + return image + + +def parse_rna_group(rna_group: List[RNA]) -> List[Tuple[int, np.ndarray]]: + """Parse a group of RNA sequences `rna_group` with grammar `GRAMMAR` + + Parameters + ---------- + rna_group: List[RNA] + A group of RNA sequences + + Returns + ------- + index, image: Tuple[int, np.ndarray] + Iterator over the result of parsing + """ + result = [] + # in grammar, nucleotides must be lowercase + glued_rna = "$".join((seq.lower() for _, seq in rna_group)) + bmg = BooleanMatrixGraph(len(glued_rna) + 1) + for i, nucleotide in enumerate(glued_rna): + bmg[nucleotide][i, i + 1] = True + + reachabilities = all_pairs_reachability_matrix(bmg, GRAMMAR) + + prefix = 0 + for index, (desc, seq) in enumerate(rna_group): + n = len(seq) + + # create white&black 8-bit image + image = np.zeros((n, n), dtype=np.uint8) + + # draw pairings + I, J, _ = reachabilities[ + prefix : (prefix + n), prefix : (prefix + n) + ].to_lists() + for i, j in zip(I, J): + image[i + 2, j - 3] = 255 + image[i + 1, j - 2] = 255 + image[i, j - 1] = 255 + + # draw nucleotides + for i, nucleotide in enumerate(seq): + image[i, i] = NUCLEOTIDE_TO_COLOR[nucleotide] - logging.debug(f"rna_to_img():\n{bmg=} \n{reachabilities=} \n{img=}") + prefix += n + 1 + result.append((index, image)) - return img + return result diff --git a/genegram/predict.py b/genegram/predict.py index b445c61..1fb7787 100644 --- a/genegram/predict.py +++ b/genegram/predict.py @@ -1,13 +1,6 @@ -import logging -import os -from collections import namedtuple from pathlib import Path import numpy as np - -# hide TensorFlow warnings -os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" - import tensorflow as tf from keras import backend as K from keras import regularizers @@ -15,158 +8,17 @@ from keras.models import Model from tensorflow.keras.layers import Layer -from genegram.parsing import RNA, rna_to_img - __all__ = [ - "PredictionContext", - "rna_predict", - "img_predict", - "img_binarize", - "img_to_ct", - "remove_multiplets", + "predict", "setup_model", "clear_session", ] -PredictionContext = namedtuple( - "PredictionContext", - [ - "rna", - "img_rna", - "img_pred", - "ct", - ], -) - def clear_session(): - logging.info("Clear TensorFlow session") K.clear_session() -def img_to_ct(img, meta, seq): - logging.info(f"Create Connectivity Table for RNA({meta}, {seq})") - - size = len(img) - ct = " " + str(size) + " " + meta + "\n" - for i in range(size): - pair = 0 - for j in range(size): - if img[i][j] == 255 or img[j][i] == 255: - pair = j + 1 - ct += ( - " " - + str(i + 1) - + " " - + seq[i] - + " " - + str(i) - + " " - + str(i + 2) - + " " - + str(pair) - + " " - + str(i + 1) - + "\n" - ) - - logging.debug(f"img_to_ct():\n{img=} \n{meta=} \n{seq=} \n{ct=}") - - return ct - - -def remove_multiplets(img): - def get_multiplets(i0, j0, img): - mps = [] - size = len(img) - for i in range(size): - if img[i0, i] == 255 and (i0, i) != (i0, j0) and i0 <= i: - mps.append((i0, i)) - if img[j0, i] == 255 and (j0, i) != (i0, j0) and j0 <= i: - mps.append((j0, i)) - if img[i, i0] == 255 and (i, i0) != (i0, j0) and i <= i0: - mps.append((i, i0)) - if img[i, j0] == 255 and (i, j0) != (i0, j0) and i <= j0: - mps.append((i, j0)) - return list(set(mps)) - - def get_stem_len(i0, j0, img): - size = len(img) - stem_len = 1 - i, j = i0 + 1, j0 - 1 - while ( - i < len(img) - and j >= 0 - and img[i][j] == 255 - and len(get_multiplets(i, j, img)) == 0 - ): - stem_len += 1 - i += 1 - j -= 1 - i, j = i0 - 1, j0 + 1 - while ( - i >= 0 - and j < len(img) - and img[i][j] == 255 - and len(get_multiplets(i, j, img)) == 0 - ): - stem_len += 1 - i -= 1 - j += 1 - return stem_len - - size = len(img) - mps_nums = dict() - for i in range(size): - for j in range(i + 1, size): - if img[i][j] == 255: - mps = get_multiplets(i, j, img) - if len(mps) > 0: - mps_nums[(i, j)] = len(mps) - mps_nums = {k: v for k, v in sorted(mps_nums.items(), key=lambda item: -item[1])} - to_delete = [] - while len(mps_nums) > 0: - for el in to_delete: - del mps_nums[el] - to_delete = [] - for (i, j) in mps_nums.keys(): - if not (i, j) in to_delete: - mps = get_multiplets(i, j, img) - if len(mps) > 0: - min_stem_len = get_stem_len(i, j, img) - i_pick, j_pick = i, j - for (i0, j0) in mps: - stem_len = get_stem_len(i0, j0, img) - if stem_len < min_stem_len: - i_pick, j_pick = i0, j0 - min_stem_len = stem_len - img[i_pick][j_pick] = 0 - to_delete.append((i_pick, j_pick)) - else: - to_delete.append((i, j)) - return img - - -# set each gray pixel of network prediction to black/white -# according to threshold coeff -def img_binarize(img, coeff=0.6): - logging.info(f"Binarize image") - - im = img.copy() - size = len(img) - for i in range(size): - for j in range(size): - if i != j: - if im[i][j] > 255 * coeff: - im[i][j] = 255 - else: - im[i][j] = 0 - - logging.debug(f"img_binarize():\n{img=} \n{coeff=} \n{im=}") - - return im - - # Model definition functions # layer that for inputs i1, i2, i3, i4 @@ -199,7 +51,7 @@ def call(self, input): # residual unit definition, classical structure in ML -def res_unit(inputs, filters, kernels, activ, bn=False, dr=0): +def res_unit(inputs, filters, kernels, activ, bn=False, dr=0.0): x = inputs if bn: x = BatchNormalization()(x) @@ -217,8 +69,8 @@ def res_unit(inputs, filters, kernels, activ, bn=False, dr=0): # residual network definition: repeating res units -# + skip sonnections(add layers) -def res_network(inputs, units_num, filters, kernels, activ="relu", bn=False, dr=0): +# + skip connections(add layers) +def res_network(inputs, units_num, filters, kernels, activ="relu", bn=False, dr=0.0): x = res_unit(inputs, filters, kernels, activ, bn, dr) x = add([x, inputs]) x = Activation(activ)(x) @@ -227,14 +79,13 @@ def res_network(inputs, units_num, filters, kernels, activ="relu", bn=False, dr= x = add([x, y]) x = Activation(activ)(x) outputs = x - model = Model(inputs=inputs, outputs=outputs) return model # model that combines several residual networks def parallel_res_network( - blocks_num, units_num, filters, kernels, activ="relu", bn=False, dr=0 + blocks_num, units_num, filters, kernels, activ="relu", bn=False, dr=0.0 ): inputs = Input(shape=(None, None, 1)) all_outputs = [] @@ -260,13 +111,13 @@ def setup_model(weights: Path): tf_session_config = tf.compat.v1.ConfigProto( intra_op_parallelism_threads=1, inter_op_parallelism_threads=1 ) + tf_session_config.gpu_options.allow_growth = True + tf_session = tf.compat.v1.Session( graph=tf.compat.v1.get_default_graph(), config=tf_session_config ) K.set_session(tf_session) - logging.info("Setup TensorFlow session") - # setup model model = parallel_res_network( blocks_num=4, @@ -278,57 +129,33 @@ def setup_model(weights: Path): dr=0.1, ) model.load_weights(weights) - - logging.info(f"Setup model") - logging.debug(f"setup_model():\n{weights=} \n{model=}") - return model -# get model output on input image pil_img -def img_predict(pil_img, model): - logging.info("Predict image") +def predict(image: np.ndarray, model: Model) -> np.ndarray: + """Get `model` output on input `pil_image` - img = np.array(pil_img) - n = len(img) - inp = np.array([img]).reshape((1, n, n, 1)) + Parameters + ---------- + image: np.ndarray + Input image + model: Model + Prediction model - pred = model.predict(inp) + Returns + ------- + array: np.ndarray + Prediction result + """ + n = image.shape[0] + inputs = np.array([image]).reshape((1, n, n, 1)) - res = np.full(shape=(n, n), fill_value=255, dtype=np.uint8) + prediction = model.predict(inputs) + + result = np.full(shape=(n, n), fill_value=255, dtype=np.uint8) for i in range(n): for j in range(n): - res[i][j] = min(pred[0][i][j][0], 255) - - logging.debug( - f"img_predict():\n{pil_img=} \n{model=} \n{img=} \n{n=} \n{inp=} \n{pred=} \n{res=}" - ) - - return res - + if prediction[0][i][j][0] < 255: + result[i][j] = prediction[0][i][j][0] -def rna_predict(rna: RNA, model): - logging.info("Predict RNA") - - img_rna = rna_to_img(rna) - - pred = img_predict(img_rna, model) - - pred_bin = img_binarize(pred) - - ct = img_to_ct( - pred_bin, - rna.description, - rna.sequence.upper(), - ) - - logging.debug( - f"rna_predict():\n{rna=} \n{model=} \n{img_rna=} \n{pred=} \n{pred_bin=} \n{ct=}" - ) - - return PredictionContext( - rna=rna, - img_rna=img_rna, - img_pred=pred_bin, - ct=ct, - ) + return result diff --git a/genegram/shared.py b/genegram/shared.py index 62cde4b..8266ef1 100644 --- a/genegram/shared.py +++ b/genegram/shared.py @@ -1,7 +1,52 @@ +from collections import namedtuple from pathlib import Path +from genegram.cfpq_pyalgo import WCNF + __all__ = [ "ROOT", + "GROUP_LEN", + "NUCLEOTIDE_TO_COLOR", + "RNA", + "GRAMMAR", ] +GROUP_LEN = 100_000 + ROOT = Path(__file__).parent.resolve() + +NUCLEOTIDE_TO_COLOR = {"A": 32, "C": 64, "G": 96, "U": 128} + +RNA = namedtuple("RNA", ["description", "sequence"]) + +GRAMMAR = WCNF.from_text( + """ +S -> S1 +S0 -> Any_str | Any_str S1 S0 +S2 -> a S0 u | g S0 c | u S0 a | c S0 g +S3 -> a S2 u | g S2 c | u S2 a | c S2 g +S4 -> a S3 u | g S3 c | u S3 a | c S3 g +S1 -> a S1 u | u S1 a | c S1 g | g S1 c | S4 +Any -> a | u | c | g +Any_str -> Any +Any_str -> Any Any +Any_str -> Any Any Any +Any_str -> Any Any Any Any +Any_str -> Any Any Any Any Any +Any_str -> Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +Any_str -> Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any Any +""" +) diff --git a/genegram/utils.py b/genegram/utils.py new file mode 100644 index 0000000..772c041 --- /dev/null +++ b/genegram/utils.py @@ -0,0 +1,166 @@ +"""Post-processing utilities""" +__all__ = [ + "binarize_image", + "create_connectivity_table", + "remove_multiplets", +] + +import numpy as np + + +def create_connectivity_table(image: np.ndarray, meta: str, seq: str) -> str: + """Creates a Connectivity Table from an RNA Secondary Structure + represented as an image + + Parameters + ---------- + image: np.ndarray + RNA Secondary Structure + meta: str + RNA description + seq: str + RNA sequence + + Returns + ------- + ct: str + Connectivity Table + """ + size = len(image) + # ct = " " + str(size) + " " + meta + "\n" + ct = f" {size} {meta}\n" + for i in range(size): + pair = 0 + for j in range(size): + if image[i][j] == 255 or image[j][i] == 255: + pair = j + 1 + ct += f" {i + 1} {seq[i]} {i} {i + 2} {pair} {i + 1}\n" + # ct += ( + # " " + # + str(i + 1) + # + " " + # + seq[i] + # + " " + # + str(i) + # + " " + # + str(i + 2) + # + " " + # + str(pair) + # + " " + # + str(i + 1) + # + "\n" + # ) + return ct + + +def remove_multiplets(image: np.ndarray) -> np.ndarray: + """Remove multiplets from network prediction of RNA Secondary Structure + + Parameters + ---------- + image: np.ndarray + RNA Secondary Structure + + Returns + ------- + image: np.ndarray + RNA Secondary Structure with removed multiplets + """ + + def get_multiplets(i0, j0, image): + mps = [] + size = len(image) + for i in range(size): + if image[i0, i] == 255 and (i0, i) != (i0, j0) and i0 <= i: + mps.append((i0, i)) + if image[j0, i] == 255 and (j0, i) != (i0, j0) and j0 <= i: + mps.append((j0, i)) + if image[i, i0] == 255 and (i, i0) != (i0, j0) and i <= i0: + mps.append((i, i0)) + if image[i, j0] == 255 and (i, j0) != (i0, j0) and i <= j0: + mps.append((i, j0)) + return list(set(mps)) + + def get_stem_len(i0, j0, image): + size = len(image) + stem_len = 1 + i, j = i0 + 1, j0 - 1 + while ( + i < len(image) + and j >= 0 + and image[i][j] == 255 + and len(get_multiplets(i, j, image)) == 0 + ): + stem_len += 1 + i += 1 + j -= 1 + i, j = i0 - 1, j0 + 1 + while ( + i >= 0 + and j < len(image) + and image[i][j] == 255 + and len(get_multiplets(i, j, image)) == 0 + ): + stem_len += 1 + i -= 1 + j += 1 + return stem_len + + size = len(image) + mps_nums = dict() + for i in range(size): + for j in range(i + 1, size): + if image[i][j] == 255: + mps = get_multiplets(i, j, image) + if len(mps) > 0: + mps_nums[(i, j)] = len(mps) + mps_nums = {k: v for k, v in sorted(mps_nums.items(), key=lambda item: -item[1])} + to_delete = [] + while len(mps_nums) > 0: + for el in to_delete: + del mps_nums[el] + to_delete = [] + for (i, j) in mps_nums.keys(): + if not (i, j) in to_delete: + mps = get_multiplets(i, j, image) + if len(mps) > 0: + min_stem_len = get_stem_len(i, j, image) + i_pick, j_pick = i, j + for (i0, j0) in mps: + stem_len = get_stem_len(i0, j0, image) + if stem_len < min_stem_len: + i_pick, j_pick = i0, j0 + min_stem_len = stem_len + image[i_pick][j_pick] = 0 + to_delete.append((i_pick, j_pick)) + else: + to_delete.append((i, j)) + return image + + +def binarize_image(image: np.ndarray, bin_coeff: float = 0.6) -> np.ndarray: + """Set each gray pixel of network prediction `image` to black/white + according to threshold coeff + + Parameters + ---------- + image: np.ndarray + Network prediction image + bin_coeff: float + Binarization coefficient + + Returns + ------- + im: np.ndarray + Binarized image + """ + im = image.copy() + size = len(image) + for i in range(size): + for j in range(size): + if i != j: + if im[i][j] > 255 * bin_coeff: + im[i][j] = 255 + else: + im[i][j] = 0 + return im diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..374b58c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" diff --git a/requirements/default.txt b/requirements/default.txt new file mode 100644 index 0000000..3a854d8 --- /dev/null +++ b/requirements/default.txt @@ -0,0 +1,3 @@ +tensorflow==2.7.0 +pygraphblas==4.2.2 +pyformlang==0.1.26 diff --git a/requirements/developer.txt b/requirements/developer.txt new file mode 100644 index 0000000..d3b5927 --- /dev/null +++ b/requirements/developer.txt @@ -0,0 +1,2 @@ +pre-commit==2.15.0 +black==22.3.0 diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 0000000..c2845bf --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1 @@ +pytest==7.0.1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ddd3612 --- /dev/null +++ b/setup.py @@ -0,0 +1,93 @@ +from pathlib import Path + +from setuptools import setup, find_packages + +root = Path(__file__).parent.resolve() + +with open(root / "README.md", "r", encoding="utf-8") as f: + long_description = f.read() + +with open(root / "genegram/__init__.py", "r", encoding="utf-8") as f: + for line in f: + if line.startswith("__version__"): + version = line.strip().split()[-1][1:-1] + break + +name = "genegram" + +description = ( + "Project for genomic sequences analysis " + "employing the combination of formal grammars and neural networks" +) + +authors = { + "vadyushkins": ("Vadim Abzalov", "vadim.i.abzalov@gmail.com"), + "LuninaPolina": ("Polina Lunina", "lunina_polina@mail.ru"), +} + +url = "https://github.com/JetBrains-Research/Genegram" + +project_urls = { + "Documentation": "https://github.com/JetBrains-Research/Genegram", + "Source Code": "https://github.com/JetBrains-Research/Genegram", + "Bug Tracker": "https://github.com/JetBrains-Research/Genegram/issues", +} + +platforms = ["Linux", "Mac OSX", "Unix"] + +keywords = [] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Intended Audience :: Education", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Unix", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Scientific/Engineering :: Bio-Informatics", + "Topic :: Scientific/Engineering :: Information Analysis", +] + + +def parse_requirements_file(filename): + with open(filename) as fid: + requires = [l.strip() for l in fid.readlines() if not l.startswith("#")] + + return requires + + +install_requires = parse_requirements_file(root / "requirements" / "default.txt") +extras_require = { + dep: parse_requirements_file(root / "requirements" / f"{dep}.txt") + for dep in ["developer", "test"] +} + +if __name__ == "__main__": + setup( + name=name, + description=description, + long_description=long_description, + author=authors["vadyushkins"][0], + author_email=authors["vadyushkins"][1], + maintainer=authors["LuninaPolina"][0], + maintainer_email=authors["LuninaPolina"][1], + version=version, + keywords=keywords, + packages=find_packages(), + package_data={"": ["weights/*.h5"]}, + platforms=platforms, + url=url, + project_urls=project_urls, + classifiers=classifiers, + install_requires=install_requires, + extras_require=extras_require, + python_requires=">=3.8", + zip_safe=False, + entry_points={"console_scripts": ["genegram=genegram.__main__:main"]}, + ) diff --git a/tests/cfpq_pyalgo/test_all_pairs_reachability_matrix.py b/tests/cfpq_pyalgo/test_all_pairs_reachability_matrix.py new file mode 100644 index 0000000..901db29 --- /dev/null +++ b/tests/cfpq_pyalgo/test_all_pairs_reachability_matrix.py @@ -0,0 +1,61 @@ +from pyformlang.cfg import CFG + +from genegram import BooleanMatrixGraph, WCNF, all_pairs_reachability_matrix + + +def test_empty_graph(): + bmg = BooleanMatrixGraph() + wcnf = WCNF.from_text("S -> a") + + I, J, _ = all_pairs_reachability_matrix(bmg, wcnf).to_lists() + + assert set(zip(I, J)) == set() + + +def test_no_reachable_vertices(): + bmg = BooleanMatrixGraph() + bmg.add_edge(0, 1, "b") + bmg.add_edge(1, 2, "c") + bmg.add_edge(2, 0, "d") + + wcnf = WCNF.from_text("S -> S S | a") + + I, J, _ = all_pairs_reachability_matrix(bmg, wcnf).to_lists() + + assert set(zip(I, J)) == set() + + +def test_worst_case(): + # The graph is two cycles of coprime lengths with a single common vertex + # The first cycle is labeled by the open bracket + # The second cycle is labeled by the close bracket + bmg = BooleanMatrixGraph() + bmg.add_edge(0, 1, "a") + bmg.add_edge(1, 2, "a") + bmg.add_edge(2, 0, "a") + bmg.add_edge(0, 3, "b") + bmg.add_edge(3, 0, "b") + + wcnf = WCNF.from_text("S -> a S b | a b") + + I, J, _ = all_pairs_reachability_matrix(bmg, wcnf).to_lists() + + assert set(zip(I, J)) == {(0, 0), (1, 0), (0, 3), (1, 3), (2, 0), (2, 3)} + + +def test_full_graph_result(): + # The case when the input graph is sparse, but the result is a full graph. + # Input graph is a cycle, all edges of which are labeled by the same token + bmg = BooleanMatrixGraph() + bmg.add_edge(0, 1, label="a") + bmg.add_edge(1, 2, label="a") + bmg.add_edge(2, 3, label="a") + bmg.add_edge(3, 0, label="a") + + wcnf = WCNF.from_text("S -> S S | a") + + I, J, _ = all_pairs_reachability_matrix(bmg, wcnf).to_lists() + + assert set(zip(I, J)) == { + (v, to) for v in range(bmg.number_of_nodes) for to in range(bmg.number_of_nodes) + } diff --git a/tests/cfpq_pyalgo/test_boolean_matrix_graph.py b/tests/cfpq_pyalgo/test_boolean_matrix_graph.py new file mode 100644 index 0000000..8be31c3 --- /dev/null +++ b/tests/cfpq_pyalgo/test_boolean_matrix_graph.py @@ -0,0 +1,53 @@ +from pygraphblas import Matrix, BOOL + +from genegram import BooleanMatrixGraph + + +def test_empty(): + bmg = BooleanMatrixGraph() + + assert bmg.number_of_nodes == 0 + assert bmg._matrices == dict() + + +def test_one_edge(): + bmg = BooleanMatrixGraph() + bmg.add_edge(0, 1, "label") + + assert bmg.number_of_nodes == 2 + assert bmg._matrices == { + "label": Matrix.from_lists( + I=[0], + J=[1], + V=[True], + nrows=2, + ncols=2, + typ=BOOL, + ) + } + + +def test_two_edges(): + bmg = BooleanMatrixGraph() + bmg.add_edge(0, 1, "A") + bmg.add_edge(1, 2, "B") + + assert bmg._number_of_nodes == 3 + assert bmg._matrices == { + "A": Matrix.from_lists( + I=[0], + J=[1], + V=[True], + nrows=3, + ncols=3, + typ=BOOL, + ), + "B": Matrix.from_lists( + I=[1], + J=[2], + V=[True], + nrows=3, + ncols=3, + typ=BOOL, + ), + } diff --git a/tests/cfpq_pyalgo/test_wcnf.py b/tests/cfpq_pyalgo/test_wcnf.py new file mode 100644 index 0000000..42e07f2 --- /dev/null +++ b/tests/cfpq_pyalgo/test_wcnf.py @@ -0,0 +1,31 @@ +from pyformlang.cfg import CFG, Production, Variable, Terminal + +from genegram import WCNF + + +def test_empty(): + cfg = CFG.from_text("S -> epsilon") + + wcnf = WCNF(cfg) + + assert wcnf.start_variable == Variable("S") + assert wcnf.variables == [Variable("S")] + assert wcnf.terminals == [] + assert wcnf.productions == [Production(Variable("S"), [])] + assert wcnf.epsilon_productions == [Production(Variable("S"), [])] + assert wcnf.unary_productions == [] + assert wcnf.binary_productions == [] + + +def test_a(): + cfg = CFG.from_text("S -> a") + + wcnf = WCNF(cfg) + + assert wcnf.start_variable == Variable("S") + assert wcnf.variables == [Variable("S")] + assert wcnf.terminals == [Terminal("a")] + assert wcnf.productions == [Production(Variable("S"), [Terminal("a")])] + assert wcnf.epsilon_productions == [] + assert wcnf.unary_productions == [Production(Variable("S"), [Terminal("a")])] + assert wcnf.binary_productions == [] diff --git a/tests/data/binarize/34551.png b/tests/data/binarize/34551.png new file mode 100644 index 0000000..9f9be6f Binary files /dev/null and b/tests/data/binarize/34551.png differ diff --git a/tests/data/binarize/34552.png b/tests/data/binarize/34552.png new file mode 100644 index 0000000..4354477 Binary files /dev/null and b/tests/data/binarize/34552.png differ diff --git a/tests/data/binarize/34553.png b/tests/data/binarize/34553.png new file mode 100644 index 0000000..679d142 Binary files /dev/null and b/tests/data/binarize/34553.png differ diff --git a/tests/data/binarize/34735.png b/tests/data/binarize/34735.png new file mode 100644 index 0000000..e9d9850 Binary files /dev/null and b/tests/data/binarize/34735.png differ diff --git a/tests/data/binarize/34736.png b/tests/data/binarize/34736.png new file mode 100644 index 0000000..ac7e3fe Binary files /dev/null and b/tests/data/binarize/34736.png differ diff --git a/tests/data/binarize/34737.png b/tests/data/binarize/34737.png new file mode 100644 index 0000000..992b05f Binary files /dev/null and b/tests/data/binarize/34737.png differ diff --git a/tests/data/binarize/34738.png b/tests/data/binarize/34738.png new file mode 100644 index 0000000..648001a Binary files /dev/null and b/tests/data/binarize/34738.png differ diff --git a/tests/data/binarize/34745.png b/tests/data/binarize/34745.png new file mode 100644 index 0000000..7b8f094 Binary files /dev/null and b/tests/data/binarize/34745.png differ diff --git a/tests/data/binarize/34767.png b/tests/data/binarize/34767.png new file mode 100644 index 0000000..44c23a6 Binary files /dev/null and b/tests/data/binarize/34767.png differ diff --git a/tests/data/binarize/34840.png b/tests/data/binarize/34840.png new file mode 100644 index 0000000..d3ec01d Binary files /dev/null and b/tests/data/binarize/34840.png differ diff --git a/tests/data/parsing/34551.png b/tests/data/parsing/34551.png new file mode 100644 index 0000000..de05415 Binary files /dev/null and b/tests/data/parsing/34551.png differ diff --git a/tests/data/parsing/34552.png b/tests/data/parsing/34552.png new file mode 100644 index 0000000..db0cfe3 Binary files /dev/null and b/tests/data/parsing/34552.png differ diff --git a/tests/data/parsing/34553.png b/tests/data/parsing/34553.png new file mode 100644 index 0000000..fd6dd08 Binary files /dev/null and b/tests/data/parsing/34553.png differ diff --git a/tests/data/parsing/34735.png b/tests/data/parsing/34735.png new file mode 100644 index 0000000..291d7f3 Binary files /dev/null and b/tests/data/parsing/34735.png differ diff --git a/tests/data/parsing/34736.png b/tests/data/parsing/34736.png new file mode 100644 index 0000000..398aef5 Binary files /dev/null and b/tests/data/parsing/34736.png differ diff --git a/tests/data/parsing/34737.png b/tests/data/parsing/34737.png new file mode 100644 index 0000000..548dfeb Binary files /dev/null and b/tests/data/parsing/34737.png differ diff --git a/tests/data/parsing/34738.png b/tests/data/parsing/34738.png new file mode 100644 index 0000000..0e317ae Binary files /dev/null and b/tests/data/parsing/34738.png differ diff --git a/tests/data/parsing/34745.png b/tests/data/parsing/34745.png new file mode 100644 index 0000000..1dfb466 Binary files /dev/null and b/tests/data/parsing/34745.png differ diff --git a/tests/data/parsing/34767.png b/tests/data/parsing/34767.png new file mode 100644 index 0000000..fba9cd5 Binary files /dev/null and b/tests/data/parsing/34767.png differ diff --git a/tests/data/parsing/34840.png b/tests/data/parsing/34840.png new file mode 100644 index 0000000..cfcd3b1 Binary files /dev/null and b/tests/data/parsing/34840.png differ diff --git a/tests/data/predict/34551.png b/tests/data/predict/34551.png new file mode 100644 index 0000000..9f9be6f Binary files /dev/null and b/tests/data/predict/34551.png differ diff --git a/tests/data/predict/34552.png b/tests/data/predict/34552.png new file mode 100644 index 0000000..abc8d2c Binary files /dev/null and b/tests/data/predict/34552.png differ diff --git a/tests/data/predict/34553.png b/tests/data/predict/34553.png new file mode 100644 index 0000000..a6b9b0e Binary files /dev/null and b/tests/data/predict/34553.png differ diff --git a/tests/data/predict/34735.png b/tests/data/predict/34735.png new file mode 100644 index 0000000..0b5ac65 Binary files /dev/null and b/tests/data/predict/34735.png differ diff --git a/tests/data/predict/34736.png b/tests/data/predict/34736.png new file mode 100644 index 0000000..59ba7d3 Binary files /dev/null and b/tests/data/predict/34736.png differ diff --git a/tests/data/predict/34737.png b/tests/data/predict/34737.png new file mode 100644 index 0000000..5676881 Binary files /dev/null and b/tests/data/predict/34737.png differ diff --git a/tests/data/predict/34738.png b/tests/data/predict/34738.png new file mode 100644 index 0000000..8ed11ef Binary files /dev/null and b/tests/data/predict/34738.png differ diff --git a/tests/data/predict/34745.png b/tests/data/predict/34745.png new file mode 100644 index 0000000..55b3c21 Binary files /dev/null and b/tests/data/predict/34745.png differ diff --git a/tests/data/predict/34767.png b/tests/data/predict/34767.png new file mode 100644 index 0000000..4602fab Binary files /dev/null and b/tests/data/predict/34767.png differ diff --git a/tests/data/predict/34840.png b/tests/data/predict/34840.png new file mode 100644 index 0000000..adc8874 Binary files /dev/null and b/tests/data/predict/34840.png differ diff --git a/tests/data/process/34551.ct b/tests/data/process/34551.ct new file mode 100644 index 0000000..181febc --- /dev/null +++ b/tests/data/process/34551.ct @@ -0,0 +1,28 @@ + 27 34551 + 1 G 0 2 27 1 + 2 G 1 3 26 2 + 3 C 2 4 25 3 + 4 C 3 5 24 4 + 5 U 4 6 22 5 + 6 C 5 7 21 6 + 7 C 6 8 20 7 + 8 A 7 9 19 8 + 9 A 8 10 18 9 + 10 G 9 11 17 10 + 11 C 10 12 0 11 + 12 U 11 13 0 12 + 13 G 12 14 0 13 + 14 U 13 15 0 14 + 15 G 14 16 0 15 + 16 C 15 17 0 16 + 17 C 16 18 10 17 + 18 U 17 19 9 18 + 19 U 18 20 8 19 + 20 G 19 21 7 20 + 21 G 20 22 6 21 + 22 G 21 23 5 22 + 23 U 22 24 0 23 + 24 G 23 25 4 24 + 25 G 24 26 3 25 + 26 C 25 27 2 26 + 27 C 26 28 1 27 diff --git a/tests/data/process/34552.ct b/tests/data/process/34552.ct new file mode 100644 index 0000000..5c54f96 --- /dev/null +++ b/tests/data/process/34552.ct @@ -0,0 +1,18 @@ + 17 34552 + 1 C 0 2 17 1 + 2 C 1 3 16 2 + 3 U 2 4 15 3 + 4 C 3 5 14 4 + 5 C 4 6 13 5 + 6 C 5 7 12 6 + 7 U 6 8 0 7 + 8 U 7 9 0 8 + 9 A 8 10 0 9 + 10 C 9 11 0 10 + 11 A 10 12 0 11 + 12 A 11 13 6 12 + 13 G 12 14 5 13 + 14 G 13 15 4 14 + 15 A 14 16 3 15 + 16 G 15 17 2 16 + 17 G 16 18 1 17 diff --git a/tests/data/process/34553.ct b/tests/data/process/34553.ct new file mode 100644 index 0000000..0c4a2c6 --- /dev/null +++ b/tests/data/process/34553.ct @@ -0,0 +1,23 @@ + 22 34553 + 1 G 0 2 22 1 + 2 G 1 3 21 2 + 3 A 2 4 20 3 + 4 G 3 5 19 4 + 5 U 4 6 18 5 + 6 G 5 7 17 6 + 7 G 6 8 16 7 + 8 C 7 9 15 8 + 9 C 8 10 14 9 + 10 G 9 11 13 10 + 11 A 10 12 0 11 + 12 A 11 13 0 12 + 13 A 12 14 10 13 + 14 G 13 15 9 14 + 15 G 14 16 8 15 + 16 C 15 17 7 16 + 17 A 16 18 6 17 + 18 U 17 19 5 18 + 19 C 18 20 4 19 + 20 U 19 21 3 20 + 21 C 20 22 2 21 + 22 C 21 23 1 22 diff --git a/tests/data/process/34735.ct b/tests/data/process/34735.ct new file mode 100644 index 0000000..8f820d8 --- /dev/null +++ b/tests/data/process/34735.ct @@ -0,0 +1,16 @@ + 15 34735 + 1 G 0 2 15 1 + 2 G 1 3 14 2 + 3 C 2 4 13 3 + 4 U 3 5 12 4 + 5 C 4 6 11 5 + 6 U 5 7 0 6 + 7 C 6 8 0 7 + 8 A 7 9 0 8 + 9 G 8 10 0 9 + 10 U 9 11 0 10 + 11 G 10 12 5 11 + 12 A 11 13 4 12 + 13 G 12 14 3 13 + 14 C 13 15 2 14 + 15 C 14 16 1 15 diff --git a/tests/data/process/34736.ct b/tests/data/process/34736.ct new file mode 100644 index 0000000..9bcf1a5 --- /dev/null +++ b/tests/data/process/34736.ct @@ -0,0 +1,28 @@ + 27 34736 + 1 G 0 2 27 1 + 2 G 1 3 26 2 + 3 U 2 4 25 3 + 4 G 3 5 24 4 + 5 C 4 6 23 5 + 6 U 5 7 22 6 + 7 C 6 8 21 7 + 8 A 7 9 0 8 + 9 G 8 10 0 9 + 10 U 9 11 0 10 + 11 A 10 12 18 11 + 12 G 11 13 0 12 + 13 G 12 14 0 13 + 14 A 13 15 0 14 + 15 G 14 16 0 15 + 16 A 15 17 0 16 + 17 C 16 18 0 17 + 18 G 17 19 11 18 + 19 A 18 20 0 19 + 20 A 19 21 0 20 + 21 C 20 22 7 21 + 22 C 21 23 6 22 + 23 G 22 24 5 23 + 24 C 23 25 4 24 + 25 A 24 26 3 25 + 26 C 25 27 2 26 + 27 C 26 28 1 27 diff --git a/tests/data/process/34737.ct b/tests/data/process/34737.ct new file mode 100644 index 0000000..f6ffe14 --- /dev/null +++ b/tests/data/process/34737.ct @@ -0,0 +1,11 @@ + 10 34737 + 1 G 0 2 10 1 + 2 G 1 3 9 2 + 3 G 2 4 8 3 + 4 C 3 5 0 4 + 5 A 4 6 0 5 + 6 A 5 7 0 6 + 7 G 6 8 0 7 + 8 C 7 9 3 8 + 9 C 8 10 2 9 + 10 C 9 11 1 10 diff --git a/tests/data/process/34738.ct b/tests/data/process/34738.ct new file mode 100644 index 0000000..bb594b2 --- /dev/null +++ b/tests/data/process/34738.ct @@ -0,0 +1,26 @@ + 25 34738 + 1 G 0 2 0 1 + 2 G 1 3 0 2 + 3 C 2 4 0 3 + 4 A 3 5 0 4 + 5 G 4 6 22 5 + 6 U 5 7 21 6 + 7 G 6 8 20 7 + 8 U 7 9 19 8 + 9 G 8 10 18 9 + 10 A 9 11 17 10 + 11 G 10 12 16 11 + 12 U 11 13 0 12 + 13 A 12 14 0 13 + 14 C 13 15 0 14 + 15 C 14 16 0 15 + 16 U 15 17 11 16 + 17 U 16 18 10 17 + 18 C 17 19 9 18 + 19 A 18 20 8 19 + 20 C 19 21 7 20 + 21 A 20 22 6 21 + 22 C 21 23 5 22 + 23 G 22 24 0 23 + 24 U 23 25 0 24 + 25 C 24 26 0 25 diff --git a/tests/data/process/34745.ct b/tests/data/process/34745.ct new file mode 100644 index 0000000..606adb8 --- /dev/null +++ b/tests/data/process/34745.ct @@ -0,0 +1,13 @@ + 12 34745 + 1 G 0 2 12 1 + 2 G 1 3 11 2 + 3 U 2 4 10 3 + 4 G 3 5 9 4 + 5 U 4 6 0 5 + 6 G 5 7 0 6 + 7 A 6 8 0 7 + 8 A 7 9 0 8 + 9 C 8 10 4 9 + 10 A 9 11 3 10 + 11 C 10 12 2 11 + 12 C 11 13 1 12 diff --git a/tests/data/process/34767.ct b/tests/data/process/34767.ct new file mode 100644 index 0000000..1cc9dbe --- /dev/null +++ b/tests/data/process/34767.ct @@ -0,0 +1,14 @@ + 13 34767 + 1 G 0 2 13 1 + 2 G 1 3 12 2 + 3 U 2 4 11 3 + 4 G 3 5 10 4 + 5 C 4 6 9 5 + 6 A 5 7 0 6 + 7 U 6 8 0 7 + 8 G 7 9 0 8 + 9 G 8 10 5 9 + 10 C 9 11 4 10 + 11 A 10 12 3 11 + 12 C 11 13 2 12 + 13 C 12 14 1 13 diff --git a/tests/data/process/34840.ct b/tests/data/process/34840.ct new file mode 100644 index 0000000..f9ad82d --- /dev/null +++ b/tests/data/process/34840.ct @@ -0,0 +1,102 @@ + 101 34840 + 1 G 0 2 0 1 + 2 G 1 3 0 2 + 3 C 2 4 0 3 + 4 G 3 5 0 4 + 5 G 4 6 0 5 + 6 U 5 7 0 6 + 7 A 6 8 24 7 + 8 C 7 9 23 8 + 9 U 8 10 22 9 + 10 A 9 11 68 10 + 11 G 10 12 20 11 + 12 U 11 13 19 12 + 13 U 12 14 18 13 + 14 G 13 15 17 14 + 15 A 14 16 0 15 + 16 G 15 17 0 16 + 17 A 16 18 14 17 + 18 A 17 19 13 18 + 19 A 18 20 12 19 + 20 C 19 21 11 20 + 21 U 20 22 10 21 + 22 A 21 23 9 22 + 23 G 22 24 8 23 + 24 C 23 25 7 24 + 25 U 24 26 0 25 + 26 C 25 27 0 26 + 27 U 26 28 0 27 + 28 G 27 29 0 28 + 29 U 28 30 0 29 + 30 A 29 31 0 30 + 31 U 30 32 0 31 + 32 C 31 33 0 32 + 33 U 32 34 0 33 + 34 G 33 35 0 34 + 35 G 34 36 77 35 + 36 C 35 37 76 36 + 37 G 36 38 75 37 + 38 G 37 39 74 38 + 39 A 38 40 0 39 + 40 C 39 41 0 40 + 41 C 40 42 73 41 + 42 C 41 43 72 42 + 43 G 42 44 71 43 + 44 U 43 45 0 44 + 45 G 44 46 70 45 + 46 G 45 47 69 46 + 47 U 46 48 68 47 + 48 G 47 49 67 48 + 49 G 48 50 62 49 + 50 A 49 51 61 50 + 51 A 50 52 60 51 + 52 C 51 53 59 52 + 53 U 52 54 58 53 + 54 G 53 55 0 54 + 55 U 54 56 0 55 + 56 G 55 57 0 56 + 57 A 56 58 0 57 + 58 A 57 59 53 58 + 59 G 58 60 52 59 + 60 U 59 61 51 60 + 61 U 60 62 50 61 + 62 C 61 63 49 62 + 63 G 62 64 0 63 + 64 G 63 65 0 64 + 65 A 64 66 0 65 + 66 A 65 67 0 66 + 67 C 66 68 48 67 + 68 A 67 69 47 68 + 69 C 68 70 46 69 + 70 C 69 71 45 70 + 71 C 70 72 43 71 + 72 G 71 73 42 72 + 73 G 72 74 41 73 + 74 C 73 75 38 74 + 75 C 74 76 37 75 + 76 G 75 77 36 76 + 77 C 76 78 35 77 + 78 A 77 79 101 78 + 79 A 78 80 100 79 + 80 C 79 81 99 80 + 81 C 80 82 98 81 + 82 C 81 83 97 82 + 83 U 82 84 96 83 + 84 G 83 85 95 84 + 85 G 84 86 94 85 + 86 G 85 87 93 86 + 87 A 86 88 92 87 + 88 G 87 89 0 88 + 89 A 88 90 0 89 + 90 G 89 91 0 90 + 91 G 90 92 0 91 + 92 U 91 93 87 92 + 93 C 92 94 86 93 + 94 C 93 95 85 94 + 95 C 94 96 84 95 + 96 A 95 97 83 96 + 97 G 96 98 82 97 + 98 G 97 99 81 98 + 99 G 98 100 80 99 + 100 U 99 101 79 100 + 101 U 100 102 78 101 diff --git a/tests/data/seq.fasta b/tests/data/seq.fasta new file mode 100644 index 0000000..b1d5cad --- /dev/null +++ b/tests/data/seq.fasta @@ -0,0 +1,20 @@ +>34551 +GGCCUCCAAGCUGUGCCUUGGGUGGCC +>34552 +CCUCCCUUACAAGGAGG +>34553 +GGAGUGGCCGAAAGGCAUCUCC +>34735 +GGCUCUCAGUGAGCC +>34736 +GGUGCUCAGUAGGAGACGAACCGCACC +>34737 +GGGCAAGCCC +>34738 +GGCAGUGUGAGUACCUUCACACGUC +>34745 +GGUGUGAACACC +>34767 +GGUGCAUGGCACC +>34840 +GGCGGUACUAGUUGAGAAACUAGCUCUGUAUCUGGCGGACCCGUGGUGGAACUGUGAAGUUCGGAACACCCGGCCGCAACCCUGGGAGAGGUCCCAGGGUU diff --git a/tests/parsing/test_parse_rna_group.py b/tests/parsing/test_parse_rna_group.py new file mode 100644 index 0000000..0bd61b2 --- /dev/null +++ b/tests/parsing/test_parse_rna_group.py @@ -0,0 +1,23 @@ +from pathlib import Path + +import numpy as np +from PIL import Image +from genegram import read_fasta_group, parse_rna_group + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_parse_rna_group(): + for rna_group in read_fasta_group(fasta, 50): + for index, image_actual in parse_rna_group(rna_group): + image_expected = np.array( + Image.open( + root.parent + / "data" + / "parsing" + / f"{rna_group[index].description}.png" + ) + ) + + assert np.array_equal(image_actual, image_expected) diff --git a/tests/parsing/test_parse_rna_single.py b/tests/parsing/test_parse_rna_single.py new file mode 100644 index 0000000..de76416 --- /dev/null +++ b/tests/parsing/test_parse_rna_single.py @@ -0,0 +1,19 @@ +from pathlib import Path + +import numpy as np +from PIL import Image + +from genegram import read_fasta_single, parse_rna_single + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_parse_rna_single(): + for rna in read_fasta_single(fasta): + image_actual = parse_rna_single(rna) + image_expected = np.array( + Image.open(root.parent / "data" / "parsing" / f"{rna.description}.png") + ) + + assert np.array_equal(image_actual, image_expected) diff --git a/tests/parsing/test_read_fasta_group.py b/tests/parsing/test_read_fasta_group.py new file mode 100644 index 0000000..58517a0 --- /dev/null +++ b/tests/parsing/test_read_fasta_group.py @@ -0,0 +1,78 @@ +from pathlib import Path + +from genegram import RNA, read_fasta_group + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_limit_0(): + data = list(read_fasta_group(fasta, 0)) + + assert data == [ + [RNA(description="34551", sequence="GGCCUCCAAGCUGUGCCUUGGGUGGCC")], + [RNA(description="34552", sequence="CCUCCCUUACAAGGAGG")], + [RNA(description="34553", sequence="GGAGUGGCCGAAAGGCAUCUCC")], + [RNA(description="34735", sequence="GGCUCUCAGUGAGCC")], + [RNA(description="34736", sequence="GGUGCUCAGUAGGAGACGAACCGCACC")], + [RNA(description="34737", sequence="GGGCAAGCCC")], + [RNA(description="34738", sequence="GGCAGUGUGAGUACCUUCACACGUC")], + [RNA(description="34745", sequence="GGUGUGAACACC")], + [RNA(description="34767", sequence="GGUGCAUGGCACC")], + [ + RNA( + description="34840", + sequence="GGCGGUACUAGUUGAGAAACUAGCUCUGUAUCUGGCGGACCCGUGGUGGAACUGUGAAGUUCGGAACACCCGGCCGCAACCCUGGGAGAGGUCCCAGGGUU", + ) + ], + ] + + +def test_limit_50(): + data = list(read_fasta_group(fasta, 50)) + + assert data == [ + [ + RNA(description="34551", sequence="GGCCUCCAAGCUGUGCCUUGGGUGGCC"), + RNA(description="34552", sequence="CCUCCCUUACAAGGAGG"), + ], + [ + RNA(description="34553", sequence="GGAGUGGCCGAAAGGCAUCUCC"), + RNA(description="34735", sequence="GGCUCUCAGUGAGCC"), + RNA(description="34736", sequence="GGUGCUCAGUAGGAGACGAACCGCACC"), + ], + [ + RNA(description="34737", sequence="GGGCAAGCCC"), + RNA(description="34738", sequence="GGCAGUGUGAGUACCUUCACACGUC"), + RNA(description="34745", sequence="GGUGUGAACACC"), + ], + [ + RNA(description="34767", sequence="GGUGCAUGGCACC"), + RNA( + description="34840", + sequence="GGCGGUACUAGUUGAGAAACUAGCUCUGUAUCUGGCGGACCCGUGGUGGAACUGUGAAGUUCGGAACACCCGGCCGCAACCCUGGGAGAGGUCCCAGGGUU", + ), + ], + ] + + +def test_limit_500(): + data = list(read_fasta_group(fasta, 500)) + + assert data == [ + [ + RNA(description="34551", sequence="GGCCUCCAAGCUGUGCCUUGGGUGGCC"), + RNA(description="34552", sequence="CCUCCCUUACAAGGAGG"), + RNA(description="34553", sequence="GGAGUGGCCGAAAGGCAUCUCC"), + RNA(description="34735", sequence="GGCUCUCAGUGAGCC"), + RNA(description="34736", sequence="GGUGCUCAGUAGGAGACGAACCGCACC"), + RNA(description="34737", sequence="GGGCAAGCCC"), + RNA(description="34738", sequence="GGCAGUGUGAGUACCUUCACACGUC"), + RNA(description="34745", sequence="GGUGUGAACACC"), + RNA(description="34767", sequence="GGUGCAUGGCACC"), + RNA( + description="34840", + sequence="GGCGGUACUAGUUGAGAAACUAGCUCUGUAUCUGGCGGACCCGUGGUGGAACUGUGAAGUUCGGAACACCCGGCCGCAACCCUGGGAGAGGUCCCAGGGUU", + ), + ] + ] diff --git a/tests/parsing/test_read_fasta_single.py b/tests/parsing/test_read_fasta_single.py new file mode 100644 index 0000000..57b04c4 --- /dev/null +++ b/tests/parsing/test_read_fasta_single.py @@ -0,0 +1,26 @@ +from pathlib import Path + +from genegram import RNA, read_fasta_single + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_read_fasta(): + data = list(read_fasta_single(fasta)) + + assert data == [ + RNA(description="34551", sequence="GGCCUCCAAGCUGUGCCUUGGGUGGCC"), + RNA(description="34552", sequence="CCUCCCUUACAAGGAGG"), + RNA(description="34553", sequence="GGAGUGGCCGAAAGGCAUCUCC"), + RNA(description="34735", sequence="GGCUCUCAGUGAGCC"), + RNA(description="34736", sequence="GGUGCUCAGUAGGAGACGAACCGCACC"), + RNA(description="34737", sequence="GGGCAAGCCC"), + RNA(description="34738", sequence="GGCAGUGUGAGUACCUUCACACGUC"), + RNA(description="34745", sequence="GGUGUGAACACC"), + RNA(description="34767", sequence="GGUGCAUGGCACC"), + RNA( + description="34840", + sequence="GGCGGUACUAGUUGAGAAACUAGCUCUGUAUCUGGCGGACCCGUGGUGGAACUGUGAAGUUCGGAACACCCGGCCGCAACCCUGGGAGAGGUCCCAGGGUU", + ), + ] diff --git a/tests/predict/test_predict.py b/tests/predict/test_predict.py new file mode 100644 index 0000000..e5e4715 --- /dev/null +++ b/tests/predict/test_predict.py @@ -0,0 +1,25 @@ +from glob import glob +from pathlib import Path + +import numpy as np +from PIL import Image, ImageChops + +from genegram import setup_model, predict, clear_session, __path__ as genegram_path + +root = Path(__file__).parent.resolve() + + +def test_predict(): + model = setup_model(Path(genegram_path[0]) / "weights" / "main.h5") + for image_path in glob(str(root.parent / "data" / "parsing" / "*")): + image = Image.open(image_path) + actual_prediction = Image.fromarray(predict(np.array(image), model)) + expected_prediction = Image.open( + root.parent / "data" / "predict" / f"{Path(image_path).stem}.png" + ) + + assert ( + ImageChops.difference(actual_prediction, expected_prediction).getbbox() + is None + ) + clear_session() diff --git a/tests/process/test_process.py b/tests/process/test_process.py new file mode 100644 index 0000000..23eb780 --- /dev/null +++ b/tests/process/test_process.py @@ -0,0 +1,18 @@ +import os +from filecmp import dircmp +from pathlib import Path + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_process(tmpdir): + tmp = tmpdir.mkdir("tmp") + os.system(f"python -m genegram -i {fasta} -o {tmp}") + + cmp = dircmp(tmp, root.parent / "data" / "process") + + assert cmp.left_only == [] + assert cmp.right_only == [] + assert cmp.diff_files == [] + assert cmp.funny_files == [] diff --git a/tests/process/test_process_group.py b/tests/process/test_process_group.py new file mode 100644 index 0000000..e5d4397 --- /dev/null +++ b/tests/process/test_process_group.py @@ -0,0 +1,19 @@ +from filecmp import dircmp +from pathlib import Path + +from genegram import process_fasta_group + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_process_fasta_single(tmpdir): + tmp = tmpdir.mkdir("tmp") + process_fasta_group(fasta, tmp) + + cmp = dircmp(tmp, root.parent / "data" / "process") + + assert cmp.left_only == [] + assert cmp.right_only == [] + assert cmp.diff_files == [] + assert cmp.funny_files == [] diff --git a/tests/process/test_process_single.py b/tests/process/test_process_single.py new file mode 100644 index 0000000..5e95eda --- /dev/null +++ b/tests/process/test_process_single.py @@ -0,0 +1,19 @@ +from filecmp import dircmp +from pathlib import Path + +from genegram import process_fasta_single + +root = Path(__file__).parent.resolve() +fasta = root.parent / "data" / "seq.fasta" + + +def test_process_fasta_single(tmpdir): + tmp = tmpdir.mkdir("tmp") + process_fasta_single(fasta, tmp) + + cmp = dircmp(tmp, root.parent / "data" / "process") + + assert cmp.left_only == [] + assert cmp.right_only == [] + assert cmp.diff_files == [] + assert cmp.funny_files == [] diff --git a/tests/utils/test_binarize_image.py b/tests/utils/test_binarize_image.py new file mode 100644 index 0000000..c3ebb65 --- /dev/null +++ b/tests/utils/test_binarize_image.py @@ -0,0 +1,34 @@ +from glob import glob +from pathlib import Path + +import numpy as np +from PIL import Image, ImageChops + +from genegram import ( + setup_model, + predict, + binarize_image, + remove_multiplets, + clear_session, + __path__ as genegram_path, +) + +root = Path(__file__).parent.resolve() + + +def test_binarize_image(): + model = setup_model(Path(genegram_path[0]) / "weights" / "main.h5") + for image_path in glob(str(root.parent / "data" / "parsing" / "*")): + image = Image.open(image_path) + prediction = predict(np.array(image), model) + pred_bin = binarize_image(prediction) + + actual_binarized = Image.fromarray(pred_bin) + expected_binarized = Image.open( + root.parent / "data" / "binarize" / f"{Path(image_path).stem}.png" + ) + + assert ( + ImageChops.difference(actual_binarized, expected_binarized).getbbox() + is None + )