diff --git a/.circleci/config.yml b/.circleci/config.yml index 5bcd6520..dcb2f03f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: steps: - checkout - run: mkdir -p /tmp/coverage && mkdir -p /tmp/test_results - - run: pip3 install -e .[dev] + - run: pip3 install . - run: name: Test command: pytest -vv tests diff --git a/.gitignore b/.gitignore index c778b7d9..f65ce068 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ dev_notes !tests/deep_mlp/end_to_end.ipynb *.egg* worktree/ +build/ +dist/ # tests .pytest_cache tests/*/cpp diff --git a/MANIFEST.in b/MANIFEST.in index 5bf45efe..15362a77 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ include LICENSE README.md -recursive-include utensor_cgen/backend/snippets/templates * +recursive-include utensor_cgen/backend/utensor/snippets/templates * diff --git a/Makefile b/Makefile index 804d8296..6eb2e9ee 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ tests: rm -f tests_log.txt make test_utils test_ir test_transformer test_frontend \ - test_matcher test_graph_constructor + test_matcher test_graph_constructor test_backend test_%: @if [ -d .venv ]; then \ @@ -12,5 +12,23 @@ test_%: pytest tests/$@ -vv -s | tee -a tests_log.txt; \ fi; +package: + rm -rf dist/* + .venv/bin/python setup.py bdist_wheel sdist + rm -rf build utensor_cgen.egg-info/ + +upload-test: package + .venv/bin/twine upload -r pypitest dist/* + +install-test: package + pip uninstall -y utensor-cgen + pip install dist/*.tar.gz + +upload: package + .venv/bin/twine upload -r pypi dist/* + clean: - rm -rf tests_log.txt *.pdf .pytest_cache + rm -rf tests_log.txt *.pdf \ + models data \ + tests/test_backend/{models,data} \ + .pytest_cache dist/ build/ diff --git a/Pipfile.lock b/Pipfile.lock index d94bc386..403da44e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -16,23 +16,23 @@ "default": { "absl-py": { "hashes": [ - "sha256:b943d1c567743ed0455878fcd60bc28ac9fae38d129d1ccfad58079da00b8951" + "sha256:75e737d6ce7723d9ff9b7aa1ba3233c34be62ef18d5859e706b8fdc828989830" ], - "version": "==0.7.1" + "version": "==0.9.0" }, "astor": { "hashes": [ - "sha256:0e41295809baf43ae8303350e031aff81ae52189b6f881f36d623fa8b2f1960e", - "sha256:37a6eed8b371f1228db08234ed7f6cfdc7817a3ed3824797e20cbb11dc2a7862" + "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5", + "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e" ], - "version": "==0.8.0" + "version": "==0.8.1" }, "attrs": { "hashes": [ - "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", - "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", + "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" ], - "version": "==19.1.0" + "version": "==19.3.0" }, "click": { "hashes": [ @@ -47,79 +47,98 @@ }, "gast": { "hashes": [ - "sha256:fe939df4583692f0512161ec1c880e0a10e71e6a232da045ab8edd3756fbadf0" + "sha256:5c7617f1f6c8b8b426819642b16b9016727ddaecd16af9a07753e537eba8a3a5" ], - "version": "==0.2.2" + "version": "==0.3.2" + }, + "graphviz": { + "hashes": [ + "sha256:241fb099e32b8e8c2acca747211c8237e40c0b89f24b1622860075d59f4c4b25", + "sha256:60acbeee346e8c14555821eab57dbf68a169e6c10bce40e83c1bf44f63a62a01" + ], + "version": "==0.13.2" }, "grpcio": { "hashes": [ - "sha256:03b78b4e7dcdfe3e257bb528cc93923f9cbbab6d5babf15a60d21e9a4a70b1a2", - "sha256:1ce0ccfbdfe84387dbcbf44adb4ae16ec7ae70e166ffab478993eb1ea1cba3ce", - "sha256:22e167a9406d73dd19ffe8ed6a485f17e6eac82505be8c108897f15e68badcbb", - "sha256:31d0aeca8d8ee2301c62c5c340e0889d653b1280d68f9fa203982cb6337b050e", - "sha256:44c7f99ca17ebbcc96fc54ed00b454d8313f1eac28c563098d8b901025aff941", - "sha256:5471444f53f9db6a1f1f11f5dbc173228881df8446380b6b98f90afb8fd8348e", - "sha256:561bca3b1bde6d6564306eb05848fd155136e9c3a25d2961129b1e2edba22fce", - "sha256:5bf58e1d2c2f55365c06e8cb5abe067b88ca2e5550fb62009c41df4b54505acf", - "sha256:6b7163d1e85d76b0815df63fcc310daec02b44532bb433f743142d4febcb181f", - "sha256:766d79cddad95f5f6020037fe60ea8b98578afdf0c59d5a60c106c1bdd886303", - "sha256:770b7372d5ca68308ff66d7baee53369fa5ce985f84bcb6aa1948c1f2f7b02f2", - "sha256:7ab178da777fc0f55b6aef5a755f99726e8e4b75e3903954df07b27059b54fcf", - "sha256:8078305e77c2f6649d36b24d8778096413e474d9d7892c6f92cfb589c9d71b2e", - "sha256:85600b63a386d860eeaa955e9335e18dd0d7e5477e9214825abf2c2884488369", - "sha256:857d9b939ae128be1c0c792eb885c7ff6a386b9dea899ac4b06f4d90a31f9d87", - "sha256:87a41630c90c179fa5c593400f30a467c498972c702f348d41e19dafeb1d319e", - "sha256:8805d486c6128cc0fcc8ecf16c4095d99a8693a541ef851429ab334e028a4a97", - "sha256:8d71b7a89c306a41ccc7741fc9409b14f5b86727455c2a1c0c7cfcb0f784e1f2", - "sha256:9e1b80bd65f8f160880cb4dad7f55697f6d37b2d7f251fc0c2128e811928f369", - "sha256:9e290c84a145ae2411ee0ec9913c41cd7500e2e7485fe93632434d84ef4fda67", - "sha256:9ec9f88b5bc94bd99372f27cdd53af1c92ba06717380b127733b953cfb181174", - "sha256:a0a02a8b4ba6deadf706d5f849539b3685b72b186a3c9ef5d43e8972ed60fb6f", - "sha256:a4059c59519f5940e01a071f74ae2a60ea8f6185b03d22a09d40c7959a36b16b", - "sha256:a6e028c2a6da2ebfa2365a5b32531d311fbfec0e3600fc27e901b64f0ff7e54e", - "sha256:adcdebf9f8463df4120c427cf6c9aed39258bccd03ed37b6939e7a145d64d6e0", - "sha256:bdec982610259d07156a58f80b8c3e69be7751a9208bc577b059c5193d087fad", - "sha256:cefc4d4251ffb73feb303d4b7e9d6c367cb60f2db16d259ea28b114045f965aa", - "sha256:d4145c8aa6afbac10ad27e408f7ce15992fe89ba5d0b4abca31c0c2729864c03", - "sha256:da76dc5ad719ee99de5ea28a5629ff92172cbb4a70d8a6ae3a5b7a53c7382ce1", - "sha256:dde2452c08ef8b6426ccab6b5b6de9f06d836d9937d6870e68153cbf8cb49348", - "sha256:e3d88091d2539a4868750914a6fe7b9ec50e42b913851fc1b77423b5bd918530", - "sha256:f9c67cfe6278499d7f83559dc6322a8bbb108e307817a3d7acbfea807b3603cc" - ], - "version": "==1.22.0" + "sha256:066630f6b62bffa291dacbee56994279a6a3682b8a11967e9ccaf3cc770fc11e", + "sha256:07e95762ca6b18afbeb3aa2793e827c841152d5e507089b1db0b18304edda105", + "sha256:0a0fb2f8e3a13537106bc77e4c63005bc60124a6203034304d9101921afa4e90", + "sha256:0c61b74dcfb302613926e785cb3542a0905b9a3a86e9410d8cf5d25e25e10104", + "sha256:13383bd70618da03684a8aafbdd9e3d9a6720bf8c07b85d0bc697afed599d8f0", + "sha256:1c6e0f6b9d091e3717e9a58d631c8bb4898be3b261c2a01fe46371fdc271052f", + "sha256:1cf710c04689daa5cc1e598efba00b028215700dcc1bf66fcb7b4f64f2ea5d5f", + "sha256:2da5cee9faf17bb8daf500cd0d28a17ae881ab5500f070a6aace457f4c08cac4", + "sha256:2f78ebf340eaf28fa09aba0f836a8b869af1716078dfe8f3b3f6ff785d8f2b0f", + "sha256:33a07a1a8e817d733588dbd18e567caad1a6fe0d440c165619866cd490c7911a", + "sha256:3d090c66af9c065b7228b07c3416f93173e9839b1d40bb0ce3dd2aa783645026", + "sha256:42b903a3596a10e2a3727bae2a76f8aefd324d498424b843cfa9606847faea7b", + "sha256:4fffbb58134c4f23e5a8312ac3412db6f5e39e961dc0eb5e3115ce5aa16bf927", + "sha256:57be5a6c509a406fe0ffa6f8b86904314c77b5e2791be8123368ad2ebccec874", + "sha256:5b0fa09efb33e2af4e8822b4eb8b2cbc201d562e3e185c439be7eaeee2e8b8aa", + "sha256:5ef42dfc18f9a63a06aca938770b69470bb322e4c137cf08cf21703d1ef4ae5c", + "sha256:6a43d2f2ff8250f200fdf7aa31fa191a997922aa9ea1182453acd705ad83ab72", + "sha256:6d8ab28559be98b02f8b3a154b53239df1aa5b0d28ff865ae5be4f30e7ed4d3f", + "sha256:6e47866b7dc14ca3a12d40c1d6082e7bea964670f1c5315ea0fb8b0550244d64", + "sha256:6edda1b96541187f73aab11800d25f18ee87e53d5f96bb74473873072bf28a0e", + "sha256:7109c8738a8a3c98cfb5dda1c45642a8d6d35dc00d257ab7a175099b2b4daecd", + "sha256:8d866aafb08657c456a18c4a31c8526ea62de42427c242b58210b9eae6c64559", + "sha256:9939727d9ae01690b24a2b159ac9dbca7b7e8e6edd5af6a6eb709243cae7b52b", + "sha256:99fd873699df17cb11c542553270ae2b32c169986e475df0d68a8629b8ef4df7", + "sha256:b6fda5674f990e15e1bcaacf026428cf50bce36e708ddcbd1de9673b14aab760", + "sha256:bdb2f3dcb664f0c39ef1312cd6acf6bc6375252e4420cf8f36fff4cb4fa55c71", + "sha256:bfd7d3130683a1a0a50c456273c21ec8a604f2d043b241a55235a78a0090ee06", + "sha256:c6c2db348ac73d73afe14e0833b18abbbe920969bf2c5c03c0922719f8020d06", + "sha256:cb7a4b41b5e2611f85c3402ac364f1d689f5d7ecbc24a55ef010eedcd6cf460f", + "sha256:cd3d3e328f20f7c807a862620c6ee748e8d57ba2a8fc960d48337ed71c6d9d32", + "sha256:d1a481777952e4f99b8a6956581f3ee866d7614100d70ae6d7e07327570b85ce", + "sha256:d1d49720ed636920bb3d74cedf549382caa9ad55aea89d1de99d817068d896b2", + "sha256:d42433f0086cccd192114343473d7dbd4aae9141794f939e2b7b83efc57543db", + "sha256:d44c34463a7c481e076f691d8fa25d080c3486978c2c41dca09a8dd75296c2d7", + "sha256:d7e5b7af1350e9c8c17a7baf99d575fbd2de69f7f0b0e6ebd47b57506de6493a", + "sha256:d9542366a0917b9b48bab1fee481ac01f56bdffc52437b598c09e7840148a6a9", + "sha256:df7cdfb40179acc9790a462c049e0b8e109481164dd7ad1a388dd67ff1528759", + "sha256:e1a9d9d2e7224d981aea8da79260c7f6932bf31ce1f99b7ccfa5eceeb30dc5d0", + "sha256:ed10e5fad105ecb0b12822f924e62d0deb07f46683a0b64416b17fd143daba1d", + "sha256:f0ec5371ce2363b03531ed522bfbe691ec940f51f0e111f0500fc0f44518c69d", + "sha256:f6580a8a4f5e701289b45fd62a8f6cb5ec41e4d77082424f8b676806dcd22564", + "sha256:f7b83e4b2842d44fce3cdc0d54db7a7e0d169a598751bf393601efaa401c83e0", + "sha256:ffec45b0db18a555fdfe0c6fa2d0a3fceb751b22b31e8fcd14ceed7bde05481e" + ], + "version": "==1.26.0" }, "h5py": { "hashes": [ - "sha256:05750b91640273c69989c657eaac34b091abdd75efc8c4824c82aaf898a2da0a", - "sha256:082a27208aa3a2286e7272e998e7e225b2a7d4b7821bd840aebf96d50977abbb", - "sha256:08e2e8297195f9e813e894b6c63f79372582787795bba2014a2db6a2de95f713", - "sha256:0dd2adeb2e9de5081eb8dcec88874e7fd35dae9a21557be3a55a3c7d491842a4", - "sha256:0f94de7a10562b991967a66bbe6dda9808e18088676834c0a4dcec3fdd3bcc6f", - "sha256:106e42e2e01e486a3d32eeb9ba0e3a7f65c12fa8998d63625fa41fb8bdc44cdb", - "sha256:1606c66015f04719c41a9863c156fc0e6b992150de21c067444bcb82e7d75579", - "sha256:1854c4beff9961e477e133143c5e5e355dac0b3ebf19c52cf7cc1b1ef757703c", - "sha256:1e9fb6f1746500ea91a00193ce2361803c70c6b13f10aae9a33ad7b5bd28e800", - "sha256:2cca17e80ddb151894333377675db90cd0279fa454776e0a4f74308376afd050", - "sha256:30e365e8408759db3778c361f1e4e0fe8e98a875185ae46c795a85e9bafb9cdf", - "sha256:3206bac900e16eda81687d787086f4ffd4f3854980d798e191a9868a6510c3ae", - "sha256:3c23d72058647cee19b30452acc7895621e2de0a0bd5b8a1e34204b9ea9ed43c", - "sha256:407b5f911a83daa285bbf1ef78a9909ee5957f257d3524b8606be37e8643c5f0", - "sha256:4162953714a9212d373ac953c10e3329f1e830d3c7473f2a2e4f25dd6241eef0", - "sha256:5fc7aba72a51b2c80605eba1c50dbf84224dcd206279d30a75c154e5652e1fe4", - "sha256:713ac19307e11de4d9833af0c4bd6778bde0a3d967cafd2f0f347223711c1e31", - "sha256:71b946d80ef3c3f12db157d7778b1fe74a517ca85e94809358b15580983c2ce2", - "sha256:8cc4aed71e20d87e0a6f02094d718a95252f11f8ed143bc112d22167f08d4040", - "sha256:9d41ca62daf36d6b6515ab8765e4c8c4388ee18e2a665701fef2b41563821002", - "sha256:a744e13b000f234cd5a5b2a1f95816b819027c57f385da54ad2b7da1adace2f3", - "sha256:b087ee01396c4b34e9dc41e3a6a0442158206d383c19c7d0396d52067b17c1cb", - "sha256:b0f03af381d33306ce67d18275b61acb4ca111ced645381387a02c8a5ee1b796", - "sha256:b9e4b8dfd587365bdd719ae178fa1b6c1231f81280b1375eef8626dfd8761bf3", - "sha256:c5dd4ec75985b99166c045909e10f0534704d102848b1d9f0992720e908928e7", - "sha256:d2b82f23cd862a9d05108fe99967e9edfa95c136f532a71cb3d28dc252771f50", - "sha256:e58a25764472af07b7e1c4b10b0179c8ea726446c7141076286e41891bf3a563", - "sha256:f3b49107fbfc77333fc2b1ef4d5de2abcd57e7ea3a1482455229494cf2da56ce" - ], - "version": "==2.9.0" + "sha256:063947eaed5f271679ed4ffa36bb96f57bc14f44dd4336a827d9a02702e6ce6b", + "sha256:13c87efa24768a5e24e360a40e0bc4c49bcb7ce1bb13a3a7f9902cec302ccd36", + "sha256:16ead3c57141101e3296ebeed79c9c143c32bdd0e82a61a2fc67e8e6d493e9d1", + "sha256:3dad1730b6470fad853ef56d755d06bb916ee68a3d8272b3bab0c1ddf83bb99e", + "sha256:51ae56894c6c93159086ffa2c94b5b3388c0400548ab26555c143e7cfa05b8e5", + "sha256:54817b696e87eb9e403e42643305f142cd8b940fe9b3b490bbf98c3b8a894cf4", + "sha256:549ad124df27c056b2e255ea1c44d30fb7a17d17676d03096ad5cd85edb32dc1", + "sha256:64f74da4a1dd0d2042e7d04cf8294e04ddad686f8eba9bb79e517ae582f6668d", + "sha256:6998be619c695910cb0effe5eb15d3a511d3d1a5d217d4bd0bebad1151ec2262", + "sha256:6ef7ab1089e3ef53ca099038f3c0a94d03e3560e6aff0e9d6c64c55fb13fc681", + "sha256:769e141512b54dee14ec76ed354fcacfc7d97fea5a7646b709f7400cf1838630", + "sha256:79b23f47c6524d61f899254f5cd5e486e19868f1823298bc0c29d345c2447172", + "sha256:7be5754a159236e95bd196419485343e2b5875e806fe68919e087b6351f40a70", + "sha256:84412798925dc870ffd7107f045d7659e60f5d46d1c70c700375248bf6bf512d", + "sha256:86868dc07b9cc8cb7627372a2e6636cdc7a53b7e2854ad020c9e9d8a4d3fd0f5", + "sha256:8bb1d2de101f39743f91512a9750fb6c351c032e5cd3204b4487383e34da7f75", + "sha256:a5f82cd4938ff8761d9760af3274acf55afc3c91c649c50ab18fcff5510a14a5", + "sha256:aac4b57097ac29089f179bbc2a6e14102dd210618e94d77ee4831c65f82f17c0", + "sha256:bffbc48331b4a801d2f4b7dac8a72609f0b10e6e516e5c480a3e3241e091c878", + "sha256:c0d4b04bbf96c47b6d360cd06939e72def512b20a18a8547fa4af810258355d5", + "sha256:c54a2c0dd4957776ace7f95879d81582298c5daf89e77fb8bee7378f132951de", + "sha256:cbf28ae4b5af0f05aa6e7551cee304f1d317dbed1eb7ac1d827cee2f1ef97a99", + "sha256:d35f7a3a6cefec82bfdad2785e78359a0e6a5fbb3f605dd5623ce88082ccd681", + "sha256:d3c59549f90a891691991c17f8e58c8544060fdf3ccdea267100fa5f561ff62f", + "sha256:d7ae7a0576b06cb8e8a1c265a8bc4b73d05fdee6429bffc9a26a6eb531e79d72", + "sha256:ecf4d0b56ee394a0984de15bceeb97cbe1fe485f1ac205121293fc44dcf3f31f", + "sha256:f0e25bb91e7a02efccb50aba6591d3fe2c725479e34769802fcdd4076abfa917", + "sha256:f23951a53d18398ef1344c186fb04b26163ca6ce449ebd23404b153fd111ded9", + "sha256:ff7d241f866b718e4584fa95f520cb19405220c501bd3a53ee11871ba5166ea2" + ], + "version": "==2.10.0" }, "idx2numpy": { "hashes": [ @@ -129,10 +148,10 @@ }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", + "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" ], - "version": "==2.10.1" + "version": "==2.10.3" }, "keras-applications": { "hashes": [ @@ -197,131 +216,142 @@ }, "numpy": { "hashes": [ - "sha256:03e311b0a4c9f5755da7d52161280c6a78406c7be5c5cc7facfbcebb641efb7e", - "sha256:0cdd229a53d2720d21175012ab0599665f8c9588b3b8ffa6095dd7b90f0691dd", - "sha256:312bb18e95218bedc3563f26fcc9c1c6bfaaf9d453d15942c0839acdd7e4c473", - "sha256:464b1c48baf49e8505b1bb754c47a013d2c305c5b14269b5c85ea0625b6a988a", - "sha256:5adfde7bd3ee4864536e230bcab1c673f866736698724d5d28c11a4d63672658", - "sha256:7724e9e31ee72389d522b88c0d4201f24edc34277999701ccd4a5392e7d8af61", - "sha256:8d36f7c53ae741e23f54793ffefb2912340b800476eb0a831c6eb602e204c5c4", - "sha256:910d2272403c2ea8a52d9159827dc9f7c27fb4b263749dca884e2e4a8af3b302", - "sha256:951fefe2fb73f84c620bec4e001e80a80ddaa1b84dce244ded7f1e0cbe0ed34a", - "sha256:9588c6b4157f493edeb9378788dcd02cb9e6a6aeaa518b511a1c79d06cbd8094", - "sha256:9ce8300950f2f1d29d0e49c28ebfff0d2f1e2a7444830fbb0b913c7c08f31511", - "sha256:be39cca66cc6806652da97103605c7b65ee4442c638f04ff064a7efd9a81d50a", - "sha256:c3ab2d835b95ccb59d11dfcd56eb0480daea57cdf95d686d22eff35584bc4554", - "sha256:eb0fc4a492cb896346c9e2c7a22eae3e766d407df3eb20f4ce027f23f76e4c54", - "sha256:ec0c56eae6cee6299f41e780a0280318a93db519bbb2906103c43f3e2be1206c", - "sha256:f4e4612de60a4f1c4d06c8c2857cdcb2b8b5289189a12053f37d3f41f06c60d0" - ], - "version": "==1.17.0" + "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", + "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", + "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", + "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", + "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", + "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", + "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", + "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", + "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", + "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", + "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", + "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", + "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", + "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", + "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", + "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", + "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", + "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", + "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", + "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", + "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" + ], + "version": "==1.17.4" }, "onnx": { "hashes": [ - "sha256:144c6812b5650e3148af607798f37009e3fda80d53222ce07fcdca94e31e1634", - "sha256:1a584a4ef62a6db178c257fffb06a9d8e61b41c0a80bfd8bcd8a253d72c4b0b4", - "sha256:352ce8ab166e3d7760bb02eff3b9b0670fbc4123229b547b25b428ff87f3af6f", - "sha256:37ace5a7a0c47d2a79edd83861eff17f6e463eafe96c8d930624bc51ca1023f6", - "sha256:3c87b805046b47b6e8f8b58aec00aa3fbc54923fecc1ec83f9fe20d9568a49f6", - "sha256:4c24e30c97b46034c932114f0276b26ac5f7bc1a98638beff988ae91dcd6ede2", - "sha256:660bdaabe27d523d9b2714ae8ce0b45a64dcb324da666e9f64cae1960600d739", - "sha256:8343fec593bb9e84aa41b666c86b00d1c6932bbb6be2ae02e646b4a8396f74e0", - "sha256:9223e3a06fbfac670966beb3206907b3a4c7eb6a9e15517979e08235049ee4c8", - "sha256:9fc6b80aa641428adacce445a479e92cfb5c418166b7af4ac68daac0d9900317", - "sha256:b01f506c85094bf41ddab83f926530d42d5c4a9ab72e56e78b41e2df7b2f87e2", - "sha256:c732490c07acece6dfa1152f74f0df3d0e625564c7f3c5e39be44e87f83c12e6", - "sha256:d5a6d8f922734704951367048307abbc41414b6a781f239193898b492c6f01e7", - "sha256:e0226ed768836de0ac5bbfd773da832c47368fe21c0bf0cea724fbe4cbaa7acd", - "sha256:f4ab108ed69819926118c54e0db85b831b66ec8ec28eb1931eef7c5bcaf82452", - "sha256:f6202b2b5e353f98cba793cc52d449e578cc242968e89a4c6cfc73fa2591e926" - ], - "version": "==1.5.0" + "sha256:051ac54cb08d9a1ae0dec5fbd79c7604ceb97ddb678bc4145e49a3f4da5d928c", + "sha256:0cae2a5511cf8522aea726507abad2aa85a5406f5ff7232ba937aadf71f77220", + "sha256:130d63b6f7bfee367c7693350fe4caea6b7067d254704bb20d136c6e7460ec56", + "sha256:3b88c3fe521151651a0403c4d131cb2e0311bd28b753ef692020a432a81ce345", + "sha256:40a47587025092c9d033f50fe4b9395c021539bcacbbaa141673ecd5eeca0aa0", + "sha256:4204fca478bcf668c606470b52cd3f42b111dcf5503b32c46480cabd9b9d4387", + "sha256:42298189743d9f390890c31907bad74899bf023bbb52c68a94b1f6d534468571", + "sha256:48acb524204137bd52a312709cb52a693832cce9e72c8f39db382d826e4bb6ec", + "sha256:4a47a4813647d07cb544429e12cf5a71262bc82edd9237c4ae91219b4ef841af", + "sha256:4fbe1630e33bd8d79d4e17a23d24bd8e464871dde37cb21e648c3728d33e64e2", + "sha256:6c90c2fdb0836e6fadd093ca053d6cebd00ac4a774759002b0b0860d652a3d31", + "sha256:912264fedd3df4647b31a4b7cd167982d54b4641eaf91bc1a9982c5eb9ebcb5f", + "sha256:926f73ad969ce649ae68be1f2255d7825a703a915a2766de8ec3f73b282f154f", + "sha256:988e20a30b38de2977f61c745ae68931ad9edbc8a687287e38555d17cd25df9d", + "sha256:9b32e04abe02cba4c4edb6c3cc15db917e734521ca8631e46bea3971a142d709", + "sha256:a78d7f0b784b26890f50cb48d7b1718db63f10ed3196cb023dd70631001c73a3", + "sha256:a7e3ab3e81684bc75ac5476b5f2c690ad8d250bc8ece02eed5445c115577e6e4", + "sha256:c67867a426eb661c661d7b2adea5c7876d7c5478b8d5af3c26bb40839b06e8c5", + "sha256:d169bebc845e9869f229b8e3f65e3144e647a251ae9594efae52de23502d6b13", + "sha256:d390ed80b8b1cffa23e25cd4e4a3f3a95c70f438cf5f160659f74f4ab5f6ee04", + "sha256:f21a7ea757cea83067ce0d2f98b701a5980b0166fc0c0f1202550fdfe16a1c80" + ], + "version": "==1.6.0" }, "onnx-tf": { "hashes": [ - "sha256:0a8e5e1e7baf269a98dc13a2b10fbf47129cf1e15c2f6a7df81d1624db99076a", - "sha256:f4641053780c196406afcc2a319fa0ac3cab3741180bbc0a8f7a61f884f6d695" + "sha256:0ecd83e481edf7024de1e191f12414c80dbaacadcea7f6abc85fe547c5649ff5" ], - "version": "==1.3.0" + "version": "==1.2.1" }, "pillow": { "hashes": [ - "sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de", - "sha256:0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f", - "sha256:0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4", - "sha256:365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed", - "sha256:38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03", - "sha256:3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992", - "sha256:3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd", - "sha256:45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68", - "sha256:49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010", - "sha256:571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555", - "sha256:5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f", - "sha256:6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad", - "sha256:6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a", - "sha256:70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826", - "sha256:70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4", - "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686", - "sha256:7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99", - "sha256:7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff", - "sha256:7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829", - "sha256:b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0", - "sha256:bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa", - "sha256:cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c", - "sha256:e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e", - "sha256:e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616", - "sha256:ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808", - "sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b" - ], - "version": "==6.1.0" + "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", + "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", + "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", + "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", + "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", + "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", + "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", + "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", + "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", + "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", + "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", + "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", + "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", + "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", + "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", + "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", + "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", + "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", + "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", + "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", + "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", + "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", + "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", + "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", + "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", + "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", + "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", + "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", + "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", + "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" + ], + "version": "==6.2.1" }, "protobuf": { "hashes": [ - "sha256:05c36022fef3c7d3562ac22402965c0c2b9fe8421f459bb377323598996e407f", - "sha256:139b7eadcca0a861d60b523cb37d9475505e0dfb07972436b15407c2b968d87e", - "sha256:15f683006cb77fb849b1f561e509b03dd2b7dcc749086b8dd1831090d0ba4740", - "sha256:2ad566b7b7cdd8717c7af1825e19f09e8fef2787b77fcb979588944657679604", - "sha256:35cfcf97642ef62108e10a9431c77733ec7eaab8e32fe4653de20403429907cb", - "sha256:387822859ecdd012fdc25ec879f7f487da6e1d5b1ae6115e227e6be208836f71", - "sha256:4df14cbe1e7134afcfdbb9f058949e31c466de27d9b2f7fb4da9e0b67231b538", - "sha256:586c4ca37a7146d4822c700059f150ac3445ce0aef6f3ea258640838bb892dc2", - "sha256:58b11e530e954d29ab3180c48dc558a409f705bf16739fd4e0d3e07924ad7add", - "sha256:63c8c98ccb8c95f41c18fb829aeeab21c6249adee4ed75354125bdc44488f30e", - "sha256:72edcbacd0c73eef507d2ff1af99a6c27df18e66a3ff4351e401182e4de62b03", - "sha256:83dc8a561b3b954fd7002c690bb83278b8d1742a1e28abba9aaef28b0c8b437d", - "sha256:913171ecc84c2726b86574e40549a0ea619d569657c5a5ff782a3be7d81401a5", - "sha256:aabb7c741d3416671c3e6fe7c52970a226e6a8274417a97d7d795f953fadef36", - "sha256:b3452bbda12b1cbe2187d416779de07b2ab4c497d83a050e43c344778763721d", - "sha256:c5d5b8d4a9212338297fa1fa44589f69b470c0ba1d38168b432d577176b386a8", - "sha256:d86ee389c2c4fc3cebabb8ce83a8e97b6b3b5dc727b7419c1ccdc7b6e545a233", - "sha256:f2db8c754de788ab8be5e108e1e967c774c0942342b4f8aaaf14063889a6cfdc" - ], - "version": "==3.9.0" + "sha256:0329e86a397db2a83f9dcbe21d9be55a47f963cdabc893c3a24f4d3a8f117c37", + "sha256:0a7219254afec0d488211f3d482d8ed57e80ae735394e584a98d8f30a8c88a36", + "sha256:14d6ac53df9cb5bb87c4f91b677c1bc5cec9c0fd44327f367a3c9562de2877c4", + "sha256:180fc364b42907a1d2afa183ccbeffafe659378c236b1ec3daca524950bb918d", + "sha256:3d7a7d8d20b4e7a8f63f62de2d192cfd8b7a53c56caba7ece95367ca2b80c574", + "sha256:3f509f7e50d806a434fe4a5fbf602516002a0f092889209fff7db82060efffc0", + "sha256:4571da974019849201fc1ec6626b9cea54bd11b6bed140f8f737c0a33ea37de5", + "sha256:56bd1d84fbf4505c7b73f04de987eef5682e5752c811141b0186a3809bfb396f", + "sha256:680c668d00b5eff08b86aef9e5ba9a705e621ea05d39071cfea8e28cb2400946", + "sha256:6b5b947dc8b3f2aec0eaad65b0b5113fcd642c358c31357c647da6281ee31104", + "sha256:6e96dffaf4d0a9a329e528b353ba62fd9ef13599688723d96bc9c165d0b6871e", + "sha256:919f0d6f6addc836d08658eba3b52be2e92fd3e76da3ce00c325d8e9826d17c7", + "sha256:9c7b19c30cf0644afd0e4218b13f637ce54382fdcb1c8f75bf3e84e49a5f6d0a", + "sha256:a2e6f57114933882ec701807f217df2fb4588d47f71f227c0a163446b930d507", + "sha256:a6b970a2eccfcbabe1acf230fbf112face1c4700036c95e195f3554d7bcb04c1", + "sha256:bc45641cbcdea068b67438244c926f9fd3e5cbdd824448a4a64370610df7c593", + "sha256:d61b14a9090da77fe87e38ba4c6c43d3533dcbeb5d84f5474e7ac63c532dcc9c", + "sha256:d6faf5dbefb593e127463f58076b62fcfe0784187be8fe1aa9167388f24a22a1" + ], + "version": "==3.11.2" }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" - ], - "version": "==5.1.2" + "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", + "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", + "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", + "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", + "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", + "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", + "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", + "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", + "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", + "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", + "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4" + ], + "version": "==5.2" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" }, "tensorboard": { "hashes": [ @@ -362,62 +392,63 @@ ], "version": "==1.1.0" }, - "torch": { + "toml": { "hashes": [ - "sha256:3f3023345c6db4db28a79f52ada186fc6307ba8a0c85252eb0a70b78eff3a443", - "sha256:87d4778e848e4c83505a0d10135d514871ea0ae2da75e0710be272ea5048f9e3", - "sha256:9f4182043f8ac72d6fd52c5189f1600c94e827b698748c355097077874e8c810", - "sha256:ee2c48794c767606f4629ee3d1f147e2fcb1a02a31b6bca252cc7c7e3ad22c4a" + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" ], - "version": "==1.1.0.post2" + "version": "==0.10.0" }, - "torchvision": { + "torch": { "hashes": [ - "sha256:03ddd9c310348e82a23348c5f5048aa8c3def067440261c84742c3cbe7e904fc", - "sha256:27d2b84553fd01c1fd71585b8dde0caa9b660a629df1c9d6d0a8be85f2932d76", - "sha256:3ed016bea513e5a77642c58794a59146658270221b564f258d6c7483cc07344f", - "sha256:40067e4e4915846e1e7d78d56fc1a26cba23265121d0bd52fb144391e7143b86", - "sha256:7a972907ea3865e9efdb1d2de349fe68b77a8f0e8b88c3fa9d9d5aa808de3d00", - "sha256:81237ef6cfc4f93f035f5b0d0d1227222e2612bf20154aab585c14f8f4ca8269", - "sha256:87fd98cd817cdb9ea5f0010a1b30eb0b297922e33a8c27cc940ed486044dc82c", - "sha256:959d636d8ea3ef425f37253a06937f3960a9488746857e3b9c335e007e80a7fd", - "sha256:a5bcbee4baf4b027e3872ebcbe1c33cce9b4edbccbd5fb739a1c25c54ce734a0", - "sha256:df51bd05bad0aaebfab20c3d9fa96fc571b7963e466afc5d1039ddfc7459f975", - "sha256:e6525572b8e91095d0252c6d4cb2936c551a044c4650857da7ce6dc294178b9f", - "sha256:fb8ea1e2f9d40dbd61eafaadf302dd062df5c3793f74599692443fdee7f9707b" + "sha256:0cec2e13a2e95c24c34f17d437f354ee2a40902e8d515a524556b350e12555dd", + "sha256:134e8291a97151b1ffeea09cb9ddde5238beb4e6d9dfb66657143d6990bfb865", + "sha256:31062923ac2e60eac676f6a0ae14702b051c158bbcf7f440eaba266b0defa197", + "sha256:3b05233481b51bb636cee63dc761bb7f602e198178782ff4159d385d1759608b", + "sha256:458f1d87e5b7064b2c39e36675d84e163be3143dd2fc806057b7878880c461bc", + "sha256:72a1c85bffd2154f085bc0a1d378d8a54e55a57d49664b874fe7c949022bf071", + "sha256:77fd8866c0bf529861ffd850a5dada2190a8d9c5167719fb0cfa89163e23b143", + "sha256:b6f01d851d1c5989d4a99b50ae0187762b15b7718dcd1a33704b665daa2402f9", + "sha256:d8e1d904a6193ed14a4fed220b00503b2baa576e71471286d1ebba899c851fae" ], - "version": "==0.3.0" + "version": "==1.3.1" }, - "typing": { + "torchvision": { "hashes": [ - "sha256:38566c558a0a94d6531012c8e917b1b8518a41e418f7f15f00e129cc80162ad3", - "sha256:53765ec4f83a2b720214727e319607879fec4acde22c4fbb54fa2604e79e44ce", - "sha256:84698954b4e6719e912ef9a42a2431407fe3755590831699debda6fba92aac55" + "sha256:0f8245d6378acc86917f58492675f93df5279abae8bc5f832e3510722191f6c9", + "sha256:1ad7593d94f6612ccb84a59467f0d10cdc213fb3e2bb91f1e773eb844787fa4c", + "sha256:2553405b9afe3cedb410873b9877eb18b1526f8b01cb7c2747e51b69a936e0b5", + "sha256:276a385f2f5fe484bf08467b5d081d9144b97eb458ba5b4a11e4640389e53149", + "sha256:66deba9c577e36f4f071decdd894bf7ba794ac133dae64b3fd02fc3f0c6b989d", + "sha256:7a458330e4efcd66f9f70127ab21fcf8cfea84acda8e707322fd2843aa6dd396", + "sha256:8ff715c2323d9eca89126824ebfa74b282a95d6f64a4743fbe9b738d2de21c77", + "sha256:dca4aadc12a123730957b501f9c5c2870d2f6727a2c28552cb7907b68b0ea10c", + "sha256:dda25ce304978bba19e6543f7dcfee4f37d2f128ec83d4ab0c7e8f991d64865f" ], - "version": "==3.7.4" + "version": "==0.4.2" }, "typing-extensions": { "hashes": [ - "sha256:2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", - "sha256:b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", - "sha256:d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed" + "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", + "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", + "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575" ], - "version": "==3.7.4" + "version": "==3.7.4.1" }, "werkzeug": { "hashes": [ - "sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4", - "sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6" + "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", + "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" ], - "version": "==0.15.5" + "version": "==0.16.0" }, "wheel": { "hashes": [ - "sha256:5e79117472686ac0c4aef5bad5172ea73a1c2d1646b808c35926bd26bdfb0c08", - "sha256:62fcfa03d45b5b722539ccbc07b190e4bfff4bb9e3a4d470dd9f6a0981002565" + "sha256:10c9da68765315ed98850f8e048347c3eb06dd81822dc2ab1d4fde9dc9702646", + "sha256:f4da1763d3becf2e2cd92a14a7c920f0f00eca30fdde9ea992c836685b9faf28" ], "markers": "python_version >= '3'", - "version": "==0.33.4" + "version": "==0.33.6" } }, "develop": { @@ -430,25 +461,18 @@ }, "astroid": { "hashes": [ - "sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4", - "sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4" + "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a", + "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42" ], "markers": "python_version >= '3'", - "version": "==2.2.5" - }, - "atomicwrites": { - "hashes": [ - "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", - "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" - ], - "version": "==1.3.0" + "version": "==2.3.3" }, "attrs": { "hashes": [ - "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", - "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", + "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" ], - "version": "==19.1.0" + "version": "==19.3.0" }, "babel": { "hashes": [ @@ -459,10 +483,10 @@ }, "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.6.16" + "version": "==2019.11.28" }, "chardet": { "hashes": [ @@ -488,19 +512,18 @@ }, "flake8": { "hashes": [ - "sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", - "sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696" + "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb", + "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca" ], "index": "pypi", - "version": "==3.7.8" + "version": "==3.7.9" }, "graphviz": { "hashes": [ - "sha256:6d0f69c107cfdc9bd1df3763fad99569bbcba29d0c52ffcbc6f266621d8bf709", - "sha256:914b8b124942d82e3e1dcef499c9fe77c10acd3d18a1cfeeb2b9de05f6d24805" + "sha256:241fb099e32b8e8c2acca747211c8237e40c0b89f24b1622860075d59f4c4b25", + "sha256:60acbeee346e8c14555821eab57dbf68a169e6c10bce40e83c1bf44f63a62a01" ], - "index": "pypi", - "version": "==0.11.1" + "version": "==0.13.2" }, "idna": { "hashes": [ @@ -518,10 +541,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:23d3d873e008a513952355379d93cbcab874c58f4f034ff657c7a87422fa64e8", - "sha256:80d2de76188eabfbfcf27e6a37342c2827801e59c4cc14b0371c56fed43820e3" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "version": "==0.19" + "markers": "python_version < '3.8'", + "version": "==1.3.0" }, "isort": { "hashes": [ @@ -532,33 +556,36 @@ }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", + "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" ], - "version": "==2.10.1" + "version": "==2.10.3" }, "lazy-object-proxy": { "hashes": [ - "sha256:159a745e61422217881c4de71f9eafd9d703b93af95618635849fe469a283661", - "sha256:23f63c0821cc96a23332e45dfaa83266feff8adc72b9bcaef86c202af765244f", - "sha256:3b11be575475db2e8a6e11215f5aa95b9ec14de658628776e10d96fa0b4dac13", - "sha256:3f447aff8bc61ca8b42b73304f6a44fa0d915487de144652816f950a3f1ab821", - "sha256:4ba73f6089cd9b9478bc0a4fa807b47dbdb8fad1d8f31a0f0a5dbf26a4527a71", - "sha256:4f53eadd9932055eac465bd3ca1bd610e4d7141e1278012bd1f28646aebc1d0e", - "sha256:64483bd7154580158ea90de5b8e5e6fc29a16a9b4db24f10193f0c1ae3f9d1ea", - "sha256:6f72d42b0d04bfee2397aa1862262654b56922c20a9bb66bb76b6f0e5e4f9229", - "sha256:7c7f1ec07b227bdc561299fa2328e85000f90179a2f44ea30579d38e037cb3d4", - "sha256:7c8b1ba1e15c10b13cad4171cfa77f5bb5ec2580abc5a353907780805ebe158e", - "sha256:8559b94b823f85342e10d3d9ca4ba5478168e1ac5658a8a2f18c991ba9c52c20", - "sha256:a262c7dfb046f00e12a2bdd1bafaed2408114a89ac414b0af8755c696eb3fc16", - "sha256:acce4e3267610c4fdb6632b3886fe3f2f7dd641158a843cf6b6a68e4ce81477b", - "sha256:be089bb6b83fac7f29d357b2dc4cf2b8eb8d98fe9d9ff89f9ea6012970a853c7", - "sha256:bfab710d859c779f273cc48fb86af38d6e9210f38287df0069a63e40b45a2f5c", - "sha256:c10d29019927301d524a22ced72706380de7cfc50f767217485a912b4c8bd82a", - "sha256:dd6e2b598849b3d7aee2295ac765a578879830fb8966f70be8cd472e6069932e", - "sha256:e408f1eacc0a68fed0c08da45f31d0ebb38079f043328dce69ff133b95c29dc1" - ], - "version": "==1.4.1" + "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", + "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", + "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", + "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", + "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", + "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", + "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", + "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", + "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", + "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", + "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", + "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", + "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", + "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", + "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", + "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", + "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", + "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", + "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", + "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", + "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" + ], + "version": "==1.4.3" }, "markupsafe": { "hashes": [ @@ -602,76 +629,85 @@ }, "more-itertools": { "hashes": [ - "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", - "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==7.2.0" + "version": "==8.0.2" }, "numpy": { "hashes": [ - "sha256:03e311b0a4c9f5755da7d52161280c6a78406c7be5c5cc7facfbcebb641efb7e", - "sha256:0cdd229a53d2720d21175012ab0599665f8c9588b3b8ffa6095dd7b90f0691dd", - "sha256:312bb18e95218bedc3563f26fcc9c1c6bfaaf9d453d15942c0839acdd7e4c473", - "sha256:464b1c48baf49e8505b1bb754c47a013d2c305c5b14269b5c85ea0625b6a988a", - "sha256:5adfde7bd3ee4864536e230bcab1c673f866736698724d5d28c11a4d63672658", - "sha256:7724e9e31ee72389d522b88c0d4201f24edc34277999701ccd4a5392e7d8af61", - "sha256:8d36f7c53ae741e23f54793ffefb2912340b800476eb0a831c6eb602e204c5c4", - "sha256:910d2272403c2ea8a52d9159827dc9f7c27fb4b263749dca884e2e4a8af3b302", - "sha256:951fefe2fb73f84c620bec4e001e80a80ddaa1b84dce244ded7f1e0cbe0ed34a", - "sha256:9588c6b4157f493edeb9378788dcd02cb9e6a6aeaa518b511a1c79d06cbd8094", - "sha256:9ce8300950f2f1d29d0e49c28ebfff0d2f1e2a7444830fbb0b913c7c08f31511", - "sha256:be39cca66cc6806652da97103605c7b65ee4442c638f04ff064a7efd9a81d50a", - "sha256:c3ab2d835b95ccb59d11dfcd56eb0480daea57cdf95d686d22eff35584bc4554", - "sha256:eb0fc4a492cb896346c9e2c7a22eae3e766d407df3eb20f4ce027f23f76e4c54", - "sha256:ec0c56eae6cee6299f41e780a0280318a93db519bbb2906103c43f3e2be1206c", - "sha256:f4e4612de60a4f1c4d06c8c2857cdcb2b8b5289189a12053f37d3f41f06c60d0" - ], - "version": "==1.17.0" + "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", + "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", + "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", + "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", + "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", + "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", + "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", + "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", + "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", + "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", + "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", + "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", + "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", + "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", + "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", + "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", + "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", + "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", + "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", + "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", + "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" + ], + "version": "==1.17.4" }, "packaging": { "hashes": [ - "sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9", - "sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe" + "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", + "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" ], - "version": "==19.1" + "version": "==19.2" }, "pillow": { "hashes": [ - "sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de", - "sha256:0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f", - "sha256:0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4", - "sha256:365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed", - "sha256:38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03", - "sha256:3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992", - "sha256:3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd", - "sha256:45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68", - "sha256:49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010", - "sha256:571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555", - "sha256:5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f", - "sha256:6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad", - "sha256:6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a", - "sha256:70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826", - "sha256:70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4", - "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686", - "sha256:7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99", - "sha256:7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff", - "sha256:7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829", - "sha256:b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0", - "sha256:bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa", - "sha256:cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c", - "sha256:e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e", - "sha256:e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616", - "sha256:ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808", - "sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b" - ], - "version": "==6.1.0" + "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", + "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", + "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", + "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", + "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", + "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", + "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", + "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", + "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", + "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", + "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", + "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", + "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", + "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", + "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", + "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", + "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", + "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", + "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", + "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", + "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", + "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", + "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", + "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", + "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", + "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", + "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", + "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", + "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", + "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" + ], + "version": "==6.2.1" }, "pluggy": { "hashes": [ - "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", - "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], - "version": "==0.12.0" + "version": "==0.13.1" }, "py": { "hashes": [ @@ -696,58 +732,56 @@ }, "pygments": { "hashes": [ - "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", - "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b", + "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe" ], - "version": "==2.4.2" + "version": "==2.5.2" }, "pylint": { "hashes": [ - "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", - "sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1" + "sha256:3db5468ad013380e987410a8d6956226963aed94ecb5f9d3a28acca6d9ac36cd", + "sha256:886e6afc935ea2590b462664b161ca9a5e40168ea99e5300935f6591ad467df4" ], "index": "pypi", - "version": "==2.3.1" + "version": "==2.4.4" }, "pyparsing": { "hashes": [ - "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", - "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4" + "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", + "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" ], - "version": "==2.4.2" + "version": "==2.4.5" }, "pytest": { "hashes": [ - "sha256:6ef6d06de77ce2961156013e9dff62f1b2688aa04d0dc244299fe7d67e09370d", - "sha256:a736fed91c12681a7b34617c8fcefe39ea04599ca72c608751c31d89579a3f77" + "sha256:6b571215b5a790f9b41f19f3531c53a45cf6bb8ef2988bc1ff9afb38270b25fa", + "sha256:e41d489ff43948babd0fad7ad5e49b8735d5d55e26628a58673c39ff61d95de4" ], "index": "pypi", - "version": "==5.0.1" + "version": "==5.3.2" }, "pytz": { "hashes": [ - "sha256:26c0b32e437e54a18161324a2fca3c4b9846b74a8dccddd843113109e1116b32", - "sha256:c894d57500a4cd2d5c71114aaab77dbab5eabd9022308ce5ac9bb93a60a6f0c7" + "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", + "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" ], - "version": "==2019.2" + "version": "==2019.3" }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" - ], - "version": "==5.1.2" + "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", + "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", + "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", + "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", + "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", + "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", + "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", + "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", + "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", + "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", + "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4" + ], + "version": "==5.2" }, "requests": { "hashes": [ @@ -767,54 +801,60 @@ }, "scipy": { "hashes": [ - "sha256:03b1e0775edbe6a4c64effb05fff2ce1429b76d29d754aa5ee2d848b60033351", - "sha256:09d008237baabf52a5d4f5a6fcf9b3c03408f3f61a69c404472a16861a73917e", - "sha256:10325f0ffac2400b1ec09537b7e403419dcd25d9fee602a44e8a32119af9079e", - "sha256:1db9f964ed9c52dc5bd6127f0dd90ac89791daa690a5665cc01eae185912e1ba", - "sha256:409846be9d6bdcbd78b9e5afe2f64b2da5a923dd7c1cd0615ce589489533fdbb", - "sha256:4907040f62b91c2e170359c3d36c000af783f0fa1516a83d6c1517cde0af5340", - "sha256:6c0543f2fdd38dee631fb023c0f31c284a532d205590b393d72009c14847f5b1", - "sha256:826b9f5fbb7f908a13aa1efd4b7321e36992f5868d5d8311c7b40cf9b11ca0e7", - "sha256:a7695a378c2ce402405ea37b12c7a338a8755e081869bd6b95858893ceb617ae", - "sha256:a84c31e8409b420c3ca57fd30c7589378d6fdc8d155d866a7f8e6e80dec6fd06", - "sha256:adadeeae5500de0da2b9e8dd478520d0a9945b577b2198f2462555e68f58e7ef", - "sha256:b283a76a83fe463c9587a2c88003f800e08c3929dfbeba833b78260f9c209785", - "sha256:c19a7389ab3cd712058a8c3c9ffd8d27a57f3d84b9c91a931f542682bb3d269d", - "sha256:c3bb4bd2aca82fb498247deeac12265921fe231502a6bc6edea3ee7fe6c40a7a", - "sha256:c5ea60ece0c0c1c849025bfc541b60a6751b491b6f11dd9ef37ab5b8c9041921", - "sha256:db61a640ca20f237317d27bc658c1fc54c7581ff7f6502d112922dc285bdabee" + "sha256:00af72998a46c25bdb5824d2b729e7dabec0c765f9deb0b504f928591f5ff9d4", + "sha256:0902a620a381f101e184a958459b36d3ee50f5effd186db76e131cbefcbb96f7", + "sha256:1e3190466d669d658233e8a583b854f6386dd62d655539b77b3fa25bfb2abb70", + "sha256:2cce3f9847a1a51019e8c5b47620da93950e58ebc611f13e0d11f4980ca5fecb", + "sha256:3092857f36b690a321a662fe5496cb816a7f4eecd875e1d36793d92d3f884073", + "sha256:386086e2972ed2db17cebf88610aab7d7f6e2c0ca30042dc9a89cf18dcc363fa", + "sha256:71eb180f22c49066f25d6df16f8709f215723317cc951d99e54dc88020ea57be", + "sha256:770254a280d741dd3436919d47e35712fb081a6ff8bafc0f319382b954b77802", + "sha256:787cc50cab3020a865640aba3485e9fbd161d4d3b0d03a967df1a2881320512d", + "sha256:8a07760d5c7f3a92e440ad3aedcc98891e915ce857664282ae3c0220f3301eb6", + "sha256:8d3bc3993b8e4be7eade6dcc6fd59a412d96d3a33fa42b0fa45dc9e24495ede9", + "sha256:9508a7c628a165c2c835f2497837bf6ac80eb25291055f56c129df3c943cbaf8", + "sha256:a144811318853a23d32a07bc7fd5561ff0cac5da643d96ed94a4ffe967d89672", + "sha256:a1aae70d52d0b074d8121333bc807a485f9f1e6a69742010b33780df2e60cfe0", + "sha256:a2d6df9eb074af7f08866598e4ef068a2b310d98f87dc23bd1b90ec7bdcec802", + "sha256:bb517872058a1f087c4528e7429b4a44533a902644987e7b2fe35ecc223bc408", + "sha256:c5cac0c0387272ee0e789e94a570ac51deb01c796b37fb2aad1fb13f85e2f97d", + "sha256:cc971a82ea1170e677443108703a2ec9ff0f70752258d0e9f5433d00dda01f59", + "sha256:dba8306f6da99e37ea08c08fef6e274b5bf8567bb094d1dbe86a20e532aca088", + "sha256:dc60bb302f48acf6da8ca4444cfa17d52c63c5415302a9ee77b3b21618090521", + "sha256:dee1bbf3a6c8f73b6b218cb28eed8dd13347ea2f87d572ce19b289d6fd3fbc59" ], "index": "pypi", - "version": "==1.3.0" + "version": "==1.4.1" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" }, "snowballstemmer": { "hashes": [ - "sha256:9f3b9ffe0809d174f7047e121431acf99c89a7040f0ca84f94ba53a498e6d0c9" + "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", + "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" ], - "version": "==1.9.0" + "version": "==2.0.0" }, "sphinx": { "hashes": [ - "sha256:22538e1bbe62b407cf5a8aabe1bb15848aa66bb79559f42f5202bbce6b757a69", - "sha256:f9a79e746b87921cabc3baa375199c6076d1270cee53915dbd24fdbeaaacc427" + "sha256:0a11e2fd31fe5c7e64b4fc53c2c022946512f021d603eb41ac6ae51d5fcbb574", + "sha256:138e39aa10f28d52aa5759fc6d1cba2be6a4b750010974047fa7d0e31addcf63" ], "index": "pypi", - "version": "==2.1.2" + "version": "==2.3.0" }, "sphinx-autoapi": { "hashes": [ - "sha256:79d11a33b436ad3c6909e1273e4c36de0c54c5da98d6aa3b4a3665204cfbcb28", - "sha256:a450cb8b5c0bdaf9c95b983f1e151d598e7d9a062db5085a8fe7b55eec8700ec" + "sha256:623646ed1be7127486cab5f4e3ffc17a9327c2d2f3fce18effc39d73975a2973", + "sha256:b6b5f008edc174768a4d1fa87bc8ba1287744c9a9ff82e49064783da7a2ad03e" ], "index": "pypi", - "version": "==1.1.0" + "version": "==1.2.1" }, "sphinxcontrib-applehelp": { "hashes": [ @@ -830,20 +870,6 @@ ], "version": "==1.0.1" }, - "sphinxcontrib-dotnetdomain": { - "hashes": [ - "sha256:985cfdba4bf67bd5af736bab76e9f206ae987c64198e6d42c7043cde11dfc374", - "sha256:c41bf7a9627f0b804be1992c4b62cbf4c757d26ff7d2541eb13f68981b180073" - ], - "version": "==0.4" - }, - "sphinxcontrib-golangdomain": { - "hashes": [ - "sha256:7bcd4bb6443d5f9b06e0e9b48dadb2e42967dd8c7d67ad03e501ea4575c55b26", - "sha256:dd49dc19140cbefad5af4f0157174eecfe075bcce4ad7a8ef725e8b81ab0744b" - ], - "version": "==0.2.0.dev0" - }, "sphinxcontrib-htmlhelp": { "hashes": [ "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", @@ -874,23 +900,28 @@ }, "typed-ast": { "hashes": [ + "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", + "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", + "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", + "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", + "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" ], - "markers": "implementation_name == 'cpython'", + "markers": "implementation_name == 'cpython' and python_version < '3.8'", "version": "==1.4.0" }, "unidecode": { @@ -902,10 +933,10 @@ }, "urllib3": { "hashes": [ - "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", - "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", + "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" ], - "version": "==1.25.3" + "version": "==1.25.7" }, "wcwidth": { "hashes": [ @@ -922,10 +953,10 @@ }, "zipp": { "hashes": [ - "sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a", - "sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec" + "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", + "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" ], - "version": "==0.5.2" + "version": "==0.6.0" } } } diff --git a/README.rst b/README.rst index 95bcccd0..68ab57d3 100644 --- a/README.rst +++ b/README.rst @@ -72,26 +72,22 @@ Overall Architecture :: - ============ +-----------------+ =================== - || model file || --> | frontend Parser | --> || uTensorGraph (IR) || - ============ +-----------------+ =================== - | - +-------------------------------+ | - | graph transformer | | - | (legalization & optimization) | <------/ - +-------------------------------+ - | - v - =========================== - || uTensorGraph || - || (legalized and optimized) || - =========================== - | - +--------------------------+ | - | backend (code generator) | <----/ + ============ +-----------------+ ============================ + || model file || --> | frontend Parser | --> || uTensorGraph (IR, generic) || + ============ +-----------------+ ============================ + | + v + +---------------------+ + ======================= | graph transformer | + || uTensorGraph || <-- | (optimization) | + || (generic, optimized) || +---------------------+ + ======================= + | + +--------------------------+ | + | backend (code generator) | <--/ +--------------------------+ | - `---> (target files, ex: model.cpp, model.hpp, weights.idx) + `---> (target files, ex: model.cpp, model.hpp, weights.idx, ...etc) Basic Usage =========== @@ -114,7 +110,8 @@ Convert Model File to C/C++ Code .. code-block:: console $ utensor-cli convert \ - --output-nodes=[,,...] + --output-nodes=[,,...] \ + [--config=config.toml] Convert given pb file into cpp/hpp files. @@ -123,6 +120,8 @@ nodes you want to output, seperated by comma for multiple values. In graph theory terminology, they are ``leaf`` nodes of your graph. +Use ``--config`` to pass a configuration file to the cli, you can use ``generate-config`` command to generate one (see below). + example ~~~~~~~ @@ -132,8 +131,34 @@ example Run ``utensor-cli convert --help`` for detailed information. -:mod:`utensor_cgen` as Library -============================== +Configuration +------------- + +``utensor-cli`` use ``toml`` as configuration format. + +You can generate configuration file of given target as following: + +.. code-block:: console + + $ utensor-cli generate-config --target [-o filename.toml] + +This command will generate a ``toml`` file listing all configurable values with its defaults. + +You can modify the value and pass the file to cli with ``--config`` flag. + +example +~~~~~~~ + +.. code-block:: console + + # generate config file + $ utensor-cli generate-config --target utensor -o myconfig.toml + + # after editting myconfig.toml + $ utensor-cli convert mymodel.pb --config=myconfig.toml --output-nodes=output,... + +Use :mod:`utensor_cgen` as Library +================================== .. subgraph-match-begine @@ -223,8 +248,8 @@ TensorFlow_ 1. Freeze your `tensorflow.Graph` - - please refer to the `official doc `_ - and read the `Freezing `_ section + - please refer to this `issue track `_ for detail + - especially this `comment `_ by Robin2091 2. Follow instructions in :ref:`install` section to install :mod:`utensor_cgen` diff --git a/example_config.toml b/example_config.toml new file mode 100644 index 00000000..6cc4500a --- /dev/null +++ b/example_config.toml @@ -0,0 +1,15 @@ +# https://github.com/toml-lang/toml +# .. +[utensor.backend] +legacy-api = true + +[utensor.backend.legacy_code_generator] +src_fname = "None" +params_dir = "data" +embed_params_dir = "/fs/data" +model_dir = "models" +transform_methods = [ "dropout(name_pattern=r\"(dropout[_\\w\\d]*)/.*\")", "linear_reorder", "quantize", "conv_pool", "inline", "biasAdd", "remove_id_op", "fake_gather_v2", "refcnt",] +save_graph = false +debug_cmt = false + +[utensor.backend.graph_lower] diff --git a/plugins/dummy_backend/__init__.py b/plugins/dummy_backend/__init__.py new file mode 100644 index 00000000..2e422f1e --- /dev/null +++ b/plugins/dummy_backend/__init__.py @@ -0,0 +1,34 @@ +from textwrap import wrap + +from utensor_cgen.backend.base import Backend +from utensor_cgen.backend import BackendManager +from utensor_cgen.utils import class_property + +@BackendManager.register +class DummyBackend(Backend): + TARGET = 'dummy-backend' + + def __init__(self, config): + if not config: + config = self.default_config + self.output_file = config[self.TARGET][self.COMPONENT]['output-file'] + + def apply(self, ugraph): + with open(self.output_file, 'w') as fid: + fid.write('#include \n\n') + fid.write('int main(int argc, char* argv[]) {\n') + fid.write(' printf("graph name: {}\\n");\n'.format(ugraph.name)) + fid.write(' printf("ops in topological sorted order:\\n");\n') + for op_name in ugraph.topo_order: + fid.write(' printf(" {}\\n");\n'.format(op_name)) + fid.write(' return 0;\n}') + + @class_property + def default_config(cls): + return { + cls.TARGET: { + cls.COMPONENT: { + 'output-file': 'list_op.c' + } + } + } diff --git a/setup.py b/setup.py index 63849a6b..3e08546e 100644 --- a/setup.py +++ b/setup.py @@ -3,27 +3,48 @@ import os from setuptools import find_packages, setup +from setuptools.command.develop import develop as _develop +from setuptools.command.install import install as _install root_dir = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(root_dir, "LICENSE")) as rf: license = rf.read() + +class _CompileFlatbuffMixin(object): + + def run(self): + super(_CompileFlatbuffMixin, self).run() + self._build_flatbuffer() + + def _build_flatbuffer(self): + install_dir = self.install_platlib + if install_dir is None: + install_dir = os.path.abspath('utensor') + + +class _Install(_CompileFlatbuffMixin, _install): pass + + +class _Develop(_CompileFlatbuffMixin, _develop): pass + + setup( name='utensor_cgen', version_config={ "starting_version": "0.0.0", - "version_format": "{tag}.dev{sha:.7s}" + "version_format": "{tag}.{sha:.7s}.dev" }, setup_requires=['better-setuptools-git-version'], + cmdclass={'install': _Install, 'develop': _Develop}, description="C code generation program for uTensor", long_description="please go to [doc](https://utensor-cgen.readthedocs.io/en/latest/) page for more information", - url="https://github.com/dboyliao/utensor_cgen", + url="https://github.com/uTensor/utensor_cgen", author="Dboy Liao", author_email="qmalliao@gmail.com", license=license, packages=find_packages(), - include_package_data=True, - package_data={"utensor_cgen": ["backend/snippets/templates/*"]}, + package_data={'utensor_cgen.backend.utensor.snippets': ["templates/*/*/*"]}, entry_points={ "console_scripts": [ "utensor-cli=utensor_cgen.cli:cli" @@ -38,7 +59,8 @@ 'torchvision', 'onnx-tf==1.2.1', 'graphviz', - 'flatbuffers' + 'flatbuffers', + 'toml', ], extras_require={ 'dev': ['pytest'] diff --git a/tests/test_backend/__init__.py b/tests/test_backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_backend/conftest.py b/tests/test_backend/conftest.py new file mode 100644 index 00000000..51cfa229 --- /dev/null +++ b/tests/test_backend/conftest.py @@ -0,0 +1,14 @@ +import os +from pytest import fixture + +@fixture(scope='session', name='mlp_ugraph') +def mlp_ugraph(): + from utensor_cgen.frontend import FrontendSelector + model_file = os.path.join( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), '..') + ), + 'deep_mlp/simple_mnist.pb' + ) + return FrontendSelector.parse(model_file, output_nodes=['y_pred']) diff --git a/tests/test_backend/test_utensor.py b/tests/test_backend/test_utensor.py new file mode 100644 index 00000000..aa41b5d8 --- /dev/null +++ b/tests/test_backend/test_utensor.py @@ -0,0 +1,19 @@ +import os + +def test_legacy_utensor(mlp_ugraph): + from utensor_cgen.backend.utensor import uTensorBackend + + this_dir = os.path.dirname(__file__) + + uTensorBackend(config={ + 'utensor': { + 'backend': { + 'legacy-api': True, + 'code_generator': { + 'model_dir': os.path.join(this_dir, 'models'), + 'params_dir': os.path.join(this_dir, 'data'), + }, + 'graph_lower': {} + }, + } + }).apply(mlp_ugraph) diff --git a/tests/test_ir/test_uTensorGraph/test_graph.py b/tests/test_ir/test_uTensorGraph/test_graph.py index ecb6a2c0..6a60e595 100644 --- a/tests/test_ir/test_uTensorGraph/test_graph.py +++ b/tests/test_ir/test_uTensorGraph/test_graph.py @@ -36,7 +36,7 @@ def test_op_info(): output_tensors=[], n_outputs=0, op_type='no_op', - backend='tensorflow', + lib_name='tensorflow', op_attr={ '_utensor_to_skip': [1, 2, 3], '_utensor_skip_this_too': None, diff --git a/tests/test_transformer/test_convpool/test_vgg.py b/tests/test_transformer/test_convpool/test_vgg.py index 8660ab2e..f9af19c1 100644 --- a/tests/test_transformer/test_convpool/test_vgg.py +++ b/tests/test_transformer/test_convpool/test_vgg.py @@ -4,9 +4,9 @@ def factory(): def test(vgg_ugraph): trans = TransformerPipeline([ - ('linear_reorder', {}), - ('quantize', {}), - ('conv_pool', {}) + 'linear_reorder', + 'quantize', + 'conv_pool', ]) new_ugraph = trans.transform(vgg_ugraph) num_conv = len(vgg_ugraph.get_ops_by_type('Conv2D')) diff --git a/tests/test_transformer/test_linear_reorder/conftest.py b/tests/test_transformer/test_linear_reorder/conftest.py index 94944398..e69dd354 100644 --- a/tests/test_transformer/test_linear_reorder/conftest.py +++ b/tests/test_transformer/test_linear_reorder/conftest.py @@ -11,9 +11,9 @@ def subject_ugraph_1(): input_1 = tf.placeholder(dtype=tf.float32, shape=[None, 512, 512, 10], name='input_1') relu_1 = tf.nn.relu(input_1, name='relu_1') max_pool_1 = tf.nn.max_pool(relu_1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool_1') - input_2 = tf.placeholder(dtype=tf.float32, shape=[None, 512, 512, 3], name='input_2') - relu_2 = tf.nn.relu(input_1, name='relu_2') - max_pool_2 = tf.nn.max_pool(relu_1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool_2') + input_2 = tf.placeholder(dtype=tf.float32, shape=[None, 512, 512, 10], name='input_2') + relu_2 = tf.nn.relu(input_2, name='relu_2') + max_pool_2 = tf.nn.max_pool(relu_2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool_2') output = tf.add(max_pool_1, max_pool_2, name='output') - subj_ugraph = GraphDefParser.parse(graph.as_graph_def(), output_nodes=['output']) + subj_ugraph = GraphDefParser.parse(graph.as_graph_def(), output_nodes=[output.op.name]) return subj_ugraph diff --git a/tests/test_transformer/test_pipeline/conftest.py b/tests/test_transformer/test_pipeline/conftest.py index 9e0c2d41..a71cf887 100644 --- a/tests/test_transformer/test_pipeline/conftest.py +++ b/tests/test_transformer/test_pipeline/conftest.py @@ -8,9 +8,11 @@ @pytest.fixture(scope='function', name='methods') def pipeline_methods(): - all_methods = [(BatchNormTransformer.METHOD_NAME, {}), - (DropoutTransformer.METHOD_NAME, {}), - (QuantizeTransformer.METHOD_NAME, {}), - (RefCntOptimizer.METHOD_NAME, {})] + all_methods = [ + BatchNormTransformer.METHOD_NAME, + DropoutTransformer.METHOD_NAME, + QuantizeTransformer.METHOD_NAME, + RefCntOptimizer.METHOD_NAME, + ] shuffle(all_methods) return all_methods diff --git a/tests/test_transformer/test_pipeline/test_pipeline_transformer.py b/tests/test_transformer/test_pipeline/test_pipeline_transformer.py index 1e1213f7..b2e64823 100644 --- a/tests/test_transformer/test_pipeline/test_pipeline_transformer.py +++ b/tests/test_transformer/test_pipeline/test_pipeline_transformer.py @@ -5,17 +5,17 @@ def test_pipeline_1(methods): pipeline = TransformerPipeline(methods) assert len(pipeline.pipeline) == len(methods) - for transformer, (method_name, _) in zip(pipeline.pipeline, methods): + for transformer, method_name in zip(pipeline.pipeline, methods): assert isinstance(transformer, pipeline.TRANSFORMER_MAP[method_name]) def test_pipeline_2(methods): pipeline = TransformerPipeline(methods) assert len(pipeline.pipeline) == len(methods) - for transformer, (method_name, _) in zip(pipeline.pipeline, methods): + for transformer, method_name in zip(pipeline.pipeline, methods): assert isinstance(transformer, pipeline.TRANSFORMER_MAP[method_name]) def test_pipeline_3(methods): pipeline = TransformerPipeline(methods) assert len(pipeline.pipeline) == len(methods) - for transformer, (method_name, _) in zip(pipeline.pipeline, methods): + for transformer, method_name in zip(pipeline.pipeline, methods): assert isinstance(transformer, pipeline.TRANSFORMER_MAP[method_name]) diff --git a/utensor_cgen/__init__.py b/utensor_cgen/__init__.py index e69de29b..c8e03414 100644 --- a/utensor_cgen/__init__.py +++ b/utensor_cgen/__init__.py @@ -0,0 +1,12 @@ +import sys + +import pkg_resources + +from utensor_cgen._extensions import _ExtensionsLoader + +__version__ = ( + pkg_resources + .get_distribution('utensor_cgen') + .version +) +sys.modules['utensor_cgen.extensions'] = _ExtensionsLoader() diff --git a/utensor_cgen/_extensions.py b/utensor_cgen/_extensions.py new file mode 100644 index 00000000..5a748c3b --- /dev/null +++ b/utensor_cgen/_extensions.py @@ -0,0 +1,26 @@ +import sys +import re +import importlib +from types import ModuleType + +class _ExtensionsLoader(ModuleType): + _ext_cache = {} + _dunder_pattern = re.compile(r'__[A-Za-z0-9_]+__') + + def __init__(self): + super(_ExtensionsLoader, self).__init__('utensor_cgen.extensions', {}) + + def __getattr__(self, ext_name): + if self._dunder_pattern.match(ext_name): + _mod = sys.modules['utensor_cgen._extensions'] + return getattr(_mod, ext_name) + if ext_name not in self._ext_cache: + ext_mod = importlib.import_module( + 'utensor_{}'.format(ext_name) + ) + self._ext_cache[ext_name] = ext_mod + sys.modules['utensor_cgen.extensions.{}'.format(ext_name)] = ext_mod + return self._ext_cache[ext_name] + + def __dir__(self): + return self._ext_cache.keys() diff --git a/utensor_cgen/backend/__init__.py b/utensor_cgen/backend/__init__.py index a6429329..7387b92d 100644 --- a/utensor_cgen/backend/__init__.py +++ b/utensor_cgen/backend/__init__.py @@ -1,2 +1 @@ -from .code_generator import CodeGenerator -from .operators import OperatorFactory \ No newline at end of file +from .api import BackendManager diff --git a/utensor_cgen/backend/api.py b/utensor_cgen/backend/api.py new file mode 100644 index 00000000..769863fa --- /dev/null +++ b/utensor_cgen/backend/api.py @@ -0,0 +1,28 @@ +from utensor_cgen.utils import class_property + +from .base import Backend +from .utensor import uTensorBackend + + +class BackendManager(object): + BACKENDS = {} + + @classmethod + def get_backend(cls, name): + if name not in cls.BACKENDS: + raise ValueError('unknown backend name: %s' % name) + return cls.BACKENDS[name] + + @classmethod + def register(cls, backend_cls): + if not issubclass(backend_cls, Backend): + raise TypeError( + 'can only register subclass of %s: get %s' % (Backend, backend_cls) + ) + cls.BACKENDS[backend_cls.TARGET] = backend_cls + + @class_property + def backends(cls): + return list(cls.BACKENDS.keys()) + +BackendManager.register(uTensorBackend) diff --git a/utensor_cgen/backend/base.py b/utensor_cgen/backend/base.py new file mode 100644 index 00000000..ed74618a --- /dev/null +++ b/utensor_cgen/backend/base.py @@ -0,0 +1,87 @@ +from utensor_cgen.utils import ( + MUST_OVERWRITEN, class_property, + parse_toml, Configuration, +) + + +class _BackendBase(object): + + COMPONENT = 'backend' + + def __new__(cls, config, *args, **kwargs): + self = object.__new__(cls) + for name in dir(self): + if name.startswith('_validate'): + validator = getattr(self, name) + validator(config, *args, **kwargs) + if isinstance(config, dict): + config = Configuration(cls.default_config, config) + self._config = config + return self + + def apply(self, ugraph): + raise NotImplementedError('all backend object must overwrite apply method') + + @class_property + def default_config(cls): + return NotImplementedError('All backends should overwrite default config') + + def __call__(self, *args, **kwargs): + return self.apply(*args, **kwargs) + + @property + def config(self): + return self._config + + def _validate_config(self, config): + assert isinstance(config, (dict, Configuration)), \ + 'expecting {}, get {}'.format(dict, type(config)) + + +class Backend(_BackendBase): + + TARGET = MUST_OVERWRITEN + + @classmethod + def _validate_target(cls, config, *args, **kwargs): + if cls.TARGET is MUST_OVERWRITEN: + raise ValueError( + 'Every Backend must overwrite TARGET attribute: {}'.format(cls) + ) + + @classmethod + def from_file(cls, file_or_path, *args, **kwargs): + config = parse_toml(file_or_path)[cls.TARGET][cls.COMPONENT] + return cls(config, *args, **kwargs) + + @classmethod + def from_config(cls, config, *args, **kwargs): + return cls(config, *args, **kwargs) + +class BackendPart(Backend): + + PART = MUST_OVERWRITEN + + @classmethod + def _validate_part(cls, config, *args, **kwargs): + if cls.PART is MUST_OVERWRITEN: + raise ValueError( + 'Every BackendPart must overwrite PART attribute: {}'.format(cls) + ) + + @classmethod + def from_config(cls, config, *args, **kwargs): + default_config = cls.default_config + if cls.TARGET in config: + config = config[cls.TARGET] + if cls.COMPONENT in config: + config = config[cls.COMPONENT] + if cls.PART in config: + config = config[cls.PART] + default_config.update(config) + return cls(default_config, *args, **kwargs) + + @classmethod + def from_file(cls, file_or_path, *args, **kwargs): + config = parse_toml(file_or_path)[cls.TARGET][cls.COMPONENT][cls.PART] + return cls(config, *args, **kwargs) diff --git a/utensor_cgen/backend/snippets/__init__.py b/utensor_cgen/backend/snippets/__init__.py deleted file mode 100644 index 0ebed11a..00000000 --- a/utensor_cgen/backend/snippets/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._snippets import * diff --git a/utensor_cgen/backend/snippets/_types.py b/utensor_cgen/backend/snippets/_types.py deleted file mode 100644 index 50610e26..00000000 --- a/utensor_cgen/backend/snippets/_types.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding:utf8 -*- -from collections import namedtuple - -import numpy as np -import tensorflow as tf - -_TYPE_MAP_VALUE = namedtuple("_TYPE_MAP_VALUE", ["importer_type_str", "tensor_type_str"]) - -NP_TYPES_MAP = { - np.dtype(tf.float32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="float", - tensor_type_str="float"), - np.dtype(tf.qint8.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="byte", - tensor_type_str="uint8_t"), - np.dtype(tf.int32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", - tensor_type_str="int"), - np.dtype(tf.int64.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", - tensor_type_str="int"), - np.dtype(tf.quint8.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="ubyte", - tensor_type_str="uint8_t"), - np.dtype(tf.qint32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", - tensor_type_str="int"), - np.dtype('uint16'): _TYPE_MAP_VALUE(importer_type_str="ushort", - tensor_type_str="uint16_t"), - np.dtype('int8'): _TYPE_MAP_VALUE(importer_type_str="int8", - tensor_type_str="q7_t"), -} -del _TYPE_MAP_VALUE diff --git a/utensor_cgen/backend/utensor/__init__.py b/utensor_cgen/backend/utensor/__init__.py new file mode 100644 index 00000000..ed85033c --- /dev/null +++ b/utensor_cgen/backend/utensor/__init__.py @@ -0,0 +1 @@ +from ._backend_impl import uTensorBackend diff --git a/utensor_cgen/backend/utensor/_backend_impl.py b/utensor_cgen/backend/utensor/_backend_impl.py new file mode 100644 index 00000000..cc5db5b7 --- /dev/null +++ b/utensor_cgen/backend/utensor/_backend_impl.py @@ -0,0 +1,62 @@ +from utensor_cgen.backend.base import Backend +from utensor_cgen.utils import ( + class_property, parse_toml, + LazyLoader, LazyAttrib, + Configuration, +) + +code_generator = LazyLoader(submod_name='backend.utensor.code_generator') +_graph_lower = LazyLoader(submod_name='backend.utensor._graph_lower') +uTensorLegacyCodeGenerator = LazyAttrib(code_generator, 'uTensorLegacyCodeGenerator') +uTensorRearchCodeGenerator = LazyAttrib(code_generator, 'uTensorRearchCodeGenerator') +uTensorLegacyGraphLower = LazyAttrib(_graph_lower, 'uTensorLegacyGraphLower') +uTensorRearchGraphLower = LazyAttrib(_graph_lower, 'uTensorRearchGraphLower') + +del code_generator, _graph_lower + +class uTensorBackend(Backend): + + TARGET = 'utensor' + + def __init__(self, config, code_generator=None, graph_lower=None): + default_config = self.default_config[self.TARGET][self.COMPONENT] + config = Configuration(default_config, config.get( + self.TARGET, + {self.COMPONENT: {}} + ).get( + self.COMPONENT, {} + ) + ) + if config['legacy-api']: + code_generator = code_generator or uTensorLegacyCodeGenerator(config=config[uTensorLegacyCodeGenerator.PART]) + graph_lower = graph_lower or uTensorLegacyGraphLower(config=config[uTensorLegacyGraphLower.PART]) + else: + code_generator = code_generator or uTensorRearchCodeGenerator(config=config[uTensorRearchCodeGenerator.PART]) + graph_lower = graph_lower or uTensorRearchGraphLower(config=config[uTensorRearchGraphLower.PART]) + self._graph_lower = graph_lower + self._code_generator = code_generator + + @class_property + def default_config(cls): + config = {} + config[cls.TARGET] = {} + config[cls.TARGET][cls.COMPONENT] = {} + config[cls.TARGET][cls.COMPONENT]['legacy-api'] = True + config[cls.TARGET][cls.COMPONENT][uTensorLegacyCodeGenerator.PART] = uTensorLegacyCodeGenerator.default_config + config[cls.TARGET][cls.COMPONENT][uTensorRearchCodeGenerator.PART] = uTensorRearchCodeGenerator.default_config + config[cls.TARGET][cls.COMPONENT][uTensorLegacyGraphLower.PART] = uTensorLegacyGraphLower.default_config + config[cls.TARGET][cls.COMPONENT][uTensorRearchGraphLower.PART] = uTensorRearchGraphLower.default_config + return config + + def apply(self, ugraph): + lower_ugraph = self._graph_lower.apply(ugraph) + self._code_generator.apply(lower_ugraph) + return lower_ugraph + + def __call__(self, ugraph): + return self.apply(ugraph) + + @classmethod + def from_file(cls, path_or_file): + config = parse_toml(path_or_file) + return cls(config=config) diff --git a/utensor_cgen/backend/utensor/_graph_lower.py b/utensor_cgen/backend/utensor/_graph_lower.py new file mode 100644 index 00000000..8ba62a17 --- /dev/null +++ b/utensor_cgen/backend/utensor/_graph_lower.py @@ -0,0 +1,61 @@ +from copy import deepcopy + +from utensor_cgen.backend.base import BackendPart +from utensor_cgen.utils import class_property + + +class uTensorLegacyGraphLower(BackendPart): + + TARGET = 'utensor' + PART = 'legacy_graph_lower' + + def apply(self, ugraph): + handler = getattr(self, 'handle_{}'.format(ugraph.lib_name)) + if handler is None: + raise RuntimeError( + 'can not lower ugraph from {} to utensor'.format(ugraph.lib_name) + ) + return handler(ugraph) + + def handle_tensorflow(self, ugraph): + return ugraph + + @class_property + def default_config(cls): + return {} + + +class uTensorRearchGraphLower(BackendPart): + TARGET = 'utensor' + PART = 'rearch_graph_lower' + + class OptypRenameManager(object): + NAME_MAP = { + 'Add': 'AddOperator', + 'Conv2D': 'ConvOperator', + 'MatMul': 'MatrixMultOperator' + } + + @classmethod + def get_new_optype(cls, op_type): + return cls.NAME_MAP.get(op_type, op_type) + + def apply(self, ugraph): + handler = getattr(self, 'handle_{}'.format(ugraph.lib_name)) + if handler is None: + raise RuntimeError( + 'can not lower ugraph from {} to utensor'.format(ugraph.lib_name) + ) + return handler(ugraph) + + def handle_tensorflow(self, ugraph): + new_ugraph = deepcopy(ugraph) + for op_info in new_ugraph.ops_info.values(): + op_info.op_type = self.OptypRenameManager.get_new_optype(op_info.op_type) + return new_ugraph + + @class_property + def default_config(cls): + return {} + + diff --git a/utensor_cgen/backend/utensor/code_generator/__init__.py b/utensor_cgen/backend/utensor/code_generator/__init__.py new file mode 100644 index 00000000..05e70bf4 --- /dev/null +++ b/utensor_cgen/backend/utensor/code_generator/__init__.py @@ -0,0 +1,2 @@ +from .legacy import uTensorLegacyCodeGenerator +from .rearch import uTensorRearchCodeGenerator \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/code_generator/legacy/__init__.py b/utensor_cgen/backend/utensor/code_generator/legacy/__init__.py new file mode 100644 index 00000000..a6f33919 --- /dev/null +++ b/utensor_cgen/backend/utensor/code_generator/legacy/__init__.py @@ -0,0 +1 @@ +from ._code_generator import uTensorLegacyCodeGenerator \ No newline at end of file diff --git a/utensor_cgen/backend/code_generator.py b/utensor_cgen/backend/utensor/code_generator/legacy/_code_generator.py similarity index 53% rename from utensor_cgen/backend/code_generator.py rename to utensor_cgen/backend/utensor/code_generator/legacy/_code_generator.py index 5ba2c674..da269a0f 100644 --- a/utensor_cgen/backend/code_generator.py +++ b/utensor_cgen/backend/utensor/code_generator/legacy/_code_generator.py @@ -6,90 +6,81 @@ import numpy as np -import tensorflow as tf -from tensorflow.core.framework.graph_pb2 import GraphDef -from tensorflow.tools.graph_transforms import TransformGraph +from utensor_cgen.backend.base import BackendPart from utensor_cgen.frontend import FrontendSelector from utensor_cgen.ir import uTensorGraph from utensor_cgen.transformer.optimizer import RefCntOptimizer from utensor_cgen.transformer.pipeline import TransformerPipeline -from utensor_cgen.utils import NamescopedKWArgsParser +from utensor_cgen.utils import (NamescopedKWArgsParser, class_property, + parse_toml, LazyLoader, Configuration) +from utensor_cgen.backend.utensor.snippets.legacy import ( + CommentSnippet, ContextGlobalArrayContainer, + ContextHeaderSnippet, ContextSnippetsContainer, + CreateTensorBinarySnippet, CreateTensorIdxSnippet +) +from utensor_cgen.backend.utensor.snippets.composer import Composer -from .operators import OperatorFactory -from .snippets import (CommentSnippet, ContextGlobalArrayContainer, - ContextHeaderSnippet, ContextSnippetsContainer, - CreateTensorBinarySnippet, CreateTensorIdxSnippet) -from .snippets.composer import Composer +from ._operators import OperatorFactory -__all__ = ["CodeGenerator"] +__all__ = ["uTensorLegacyCodeGenerator"] _logger = logging.getLogger('utensor-cli') +tf = LazyLoader('tensorflow') -class CodeGenerator(object): - def __init__(self, model_file, - idx_dir, - embed_data_dir, - trans_methods, # [(trans_name, kwargs),...] - output_nodes, - save_graph=False, - debug_cmt=False): - self.model_file = model_file - if not os.path.exists(idx_dir): - os.makedirs(idx_dir) - self.idx_dir = idx_dir - self.embed_data_dir = embed_data_dir.rstrip("/") - self.trans_methods = trans_methods - self.output_nodes = output_nodes - self.save_graph = save_graph - self.debug_cmt = debug_cmt - - def generate(self, src_fname): - _, ext = os.path.splitext(self.model_file) - parser_cls = FrontendSelector.select_parser(ext) - ugraph = parser_cls.parse(self.model_file, self.output_nodes) - self._generate(src_fname, ugraph) - - def _generate(self, src_fname, ugraph): +class uTensorLegacyCodeGenerator(BackendPart, object): + + TARGET = 'utensor' + PART = 'legacy_code_generator' + + def __init__(self, config): + final_config = Configuration(self.default_config, config) + self.src_fname = final_config['src_fname'] + self.params_dir = final_config['params_dir'].rstrip('/') + if not os.path.exists(self.params_dir): + os.makedirs(self.params_dir) + self.embed_data_dir = final_config['embed_params_dir'].rstrip('/') + self.model_dir = final_config['model_dir'].rstrip('/') + self.trans_methods = final_config['transform_methods'] + self.save_graph = final_config['save_graph'] + self.debug_cmt = final_config['debug_cmt'] + + @classmethod + def from_kwargs(cls, **kwargs): + return cls(config=kwargs) + + def apply(self, ugraph): """Generate source and header files """ - fname, _ = os.path.splitext(src_fname) - graph_name, _ = os.path.splitext(os.path.basename(self.model_file)) - guard_name = fname.replace('/', '_') - weightheader_fname = '{}_weight.hpp'.format(fname) - header_snippet = ContextHeaderSnippet(guard_name, graph_name) + src_fname = self.src_fname + if src_fname == 'None': + src_fname = '{}.cpp'.format(ugraph.name) + header_snippet = ContextHeaderSnippet( + '_MODELS_{}'.format(ugraph.name), + ugraph.name + ) weight_container = ContextGlobalArrayContainer() composer = Composer() - header_fname = '{}.hpp'.format(fname) - header_name = os.path.basename(header_fname) - weightheader_name = os.path.basename(weightheader_fname) - container = ContextSnippetsContainer(graph_name, header_name, weightheader_name) + header_fname = '{}.hpp'.format(ugraph.name) + weight_header_fname = '{}_weight.hpp'.format(ugraph.name) + container = ContextSnippetsContainer(ugraph.name, header_fname, weight_header_fname) opFactory = OperatorFactory() self._check_non_quantized(ugraph) - _logger.info("Transforming graph: %s", self.model_file) - def formatter(name, kwargs): - if kwargs: - return '{}({})'.format( - name, - ', '.join(['{}={!r}'.format(k, v) for k, v in kwargs.items()]) - ) - else: - return name - _logger.info("Transform pipeline: %s", ' -> '.join([ - formatter(name, kwargs) for name, kwargs in self.trans_methods - ]) - ) + _logger.info("Transforming graph: %s", ugraph.name) + _logger.info("Transform pipeline: %s", ' -> '.join(self.trans_methods)) quant_ugraph = self._transform_graph(ugraph, self.trans_methods) _logger.info('Graph transormation done') if self.save_graph: _logger.info('Saving transformed graph') - pkl_fname = "quant_{}.pkl".format(graph_name) + pkl_fname = "quant_{}.pkl".format(ugraph.name) with open(pkl_fname, 'wb') as fid: pickle.dump(quant_ugraph, fid) _logger.info('{} saved'.format(pkl_fname)) + if not os.path.exists(os.path.join(self.params_dir, ugraph.name)): + os.makedirs(os.path.join(self.params_dir, ugraph.name)) for op_id, op_name in enumerate(quant_ugraph.topo_order): op_info = quant_ugraph.ops_info[op_name] op_type = op_info.op_type @@ -106,7 +97,7 @@ def formatter(name, kwargs): # TODO: the operator may correspond to multiple snippets (such as InlinTensor) # weight_container is passed to function for workaround snippet = opFactory.createOperatorSnippet(op_info, - idx_dir=self.idx_dir, + idx_dir=os.path.join(self.params_dir, ugraph.name), embed_data_dir=self.embed_data_dir, weight_container=weight_container, data_manager=quant_ugraph.data_manager) @@ -119,22 +110,47 @@ def formatter(name, kwargs): container.add_snippet(cmt_snippet) composer.add_snippet(container) - if any([method_name == 'inline' for method_name, _ in self.trans_methods]): - _logger.info("Generate weight file: %s", weightheader_fname) - with open(weightheader_fname, "w") as wf: + # generate cpp/hpp files + if not os.path.exists(self.model_dir): + os.makedirs(self.model_dir) + if any([method == 'inline' for method in self.trans_methods]): + _logger.info("Generate weight file: %s", weight_header_fname) + with open(os.path.join(self.model_dir, weight_header_fname), "w") as wf: wf.write('// Auto generated by utensor-cli\n\n') wf.write(weight_container.render()) else: - container.remove_header('"{}"'.format(weightheader_name)) + container.remove_header('"{}"'.format(weight_header_fname)) _logger.info("Generate header file: %s", header_fname) - with open(header_fname, "w") as wf: + with open(os.path.join(self.model_dir, header_fname), "w") as wf: wf.write('// Auto generated by utensor-cli\n\n') wf.write(header_snippet.render()) _logger.info("Generate source file: %s", src_fname) - with open(src_fname, "w") as wf: + with open(os.path.join(self.model_dir, src_fname), "w") as wf: wf.write('// Auto generated by utensor-cli\n\n') wf.write(composer.compose()) + + @class_property + def default_config(cls): + config = {} + config['src_fname'] = 'None' + config['params_dir'] = 'data' + config['embed_params_dir'] = '/fs/data' + config['model_dir'] = 'models' + config['transform_methods'] = [ + 'dropout(name_pattern=r"(dropout[_\w\d]*)/.*")', + 'linear_reorder', + 'quantize', + 'conv_pool', + 'inline', + 'biasAdd', + 'remove_id_op', + 'fake_gather_v2', + 'refcnt' + ] + config['save_graph'] = False + config['debug_cmt'] = False + return config @classmethod def _check_non_quantized(cls, ugraph): diff --git a/utensor_cgen/backend/operators.py b/utensor_cgen/backend/utensor/code_generator/legacy/_operators.py similarity index 98% rename from utensor_cgen/backend/operators.py rename to utensor_cgen/backend/utensor/code_generator/legacy/_operators.py index 4f7f939a..bcc87450 100644 --- a/utensor_cgen/backend/operators.py +++ b/utensor_cgen/backend/utensor/code_generator/legacy/_operators.py @@ -7,24 +7,25 @@ import numpy as np import idx2numpy as idx2np -import tensorflow as tf from utensor_cgen.ir import OperationInfo, TensorInfo from utensor_cgen.ir.converter import (AttrValueConverter, DataTypeConverter, GenericTensorConverterMixin) from utensor_cgen.logger import logger from utensor_cgen.matcher import OpEqualityDelegate, _morphism from utensor_cgen.transformer.optimizer import RefCntOptimizer -from utensor_cgen.utils import NamescopedKWArgsParser +from utensor_cgen.utils import NamescopedKWArgsParser, LazyLoader -from .snippets import * # pylint: disable=W0401,W0614 +from utensor_cgen.backend.utensor.snippets.legacy import * # pylint: disable=W0401,W0614 __all__ = ['OperatorFactory', 'OpNotSupportedError'] +tf = LazyLoader('tensorflow') + class OpNotSupportedError(Exception): pass -class OperatorFactory(): - # Can easily do something smarter +class OperatorFactory(object): + _operators = {} def createOperatorSnippet(self, op_info, **kwargs): @@ -138,7 +139,7 @@ def build_op_info(cls, ugraph, name, value, **kwargs): ) }, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow') + lib_name=kwargs.get('lib_name', 'tensorflow'), ) def _tf_prepare_tensor_name(self, tensor_name): @@ -202,7 +203,7 @@ def build_op_info(cls, ugraph, name, tensor_x, tensor_y, **kwargs): ) }, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow') + lib_name=kwargs.get('lib_name', 'tensorflow'), ) @@ -265,7 +266,7 @@ def build_op_info(cls, ugraph, name, input_tensor, dtype=np.dtype('int64'), axis ], op_attr=op_attr, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow') + lib_name=kwargs.get('lib_name', 'tensorflow'), ) @@ -350,7 +351,7 @@ def build_op_info(cls, ugraph, name, tensor, axis=-1, keepdims=False, **kwargs): k: AttrValueConverter.get_generic_value(v) for k, v in node_def.attr.items() }, - backend=kwargs.get('backend', 'tensorflow'), + lib_name=kwargs.get('lib_name', 'tensorflow'), ugraph=ugraph ) @@ -408,7 +409,7 @@ def build_op_info(cls, ugraph, name, tensor, axis=-1, keepdims=False, **kwargs): ) ], op_type=cls.op_type, - backend=kwargs.get('backend', 'tensorflow'), + lib_name=kwargs.get('lib_name', 'tensorflow'), ugraph=ugraph, op_attr={ k: AttrValueConverter.get_generic_value(v) @@ -477,7 +478,7 @@ def build_op_info( ) ], op_type=cls.op_type, - backend=kwargs.get('backend', 'tensorflow'), + lib_name=kwargs.get('lib_name', 'tensorflow'), ugraph=ugraph, op_attr={ k: AttrValueConverter.get_generic_value(v) @@ -564,7 +565,7 @@ def build_op_info(cls, ugraph, name, tensor, axis=-1, keepdims=False, **kwargs): ) ], op_type=cls.op_type, - backend=kwargs.get('backend', 'tensorflow'), + lib_name=kwargs.get('lib_name', 'tensorflow'), ugraph=ugraph, op_attr={ k: AttrValueConverter.get_generic_value(v) @@ -654,7 +655,7 @@ def build_op_info(cls, ugraph, name, tensor_x, tensor_w, **kwargs): ) }, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow') + lib_name=kwargs.get('lib_name', 'tensorflow'), ) @@ -726,7 +727,7 @@ def build_op_info(cls, ugraph, name, tensor, **kwargs): ) }, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow') + lib_name=kwargs.get('lib_name', 'tensorflow'), ) @@ -894,7 +895,6 @@ class _CMSIS_NN_FCOperator(_Operator): def __init__(self, op_info, **kwargs): _Operator.__init__(self) - #import pdb; pdb.set_trace() # Note order of inputs/outputs is preserved inputs = [tensor_info.name for tensor_info in op_info.input_tensors] output = op_info.output_tensors[0].name @@ -972,7 +972,7 @@ def build_op_info(cls, ugraph, name, tensor_x, tensor_w, stride_height, stride_w op_type=cls.op_type, op_attr=op_attr, ugraph=ugraph, - backend=kwargs.get('backend', 'tensorflow'), + lib_name=kwargs.get('lib_name', 'tensorflow'), ) @OperatorFactory.register diff --git a/utensor_cgen/backend/utensor/code_generator/rearch/__init__.py b/utensor_cgen/backend/utensor/code_generator/rearch/__init__.py new file mode 100644 index 00000000..45c2164e --- /dev/null +++ b/utensor_cgen/backend/utensor/code_generator/rearch/__init__.py @@ -0,0 +1 @@ +from ._code_generator import uTensorRearchCodeGenerator \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/code_generator/rearch/_code_generator.py b/utensor_cgen/backend/utensor/code_generator/rearch/_code_generator.py new file mode 100644 index 00000000..65c53e1d --- /dev/null +++ b/utensor_cgen/backend/utensor/code_generator/rearch/_code_generator.py @@ -0,0 +1,170 @@ +import os +import pickle +import re +from itertools import chain +from pathlib import Path + +from utensor_cgen.backend.base import BackendPart +from utensor_cgen.backend.utensor.snippets.composer import Composer +from utensor_cgen.backend.utensor.snippets.legacy import ( + ContextGlobalArrayContainer, WeightSnippet) +from utensor_cgen.backend.utensor.snippets.rearch import SimpleContainer +from utensor_cgen.backend.utensor.snippets.template_env import env +from utensor_cgen.transformer.pipeline import TransformerPipeline +from utensor_cgen.utils import Configuration, class_property + +from ._operators import OperatorFactory + + +class uTensorRearchCodeGenerator(BackendPart): + + TARGET = 'utensor' + PART = 'rearch_code_generator' + + def __init__(self, config): + final_config = Configuration(self.default_config, config) + self.src_fname = final_config['src_fname'] + self.header_fname = final_config['header_fname'] + self.params_dir = final_config['params_dir'].rstrip('/') + self.trans_methods = final_config['transform_methods'] + self.meta_data_pool_size = final_config['meta_data_pool_size'] + self.ram_data_pool_size = final_config['ram_data_pool_size'] + self.model_dir = final_config['model_dir'].rstrip('/') + self.save_graph = final_config['save_graph'] + + def apply(self, ugraph): + src_fname = self.src_fname + if src_fname == 'None': + src_fname = '{}.cpp'.format(ugraph.name) + pipeline = TransformerPipeline(self.trans_methods) + new_ugraph = pipeline.transform(ugraph) + if self.save_graph: + with open('transformed_{}.pkl'.format(ugraph.name), 'wb') as fid: + pickle.dump(new_ugraph, fid) + # 1. find all ops required + ops = set() + placeholders = set() + tensor_var_map = {} # tensor name -> var name + for op_info in new_ugraph.ops_info.values(): + for tensor in op_info.output_tensors: + tensor_var_name = re.sub(r'[:/]', '', tensor.name) + tensor_var_map[tensor.name] = tensor_var_name + if op_info.op_type == 'Placeholder': + placeholders.add(tensor_var_name) + if op_info.op_type not in ['Placeholder', 'Inline']: + ops.add( + OperatorFactory.get_opertor(op_info) + ) + # 2. ops/tensors declaration + declare_snippets = [] + ops_map = {} # op -> op variable name + for i, op in enumerate(ops): + op_var_name = 'op_{:03d}'.format(i) + ops_map[op] = op_var_name + declare_snippets.append(op.get_declare_snippet(op_var_name)) + weight_snippets = [] + for op_info in filter(lambda op_info: op_info.op_type == 'Inline', new_ugraph.ops_info.values()): + tensor = op_info.output_tensors[0] + buffer_name = 'data_{}'.format(tensor.name.replace(':', '_').replace('/', '_')) + weight_snippets.append( + WeightSnippet( + buffer_name, + tensor.dtype, + tensor.shape, + op_info.op_attr['value'].value.np_array.ravel() + ) + ) + declare_snippets.append( + OperatorFactory.get_opertor(op_info).get_declare_snippet( + tensor_var_name=tensor_var_map[tensor.name], + buffer_var_name=buffer_name, + tensor=tensor + ) + ) + # 3. evaluation snippets + eval_snippets = [] + for op_name in new_ugraph.topo_order: + op_info = new_ugraph.ops_info[op_name] + if op_info.op_type in ['Placeholder', 'Inline']: + continue + op = OperatorFactory.get_opertor(op_info) + op_name = ops_map[op] + eval_snippets.append( + op.get_eval_snippet(op_info, op_name, tensor_var_map) + ) + template_vars = {} + template_vars['model_name'] = ugraph.name + template_vars['meta_data_pool_size'] = self._compute_meta_data_size(new_ugraph) + template_vars['ram_data_pool_size'] = self._compute_ram_data_size(new_ugraph) + template_vars['placeholders'] = placeholders + template_vars['out_tensor_var_names'] = [ + tensor_var_map[tensor.name] for tensor in chain(*[ + new_ugraph.ops_info[op_name].output_tensors + for op_name in new_ugraph.output_nodes + ]) + ] + # 4. write files + params_dir = Path(self.params_dir) / ugraph.name + params_dir.mkdir(parents=True, exist_ok=True) + weight_header_fname = None + if weight_snippets: + with (params_dir / 'params_{}.hpp'.format(ugraph.name)).open('w') as fid: + weight_container = ContextGlobalArrayContainer(snippets=weight_snippets) + fid.write(weight_container.render()) + weight_header_fname = fid.name + + # # generate the computation function + model_file_dir = Path(self.model_dir) + header_fname = self.header_fname == 'None' and '{}.hpp'.format(ugraph.name) or self.header_fname + container_snippet = SimpleContainer(declare_snippets=declare_snippets, eval_snippests=eval_snippets) + container_snippet.template_vars.update(template_vars) + (model_file_dir / ugraph.name).mkdir(parents=True, exist_ok=True) + with (model_file_dir / ugraph.name / header_fname).open('w') as fid: + template = env.get_template('snippets/rearch/simple.hpp') + fid.write(template.render(**template_vars)) + container_snippet.add_header(fid.name) + if weight_header_fname: + container_snippet.add_header(weight_header_fname) + composer = Composer(snippets=[container_snippet]) + src_fname = self.src_fname == 'None' and '{}.cpp'.format(ugraph.name) or self.src_fname + with (model_file_dir / ugraph.name / src_fname ).open('w') as fid: + fid.write(composer.compose()) + + @class_property + def default_config(cls): + config = {} + config['src_fname'] = 'None' + config['header_fname'] = 'None' + config['params_dir'] = 'data' + config['model_dir'] = 'models' + config['transform_methods'] = [ + 'dropout(name_pattern=r"(dropout[_\w\d]*)/.*")', + # 'linear_reorder', + # 'quantize', + # 'conv_pool', + 'inline', + 'biasAdd', + 'remove_id_op', + 'fake_gather_v2', + # 'refcnt' + ] + config['meta_data_pool_size'] = 'auto' + config['ram_data_pool_size'] = 'auto' + config['save_graph'] = False + return config + + def _compute_meta_data_size(self, ugraph): + if self.meta_data_pool_size == 'auto': + # TODO: compute actual meta data size with ugraph + size = 256 + else: + size = self.meta_data_pool_size + return size + + def _compute_ram_data_size(self, ugraph): + if self.ram_data_pool_size == 'auto': + # TODO: compute actual ram data size with ugraph + size = 256 + else: + size = self.ram_data_pool_size + return size diff --git a/utensor_cgen/backend/utensor/code_generator/rearch/_operators.py b/utensor_cgen/backend/utensor/code_generator/rearch/_operators.py new file mode 100644 index 00000000..9f34857a --- /dev/null +++ b/utensor_cgen/backend/utensor/code_generator/rearch/_operators.py @@ -0,0 +1,127 @@ +from six import with_metaclass + +from utensor_cgen.backend.utensor.snippets._types import NP_TYPES_MAP +from utensor_cgen.backend.utensor.snippets.rearch import (AddOpEvalSnippet, + DeclareOpSnippet, + RomTensorSnippet) + +__all__ = ['OperatorFactory', 'OpNotSupportedError'] + + +class OpNotSupportedError(Exception): pass + + +class OperatorFactory(object): + + _operators = {} + + @classmethod + def get_opertor(cls, op_info): + op_type = op_info.op_type + op_cls = cls._operators.get(op_type) + if op_cls is None: + raise OpNotSupportedError( + '{} not supported in utensor_cgen'.format(op_type) + ) + return op_cls(op_info) + + @classmethod + def register(cls, op_cls): + cls._operators[op_cls.op_type] = op_cls + return op_cls + + @classmethod + def support_op_types(cls): + """Return the set of all supported ops + """ + return set(cls._operators.keys()) + + @classmethod + def is_supported(cls, op_type): + if op_type != 'Placeholder' and op_type not in cls._operators: + return False + return True + + +class _OperatorMeta(type): + + def __new__(mcls, name, bases, attrib): + attrib['_cache'] = {} + cls = type.__new__(mcls, name, bases, attrib) + return cls + + +class _Operator(with_metaclass(_OperatorMeta), object): + + def __new__(cls, op_info): + in_dtypes = tuple(t.dtype for t in op_info.input_tensors) + out_dtypes = tuple(t.dtype for t in op_info.output_tensors) + type_signature = (in_dtypes, out_dtypes) + if type_signature not in cls._cache: + self = object.__new__(cls) + self.in_dtypes = in_dtypes + self.out_dtypes = out_dtypes + cls._cache[type_signature] = self + return cls._cache[type_signature] + + def get_declare_snippet(self, op_var_name, **kwargs): + raise NotImplementedError( + 'base get_declare_snippet invoked: {}'.format(type(self)) + ) + + def get_eval_snippet(self, op_info, op_name, tensor_var_map, **kwargs): + raise NotImplementedError( + 'base get_eval_snippet invoked: {}'.format(type(self)) + ) + + +@OperatorFactory.register +class _AddOperator(_Operator): + + op_type = 'AddOperator' + + def get_declare_snippet(self, op_var_name): + snippet = DeclareOpSnippet( + op_type=self.op_type, + dtypes=[NP_TYPES_MAP[self.in_dtypes[0]].tensor_type_str], + op_var_name=op_var_name, + ) + return snippet + + def get_eval_snippet(self, op_info, op_name, tensor_var_map): + snippet = AddOpEvalSnippet( + op_info=op_info, + op_name=op_name, + tensor_var_map=tensor_var_map, + dtypes=[op_info.input_tensors[0].dtype] + ) + return snippet + + +@OperatorFactory.register +class _MatmulOperator(_Operator): + + op_type = 'MatrixMultOperator' + + +@OperatorFactory.register +class _ConvOperator(_Operator): + + op_type = 'ConvOperator' + + +@OperatorFactory.register +class _InlineOperator(_Operator): + + op_type = 'Inline' + + def __init__(self, op_info): + self._tensor = op_info.output_tensors[0] + + def get_declare_snippet(self, tensor_var_name, buffer_var_name, tensor): + snippet = RomTensorSnippet( + tensor_var_name=tensor_var_name, + buffer_var_name=buffer_var_name, + tensor=tensor + ) + return snippet diff --git a/utensor_cgen/backend/utensor/snippets/__init__.py b/utensor_cgen/backend/utensor/snippets/__init__.py new file mode 100644 index 00000000..81c3b0fd --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/__init__.py @@ -0,0 +1 @@ +from .template_env import env \ No newline at end of file diff --git a/utensor_cgen/backend/snippets/_base.py b/utensor_cgen/backend/utensor/snippets/_base.py similarity index 84% rename from utensor_cgen/backend/snippets/_base.py rename to utensor_cgen/backend/utensor/snippets/_base.py index 96b5f00b..0d03add4 100644 --- a/utensor_cgen/backend/snippets/_base.py +++ b/utensor_cgen/backend/utensor/snippets/_base.py @@ -2,6 +2,8 @@ from abc import ABCMeta from copy import deepcopy +from utensor_cgen.utils import MUST_OVERWRITEN + from .template_env import env as _env __all__ = ["Snippet", "SnippetContainerBase"] @@ -9,14 +11,14 @@ class SnippetBase(object): __metaclass__ = ABCMeta - __template_name__ = None - __headers__ = None + __template_name__ = MUST_OVERWRITEN + __headers__ = MUST_OVERWRITEN def __init__(self): - if self.__template_name__ is None: - raise ValueError('No {}.__template_name__ not defined'.format(type(self))) - if self.__headers__ is None: - raise ValueError('No {}.__headers__ not defined'.format(type(self))) + if self.__template_name__ is MUST_OVERWRITEN: + raise ValueError('must overwrite {}.__template_name__'.format(type(self))) + if self.__headers__ is MUST_OVERWRITEN: + raise ValueError('must overwrite {}.__headers__'.format(type(self))) if not isinstance(self.__headers__, set): raise ValueError('__headers__ should be of type set, get {}'.format(type(self.__headers__))) self.template_vars = {} diff --git a/utensor_cgen/backend/utensor/snippets/_types.py b/utensor_cgen/backend/utensor/snippets/_types.py new file mode 100644 index 00000000..24f875ef --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/_types.py @@ -0,0 +1,97 @@ +# -*- coding:utf8 -*- +from collections import namedtuple + +import numpy as np + +from utensor_cgen.utils import LazyLoader + +tf = LazyLoader('tensorflow') + + +class NumpyTypesMap(object): + _obj = None + _NP_TYPES_MAP = {} + _inited = False + + def __new__(cls): + if cls._obj is None: + self = object.__new__(cls) + cls._obj = self + return cls._obj + + def __getitem__(self, key): + cls = type(self) + cls._init() + return cls._NP_TYPES_MAP[key] + + def __setitem__(self, key, value): + cls = type(self) + cls._init() + cls._NP_TYPES_MAP[key] = value + + def __contains__(self, key): + cls = type(self) + cls._init() + return key in cls._NP_TYPES_MAP + + @classmethod + def _init(cls): + if not cls._inited: + _TYPE_MAP_VALUE = namedtuple("_TYPE_MAP_VALUE", ["importer_type_str", "tensor_type_str"]) + cls._NP_TYPES_MAP = { + np.dtype(tf.float32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="float", + tensor_type_str="float"), + np.dtype(tf.qint8.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="byte", + tensor_type_str="uint8_t"), + np.dtype(tf.int32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", + tensor_type_str="int"), + np.dtype(tf.int64.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", + tensor_type_str="int"), + np.dtype(tf.quint8.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="ubyte", + tensor_type_str="uint8_t"), + np.dtype(tf.qint32.as_numpy_dtype): _TYPE_MAP_VALUE(importer_type_str="int", + tensor_type_str="int"), + np.dtype('uint16'): _TYPE_MAP_VALUE(importer_type_str="ushort", + tensor_type_str="uint16_t"), + np.dtype('int8'): _TYPE_MAP_VALUE(importer_type_str="int8", + tensor_type_str="q7_t"), + } + cls._inited = True + +NP_TYPES_MAP = NumpyTypesMap() + +class Numpy2uTensorTypesMap(object): + _obj = None + _NP_TYPES_MAP = {} + _inited = False + + def __new__(cls): + if cls._obj is None: + cls._obj = object.__new__(cls) + return cls._obj + + def __getitem__(self, key): + cls = type(self) + cls._init() + return cls._NP_TYPES_MAP.get(key, 'undefined') + + def __contains__(self, key): + cls = type(self) + cls._init() + return key in cls._NP_TYPES_MAP + + @classmethod + def _init(cls): + if not cls._inited: + cls._NP_TYPES_MAP = { + np.dtype('int8'): 'i8', + np.dtype('uint8'): 'u8', + np.dtype('int16'): 'i16', + np.dtype('uint16'): 'u16', + np.dtype('int32'): 'i32', + np.dtype('uint32'): 'u32', + np.dtype('float32'): 'flt', + } + cls._inited = True + +UTENSOR_TYPES_MAP = Numpy2uTensorTypesMap() \ No newline at end of file diff --git a/utensor_cgen/backend/snippets/composer.py b/utensor_cgen/backend/utensor/snippets/composer.py similarity index 64% rename from utensor_cgen/backend/snippets/composer.py rename to utensor_cgen/backend/utensor/snippets/composer.py index 80e533cb..3da608ff 100644 --- a/utensor_cgen/backend/snippets/composer.py +++ b/utensor_cgen/backend/utensor/snippets/composer.py @@ -1,7 +1,7 @@ # -*- coding:utf8 -*- import re -from ._snippets import Snippet, SnippetContainerBase +from ._base import Snippet, SnippetContainerBase, SnippetBase _STD_PATTERN = re.compile(r'^<[\w]+(.h|.hpp)?>$') @@ -11,11 +11,9 @@ class Composer(object): def __init__(self, snippets=None): if snippets is None: snippets = [] + self._snippets = [] for snp in snippets: - if not isinstance(snp, (Snippet, SnippetContainerBase)): - msg = "expecting Snippet/SnippetContainerBase objects, get {}".format(type(snp)) - raise TypeError(msg) - self._snippets = snippets + self.add_snippet(snp) self._cached = False self._text = "" @@ -29,7 +27,7 @@ def compose(self): return self._text def add_snippet(self, snippet): - if not isinstance(snippet, (Snippet, SnippetContainerBase)): + if not isinstance(snippet, (SnippetBase, Snippet, SnippetContainerBase)): msg = "expecting Snippet/SnippetContainerBase object, get {}".format(type(snippet)) raise ValueError(msg) self._cached = False @@ -39,8 +37,8 @@ def _compose_header(self): unique_headers = set([]) for snp in self._snippets: unique_headers.update(snp.headers) - headers = [(header, 0) if _STD_PATTERN.match(header) else (header, 1) for header in unique_headers] - headers = [t[0] for t in sorted(headers, key=lambda t: t[1], reverse=True)] + headers = [(0, header) if _STD_PATTERN.match(header) else (1, header) for header in unique_headers] + headers = [t[1] for t in sorted(headers, reverse=True)] for header in headers: self._text += "#include {}\n".format(header) self._text += "\n\n" diff --git a/utensor_cgen/backend/utensor/snippets/legacy/__init__.py b/utensor_cgen/backend/utensor/snippets/legacy/__init__.py new file mode 100644 index 00000000..f2204d0b --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/legacy/__init__.py @@ -0,0 +1 @@ +from ._snippets import * \ No newline at end of file diff --git a/utensor_cgen/backend/snippets/_snippets.py b/utensor_cgen/backend/utensor/snippets/legacy/_snippets.py similarity index 92% rename from utensor_cgen/backend/snippets/_snippets.py rename to utensor_cgen/backend/utensor/snippets/legacy/_snippets.py index dff12c67..f798012a 100644 --- a/utensor_cgen/backend/snippets/_snippets.py +++ b/utensor_cgen/backend/utensor/snippets/legacy/_snippets.py @@ -1,11 +1,10 @@ # -*- coding:utf8 -*- import numpy as np -from ._base import Snippet, SnippetContainerBase # pylint: disable=W0611 -from ._types import NP_TYPES_MAP +from .._base import Snippet, SnippetContainerBase # pylint: disable=W0611 +from .._types import NP_TYPES_MAP -__all__ = ["Snippet", "SnippetContainerBase", - "CreateTensorIdxSnippet", "CreateTensorNewSnippet", +__all__ = ["CreateTensorIdxSnippet", "CreateTensorNewSnippet", "AddOpSnippet", "MinOpSnippet", "MaxOpSnippet", "ArgMaxOpSnippet", "DequantizeOpSnippet", "QuantizedMaxPoolSnippet", "MaxPoolSnippet", @@ -26,7 +25,7 @@ # TODO: Better abstraction, i.e a better backend for code generation class CreateTensorIdxSnippet(Snippet): - __template_name__ = "snippets/create_tensor_idx.cpp" + __template_name__ = "snippets/legacy/create_tensor_idx.cpp" __headers__ = set(['"uTensor/loaders/tensorIdxImporter.hpp"', '"uTensor/core/context.hpp"', '"uTensor/core/tensor.hpp"']) @@ -56,7 +55,7 @@ def __init__(self, data_dir, tensor_name, np_dtype, self.template_vars["to_eval"] = to_eval class CreateTensorRamSnippet(Snippet): - __template_name__ = "snippets/create_tensor_new.cpp" + __template_name__ = "snippets/legacy/create_tensor_new.cpp" __headers__ = set(['"uTensor/core/context.hpp"', '"uTensor/core/tensor.hpp"']) @@ -87,7 +86,7 @@ def _to_shape_str(self, shape): class CreateTensorBinarySnippet(Snippet): - __template_name__ = "snippets/create_tensor_binary.cpp" + __template_name__ = "snippets/legacy/create_tensor_binary.cpp" __headers__ = set(['"uTensor/core/context.hpp"', '"uTensor/core/tensor.hpp"']) @@ -124,7 +123,7 @@ def _to_shape_str(self, shape): class CreateTensorNewSnippet(Snippet): - __template_name__ = "snippets/create_tensor_new.cpp" + __template_name__ = "snippets/legacy/create_tensor_new.cpp" __headers__ = set(['"uTensor/core/context.hpp"', '"uTensor/core/tensor.hpp"']) def __init__(self, tensor_name, np_dtype, @@ -170,7 +169,7 @@ def _permute_args(args, perm=None): class AddOpSnippet(Snippet): - __template_name__ = "snippets/add_op.cpp" + __template_name__ = "snippets/legacy/add_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, output, np_dtype, @@ -187,7 +186,7 @@ def __init__(self, inputs, output, np_dtype, class MinOpSnippet(Snippet): - __template_name__ = "snippets/min_op.cpp" + __template_name__ = "snippets/legacy/min_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, output, out_dtype, @@ -207,7 +206,7 @@ def __init__(self, inputs, output, out_dtype, class MaxOpSnippet(Snippet): - __template_name__ = "snippets/max_op.cpp" + __template_name__ = "snippets/legacy/max_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, output, out_dtype, @@ -226,7 +225,7 @@ def __init__(self, inputs, output, out_dtype, self.template_vars["address"] = address class MaxPoolSnippet(Snippet): - __template_name__ = "snippets/max_pool_op.cpp" + __template_name__ = "snippets/legacy/max_pool_op.cpp" __headers__ = set(['"uTensor/ops/NnOps.hpp"']) def __init__(self, inputs, output, dtype, @@ -250,7 +249,7 @@ def __init__(self, inputs, output, dtype, class QuantizedMaxPoolSnippet(Snippet): - __template_name__ = "snippets/qmax_pool_op.cpp" + __template_name__ = "snippets/legacy/qmax_pool_op.cpp" __headers__ = set(['"uTensor/ops/NnOps.hpp"']) def __init__(self, inputs, outputs, dtype, @@ -279,7 +278,7 @@ def __init__(self, inputs, outputs, dtype, class ArgMaxOpSnippet(Snippet): - __template_name__ = "snippets/argmax_op.cpp" + __template_name__ = "snippets/legacy/argmax_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, output, in_dtype, out_dtype, @@ -298,7 +297,7 @@ def __init__(self, inputs, output, in_dtype, out_dtype, class DequantizeOpSnippet(Snippet): - __template_name__ = "snippets/dequantize_op.cpp" + __template_name__ = "snippets/legacy/dequantize_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, out_dtype, @@ -315,7 +314,7 @@ def __init__(self, inputs, output, out_dtype, self.template_vars["address"] = address class MatMulOpSnippet(Snippet): - __template_name__ = "snippets/matmul_op.cpp" + __template_name__ = "snippets/legacy/matmul_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, output, x_dtype, w_dtype, out_dtype, @@ -333,7 +332,7 @@ def __init__(self, inputs, output, x_dtype, w_dtype, out_dtype, class QuantizedMatMulOpSnippet(Snippet): - __template_name__ = "snippets/qmatmul_op.cpp" + __template_name__ = "snippets/legacy/qmatmul_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, @@ -361,7 +360,7 @@ def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, class QuantizedAddOpSnippet(Snippet): - __template_name__ = "snippets/qadd_op.cpp" + __template_name__ = "snippets/legacy/qadd_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, @@ -388,7 +387,7 @@ def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, self.template_vars['address'] = address class QuantizedMulOpSnippet(Snippet): - __template_name__ = "snippets/qmul_op.cpp" + __template_name__ = "snippets/legacy/qmul_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, @@ -414,7 +413,7 @@ def __init__(self, inputs, outputs, x_dtype, w_dtype, out_dtype, class QuantizeV2OpSnippet(Snippet): - __template_name__ = "snippets/quantV2_op.cpp" + __template_name__ = "snippets/legacy/quantV2_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, outputs, out_dtype, @@ -437,7 +436,7 @@ def __init__(self, inputs, outputs, out_dtype, class ReluOpSnippet(Snippet): - __template_name__ = "snippets/relu_op.cpp" + __template_name__ = "snippets/legacy/relu_op.cpp" __headers__ = set(['"uTensor/ops/NnOps.hpp"']) def __init__(self, inputs, output, in_dtype, out_dtype, @@ -453,7 +452,7 @@ def __init__(self, inputs, output, in_dtype, out_dtype, self.template_vars["to_eval"] = to_eval class QuantizedReluOpSnippet(Snippet): - __template_name__ = "snippets/qrelu_op.cpp" + __template_name__ = "snippets/legacy/qrelu_op.cpp" __headers__ = set(['"uTensor/ops/NnOps.hpp"']) def __init__(self, inputs, outputs, in_dtype, out_dtypes, qout_dtype, @@ -477,7 +476,7 @@ def __init__(self, inputs, outputs, in_dtype, out_dtypes, qout_dtype, self.template_vars["address"] = address class RequantizationRangeOpSnippet(Snippet): - __template_name__ = "snippets/requant_range_op.cpp" + __template_name__ = "snippets/legacy/requant_range_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, outputs, out_dtype, @@ -500,7 +499,7 @@ def __init__(self, inputs, outputs, out_dtype, class RequantizeOpSnippet(Snippet): - __template_name__ = "snippets/requant_op.cpp" + __template_name__ = "snippets/legacy/requant_op.cpp" __headers__ = set(['"uTensor/ops/MathOps.hpp"']) def __init__(self, inputs, outputs, qout_dtype, range_dtype, @@ -528,7 +527,7 @@ def __init__(self, inputs, outputs, qout_dtype, range_dtype, class StridedSliceOpSnippet(Snippet): - __template_name__ = "snippets/strided_slice_op.cpp" + __template_name__ = "snippets/legacy/strided_slice_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, dtype, out_dtype, @@ -550,7 +549,7 @@ def __init__(self, inputs, output, dtype, out_dtype, self.template_vars["to_eval"] = to_eval class PackOpSnippet(Snippet): - __template_name__ = "snippets/pack_op.cpp" + __template_name__ = "snippets/legacy/pack_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, dtype, out_dtype, N, axis, @@ -568,7 +567,7 @@ def __init__(self, inputs, output, dtype, out_dtype, N, axis, self.template_vars["to_eval"] = to_eval class ShapeOpSnippet(Snippet): - __template_name__ = "snippets/shape_op.cpp" + __template_name__ = "snippets/legacy/shape_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, out_dtype, @@ -583,7 +582,7 @@ def __init__(self, inputs, output, out_dtype, self.template_vars["to_eval"] = to_eval class SoftmaxOpSnippet(Snippet): - __template_name__ = "snippets/softmax_op.cpp" + __template_name__ = "snippets/legacy/softmax_op.cpp" __headers__ = set(['"uTensor/ops/NnOps.hpp"']) def __init__(self, input_tname, output_tname, @@ -603,7 +602,7 @@ def __init__(self, input_tname, output_tname, class ReshapeOpSnippet(Snippet): - __template_name__ = "snippets/reshape_op.cpp" + __template_name__ = "snippets/legacy/reshape_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, dtype, @@ -621,7 +620,7 @@ def __init__(self, inputs, output, dtype, class QuantizedReshapeOpSnippet(Snippet): - __template_name__ = "snippets/qreshape_op.cpp" + __template_name__ = "snippets/legacy/qreshape_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, outputs, @@ -635,7 +634,7 @@ def __init__(self, inputs, outputs, self.template_vars["to_eval"] = to_eval class CMSISNNFCOpSnippet(Snippet): - __template_name__ = "snippets/cmsis_nn_fc_op.cpp" + __template_name__ = "snippets/legacy/cmsis_nn_fc_op.cpp" __headers__ = set(['"uTensor/ops/cmsis_ops/FullyConnectedOps.hpp"']) def __init__(self, inputs, output, in_dtypes, out_dtype, @@ -651,7 +650,7 @@ def __init__(self, inputs, output, in_dtypes, out_dtype, self.template_vars["to_eval"] = to_eval class Conv2DOpSnippet(Snippet): - __template_name__ = "snippets/conv2d_op.cpp" + __template_name__ = "snippets/legacy/conv2d_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, output, strides, padding, @@ -671,7 +670,7 @@ def __init__(self, inputs, output, strides, padding, self.template_vars["to_eval"] = to_eval class FusedConv2DMaxpoolOpSnippet(Snippet): - __template_name__ = "snippets/fused_conv2d_maxpool_op.cpp" + __template_name__ = "snippets/legacy/fused_conv2d_maxpool_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, output, strides, ksize, padding, @@ -692,7 +691,7 @@ def __init__(self, inputs, output, strides, ksize, padding, self.template_vars["to_eval"] = to_eval class QuantizedFusedConv2DMaxpoolOpSnippet(Snippet): - __template_name__ = "snippets/quantized_fused_conv2d_maxpool_op.cpp" + __template_name__ = "snippets/legacy/quantized_fused_conv2d_maxpool_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, outputs, strides, ksize, padding, @@ -717,7 +716,7 @@ def __init__(self, inputs, outputs, strides, ksize, padding, self.template_vars["to_eval"] = to_eval class Conv2DQuantOpSnippet(Snippet): - __template_name__ = "snippets/qconv2d_op.cpp" + __template_name__ = "snippets/legacy/qconv2d_op.cpp" __headers__ = set(['"uTensor/ops/MatrixOps.hpp"']) def __init__(self, inputs, outputs, strides, padding, @@ -742,7 +741,7 @@ def __init__(self, inputs, outputs, strides, padding, self.template_vars["to_eval"] = to_eval class Uint8Q7OriginSnippet(Snippet): - __template_name__ = "snippets/cmsis_uint8q7origin_op.cpp" + __template_name__ = "snippets/legacy/cmsis_uint8q7origin_op.cpp" __headers__ = set(['"uTensor/ops/cmsis_ops/supportOps.hpp"']) def __init__(self, inputs, output, @@ -756,7 +755,7 @@ def __init__(self, inputs, output, self.template_vars["to_eval"] = to_eval class QuantRangeForMultiplicationSnippet(Snippet): - __template_name__ = "snippets/quant_range_for_multiplication_op.cpp" + __template_name__ = "snippets/legacy/quant_range_for_multiplication_op.cpp" __headers__ = set(['"uTensor/ops/cmsis_ops/supportOps.hpp"']) def __init__(self, inputs, outputs, out_dtype, @@ -776,7 +775,7 @@ def __init__(self, inputs, outputs, out_dtype, self.template_vars["to_eval"] = to_eval class CommentSnippet(Snippet): - __template_name__ = "snippets/comments.cpp" + __template_name__ = "snippets/legacy/comments.cpp" __headers__ = set([]) def __init__(self, comments): @@ -785,7 +784,7 @@ def __init__(self, comments): class ContextHeaderSnippet(Snippet): - __template_name__ = "snippets/get_ctx.hpp" + __template_name__ = "snippets/legacy/get_ctx.hpp" __headers__ = set(['"uTensor/core/context.hpp"', '"uTensor/core/tensor.hpp"']) def __init__(self, guard_name, graph_name, placeholders=None): @@ -797,7 +796,7 @@ def __init__(self, guard_name, graph_name, placeholders=None): self.template_vars["placeholders"] = placeholders class WeightSnippet(Snippet): - __template_name__ = "snippets/weight_snippet.hpp" + __template_name__ = "snippets/legacy/weight_snippet.hpp" __headers__ = set([]) def __init__(self, inline_name, type, shape, value): @@ -810,7 +809,7 @@ def __init__(self, inline_name, type, shape, value): class ContextGlobalArrayContainer(SnippetContainerBase): - __template_name__ = "containers/weight_header.hpp" + __template_name__ = "containers/legacy/weight_header.hpp" __headers__ = set([]) def __init__(self, snippets=None): @@ -818,7 +817,7 @@ def __init__(self, snippets=None): class ContextSnippetsContainer(SnippetContainerBase): - __template_name__ = "containers/get_ctx.cpp" + __template_name__ = "containers/legacy/get_ctx.cpp" __headers__ = set([]) def __init__(self, @@ -840,7 +839,7 @@ def __init__(self, self.add_header('"{}"'.format(ctx_weightheader_name)) class GatherOpSnippet(Snippet): - __template_name__ = "snippets/gather_op.cpp" + __template_name__ = "snippets/legacy/gather_op.cpp" __headers__ = set(['"uTensor/ops/ArrayOps.hpp"']) def __init__(self, inputs, output, np_dtype, diff --git a/utensor_cgen/backend/utensor/snippets/rearch/__init__.py b/utensor_cgen/backend/utensor/snippets/rearch/__init__.py new file mode 100644 index 00000000..f2204d0b --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/rearch/__init__.py @@ -0,0 +1 @@ +from ._snippets import * \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/snippets/rearch/_snippets.py b/utensor_cgen/backend/utensor/snippets/rearch/_snippets.py new file mode 100644 index 00000000..552469a6 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/rearch/_snippets.py @@ -0,0 +1,117 @@ +from utensor_cgen.backend.utensor.snippets._types import UTENSOR_TYPES_MAP, NP_TYPES_MAP +from utensor_cgen.backend.utensor.snippets._base import Snippet, SnippetBase + +__all__ = ['RomTensorSnippet', 'DeclareOpSnippet', 'AddOpEvalSnippet', 'SimpleContainer'] + +class _SnippetBase(Snippet): + __headers__ = set(['"uTensor/uTensor.hpp"']) + + +class RomTensorSnippet(_SnippetBase): + __template_name__ = 'snippets/rearch/declare_rom_tensor.cpp' + + def __init__(self, tensor_var_name, buffer_var_name, tensor): + Snippet.__init__(self) + shape = tensor.shape or [1] + self.template_vars = { + 'tensor_var_name': tensor_var_name, + 'shape': shape, + 'buffer_var_name': buffer_var_name, + 'utensor_dtype': UTENSOR_TYPES_MAP[tensor.dtype] + } + + +class DeclareOpSnippet(_SnippetBase): + __template_name__ = 'snippets/rearch/declare_op.cpp' + + def __init__(self, op_type, dtypes, op_var_name): + Snippet.__init__(self) + self.template_vars['op_type'] = op_type + self.template_vars['dtypes'] = dtypes + self.template_vars['op_var_name'] = op_var_name + + +class OpEvalSnippet(_SnippetBase): + __template_name__ = 'snippets/rearch/eval_op.cpp' + __inputs__ = [] + __outputs__ = [] + + + def __init__(self, op_info, op_name, tensor_var_map, dtypes): + Snippet.__init__(self) + input_map = { + name: tensor_var_map[tensor.name] + for name, tensor in zip(self.__inputs__, op_info.input_tensors) + } + output_map = { + name: tensor_var_map[tensor.name] + for name, tensor in zip(self.__outputs__, op_info.output_tensors) + } + out_shapes_map = { + tensor_var_map[tensor.name]: tensor.shape or [1] + for tensor in op_info.output_tensors + } + out_dtypes_map = { + tensor_var_map[tensor.name]: UTENSOR_TYPES_MAP[tensor.dtype] + for tensor in op_info.output_tensors + } + utensor_dtypes = [NP_TYPES_MAP[dtype].tensor_type_str for dtype in dtypes] + if utensor_dtypes: + op_type = '{}<{}>'.format(op_info.op_type, ', '.join(utensor_dtypes)) + else: + op_type = op_info.op_type + self.template_vars['op_type'] = op_type + self.template_vars['op_name'] = op_name + self.template_vars['input_map'] = input_map + self.template_vars['output_map'] = output_map + self.template_vars['out_shapes_map'] = out_shapes_map + self.template_vars['out_dtypes_map'] = out_dtypes_map + + +class AddOpEvalSnippet(OpEvalSnippet): + __inputs__ = ['a', 'b'] + __outputs__ = ['c'] + + +class SimpleContainer(SnippetBase): + __headers__ = set(['"uTensor/uTensor.hpp"', ""]) + __template_name__ = 'containers/rearch/simple.cpp' + + def __init__(self, declare_snippets=None, eval_snippests=None): + if declare_snippets is None: + declare_snippets = [] + if eval_snippests is None: + eval_snippests = [] + SnippetBase.__init__(self) + self._declare_snippets = [] + self._eval_snippests = [] + for snp in declare_snippets: + self.add_declare_snippet(snp) + for snp in eval_snippests: + self.add_eval_snippet(snp) + + def add_declare_snippet(self, snippet): + self.__headers__.update(snippet.headers) + self._declare_snippets.append(snippet) + + def add_eval_snippet(self, snippet): + self.__headers__.update(snippet.headers) + self._eval_snippests.append(snippet) + + def add_header(self, header, *headers): + self._add_header(header) + for header in headers: + self._add_header(header) + return self + + def _add_header(self, header): + if not header.startswith('"') and not header.startswith("<"): + header = '"{}"'.format(header) + self.__headers__.add(header) + + def render(self): + return self.template.render( + declare_snippets=self._declare_snippets, + eval_snippets=self._eval_snippests, + **self.template_vars + ) diff --git a/utensor_cgen/backend/snippets/template_env.py b/utensor_cgen/backend/utensor/snippets/template_env.py similarity index 53% rename from utensor_cgen/backend/snippets/template_env.py rename to utensor_cgen/backend/utensor/snippets/template_env.py index f033e30b..62de4790 100644 --- a/utensor_cgen/backend/snippets/template_env.py +++ b/utensor_cgen/backend/utensor/snippets/template_env.py @@ -1,12 +1,14 @@ # -*- coding:utf8 -*- -from jinja2 import Environment, PackageLoader +import os +from jinja2 import Environment, FileSystemLoader -_loader = PackageLoader('utensor_cgen', 'backend/snippets/templates') +_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates/') +_loader = FileSystemLoader(searchpath=_dir) env = Environment(loader=_loader, trim_blocks=True, lstrip_blocks=True) env.globals.update(zip=zip) -del _loader +del _loader, _dir # useful references # - https://gist.github.com/wrunk/1317933/d204be62e6001ea21e99ca0a90594200ade2511e diff --git a/utensor_cgen/backend/snippets/templates/containers/get_ctx.cpp b/utensor_cgen/backend/utensor/snippets/templates/containers/legacy/get_ctx.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/containers/get_ctx.cpp rename to utensor_cgen/backend/utensor/snippets/templates/containers/legacy/get_ctx.cpp diff --git a/utensor_cgen/backend/snippets/templates/containers/weight_header.hpp b/utensor_cgen/backend/utensor/snippets/templates/containers/legacy/weight_header.hpp similarity index 75% rename from utensor_cgen/backend/snippets/templates/containers/weight_header.hpp rename to utensor_cgen/backend/utensor/snippets/templates/containers/legacy/weight_header.hpp index e1d89bcb..a77f1341 100644 --- a/utensor_cgen/backend/snippets/templates/containers/weight_header.hpp +++ b/utensor_cgen/backend/utensor/snippets/templates/containers/legacy/weight_header.hpp @@ -1,3 +1,5 @@ +#include + {% for snippet in snippets%} {{snippet.render()}} {% endfor %} diff --git a/utensor_cgen/backend/utensor/snippets/templates/containers/rearch/simple.cpp b/utensor_cgen/backend/utensor/snippets/templates/containers/rearch/simple.cpp new file mode 100644 index 00000000..fb1c60c3 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/containers/rearch/simple.cpp @@ -0,0 +1,20 @@ +using namespace uTensor; + +{%for snippet in declare_snippets%} +{{snippet.render()}} +{%endfor%} +localCircularArenaAllocator<{{meta_data_pool_size}}> meta_allocator; +localCircularArenaAllocator<{{ram_data_pool_size}}> ram_allocator; + +void compute_{{model_name}}({%for pl in placeholders%}Tensor& {{pl}}, {%endfor%}std::vector& outputs){ + Context::get_default_context()->set_metadata_allocator(&meta_allocator); + Context::get_default_context()->set_ram_data_allocator(&ram_allocator); + + {%for snippet in eval_snippets%} + {{snippet.render()}} + + {%endfor%} + {%for out_var in out_tensor_var_names%} + outputs.push_back({{out_var}}); + {%endfor%} +} diff --git a/utensor_cgen/backend/snippets/templates/snippets/add_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/add_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/add_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/add_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/argmax_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/argmax_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/argmax_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/argmax_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/cmsis_nn_fc_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/cmsis_nn_fc_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/cmsis_nn_fc_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/cmsis_nn_fc_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/cmsis_uint8q7origin_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/cmsis_uint8q7origin_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/cmsis_uint8q7origin_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/cmsis_uint8q7origin_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/comments.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/comments.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/comments.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/comments.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/conv2d_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/conv2d_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/conv2d_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/conv2d_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/create_tensor_binary.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_binary.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/create_tensor_binary.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_binary.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/create_tensor_idx.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_idx.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/create_tensor_idx.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_idx.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/create_tensor_new.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_new.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/create_tensor_new.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/create_tensor_new.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/dequantize_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/dequantize_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/dequantize_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/dequantize_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/fused_conv2d_maxpool_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/fused_conv2d_maxpool_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/fused_conv2d_maxpool_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/fused_conv2d_maxpool_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/gather_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/gather_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/gather_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/gather_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/get_ctx.hpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/get_ctx.hpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/get_ctx.hpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/get_ctx.hpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/matmul_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/matmul_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/matmul_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/matmul_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/max_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/max_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/max_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/max_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/max_pool_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/max_pool_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/max_pool_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/max_pool_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/min_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/min_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/min_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/min_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/pack_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/pack_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/pack_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/pack_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qadd_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qadd_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qadd_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qadd_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qconv2d_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qconv2d_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qconv2d_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qconv2d_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qmatmul_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmatmul_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qmatmul_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmatmul_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qmax_pool_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmax_pool_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qmax_pool_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmax_pool_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qmul_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmul_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qmul_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qmul_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qrelu_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qrelu_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qrelu_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qrelu_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/qreshape_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qreshape_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/qreshape_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/qreshape_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/quantV2_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quantV2_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/quantV2_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quantV2_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/quant_range_for_multiplication_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quant_range_for_multiplication_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/quant_range_for_multiplication_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quant_range_for_multiplication_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/quantized_fused_conv2d_maxpool_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quantized_fused_conv2d_maxpool_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/quantized_fused_conv2d_maxpool_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/quantized_fused_conv2d_maxpool_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/relu_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/relu_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/relu_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/relu_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/requant_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/requant_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/requant_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/requant_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/requant_range_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/requant_range_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/requant_range_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/requant_range_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/reshape_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/reshape_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/reshape_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/reshape_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/shape_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/shape_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/shape_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/shape_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/softmax_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/softmax_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/softmax_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/softmax_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/strided_slice_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/strided_slice_op.cpp similarity index 100% rename from utensor_cgen/backend/snippets/templates/snippets/strided_slice_op.cpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/strided_slice_op.cpp diff --git a/utensor_cgen/backend/snippets/templates/snippets/weight_snippet.hpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/weight_snippet.hpp similarity index 83% rename from utensor_cgen/backend/snippets/templates/snippets/weight_snippet.hpp rename to utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/weight_snippet.hpp index 2ccf15fc..ca098aa2 100644 --- a/utensor_cgen/backend/snippets/templates/snippets/weight_snippet.hpp +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/legacy/weight_snippet.hpp @@ -1,3 +1 @@ -#include - const {{ type }} {{ inline_name }} [ {{ length }} ] = { {% for item in value %} {{ item }}, {% endfor %} }; diff --git a/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_op.cpp new file mode 100644 index 00000000..4fc8112d --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_op.cpp @@ -0,0 +1,5 @@ +{%if dtypes %} +{{op_type}}<{%for t in dtypes[:-1]%}{{t}},{%endfor%}{{dtypes[-1]}}> {{op_var_name}}; +{%else%} +{{op_type}} {{op_var_name}}; +{%endif%} \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_rom_tensor.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_rom_tensor.cpp new file mode 100644 index 00000000..7aea2018 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/declare_rom_tensor.cpp @@ -0,0 +1 @@ +Tensor {{tensor_var_name}} = new RomTensor({ {%for s in shape[:-1]%}{{s}}, {%endfor%}{{shape[-1]}} }, {{utensor_dtype}}, {{buffer_var_name}}); \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/eval_op.cpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/eval_op.cpp new file mode 100644 index 00000000..6915a007 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/eval_op.cpp @@ -0,0 +1,15 @@ +{%for tensor_var, shape in out_shapes_map.items()%} +Tensor {{tensor_var}} = new RamTensor({ {%for s in shape[:-1]%}{{s}}, {%endfor%}{{shape[-1]}} }, {{out_dtypes_map[tensor_var]}}); +{%endfor%} + {{op_name}} + .set_inputs({ +{%for name, tensor_var in input_map.items()%} + { {{op_type}}::{{name}}, {{tensor_var}} }, +{%endfor%} + }) + .set_outputs({ +{%for name, tensor_var in output_map.items()%} + { {{op_type}}::{{name}}, {{tensor_var}}} +{%endfor%} + }) + .eval(); diff --git a/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/simple.hpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/simple.hpp new file mode 100644 index 00000000..90e379a8 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/simple.hpp @@ -0,0 +1,9 @@ +#ifndef __{{model_name.upper()}}_H +#define __{{model_name.upper()}}_H + +#include +#include "uTensor/core/tensor.hpp" + +void compute_{{model_name}}({%for pl in placeholders%}utensor::Tensor& {{pl}}, {%endfor%}std::vector& outputs); + +#endif // __{{model_name.upper()}}_H \ No newline at end of file diff --git a/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/weight.hpp b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/weight.hpp new file mode 100644 index 00000000..bf9dd673 --- /dev/null +++ b/utensor_cgen/backend/utensor/snippets/templates/snippets/rearch/weight.hpp @@ -0,0 +1 @@ +const {{ dtype }} {{ var_name }} [{{ size }}] = { {%for elem in data%} {{elem}}, {%endfor%} }; \ No newline at end of file diff --git a/utensor_cgen/cli.py b/utensor_cgen/cli.py index dece5d1a..74cbeead 100644 --- a/utensor_cgen/cli.py +++ b/utensor_cgen/cli.py @@ -1,116 +1,106 @@ #-*- coding:utf8 -*- +import importlib import os +import re import sys from pathlib import Path import click -import pkg_resources +from toml import dumps, loads -from utensor_cgen.backend.operators import OperatorFactory -from utensor_cgen.utils import NArgsKwargsParam, NArgsParam +from utensor_cgen import __version__ +from utensor_cgen.backend.api import BackendManager +from utensor_cgen.utils import NArgsParam -_version = ( - pkg_resources - .get_distribution('utensor_cgen') - .version -) def _get_pb_model_name(path): return os.path.basename(os.path.splitext(path)[0]) -def _load_transformer(path): - # FIXME: better way to activate user plugins other than exec - from utensor_cgen.transformer import TransformerPipeline, Transformer - - _globals = {} - transform_plugin = Path(path).absolute() - with transform_plugin.open('r') as fid: - exec(fid.read(), _globals) - for obj in _globals.values(): - if obj is Transformer: - continue - if isinstance(obj, type) and issubclass(obj, Transformer): - TransformerPipeline.register_transformer(obj) - +def _load_plugin(path): + path = Path(path) + if not path.exists(): + raise RuntimeError('{} does not exists'.format(path)) + sys.path.insert(0, str(path.parent)) + mod_name = re.sub(r'.py$', '', path.name.split()[0]) + importlib.import_module(mod_name) + sys.path.pop(0) @click.group(name='utensor-cli') @click.help_option('-h', '--help') -@click.version_option(_version, - '-V', '--version') -@click.option("--transform-plugin", - default=None, - help="path of the python file which user-defined transformers live", - metavar="MODULE.py", +@click.version_option( + __version__, + '-V', '--version' ) -def cli(transform_plugin): - if transform_plugin is not None: - _load_transformer(transform_plugin) +@click.option( + "-p", + "--plugin", + multiple=True, + help="path of the python module which will be loaded as plugin", + metavar="MODULE", +) +def cli(plugin): + for module in plugin: + _load_plugin(module) + +@cli.command(name='list-backends', help='list all available backends') +@click.help_option('-h', '--help') +def list_backends(): + click.secho('Available backends:', fg='green', bold=True) + for backend in BackendManager.backends: + click.secho( + ' - {}'.format(backend), fg='green' + ) + +@cli.command(name='generate-config', help='generate config toml file') +@click.help_option('-h', '--help') +@click.option('--target', required=True, help='target framework/platform') +@click.option('-o', '--output', default='utensor_cli.toml', metavar='CONFIG.toml', help='the output config file name') +def generate_config(target, output): + backend_cls = BackendManager.get_backend(target) + config = backend_cls.default_config + click.secho( + 'generating config file: {}'.format(output), + fg='white', + bold=True, + ) + with open(output, 'w') as fid: + fid.write( + '# https://github.com/toml-lang/toml\n' + '# ..\n' + ) + fid.write(dumps(config)) @cli.command(name='convert', help='convert graph to cpp/hpp files') @click.help_option('-h', '--help') -@click.argument('pb_file', required=True, metavar='MODEL.pb') -@click.option('-o', '--output', - metavar="FILE.cpp", - help="output source file name, header file will be named accordingly. (defaults to protobuf name, e.g.: my_model.cpp)") -@click.option('-d', '--data-dir', - metavar='DIR', - help="output directory for tensor data idx files", - show_default=True) -@click.option('-D', '--embed-data-dir', - metavar='EMBED_DIR', - help=("the data dir on the develop board " - "(default: the value as the value of -d/data-dir flag)")) -@click.option('--debug-comment', - is_flag=True, - help="Add debug comments in the output source file", - show_default=True) -@click.option("--output-nodes", - type=NArgsParam(), - metavar="NODE_NAME,NODE_NAME,...", - required=True, - help="list of output nodes") -@click.option("--transform-methods", - type=NArgsKwargsParam(sep='|>'), - default=( - 'dropout(name_pattern=r"(dropout[_\w\d]*)/.*")|>linear_reorder' - '|>quantize|>conv_pool|>inline|>biasAdd|>remove_id_op' - '|>fake_gather_v2|>refcnt' - ), - help='optimization pipeline', - metavar='METHOD[|>METHOD|>...]', - show_default=True) -@click.option("-m", "--model-dir", - metavar="DIR", - default="models", - help="output directory for tensor data idx files", - show_default=True) -@click.option("--save-graph", - is_flag=True, - help="save transformed graph") -def convert_graph(pb_file, output, data_dir, embed_data_dir, save_graph, - debug_comment, output_nodes, transform_methods, model_dir): - from utensor_cgen.backend import CodeGenerator - - if pb_file is None: - raise ValueError("No pb file given") - - if not os.path.exists(model_dir): - os.makedirs(model_dir) - # MODEL should default to pb_file - if data_dir is None: - data_dir = os.path.join("constants", _get_pb_model_name(pb_file)) - - if output is None: - output = "{}.cpp".format(_get_pb_model_name(pb_file)) - model_path = os.path.join(model_dir, output) - - if embed_data_dir is None: - embed_data_dir = os.path.join("/fs", data_dir) - # TODO: pass transformation kwargs to codegenerator (better argument parser) - generator = CodeGenerator(pb_file, data_dir, embed_data_dir, - transform_methods, output_nodes, - save_graph, debug_comment) - generator.generate(model_path) +@click.argument( + 'model_file', + required=True, + metavar='MODEL_FILE.{pb,onnx,pkl}', +) +@click.option( + '--output-nodes', + type=NArgsParam(), + metavar="NODE_NAME,NODE_NAME,...", + required=True, + help="list of output nodes" +) +@click.option('--config', default='utensor_cli.toml', show_default=True, metavar='CONFIG.toml') +@click.option('--target', + default='utensor', + show_default=True, + help='target framework/platform' +) +def convert_graph(model_file, output_nodes, config, target): + from utensor_cgen.frontend import FrontendSelector + + if os.path.exists(config): + with open(config) as fid: + config = loads(fid.read()) + else: + config = {} + ugraph = FrontendSelector.parse(model_file, output_nodes, config) + backend = BackendManager.get_backend(target)(config) + backend.apply(ugraph) @cli.command(name='list-trans-methods', help='list all available graph transformation') @click.help_option('-h', '--help') @@ -141,27 +131,31 @@ def list_trans_methods(verbose): help="list of output nodes") @click.argument('model_file', required=True, metavar='MODEL.{pb,pkl}') def show_graph(model_file, **kwargs): + import pickle + from utensor_cgen.frontend import FrontendSelector _, ext = os.path.splitext(model_file) output_nodes = kwargs.pop('output_nodes') - if ext == '.pb' or ext == '.pbtxt': - _show_pb_file(model_file, output_nodes=output_nodes, **kwargs) - elif ext == '.pkl': - import pickle + + if ext == '.pkl': with open(model_file, 'rb') as fid: ugraph = pickle.load(fid) _show_ugraph(ugraph, **kwargs) - else: - msg = click.style('unknown file extension: {}'.format(ext), fg='red', bold=True) - click.echo(msg, file=sys.stderr) + return 0 -def _show_pb_file(pb_file, output_nodes, **kwargs): - import tensorflow as tf - from utensor_cgen.frontend.tensorflow import GraphDefParser - ugraph = GraphDefParser.parse(pb_file, output_nodes=output_nodes) - _show_ugraph(ugraph, **kwargs) + try: + parser = FrontendSelector.select_parser(ext) + ugraph = parser.parse(model_file, output_nodes) + _show_ugraph(ugraph, **kwargs) + return 0 + except RuntimeError as err: + msg = err.args[0] + click.secho(msg, fg='red', bold=True) + return 1 def _show_ugraph(ugraph, oneline=False, ignore_unknown_op=False): import textwrap + from utensor_cgen.backend.utensor.code_generator.legacy._operators import OperatorFactory + unknown_ops = set([]) if oneline: tmpl = click.style("{op_name} ", fg='yellow', bold=True) + \ @@ -200,6 +194,10 @@ def _show_ugraph(ugraph, oneline=False, ignore_unknown_op=False): if not OperatorFactory.is_supported(op_info.op_type): unknown_ops.add(op_info) click.echo('\n'.join(paragraphs)) + click.secho( + 'topological ordered ops: {}'.format(ugraph.topo_order), + fg='white', bold=True, + ) if unknown_ops and not ignore_unknown_op: click.echo( click.style('Unknown Ops Detected', fg='red', bold=True) diff --git a/utensor_cgen/frontend/__init__.py b/utensor_cgen/frontend/__init__.py index ba0cc7e8..f122ea18 100644 --- a/utensor_cgen/frontend/__init__.py +++ b/utensor_cgen/frontend/__init__.py @@ -21,12 +21,20 @@ def _register(parser_cls): return _register + @classmethod + def parse(cls, model_file, output_nodes, config=None): + if config is None: + config = {} + _, ext = os.path.splitext(model_file) + parser = cls.select_parser(ext)(config) + return parser.parse(model_file, output_nodes) + @classmethod def select_parser(cls, file_ext): cls._setup() parser_cls = cls._parser_map.get(file_ext, None) if parser_cls is None: - raise RuntimeError("unknown model file ext found: %s" % file_ext) + raise RuntimeError("unknown model file extension: %s" % file_ext) return parser_cls @classmethod diff --git a/utensor_cgen/frontend/base.py b/utensor_cgen/frontend/base.py index 6f6ba1ae..86131f65 100644 --- a/utensor_cgen/frontend/base.py +++ b/utensor_cgen/frontend/base.py @@ -1,8 +1,16 @@ from abc import ABCMeta, abstractmethod + class Parser(object): __metaclass__ = ABCMeta + def __new__(cls, config): + if not isinstance(config, dict): + raise ValueError('expecting dict as config, get {}'.format(type(config))) + self = object.__new__(cls) + self._config = config + return self + @classmethod @abstractmethod def parse(cls, fname, outupt_nodes): diff --git a/utensor_cgen/frontend/tensorflow.py b/utensor_cgen/frontend/tensorflow.py index 899838c1..263faa92 100644 --- a/utensor_cgen/frontend/tensorflow.py +++ b/utensor_cgen/frontend/tensorflow.py @@ -1,5 +1,7 @@ from __future__ import absolute_import +import os + import numpy as np import six @@ -8,7 +10,8 @@ from utensor_cgen.frontend import FrontendSelector from utensor_cgen.frontend.base import Parser from utensor_cgen.ir.base import OperationInfo, TensorInfo, uTensorGraph -from utensor_cgen.utils import topologic_order_graph +from utensor_cgen.legalizer import Legalizer +from utensor_cgen.utils import random_str, topologic_order_graph @FrontendSelector.register(target_exts=['.pb', '.pbtxt']) @@ -16,7 +19,7 @@ class GraphDefParser(Parser): @classmethod def parse(cls, pb_file, output_nodes=None): - graph_def = cls._load_graph_def(pb_file) + graph_def, graph_name = cls._load_graph_def(pb_file) if not cls._tf_is_freeze_graph(graph_def): raise ValueError('Given graph_def is not freezed') if output_nodes is None: @@ -25,9 +28,11 @@ def parse(cls, pb_file, output_nodes=None): graph = tf.Graph() with graph.as_default(): tf.import_graph_def(graph_def, name='') - - ugraph = uTensorGraph(output_nodes=output_nodes, - backend="tensorflow") + ugraph = uTensorGraph( + name=graph_name, + output_nodes=output_nodes, + lib_name="tensorflow", + ) for node in graph_def.node: op = graph.get_operation_by_name(node.name) in_tensors = [TensorInfo(name=tensor.name, @@ -52,19 +57,21 @@ def parse(cls, pb_file, output_nodes=None): output_tensors=out_tensors, n_outputs=len(out_tensors), op_type=op_type, - backend='tensorflow', + lib_name='tensorflow', op_attr=op_attr, ugraph=ugraph) op_info.op_attr['tensorflow__device'] = node.device ugraph.ops_info[node.name] = op_info topologic_order_graph(ugraph) + ugraph = Legalizer.legalize(ugraph, {}) return ugraph @staticmethod def _load_graph_def(pb_file): if isinstance(pb_file, tf.GraphDef): - return pb_file + return pb_file, 'tf_graph_{}'.format(random_str(6)) assert isinstance(pb_file, six.string_types) + graph_name, _ = os.path.splitext(os.path.basename(pb_file)) graph_def = tf.GraphDef() if pb_file[-3:] == ".pb": with open(pb_file, 'rb') as fid: @@ -74,8 +81,7 @@ def _load_graph_def(pb_file): text_format.Parse(fid.read(), graph_def) else: raise ValueError('unknown file format: %s' % pb_file) - return graph_def - + return graph_def, graph_name @staticmethod def _tf_parse_tshape(t_shape): diff --git a/utensor_cgen/ir/base.py b/utensor_cgen/ir/base.py index 538a7d4e..d1137786 100644 --- a/utensor_cgen/ir/base.py +++ b/utensor_cgen/ir/base.py @@ -4,10 +4,10 @@ from functools import reduce import attr -import numpy as np import six from attr.validators import instance_of +import numpy as np import tensorflow as tf from tensorflow.core.framework.attr_value_pb2 import AttrValue as _AttrValue from tensorflow.core.framework.attr_value_pb2 import \ @@ -38,7 +38,7 @@ def __copy__(self): class IRBase(object): @property - def all_supported_backends(self): + def all_supported_libs(self): return ['tensorflow'] @@ -69,20 +69,21 @@ class TensorInfo(IRBase, _NoShallowCopyMixin): dtype = attr.ib(validator=instance_of(np.dtype)) shape = attr.ib(validator=instance_of((list, type(None)))) - @shape.validator def check(self, attrib, shape_values): if shape_values is not None: for v in shape_values: assert isinstance(v, (int, type(None))), \ "shape should be a list of integers" - + _ugraph = attr.ib(repr=False) @_ugraph.validator def check(self, attrib, value): if not isinstance(value, uTensorGraph): raise ValueError('Expecting a uTensorGraph, get {}'.format(type(value))) + attributes = attr.ib(factory=dict, validator=instance_of(dict)) + _NULL_PREFIX = 'utensor_null' def move_into(self, ugraph): @@ -153,14 +154,14 @@ def op(self): return op @property - def backend(self): + def lib_name(self): """ - the name of backend library/framework used for training + the name of training library/framework the graph :rtype: six.string_types """ - return self._ugraph.backend + return self._ugraph._lib_name @property def is_null_tensor(self): @@ -189,7 +190,13 @@ def __hash__(self): def __eq__(self, other): if not isinstance(other, type(self)): return False - return (self.name == other.name) and (self._ugraph is other._ugraph) + return ( + (self.name == other.name) and + (self._ugraph is other._ugraph) and + (self.op_name == other.op_name) and + (self.dtype == other.dtype) and + (self.shape == other.shape) + ) @attr.s(cmp=False, repr=False) @@ -207,9 +214,8 @@ class OperationInfo(IRBase, _NoShallowCopyMixin): :param op_type: the type of the node (ex: ``Add``) :type op_type: str - :param backend: the name of the backend, the library/framework for the training phase - {'tensorflow', 'pytorch'} - :type backend: str + :param lib_name: the name of the training library/framework, {'tensorflow', 'pytorch'} + :type lib_name: str :param ugraph: the graph which owns this op :type ugraph: :py:class:`.uTensorGraph` @@ -226,7 +232,7 @@ class OperationInfo(IRBase, _NoShallowCopyMixin): - The values of such keys will be saved **as-is** without any type conversion. """ name = attr.ib(type=str) - _backend = attr.ib(type=str) + _lib_name = attr.ib(type=str) _ugraph = attr.ib(repr=False) @_ugraph.validator def check(self, attrib, value): @@ -295,13 +301,13 @@ def ugraph(self): return self._ugraph @property - def backend(self): + def lib_name(self): """ - The name of backend library/framework + The name of training library/framework :rtype: six.strings_type """ - return self._backend + return self._lib_name @property def input_nodes(self): @@ -390,7 +396,7 @@ def __deepcopy__(self, memo): output_tensors=deepcopy(self.output_tensors, memo), n_outputs=self.n_outputs, op_type=self.op_type, - backend=self.backend, + lib_name=self.lib_name, op_attr=deepcopy(self.op_attr, memo), ugraph=memo['ugraph']) return op_info @@ -424,10 +430,9 @@ class uTensorGraph(IRBase, _NoShallowCopyMixin, uTensorGraphBuilderMixin): :param ops_info: a dict with key as string, the op's name, and value as an instance of :class:`.OperationInfo` :type ops_info: dict - :param backend: the name of backend library/framework - the graph trained with. Can only be ``'tensorflow'`` - or ``'pytorch'`` (future work) - :type backend: str + :param lib_name: the name of library/framework training the graph. + Can only be ``'tensorflow'`` or ``'pytorch'`` (future work) + :type lib_name: str .. @@ -441,7 +446,7 @@ class uTensorGraph(IRBase, _NoShallowCopyMixin, uTensorGraphBuilderMixin): 1. create a empty graph - give a list of names of output nodes (required) - - (optional) give backend string + - (optional) give `lib_name` string - leave **ops_info** empty 2. setup the **ops_info** @@ -452,8 +457,9 @@ class uTensorGraph(IRBase, _NoShallowCopyMixin, uTensorGraphBuilderMixin): """ KWPARSER_PATTERN = re.compile(r'^([^\d\W][\w\d_]*)__([^\d\W][\w\d_]*)') + name = attr.ib(default='model_graph') output_nodes = attr.ib(factory=list) - _backend = attr.ib(default='tensorflow', type=six.string_types) + _lib_name = attr.ib(default='tensorflow', type=six.string_types) ops_info = attr.ib(factory=dict) # non-init topo_order = attr.ib(factory=list, init=False) @@ -468,6 +474,7 @@ def __attrs_post_init__(self): raise ValueError( 'output_nodes should be list of str: {}'.format(self.output_nodes) ) + self.name = self.name.replace('/', '_') def get_ops_by_type(self, given_op_type): """ @@ -556,13 +563,13 @@ def input_tensors(self): return in_tensors @property - def backend(self): + def lib_name(self): """ - the name of backend library/framework + the name of training library/framework :rtype: six.strings_type """ - return self._backend + return self._lib_name @property def graph_def(self): @@ -573,8 +580,8 @@ def graph_def(self): """ if self.output_nodes and not self.topo_order: raise RuntimeError('the graph is not topological sorted') - assert self._backend == 'tensorflow', \ - 'Convert a uTensorGraph to tf.GraphDef from a non-tf backend' + assert self._lib_name == 'tensorflow', \ + 'Can not convert a uTensorGraph to tf.GraphDef from a non-tf graph' graph_def = tf.GraphDef() for node_name in self.topo_order: op_info = self.ops_info[node_name] @@ -652,8 +659,9 @@ def unsafe_merge_into(self, other_ugraph): def __deepcopy__(self, memo): new_graph = uTensorGraph( + name=self.name, output_nodes=self.output_nodes, - backend=self._backend + lib_name=self._lib_name ) memo['ugraph'] = new_graph new_graph.ops_info = { @@ -663,7 +671,7 @@ def __deepcopy__(self, memo): if self.data_manager: new_graph.data_manager = DataManager({}) new_graph.data_manager.StorageCenter = deepcopy(self.data_manager.StorageCenter) - new_graph._backend = self._backend + new_graph._lib_name = self._lib_name topologic_order_graph(new_graph) return new_graph @@ -686,8 +694,8 @@ def __attrs_post_init__(self): self.ops_info[name] = self._ugraph.ops_info[name] @property - def backend(self): - return self._ugraph.backend + def lib_name(self): + return self._ugraph._lib_name @property def input_ops(self): diff --git a/utensor_cgen/ir/graph_builder.py b/utensor_cgen/ir/graph_builder.py index 83dc04ff..17c5d3b4 100644 --- a/utensor_cgen/ir/graph_builder.py +++ b/utensor_cgen/ir/graph_builder.py @@ -3,7 +3,7 @@ from utensor_cgen.utils import LazyLoader, topologic_order_graph -operators = LazyLoader('backend.operators') +operators = LazyLoader(submod_name='backend.utensor.code_generator.legacy._operators') class GraphFinalizedError(Exception): pass diff --git a/utensor_cgen/legalizer/__init__.py b/utensor_cgen/legalizer/__init__.py new file mode 100644 index 00000000..15e82b78 --- /dev/null +++ b/utensor_cgen/legalizer/__init__.py @@ -0,0 +1,34 @@ +from .base import LegalizerBase +from .tensorflow import GraphDefLegalizer + + +class Legalizer(object): + LEGALIZER_MAP = {} + + @classmethod + def register(cls, legalizer=None): + def _register(legalizer_): + if not issubclass(legalizer_, LegalizerBase): + raise TypeError( + 'expecting subclass of {}, get {}'.format(LegalizerBase, legalizer_) + ) + cls.LEGALIZER_MAP[legalizer_.TARGET] = legalizer_ + return legalizer_ + if legalizer is None: + return _register + else: + return _register(legalizer) + + @classmethod + def legalize(cls, ugraph, config=None): + if config is None: + config = {} + legalizer_cls = cls.LEGALIZER_MAP.get(ugraph.lib_name) + if legalizer_cls is None: + raise ValueError( + 'graph of unsupported lib given: {}'.format(ugraph.lib_name) + ) + legalizer = legalizer_cls(config) + return legalizer.legalize(ugraph) + +Legalizer.register(GraphDefLegalizer) diff --git a/utensor_cgen/legalizer/base.py b/utensor_cgen/legalizer/base.py new file mode 100644 index 00000000..e401eb8b --- /dev/null +++ b/utensor_cgen/legalizer/base.py @@ -0,0 +1,29 @@ +from utensor_cgen.utils import MUST_OVERWRITEN + + +class LegalizerBase(object): + + TARGET = MUST_OVERWRITEN + COMPONET = 'legalizer' + + def __new__(cls, config): + if not isinstance(config, dict): + raise TypeError( + 'expecting dict as config, get {}'.format(type(config)) + ) + if cls.TARGET is MUST_OVERWRITEN: + raise ValueError('cls.TARGET must be overwriten') + self = object.__new__(cls) + self._config = config + return self + + def legalize(self, ugraph): + ugraph = self.legalize_ops(ugraph) + ugraph = self.legalize_dtype(ugraph) + return ugraph + + def legalize_ops(self, ugraph): + raise NotImplementedError('abstract ops legalizer get called') + + def legalize_dtype(self, ugraph): + raise NotImplementedError('abstract dtype legalizer get called') diff --git a/utensor_cgen/legalizer/tensorflow.py b/utensor_cgen/legalizer/tensorflow.py new file mode 100644 index 00000000..e2850c68 --- /dev/null +++ b/utensor_cgen/legalizer/tensorflow.py @@ -0,0 +1,23 @@ +from .base import LegalizerBase + + +class GraphDefLegalizer(LegalizerBase): + TARGET = 'tensorflow' + + def legalize_ops(self, ugraph): + '''Legalize ops to generic ops in given graph + ''' + if not ugraph.lib_name == self.TARGET: + raise ValueError( + 'expecting tensorflow graph, get {}'.format(ugraph.lib_name) + ) + return ugraph + + def legalize_dtype(self, ugraph): + '''Legalize data types of tensors in given graph + ''' + if not ugraph.lib_name == self.TARGET: + raise ValueError( + 'expecting tensorflow graph, get {}'.format(ugraph.lib_name) + ) + return ugraph diff --git a/utensor_cgen/matcher/_matcher_impl.py b/utensor_cgen/matcher/_matcher_impl.py index a2bacb2a..4eca39e8 100644 --- a/utensor_cgen/matcher/_matcher_impl.py +++ b/utensor_cgen/matcher/_matcher_impl.py @@ -73,9 +73,7 @@ def query(cls, sub_op, patrn_op): equivalent_ops : List[OperationInfo] a list of equivalent ops derieved from `sub_op` """ - # to activate all configurations - import utensor_cgen.backend.operators as _ - + cls._setup() is_eq = False equivalent_ops = [] if sub_op is None or patrn_op is None: @@ -91,7 +89,7 @@ def query(cls, sub_op, patrn_op): equivalent_ops.append( OperationInfo( name=sub_op.name, - backend=sub_op.backend, + lib_name=sub_op.lib_name, ugraph=sub_op.ugraph, input_tensors=[sub_op.input_tensors[j] for j in perm], n_inputs=sub_op.n_inputs, @@ -107,7 +105,11 @@ def query(cls, sub_op, patrn_op): equivalent_ops = [MetaOperationInfo(op_info=sub_op, morphism=morphism)] return is_eq, equivalent_ops - + + @classmethod + def _setup(cls): + # to activate all configurations + import utensor_cgen.backend.utensor.code_generator.legacy._operators @attr.s class uTensorGraphMatcher(object): diff --git a/utensor_cgen/transformer/conv_pool.py b/utensor_cgen/transformer/conv_pool.py index c4950a1d..21cae4e0 100644 --- a/utensor_cgen/transformer/conv_pool.py +++ b/utensor_cgen/transformer/conv_pool.py @@ -58,8 +58,9 @@ def transform(self, ugraph): def __call__(self, match): op_name = 'quant_conv_pool' repl_ugraph = uTensorGraph( + name='{}_repl_graph'.format(op_name), output_nodes=[op_name], - backend=match.subject_ugraph.backend + lib_name=match.subject_ugraph.lib_name ) subj_conv_op = match.patrn2subj_op_map['conv/eightbit'] subj_pool_op = match.patrn2subj_op_map['maxpool/eightbit'] @@ -84,7 +85,7 @@ def __call__(self, match): output_tensors=output_tensors, n_outputs=len(output_tensors), op_type='QuantizedFusedConv2DMaxpool', - backend=subj_conv_op.backend, + lib_name=subj_conv_op.lib_name, op_attr={ '_utensor_conv': subj_conv_op.op_attr, '_utensor_pool': subj_pool_op.op_attr, diff --git a/utensor_cgen/transformer/ns_transformer.py b/utensor_cgen/transformer/ns_transformer.py index e6c66869..f4fdee2d 100644 --- a/utensor_cgen/transformer/ns_transformer.py +++ b/utensor_cgen/transformer/ns_transformer.py @@ -269,7 +269,7 @@ def __init__(self, name_pattern=r'(dropout[_\w\d]*)/.*'): self._op_name_pattern = re.compile(name_pattern) def transform(self, ugraph): - new_graph = uTensorGraph(output_nodes=ugraph.output_nodes) + new_graph = uTensorGraph(name=ugraph.name, output_nodes=ugraph.output_nodes) dropout_input_map = self._find_input(ugraph) new_ops_info = {} for node_name in ugraph.ops_info: @@ -299,12 +299,12 @@ def transform(self, ugraph): output_tensors=out_t_infos, n_outputs=len(out_t_infos), op_type=op_info.op_type, - backend=op_info.backend, + lib_name=op_info.lib_name, op_attr=op_attr, ugraph=new_graph) new_ops_info[node_name] = new_op_info new_graph.ops_info = new_ops_info - new_graph._backend = ugraph._backend + new_graph._lib_name = ugraph._lib_name return new_graph def _find_dropout_clusters(self, ugraph): @@ -380,11 +380,11 @@ def pattern_ugraph(self): def transform(self, ugraph): new_ugraph = deepcopy(ugraph) - if new_ugraph.backend == 'tensorflow': + if new_ugraph.lib_name == 'tensorflow': new_ugraph = self._transform_tf(new_ugraph) else: raise ValueError( - 'only support dropout transformer for tensorflow: get {}'.format(new_ugraph.backend) + 'dropout transformer only support tensorflow graph, get {}'.format(new_ugraph.lib_name) ) return new_ugraph diff --git a/utensor_cgen/transformer/optimizer.py b/utensor_cgen/transformer/optimizer.py index 89d96c59..e83d081c 100644 --- a/utensor_cgen/transformer/optimizer.py +++ b/utensor_cgen/transformer/optimizer.py @@ -54,10 +54,10 @@ def __init__(self, **kwargs): self.prune_graph = True def transform(self, ugraph): - if ugraph.backend == 'tensorflow': + if ugraph.lib_name == 'tensorflow': return self._transform_tf(ugraph) else: - raise RuntimeError('unsupported backend: {}'.format(ugraph.backend)) + raise RuntimeError('unsupported lib_name: {}'.format(ugraph.lib_name)) def _transform_tf(self, ugraph): ops_to_remove = [ diff --git a/utensor_cgen/transformer/pipeline.py b/utensor_cgen/transformer/pipeline.py index da4fe2fb..500f58a0 100644 --- a/utensor_cgen/transformer/pipeline.py +++ b/utensor_cgen/transformer/pipeline.py @@ -1,3 +1,6 @@ +import re +from ast import literal_eval + from .base import Transformer @@ -5,16 +8,25 @@ class TransformerPipeline(object): TRANSFORMER_MAP = {} + _trans_name_patrn = re.compile(r"(\w[\w]*)\(?") + def __init__(self, methods): """ methods : list - list of tuples, (transform_name, kwargs) + list of tuples of type Tuple[Type[Transformer], dict] or a string expression + of the transformer such as 'dropout(name_pattern=r"(dropout[_\w\d]*)/.*")' """ self._pipeline = [] - for method, kwargs in methods: - trans_cls = self.TRANSFORMER_MAP.get(method, None) - if trans_cls is None: - raise ValueError("Unknown transformation method: {}".format(method)) + for method_or_str in methods: + if isinstance(method_or_str, str): + method, kwargs = self._parse_expr(method_or_str) + trans_cls = self.TRANSFORMER_MAP.get(method, None) + if trans_cls is None: + raise ValueError("Unknown transformation method: {}".format(method)) + else: + trans_cls, kwargs = method_or_str + if not issubclass(trans_cls, Transformer): + raise TypeError("expecting subclass of {}, get {}".format(Transformer, trans_cls)) transformer = trans_cls(**kwargs) self._pipeline.append(transformer) @@ -44,3 +56,28 @@ def register(trans_cls): if trans_cls is None: return register return register(trans_cls) + + @classmethod + def _parse_expr(cls, expr): + trans_match = cls._trans_name_patrn.match(expr) + if not trans_match: + raise ValueError("Invalid args detected: {}".format(expr)) + trans_name = trans_match.group(1) + _, end = trans_match.span() + if end == len(expr): + kwargs = {} + else: + if not expr.endswith(")"): + raise ValueError("parentheses mismatch: {}".format(expr)) + kwargs = cls._get_kwargs(expr[end:-1]) + return trans_name, kwargs + + @classmethod + def _get_kwargs(cls, kws_str): + kw_arg_strs = [s.strip() for s in kws_str.split(',')] + kwargs = {} + for kw_str in kw_arg_strs: + name, v_str = kw_str.split('=') + value = literal_eval(v_str) + kwargs[name] = value + return kwargs diff --git a/utensor_cgen/transformer/quantize.py b/utensor_cgen/transformer/quantize.py index 1fdf9ecc..d876ed69 100644 --- a/utensor_cgen/transformer/quantize.py +++ b/utensor_cgen/transformer/quantize.py @@ -13,7 +13,6 @@ class QuantizeTransformer(Transformer): KWARGS_NAMESCOPE = '_quantize' def transform(self, ugraph): - #import pdb; pdb.set_trace() graph_def = ugraph.graph_def quant_graph_def = TransformGraph(input_graph_def=graph_def, inputs=[], diff --git a/utensor_cgen/utils.py b/utensor_cgen/utils.py index 28fea2a7..006bf0dd 100644 --- a/utensor_cgen/utils.py +++ b/utensor_cgen/utils.py @@ -9,18 +9,59 @@ from random import choice from string import ascii_letters, digits +import attr import numpy as np from click.types import ParamType +from toml import loads as _parse import idx2numpy as idx2np -import tensorflow as tf -from tensorflow.python.framework import graph_util -from tensorflow.tools.graph_transforms import TransformGraph from utensor_cgen.logger import logger __all__ = ["save_idx", "save_consts", "save_graph", "log_graph", "NamescopedKWArgsParser", "NArgsParam", "MUST_OVERWRITEN"] +class LazyLoader(types.ModuleType): + + def __init__(self, module_name='utensor_cgen', submod_name=None): + self._module_name = '{}{}'.format( + module_name, + submod_name and '.{}'.format(submod_name) or '' + ) + self._mod = None + super(LazyLoader, self).__init__(self._module_name) + + def _load(self): + if self._mod is None: + self._mod = importlib.import_module( + self._module_name + ) + return self._mod + + def __getattr__(self, attrb): + return getattr(self._load(), attrb) + + def __dir__(self): + return dir(self._load()) + +tf = LazyLoader('tensorflow') +tf_python = LazyLoader('tensorflow', 'python.framework') + +class LazyAttrib(object): + + def __init__(self, obj, attr_name): + self._obj = obj + self._attr_name = attr_name + + def __getattr__(self, name): + return getattr(self.attrib, name) + + def __call__(self, *args, **kwargs): + return self.attrib(*args, **kwargs) + + @property + def attrib(self): + return getattr(self._obj, self._attr_name) + def log_graph(graph_or_graph_def, logdir): if isinstance(graph_or_graph_def, tf.GraphDef): @@ -96,8 +137,8 @@ def prepare_meta_graph(meta_graph_path, output_nodes, chkp_path=None): chkp_path = meta_graph_path.replace(".meta", "") with tf.Session(graph=graph) as sess: saver.restore(sess, chkp_path) - graph_def = graph_util.remove_training_nodes(sess.graph_def) - sub_graph_def = graph_util.convert_variables_to_constants(sess=sess, + graph_def = tf_python.graph_util.remove_training_nodes(sess.graph_def) + sub_graph_def = tf_python.graph_util.convert_variables_to_constants(sess=sess, input_graph_def=graph_def, output_node_names=output_nodes) return sub_graph_def @@ -273,38 +314,6 @@ def convert(self, value, param, ctx): return final_args -class NArgsKwargsParam(NArgsParam): - - _trans_name_patrn = re.compile(r"(\w[\w]*)\(?") - - def convert(self, value, param, ctx): - args = super(NArgsKwargsParam, self).convert(value, param, ctx) - return [self._parse_kwargs(arg) for arg in args] - - def _parse_kwargs(self, arg): - trans_match = self._trans_name_patrn.match(arg) - if not trans_match: - raise ValueError("Invalid args detected: {}".format(arg)) - trans_name = trans_match.group(1) - _, end = trans_match.span() - if end == len(arg): - kwargs = {} - else: - if not arg.endswith(")"): - raise ValueError("parentheses mismatch: {}".format(arg)) - kwargs = self._get_kwargs(arg[end:-1]) - return trans_name, kwargs - - def _get_kwargs(self, kws_str): - kw_arg_strs = [s.strip() for s in kws_str.split(',')] - kwargs = {} - for kw_str in kw_arg_strs: - name, v_str = kw_str.split('=') - value = literal_eval(v_str) - kwargs[name] = value - return kwargs - - class _MustOverwrite(object): _obj = None @@ -313,6 +322,7 @@ def __new__(cls, *args, **kwargs): cls._obj = object.__new__(cls, *args, **kwargs) return cls._obj + MUST_OVERWRITEN = _MustOverwrite() @@ -342,7 +352,7 @@ def get_topologic_order(ugraph, init_nodes=None): - `Topological Sorting (wiki) `_ """ - if ugraph.backend != "tensorflow": + if ugraph.lib_name != "tensorflow": raise ValueError( "topologic_order_graph works only on tensorflow graph" ) @@ -450,20 +460,86 @@ def random_str(length=8): return ''.join(chars) -class LazyLoader(types.ModuleType): +class class_property(object): + + def __init__(self, getter): + self._getter = getter + + def __get__(self, obj, objtype=None): + if objtype is None: + return self._getter(obj) + return self._getter(objtype) - def __init__(self, submod_name): - self._submod_name = submod_name - self._submod = None - super(LazyLoader, self).__init__(submod_name) - def _load(self): - if self._submod is None: - self._submod = importlib.import_module('utensor_cgen.{}'.format(self._submod_name)) - return self._submod +@attr.s +class Pipeline(object): + _funcs = attr.ib(factory=list) - def __getattr__(self, attrb): - return getattr(self._load(), attrb) + def __call__(self, *args, **kwargs): + result = None + for func in self._funcs: + if result is None: + result = func(*args, **kwargs) + else: + result = func(*result) + return result + + def __getitem__(self, slice_obj): + cls = type(self) + return cls(funcs=self._funcs[slice_obj]) + + +def parse_toml(file_or_path): + if isinstance(file_or_path, str): + fid = open(file_or_path, 'r') + doc = _parse(fid.read()) + fid.close() + return doc + + +class Configuration(object): + def __init__(self, defaults=None, user_config=None): + if defaults is None: + defaults = {} + if user_config is None: + user_config = {} + self._defaults = defaults + self._user_config = user_config + + @property + def defaults(self): + return self._defaults + + @property + def user_config(self): + return self._user_config + + def get(self, key, default=None): + value = default + if key in self._user_config: + value = self._user_config[key] + elif key in self._defaults: + value = self._defaults[key] + return value + + + def __getitem__(self, key): + if key not in self: + raise KeyError('invalid key: {}'.format(key)) + value = self._user_config.get( + key, self._defaults[key] + ) + if isinstance(value, dict): + value = type(self)(value, {}) + return value - def __dir__(self): - return dir(self._load()) + def __contains__(self, key): + return key in self._user_config or key in self._defaults + + def __repr__(self): + return ( + 'Configuration(\n' + ' defaults={},\n' + ' user_config={} \n' + ')' + ).format(self._defaults, self._user_config)