anhtld commited on
Commit
c15cde2
·
verified ·
1 Parent(s): 89cc407

Auto-sync: 2026-06-25 22:43:25 (part 28)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/INSTALLER +1 -0
  3. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/METADATA +48 -0
  4. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/RECORD +0 -0
  5. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/WHEEL +5 -0
  6. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/entry_points.txt +5 -0
  7. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/licenses/LICENSE +84 -0
  8. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/licenses/NOTICE +456 -0
  9. outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/top_level.txt +2 -0
  10. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/iter/utils.py +60 -0
  11. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/__init__.py +20 -0
  12. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/callable.py +67 -0
  13. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/combinatorics.py +132 -0
  14. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/combining.py +111 -0
  15. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/grouping.py +76 -0
  16. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/utils.py +61 -0
  17. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/__init__.py +0 -0
  18. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/common.py +414 -0
  19. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/decoder.py +389 -0
  20. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/snapshot.py +65 -0
  21. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/dataset.py +511 -0
  22. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/distributed.py +157 -0
  23. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/graph.py +163 -0
  24. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/graph_settings.py +173 -0
  25. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/sampler.py +354 -0
  26. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/deterministic.py +22 -0
  27. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/dlpack.py +231 -0
  28. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/file_baton.py +63 -0
  29. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/flop_counter.py +1017 -0
  30. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/__init__.py +1 -0
  31. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/constants.py +66 -0
  32. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/cuda_to_hip_mappings.py +0 -0
  33. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/hipify_python.py +1175 -0
  34. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/version.py +1 -0
  35. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hooks.py +257 -0
  36. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/jit/__init__.py +0 -0
  37. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/jit/log_extract.py +118 -0
  38. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/mkldnn.py +238 -0
  39. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/mobile_optimizer.py +135 -0
  40. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/__init__.py +450 -0
  41. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/__main__.py +5 -0
  42. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/code.js +689 -0
  43. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/htm.mjs +2 -0
  44. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/preact.mjs +2 -0
  45. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/skeleton.html +21 -0
  46. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_zoo.py +2 -0
  47. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/module_tracker.py +160 -0
  48. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/serialization/__init__.py +1 -0
  49. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/serialization/config.py +25 -0
  50. outputs/audit_venv/lib/python3.11/site-packages/torch/utils/show_pickle.py +151 -0
.gitattributes CHANGED
@@ -131,3 +131,5 @@ outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so filter
131
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so filter=lfs diff=lfs merge=lfs -text
132
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_cuda_linalg.so filter=lfs diff=lfs merge=lfs -text
133
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_python.so filter=lfs diff=lfs merge=lfs -text
 
 
 
131
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so filter=lfs diff=lfs merge=lfs -text
132
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_cuda_linalg.so filter=lfs diff=lfs merge=lfs -text
133
  outputs/audit_venv/lib/python3.11/site-packages/torch/lib/libtorch_python.so filter=lfs diff=lfs merge=lfs -text
134
+ outputs/audit_venv/lib/python3.11/site-packages/yaml/_yaml.cpython-311-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
135
+ outputs/external_vla_export_maniskill_full_no_images/train.jsonl filter=lfs diff=lfs merge=lfs -text
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/METADATA ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.2
2
+ Name: torch
3
+ Version: 2.12.1+computecanada
4
+ Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration
5
+ Description-Content-Type: text/markdown
6
+ Keywords: pytorch,machine learning
7
+ Classifier: Development Status :: 5 - Production/Stable
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Intended Audience :: Education
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Topic :: Scientific/Engineering
12
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Classifier: Topic :: Software Development
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Programming Language :: C++
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Author-email: PyTorch Team <packages@pytorch.org>
25
+ Project-URL: Homepage, https://pytorch.org
26
+ Project-URL: Repository, https://github.com/pytorch/pytorch
27
+ Project-URL: Documentation, https://pytorch.org/docs
28
+ Project-URL: Issue Tracker, https://github.com/pytorch/pytorch/issues
29
+ Project-URL: Forum, https://discuss.pytorch.org
30
+ Requires-Python: >=3.10
31
+ Requires-Dist: filelock
32
+ Requires-Dist: typing-extensions>=4.10.0
33
+ Requires-Dist: setuptools<82
34
+ Requires-Dist: sympy>=1.13.3
35
+ Requires-Dist: networkx>=2.5.1
36
+ Requires-Dist: jinja2
37
+ Requires-Dist: fsspec>=0.8.5
38
+ Requires-Dist: optree>=0.13.0; extra == "optree"
39
+ Requires-Dist: opt-einsum>=3.3; extra == "opt-einsum"
40
+ Requires-Dist: pyyaml; extra == "pyyaml"
41
+ Provides-Extra: optree
42
+ Provides-Extra: opt-einsum
43
+ Provides-Extra: pyyaml
44
+ License-File: LICENSE
45
+ License-File: NOTICE
46
+ Dynamic: license-file
47
+
48
+ BSD-3-Clause
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/RECORD ADDED
The diff for this file is too large to render. See raw diff
 
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/WHEEL ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: wheelfile 0.0.8
3
+ Root-Is-Purelib: true
4
+ Tag: cp311-cp311-linux_x86_64
5
+
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/entry_points.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [console_scripts]
2
+ torchrun = torch.distributed.run:main
3
+
4
+ [torchrun.logs_specs]
5
+ default = torch.distributed.elastic.multiprocessing:DefaultLogsSpecs
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/licenses/LICENSE ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ From PyTorch:
2
+
3
+ Copyright (c) 2016- Facebook, Inc (Adam Paszke)
4
+ Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
5
+ Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
6
+ Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
7
+ Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
8
+ Copyright (c) 2011-2013 NYU (Clement Farabet)
9
+ Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
10
+ Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
11
+ Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
12
+
13
+ From Caffe2:
14
+
15
+ Copyright (c) 2016-present, Facebook Inc. All rights reserved.
16
+
17
+ All contributions by Facebook:
18
+ Copyright (c) 2016 Facebook Inc.
19
+
20
+ All contributions by Google:
21
+ Copyright (c) 2015 Google Inc.
22
+ All rights reserved.
23
+
24
+ All contributions by Yangqing Jia:
25
+ Copyright (c) 2015 Yangqing Jia
26
+ All rights reserved.
27
+
28
+ All contributions by Kakao Brain:
29
+ Copyright 2019-2020 Kakao Brain
30
+
31
+ All contributions by Cruise LLC:
32
+ Copyright (c) 2022 Cruise LLC.
33
+ All rights reserved.
34
+
35
+ All contributions by Tri Dao:
36
+ Copyright (c) 2024 Tri Dao.
37
+ All rights reserved.
38
+
39
+ All contributions by Arm:
40
+ Copyright (c) 2021, 2023-2025 Arm Limited and/or its affiliates
41
+
42
+ All contributions from Caffe:
43
+ Copyright(c) 2013, 2014, 2015, the respective contributors
44
+ All rights reserved.
45
+
46
+ All other contributions:
47
+ Copyright(c) 2015, 2016 the respective contributors
48
+ All rights reserved.
49
+
50
+ Caffe2 uses a copyright model similar to Caffe: each contributor holds
51
+ copyright over their contributions to Caffe2. The project versioning records
52
+ all such contribution and copyright details. If a contributor wants to further
53
+ mark their specific copyright on a particular contribution, they should
54
+ indicate their copyright solely in the commit message of the change when it is
55
+ committed.
56
+
57
+ All rights reserved.
58
+
59
+ Redistribution and use in source and binary forms, with or without
60
+ modification, are permitted provided that the following conditions are met:
61
+
62
+ 1. Redistributions of source code must retain the above copyright
63
+ notice, this list of conditions and the following disclaimer.
64
+
65
+ 2. Redistributions in binary form must reproduce the above copyright
66
+ notice, this list of conditions and the following disclaimer in the
67
+ documentation and/or other materials provided with the distribution.
68
+
69
+ 3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America
70
+ and IDIAP Research Institute nor the names of its contributors may be
71
+ used to endorse or promote products derived from this software without
72
+ specific prior written permission.
73
+
74
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
75
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
76
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
77
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
78
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
79
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
80
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
81
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
82
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
83
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
84
+ POSSIBILITY OF SUCH DAMAGE.
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/licenses/NOTICE ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ =======================================================================
2
+ Software under third_party
3
+ =======================================================================
4
+ Software libraries under third_party are provided as github submodule
5
+ links, and their content is not part of the Caffe2 codebase. Their
6
+ licences can be found under the respective software repositories.
7
+
8
+ =======================================================================
9
+ Earlier BSD License
10
+ =======================================================================
11
+ Early development of Caffe2 in 2015 and early 2016 is licensed under the
12
+ BSD license. The license is attached below:
13
+
14
+ All contributions by Facebook:
15
+ Copyright (c) 2016 Facebook Inc.
16
+
17
+ All contributions by Google:
18
+ Copyright (c) 2015 Google Inc.
19
+ All rights reserved.
20
+
21
+ All contributions by Yangqing Jia:
22
+ Copyright (c) 2015 Yangqing Jia
23
+ All rights reserved.
24
+
25
+ All contributions by Kakao Brain:
26
+ Copyright 2019-2020 Kakao Brain
27
+
28
+ All other contributions:
29
+ Copyright(c) 2015, 2016 the respective contributors
30
+ All rights reserved.
31
+
32
+ Redistribution and use in source and binary forms, with or without
33
+ modification, are permitted provided that the following conditions are met:
34
+
35
+ 1. Redistributions of source code must retain the above copyright notice, this
36
+ list of conditions and the following disclaimer.
37
+ 2. Redistributions in binary form must reproduce the above copyright notice,
38
+ this list of conditions and the following disclaimer in the documentation
39
+ and/or other materials provided with the distribution.
40
+
41
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
42
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
45
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51
+
52
+
53
+ =======================================================================
54
+ Caffe's BSD License
55
+ =======================================================================
56
+ Some parts of the caffe2 code is derived from the original Caffe code, which is
57
+ created by Yangqing Jia and is now a BSD-licensed open-source project. The Caffe
58
+ license is as follows:
59
+
60
+ COPYRIGHT
61
+
62
+ All contributions by the University of California:
63
+ Copyright (c) 2014, The Regents of the University of California (Regents)
64
+ All rights reserved.
65
+
66
+ All other contributions:
67
+ Copyright (c) 2014, the respective contributors
68
+ All rights reserved.
69
+
70
+ Caffe uses a shared copyright model: each contributor holds copyright over
71
+ their contributions to Caffe. The project versioning records all such
72
+ contribution and copyright details. If a contributor wants to further mark
73
+ their specific copyright on a particular contribution, they should indicate
74
+ their copyright solely in the commit message of the change when it is
75
+ committed.
76
+
77
+ LICENSE
78
+
79
+ Redistribution and use in source and binary forms, with or without
80
+ modification, are permitted provided that the following conditions are met:
81
+
82
+ 1. Redistributions of source code must retain the above copyright notice, this
83
+ list of conditions and the following disclaimer.
84
+ 2. Redistributions in binary form must reproduce the above copyright notice,
85
+ this list of conditions and the following disclaimer in the documentation
86
+ and/or other materials provided with the distribution.
87
+
88
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
89
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
90
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
91
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
92
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
93
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
94
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
95
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
96
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
97
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
98
+
99
+ CONTRIBUTION AGREEMENT
100
+
101
+ By contributing to the BVLC/caffe repository through pull-request, comment,
102
+ or otherwise, the contributor releases their content to the
103
+ license and copyright terms herein.
104
+
105
+ =======================================================================
106
+ Caffe2's Apache License
107
+ =======================================================================
108
+
109
+ This repo contains Caffe2 code, which was previously licensed under
110
+ Apache License Version 2.0:
111
+
112
+ Apache License
113
+ Version 2.0, January 2004
114
+ http://www.apache.org/licenses/
115
+
116
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
117
+
118
+ 1. Definitions.
119
+
120
+ "License" shall mean the terms and conditions for use, reproduction,
121
+ and distribution as defined by Sections 1 through 9 of this document.
122
+
123
+ "Licensor" shall mean the copyright owner or entity authorized by
124
+ the copyright owner that is granting the License.
125
+
126
+ "Legal Entity" shall mean the union of the acting entity and all
127
+ other entities that control, are controlled by, or are under common
128
+ control with that entity. For the purposes of this definition,
129
+ "control" means (i) the power, direct or indirect, to cause the
130
+ direction or management of such entity, whether by contract or
131
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
132
+ outstanding shares, or (iii) beneficial ownership of such entity.
133
+
134
+ "You" (or "Your") shall mean an individual or Legal Entity
135
+ exercising permissions granted by this License.
136
+
137
+ "Source" form shall mean the preferred form for making modifications,
138
+ including but not limited to software source code, documentation
139
+ source, and configuration files.
140
+
141
+ "Object" form shall mean any form resulting from mechanical
142
+ transformation or translation of a Source form, including but
143
+ not limited to compiled object code, generated documentation,
144
+ and conversions to other media types.
145
+
146
+ "Work" shall mean the work of authorship, whether in Source or
147
+ Object form, made available under the License, as indicated by a
148
+ copyright notice that is included in or attached to the work
149
+ (an example is provided in the Appendix below).
150
+
151
+ "Derivative Works" shall mean any work, whether in Source or Object
152
+ form, that is based on (or derived from) the Work and for which the
153
+ editorial revisions, annotations, elaborations, or other modifications
154
+ represent, as a whole, an original work of authorship. For the purposes
155
+ of this License, Derivative Works shall not include works that remain
156
+ separable from, or merely link (or bind by name) to the interfaces of,
157
+ the Work and Derivative Works thereof.
158
+
159
+ "Contribution" shall mean any work of authorship, including
160
+ the original version of the Work and any modifications or additions
161
+ to that Work or Derivative Works thereof, that is intentionally
162
+ submitted to Licensor for inclusion in the Work by the copyright owner
163
+ or by an individual or Legal Entity authorized to submit on behalf of
164
+ the copyright owner. For the purposes of this definition, "submitted"
165
+ means any form of electronic, verbal, or written communication sent
166
+ to the Licensor or its representatives, including but not limited to
167
+ communication on electronic mailing lists, source code control systems,
168
+ and issue tracking systems that are managed by, or on behalf of, the
169
+ Licensor for the purpose of discussing and improving the Work, but
170
+ excluding communication that is conspicuously marked or otherwise
171
+ designated in writing by the copyright owner as "Not a Contribution."
172
+
173
+ "Contributor" shall mean Licensor and any individual or Legal Entity
174
+ on behalf of whom a Contribution has been received by Licensor and
175
+ subsequently incorporated within the Work.
176
+
177
+ 2. Grant of Copyright License. Subject to the terms and conditions of
178
+ this License, each Contributor hereby grants to You a perpetual,
179
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
180
+ copyright license to reproduce, prepare Derivative Works of,
181
+ publicly display, publicly perform, sublicense, and distribute the
182
+ Work and such Derivative Works in Source or Object form.
183
+
184
+ 3. Grant of Patent License. Subject to the terms and conditions of
185
+ this License, each Contributor hereby grants to You a perpetual,
186
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
187
+ (except as stated in this section) patent license to make, have made,
188
+ use, offer to sell, sell, import, and otherwise transfer the Work,
189
+ where such license applies only to those patent claims licensable
190
+ by such Contributor that are necessarily infringed by their
191
+ Contribution(s) alone or by combination of their Contribution(s)
192
+ with the Work to which such Contribution(s) was submitted. If You
193
+ institute patent litigation against any entity (including a
194
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
195
+ or a Contribution incorporated within the Work constitutes direct
196
+ or contributory patent infringement, then any patent licenses
197
+ granted to You under this License for that Work shall terminate
198
+ as of the date such litigation is filed.
199
+
200
+ 4. Redistribution. You may reproduce and distribute copies of the
201
+ Work or Derivative Works thereof in any medium, with or without
202
+ modifications, and in Source or Object form, provided that You
203
+ meet the following conditions:
204
+
205
+ (a) You must give any other recipients of the Work or
206
+ Derivative Works a copy of this License; and
207
+
208
+ (b) You must cause any modified files to carry prominent notices
209
+ stating that You changed the files; and
210
+
211
+ (c) You must retain, in the Source form of any Derivative Works
212
+ that You distribute, all copyright, patent, trademark, and
213
+ attribution notices from the Source form of the Work,
214
+ excluding those notices that do not pertain to any part of
215
+ the Derivative Works; and
216
+
217
+ (d) If the Work includes a "NOTICE" text file as part of its
218
+ distribution, then any Derivative Works that You distribute must
219
+ include a readable copy of the attribution notices contained
220
+ within such NOTICE file, excluding those notices that do not
221
+ pertain to any part of the Derivative Works, in at least one
222
+ of the following places: within a NOTICE text file distributed
223
+ as part of the Derivative Works; within the Source form or
224
+ documentation, if provided along with the Derivative Works; or,
225
+ within a display generated by the Derivative Works, if and
226
+ wherever such third-party notices normally appear. The contents
227
+ of the NOTICE file are for informational purposes only and
228
+ do not modify the License. You may add Your own attribution
229
+ notices within Derivative Works that You distribute, alongside
230
+ or as an addendum to the NOTICE text from the Work, provided
231
+ that such additional attribution notices cannot be construed
232
+ as modifying the License.
233
+
234
+ You may add Your own copyright statement to Your modifications and
235
+ may provide additional or different license terms and conditions
236
+ for use, reproduction, or distribution of Your modifications, or
237
+ for any such Derivative Works as a whole, provided Your use,
238
+ reproduction, and distribution of the Work otherwise complies with
239
+ the conditions stated in this License.
240
+
241
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
242
+ any Contribution intentionally submitted for inclusion in the Work
243
+ by You to the Licensor shall be under the terms and conditions of
244
+ this License, without any additional terms or conditions.
245
+ Notwithstanding the above, nothing herein shall supersede or modify
246
+ the terms of any separate license agreement you may have executed
247
+ with Licensor regarding such Contributions.
248
+
249
+ 6. Trademarks. This License does not grant permission to use the trade
250
+ names, trademarks, service marks, or product names of the Licensor,
251
+ except as required for reasonable and customary use in describing the
252
+ origin of the Work and reproducing the content of the NOTICE file.
253
+
254
+ 7. Disclaimer of Warranty. Unless required by applicable law or
255
+ agreed to in writing, Licensor provides the Work (and each
256
+ Contributor provides its Contributions) on an "AS IS" BASIS,
257
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
258
+ implied, including, without limitation, any warranties or conditions
259
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
260
+ PARTICULAR PURPOSE. You are solely responsible for determining the
261
+ appropriateness of using or redistributing the Work and assume any
262
+ risks associated with Your exercise of permissions under this License.
263
+
264
+ 8. Limitation of Liability. In no event and under no legal theory,
265
+ whether in tort (including negligence), contract, or otherwise,
266
+ unless required by applicable law (such as deliberate and grossly
267
+ negligent acts) or agreed to in writing, shall any Contributor be
268
+ liable to You for damages, including any direct, indirect, special,
269
+ incidental, or consequential damages of any character arising as a
270
+ result of this License or out of the use or inability to use the
271
+ Work (including but not limited to damages for loss of goodwill,
272
+ work stoppage, computer failure or malfunction, or any and all
273
+ other commercial damages or losses), even if such Contributor
274
+ has been advised of the possibility of such damages.
275
+
276
+ 9. Accepting Warranty or Additional Liability. While redistributing
277
+ the Work or Derivative Works thereof, You may choose to offer,
278
+ and charge a fee for, acceptance of support, warranty, indemnity,
279
+ or other liability obligations and/or rights consistent with this
280
+ License. However, in accepting such obligations, You may act only
281
+ on Your own behalf and on Your sole responsibility, not on behalf
282
+ of any other Contributor, and only if You agree to indemnify,
283
+ defend, and hold each Contributor harmless for any liability
284
+ incurred by, or claims asserted against, such Contributor by reason
285
+ of your accepting any such warranty or additional liability.
286
+
287
+ =======================================================================
288
+ Cephes's 3-Clause BSD License
289
+ =======================================================================
290
+
291
+ Code derived from implementations in the Cephes Math Library should mention
292
+ its derivation and reference the following license:
293
+
294
+ 3-Clause BSD License for the Cephes Math Library
295
+ Copyright (c) 2018, Steven Moshier
296
+ All rights reserved.
297
+
298
+ Redistribution and use in source and binary forms, with or without
299
+ modification, are permitted provided that the following conditions are met:
300
+
301
+ * Redistributions of source code must retain the above copyright
302
+ notice, this list of conditions and the following disclaimer.
303
+
304
+ * Redistributions in binary form must reproduce the above copyright
305
+ notice, this list of conditions and the following disclaimer in the
306
+ documentation and/or other materials provided with the distribution.
307
+
308
+ * Neither the name of the nor the
309
+ names of its contributors may be used to endorse or promote products
310
+ derived from this software without specific prior written permission.
311
+
312
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
313
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
314
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
315
+ DISCLAIMED. IN NO EVENT SHALL Steven Moshier BE LIABLE FOR ANY
316
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
317
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
318
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
319
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
320
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
321
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
322
+
323
+
324
+ =======================================================================
325
+ SciPy's 3-Clause BSD License
326
+ =======================================================================
327
+
328
+ Code derived from implementations in SciPy should mention its derivation
329
+ and reference the following license:
330
+
331
+ Copyright (c) 2001-2002 Enthought, Inc. 2003-2019, SciPy Developers.
332
+ All rights reserved.
333
+
334
+ Redistribution and use in source and binary forms, with or without
335
+ modification, are permitted provided that the following conditions
336
+ are met:
337
+
338
+ 1. Redistributions of source code must retain the above copyright
339
+ notice, this list of conditions and the following disclaimer.
340
+
341
+ 2. Redistributions in binary form must reproduce the above
342
+ copyright notice, this list of conditions and the following
343
+ disclaimer in the documentation and/or other materials provided
344
+ with the distribution.
345
+
346
+ 3. Neither the name of the copyright holder nor the names of its
347
+ contributors may be used to endorse or promote products derived
348
+ from this software without specific prior written permission.
349
+
350
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
351
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
352
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
353
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
354
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
355
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
356
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
357
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
358
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
359
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
360
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
361
+
362
+ =======================================================================
363
+ Boost's 1.0 Software License
364
+ =======================================================================
365
+
366
+ Code derived from implementations in Boost 1.0 should mention its
367
+ derivation and reference the following license:
368
+
369
+ Boost Software License - Version 1.0 - August 17th, 2003
370
+
371
+ Permission is hereby granted, free of charge, to any person or organization
372
+ obtaining a copy of the software and accompanying documentation covered by
373
+ this license (the "Software") to use, reproduce, display, distribute,
374
+ execute, and transmit the Software, and to prepare derivative works of the
375
+ Software, and to permit third-parties to whom the Software is furnished to
376
+ do so, all subject to the following:
377
+
378
+ The copyright notices in the Software and this entire statement, including
379
+ the above license grant, this restriction and the following disclaimer,
380
+ must be included in all copies of the Software, in whole or in part, and
381
+ all derivative works of the Software, unless such copies or derivative
382
+ works are solely in the form of machine-executable object code generated by
383
+ a source language processor.
384
+
385
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
386
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
387
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
388
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
389
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
390
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
391
+ DEALINGS IN THE SOFTWARE.
392
+
393
+ END OF TERMS AND CONDITIONS
394
+
395
+ APPENDIX: How to apply the Apache License to your work.
396
+
397
+ To apply the Apache License to your work, attach the following
398
+ boilerplate notice, with the fields enclosed by brackets "[]"
399
+ replaced with your own identifying information. (Don't include
400
+ the brackets!) The text should be enclosed in the appropriate
401
+ comment syntax for the file format. We also recommend that a
402
+ file or class name and description of purpose be included on the
403
+ same "printed page" as the copyright notice for easier
404
+ identification within third-party archives.
405
+
406
+ Copyright [yyyy] [name of copyright owner]
407
+
408
+ Licensed under the Apache License, Version 2.0 (the "License");
409
+ you may not use this file except in compliance with the License.
410
+ You may obtain a copy of the License at
411
+
412
+ http://www.apache.org/licenses/LICENSE-2.0
413
+
414
+ Unless required by applicable law or agreed to in writing, software
415
+ distributed under the License is distributed on an "AS IS" BASIS,
416
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
417
+ See the License for the specific language governing permissions and
418
+ limitations under the License.
419
+
420
+ =======================================================================
421
+ PILLOW-SIMD Software License
422
+ =======================================================================
423
+
424
+ Code derived from implementations in PILLOW-SIMD should mention its derivation
425
+ and reference the following license:
426
+
427
+ The Python Imaging Library (PIL) is
428
+
429
+ Copyright © 1997-2011 by Secret Labs AB
430
+ Copyright © 1995-2011 by Fredrik Lundh
431
+
432
+ Pillow is the friendly PIL fork. It is
433
+
434
+ Copyright © 2010-2022 by Alex Clark and contributors
435
+
436
+ Like PIL, Pillow is licensed under the open source HPND License:
437
+
438
+ By obtaining, using, and/or copying this software and/or its associated
439
+ documentation, you agree that you have read, understood, and will comply
440
+ with the following terms and conditions:
441
+
442
+ Permission to use, copy, modify, and distribute this software and its
443
+ associated documentation for any purpose and without fee is hereby granted,
444
+ provided that the above copyright notice appears in all copies, and that
445
+ both that copyright notice and this permission notice appear in supporting
446
+ documentation, and that the name of Secret Labs AB or the author not be
447
+ used in advertising or publicity pertaining to distribution of the software
448
+ without specific, written prior permission.
449
+
450
+ SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
451
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
452
+ IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL,
453
+ INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
454
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
455
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
456
+ PERFORMANCE OF THIS SOFTWARE.
outputs/audit_venv/lib/python3.11/site-packages/torch-2.12.1+computecanada.dist-info/top_level.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ torch
2
+ torchgen
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/iter/utils.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import warnings
3
+ from collections.abc import Iterable, Iterator, Sized
4
+ from typing import TypeVar
5
+
6
+ from torch.utils.data.datapipes.datapipe import IterDataPipe
7
+
8
+
9
+ _T = TypeVar("_T")
10
+
11
+ __all__ = ["IterableWrapperIterDataPipe"]
12
+
13
+
14
+ class IterableWrapperIterDataPipe(IterDataPipe[_T]):
15
+ r"""
16
+ Wraps an iterable object to create an IterDataPipe.
17
+
18
+ Args:
19
+ iterable: Iterable object to be wrapped into an IterDataPipe
20
+ deepcopy: Option to deepcopy input iterable object for each
21
+ iterator. The copy is made when the first element is read in ``iter()``.
22
+
23
+ .. note::
24
+ If ``deepcopy`` is explicitly set to ``False``, users should ensure
25
+ that the data pipeline doesn't contain any in-place operations over
26
+ the iterable instance to prevent data inconsistency across iterations.
27
+
28
+ Example:
29
+ >>> # xdoctest: +SKIP
30
+ >>> from torchdata.datapipes.iter import IterableWrapper
31
+ >>> dp = IterableWrapper(range(10))
32
+ >>> list(dp)
33
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
34
+ """
35
+
36
+ def __init__(self, iterable: Iterable[_T], deepcopy: bool = True) -> None:
37
+ self.iterable = iterable
38
+ self.deepcopy = deepcopy
39
+
40
+ def __iter__(self) -> Iterator[_T]:
41
+ source_data = self.iterable
42
+ if self.deepcopy:
43
+ try:
44
+ source_data = copy.deepcopy(self.iterable)
45
+ # For the case that data cannot be deep-copied,
46
+ # all in-place operations will affect iterable variable.
47
+ # When this DataPipe is iterated second time, it will
48
+ # yield modified items.
49
+ except TypeError:
50
+ warnings.warn(
51
+ "The input iterable can not be deepcopied, "
52
+ "please be aware of in-place modification would affect source data.",
53
+ stacklevel=2,
54
+ )
55
+ yield from source_data
56
+
57
+ def __len__(self) -> int:
58
+ if isinstance(self.iterable, Sized):
59
+ return len(self.iterable)
60
+ raise TypeError(f"{type(self).__name__} instance doesn't have valid length")
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/__init__.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Functional DataPipe
2
+ from torch.utils.data.datapipes.map.callable import MapperMapDataPipe as Mapper
3
+ from torch.utils.data.datapipes.map.combinatorics import (
4
+ ShufflerIterDataPipe as Shuffler,
5
+ )
6
+ from torch.utils.data.datapipes.map.combining import (
7
+ ConcaterMapDataPipe as Concater,
8
+ ZipperMapDataPipe as Zipper,
9
+ )
10
+ from torch.utils.data.datapipes.map.grouping import BatcherMapDataPipe as Batcher
11
+ from torch.utils.data.datapipes.map.utils import (
12
+ SequenceWrapperMapDataPipe as SequenceWrapper,
13
+ )
14
+
15
+
16
+ __all__ = ["Batcher", "Concater", "Mapper", "SequenceWrapper", "Shuffler", "Zipper"]
17
+
18
+ # Please keep this list sorted
19
+ if __all__ != sorted(__all__):
20
+ raise AssertionError("__all__ is not sorted")
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/callable.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from collections.abc import Callable
3
+ from typing import TypeVar
4
+
5
+ from torch.utils.data.datapipes._decorator import functional_datapipe
6
+ from torch.utils.data.datapipes.datapipe import MapDataPipe
7
+ from torch.utils.data.datapipes.utils.common import _check_unpickable_fn
8
+
9
+
10
+ __all__ = ["MapperMapDataPipe", "default_fn"]
11
+
12
+
13
+ _T_co = TypeVar("_T_co", covariant=True)
14
+
15
+
16
+ # Default function to return each item directly
17
+ # In order to keep datapipe picklable, eliminates the usage
18
+ # of python lambda function
19
+ def default_fn(data):
20
+ return data
21
+
22
+
23
+ @functional_datapipe("map")
24
+ class MapperMapDataPipe(MapDataPipe[_T_co]):
25
+ r"""
26
+ Apply the input function over each item from the source DataPipe (functional name: ``map``).
27
+
28
+ The function can be any regular Python function or partial object. Lambda
29
+ function is not recommended as it is not supported by pickle.
30
+
31
+ Args:
32
+ datapipe: Source MapDataPipe
33
+ fn: Function being applied to each item
34
+
35
+ Example:
36
+ >>> # xdoctest: +SKIP
37
+ >>> from torchdata.datapipes.map import SequenceWrapper, Mapper
38
+ >>> def add_one(x):
39
+ ... return x + 1
40
+ >>> dp = SequenceWrapper(range(10))
41
+ >>> map_dp_1 = dp.map(add_one)
42
+ >>> list(map_dp_1)
43
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
44
+ >>> map_dp_2 = Mapper(dp, lambda x: x + 1)
45
+ >>> list(map_dp_2)
46
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
47
+ """
48
+
49
+ datapipe: MapDataPipe
50
+ fn: Callable
51
+
52
+ def __init__(
53
+ self,
54
+ datapipe: MapDataPipe,
55
+ fn: Callable = default_fn,
56
+ ) -> None:
57
+ super().__init__()
58
+ self.datapipe = datapipe
59
+ _check_unpickable_fn(fn)
60
+ self.fn = fn # type: ignore[assignment]
61
+
62
+ def __len__(self) -> int:
63
+ # pyrefly: ignore [bad-argument-type]
64
+ return len(self.datapipe)
65
+
66
+ def __getitem__(self, index) -> _T_co:
67
+ return self.fn(self.datapipe[index])
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/combinatorics.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import random
3
+ from collections.abc import Iterator
4
+ from typing import TypeVar
5
+
6
+ import torch
7
+ from torch.utils.data.datapipes.datapipe import IterDataPipe, MapDataPipe
8
+
9
+
10
+ __all__ = ["ShufflerIterDataPipe"]
11
+
12
+
13
+ _T_co = TypeVar("_T_co", covariant=True)
14
+
15
+
16
+ # @functional_datapipe('shuffle')
17
+ class ShufflerIterDataPipe(IterDataPipe[_T_co]):
18
+ r"""
19
+ Shuffle the input MapDataPipe via its indices (functional name: ``shuffle``).
20
+
21
+ When it is used with :class:`~torch.utils.data.DataLoader`, the methods to
22
+ set up random seed are different based on :attr:`num_workers`.
23
+
24
+ For single-process mode (:attr:`num_workers == 0`), the random seed is set before
25
+ the :class:`~torch.utils.data.DataLoader` in the main process. For multi-process
26
+ mode (:attr:`num_worker > 0`), ``worker_init_fn`` is used to set up a random seed
27
+ for each worker process.
28
+
29
+ Args:
30
+ datapipe: MapDataPipe being shuffled
31
+ indices: a list of indices of the MapDataPipe. If not provided, we assume it uses 0-based indexing
32
+
33
+ Example:
34
+ >>> # xdoctest: +SKIP
35
+ >>> from torchdata.datapipes.map import SequenceWrapper
36
+ >>> dp = SequenceWrapper(range(10))
37
+ >>> shuffle_dp = dp.shuffle().set_seed(0)
38
+ >>> list(shuffle_dp)
39
+ [7, 8, 1, 5, 3, 4, 2, 0, 9, 6]
40
+ >>> list(shuffle_dp)
41
+ [6, 1, 9, 5, 2, 4, 7, 3, 8, 0]
42
+ >>> # Reset seed for Shuffler
43
+ >>> shuffle_dp = shuffle_dp.set_seed(0)
44
+ >>> list(shuffle_dp)
45
+ [7, 8, 1, 5, 3, 4, 2, 0, 9, 6]
46
+
47
+ Note:
48
+ Even thought this ``shuffle`` operation takes a ``MapDataPipe`` as the input, it would return an
49
+ ``IterDataPipe`` rather than a ``MapDataPipe``, because ``MapDataPipe`` should be non-sensitive to
50
+ the order of data order for the sake of random reads, but ``IterDataPipe`` depends on the order
51
+ of data during data-processing.
52
+ """
53
+
54
+ datapipe: MapDataPipe[_T_co]
55
+ _enabled: bool
56
+ _seed: int | None
57
+ _rng: random.Random
58
+
59
+ def __init__(
60
+ self,
61
+ datapipe: MapDataPipe[_T_co],
62
+ *,
63
+ indices: list | None = None,
64
+ ) -> None:
65
+ super().__init__()
66
+ self.datapipe = datapipe
67
+ # pyrefly: ignore [bad-argument-type]
68
+ self.indices = list(range(len(datapipe))) if indices is None else indices
69
+ self._enabled = True
70
+ self._seed = None
71
+ self._rng = random.Random()
72
+ self._shuffled_indices: list = self.indices
73
+
74
+ def set_shuffle(self, shuffle=True):
75
+ self._enabled = shuffle
76
+ return self
77
+
78
+ def set_seed(self, seed: int):
79
+ self._seed = seed
80
+ return self
81
+
82
+ def __iter__(self) -> Iterator[_T_co]:
83
+ if not self._enabled:
84
+ for idx in self.indices:
85
+ yield self.datapipe[idx]
86
+ else:
87
+ while self._shuffled_indices:
88
+ idx = self._shuffled_indices.pop()
89
+ yield self.datapipe[idx]
90
+
91
+ def reset(self) -> None:
92
+ if self._enabled and self._seed is None:
93
+ self._seed = int(torch.empty((), dtype=torch.int64).random_().item())
94
+ self._rng.seed(self._seed)
95
+ self._seed = None
96
+ self._shuffled_indices = self._rng.sample(self.indices, len(self.indices))
97
+
98
+ def __len__(self) -> int:
99
+ # pyrefly: ignore [bad-argument-type]
100
+ return len(self.datapipe)
101
+
102
+ def __getstate__(self):
103
+ state = (
104
+ self.datapipe,
105
+ self.indices,
106
+ self._enabled,
107
+ self._seed,
108
+ self._rng.getstate(),
109
+ self._shuffled_indices,
110
+ self._valid_iterator_id,
111
+ self._number_of_samples_yielded,
112
+ )
113
+ if IterDataPipe.getstate_hook is not None:
114
+ return IterDataPipe.getstate_hook(state)
115
+ return state
116
+
117
+ def __setstate__(self, state):
118
+ (
119
+ self.datapipe,
120
+ self.indices,
121
+ self._enabled,
122
+ self._seed,
123
+ rng_state,
124
+ self._shuffled_indices,
125
+ self._valid_iterator_id,
126
+ self._number_of_samples_yielded,
127
+ ) = state
128
+ self._rng = random.Random()
129
+ self._rng.setstate(rng_state)
130
+
131
+
132
+ MapDataPipe.register_datapipe_as_function("shuffle", ShufflerIterDataPipe)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/combining.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from collections.abc import Sized
3
+ from typing import TypeVar
4
+
5
+ from torch.utils.data.datapipes._decorator import functional_datapipe
6
+ from torch.utils.data.datapipes.datapipe import MapDataPipe
7
+
8
+
9
+ __all__ = ["ConcaterMapDataPipe", "ZipperMapDataPipe"]
10
+
11
+ _T_co = TypeVar("_T_co", covariant=True)
12
+
13
+
14
+ @functional_datapipe("concat")
15
+ class ConcaterMapDataPipe(MapDataPipe):
16
+ r"""
17
+ Concatenate multiple Map DataPipes (functional name: ``concat``).
18
+
19
+ The new index of is the cumulative sum of source DataPipes.
20
+ For example, if there are 2 source DataPipes both with length 5,
21
+ index 0 to 4 of the resulting `ConcatMapDataPipe` would refer to
22
+ elements of the first DataPipe, and 5 to 9 would refer to elements
23
+ of the second DataPipe.
24
+
25
+ Args:
26
+ datapipes: Map DataPipes being concatenated
27
+
28
+ Example:
29
+ >>> # xdoctest: +SKIP
30
+ >>> from torchdata.datapipes.map import SequenceWrapper
31
+ >>> dp1 = SequenceWrapper(range(3))
32
+ >>> dp2 = SequenceWrapper(range(3))
33
+ >>> concat_dp = dp1.concat(dp2)
34
+ >>> list(concat_dp)
35
+ [0, 1, 2, 0, 1, 2]
36
+ """
37
+
38
+ datapipes: tuple[MapDataPipe]
39
+
40
+ def __init__(self, *datapipes: MapDataPipe) -> None:
41
+ if len(datapipes) == 0:
42
+ raise ValueError("Expected at least one DataPipe, but got nothing")
43
+ if not all(isinstance(dp, MapDataPipe) for dp in datapipes):
44
+ raise TypeError("Expected all inputs to be `MapDataPipe`")
45
+ # pyrefly: ignore [unsafe-overlap]
46
+ if not all(isinstance(dp, Sized) for dp in datapipes):
47
+ raise TypeError("Expected all inputs to be `Sized`")
48
+ self.datapipes = datapipes # type: ignore[assignment]
49
+
50
+ def __getitem__(self, index) -> _T_co: # type: ignore[type-var]
51
+ offset = 0
52
+ for dp in self.datapipes:
53
+ # pyrefly: ignore [bad-argument-type]
54
+ if index - offset < len(dp):
55
+ return dp[index - offset]
56
+ else:
57
+ # pyrefly: ignore [bad-argument-type]
58
+ offset += len(dp)
59
+ raise IndexError(f"Index {index} is out of range.")
60
+
61
+ def __len__(self) -> int:
62
+ # pyrefly: ignore [bad-argument-type]
63
+ return sum(len(dp) for dp in self.datapipes)
64
+
65
+
66
+ @functional_datapipe("zip")
67
+ class ZipperMapDataPipe(MapDataPipe[tuple[_T_co, ...]]):
68
+ r"""
69
+ Aggregates elements into a tuple from each of the input DataPipes (functional name: ``zip``).
70
+
71
+ This MataPipe is out of bound as soon as the shortest input DataPipe is exhausted.
72
+
73
+ Args:
74
+ *datapipes: Map DataPipes being aggregated
75
+
76
+ Example:
77
+ >>> # xdoctest: +SKIP
78
+ >>> from torchdata.datapipes.map import SequenceWrapper
79
+ >>> dp1 = SequenceWrapper(range(3))
80
+ >>> dp2 = SequenceWrapper(range(10, 13))
81
+ >>> zip_dp = dp1.zip(dp2)
82
+ >>> list(zip_dp)
83
+ [(0, 10), (1, 11), (2, 12)]
84
+ """
85
+
86
+ datapipes: tuple[MapDataPipe[_T_co], ...]
87
+
88
+ def __init__(self, *datapipes: MapDataPipe[_T_co]) -> None:
89
+ if len(datapipes) == 0:
90
+ raise ValueError("Expected at least one DataPipe, but got nothing")
91
+ if not all(isinstance(dp, MapDataPipe) for dp in datapipes):
92
+ raise TypeError("Expected all inputs to be `MapDataPipe`")
93
+ # pyrefly: ignore [unsafe-overlap]
94
+ if not all(isinstance(dp, Sized) for dp in datapipes):
95
+ raise TypeError("Expected all inputs to be `Sized`")
96
+ self.datapipes = datapipes
97
+
98
+ def __getitem__(self, index) -> tuple[_T_co, ...]:
99
+ res = []
100
+ for dp in self.datapipes:
101
+ try:
102
+ res.append(dp[index])
103
+ except IndexError as e:
104
+ raise IndexError(
105
+ f"Index {index} is out of range for one of the input MapDataPipes {dp}."
106
+ ) from e
107
+ return tuple(res)
108
+
109
+ def __len__(self) -> int:
110
+ # pyrefly: ignore [bad-argument-type]
111
+ return min(len(dp) for dp in self.datapipes)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/grouping.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from collections.abc import Sized
3
+ from typing import TypeVar
4
+
5
+ from torch.utils.data.datapipes._decorator import functional_datapipe
6
+ from torch.utils.data.datapipes.datapipe import DataChunk, MapDataPipe
7
+
8
+
9
+ __all__ = ["BatcherMapDataPipe"]
10
+
11
+
12
+ _T = TypeVar("_T")
13
+
14
+
15
+ @functional_datapipe("batch")
16
+ class BatcherMapDataPipe(MapDataPipe[DataChunk]):
17
+ r"""
18
+ Create mini-batches of data (functional name: ``batch``).
19
+
20
+ An outer dimension will be added as ``batch_size`` if ``drop_last`` is set to ``True``,
21
+ or ``length % batch_size`` for the last batch if ``drop_last`` is set to ``False``.
22
+
23
+ Args:
24
+ datapipe: Iterable DataPipe being batched
25
+ batch_size: The size of each batch
26
+ drop_last: Option to drop the last batch if it's not full
27
+
28
+ Example:
29
+ >>> # xdoctest: +SKIP
30
+ >>> from torchdata.datapipes.map import SequenceWrapper
31
+ >>> dp = SequenceWrapper(range(10))
32
+ >>> batch_dp = dp.batch(batch_size=2)
33
+ >>> list(batch_dp)
34
+ [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
35
+ """
36
+
37
+ datapipe: MapDataPipe
38
+ batch_size: int
39
+ drop_last: bool
40
+
41
+ def __init__(
42
+ self,
43
+ datapipe: MapDataPipe[_T],
44
+ batch_size: int,
45
+ drop_last: bool = False,
46
+ wrapper_class: type[DataChunk] = DataChunk,
47
+ ) -> None:
48
+ if batch_size <= 0:
49
+ raise AssertionError("Batch size is required to be larger than 0!")
50
+ super().__init__()
51
+ self.datapipe = datapipe
52
+ self.batch_size = batch_size
53
+ self.drop_last = drop_last
54
+ self.wrapper_class = wrapper_class
55
+
56
+ def __getitem__(self, index) -> DataChunk:
57
+ batch: list = []
58
+ indices = range(index * self.batch_size, (index + 1) * self.batch_size)
59
+ try:
60
+ batch.extend(self.datapipe[i] for i in indices)
61
+ return self.wrapper_class(batch)
62
+ except IndexError as e:
63
+ if not self.drop_last and len(batch) > 0:
64
+ return self.wrapper_class(batch)
65
+ else:
66
+ raise IndexError(f"Index {index} is out of bound.") from e
67
+
68
+ def __len__(self) -> int:
69
+ # pyrefly: ignore [unsafe-overlap]
70
+ if isinstance(self.datapipe, Sized):
71
+ if self.drop_last:
72
+ return len(self.datapipe) // self.batch_size
73
+ else:
74
+ return (len(self.datapipe) + self.batch_size - 1) // self.batch_size
75
+ else:
76
+ raise TypeError(f"{type(self).__name__} instance doesn't have valid length")
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/map/utils.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import warnings
3
+ from collections.abc import Mapping, Sequence
4
+ from typing import Any, TypeVar
5
+
6
+ from torch.utils.data.datapipes.datapipe import MapDataPipe
7
+
8
+
9
+ _T = TypeVar("_T")
10
+
11
+ __all__ = ["SequenceWrapperMapDataPipe"]
12
+
13
+
14
+ class SequenceWrapperMapDataPipe(MapDataPipe[_T]):
15
+ r"""
16
+ Wraps a sequence object into a MapDataPipe.
17
+
18
+ Args:
19
+ sequence: Sequence object to be wrapped into an MapDataPipe
20
+ deepcopy: Option to deepcopy input sequence object
21
+
22
+ .. note::
23
+ If ``deepcopy`` is set to False explicitly, users should ensure
24
+ that data pipeline doesn't contain any in-place operations over
25
+ the iterable instance, in order to prevent data inconsistency
26
+ across iterations.
27
+
28
+ Example:
29
+ >>> # xdoctest: +SKIP
30
+ >>> from torchdata.datapipes.map import SequenceWrapper
31
+ >>> dp = SequenceWrapper(range(10))
32
+ >>> list(dp)
33
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
34
+ >>> dp = SequenceWrapper({"a": 100, "b": 200, "c": 300, "d": 400})
35
+ >>> dp["a"]
36
+ 100
37
+ """
38
+
39
+ sequence: Sequence[_T] | Mapping[Any, _T]
40
+
41
+ def __init__(
42
+ self, sequence: Sequence[_T] | Mapping[Any, _T], deepcopy: bool = True
43
+ ) -> None:
44
+ if deepcopy:
45
+ try:
46
+ self.sequence = copy.deepcopy(sequence)
47
+ except TypeError:
48
+ warnings.warn(
49
+ "The input sequence can not be deepcopied, "
50
+ "please be aware of in-place modification would affect source data",
51
+ stacklevel=2,
52
+ )
53
+ self.sequence = sequence
54
+ else:
55
+ self.sequence = sequence
56
+
57
+ def __getitem__(self, index: int) -> _T:
58
+ return self.sequence[index]
59
+
60
+ def __len__(self) -> int:
61
+ return len(self.sequence)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/__init__.py ADDED
File without changes
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/common.py ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import fnmatch
3
+ import functools
4
+ import inspect
5
+ import os
6
+ import warnings
7
+ from collections.abc import Callable, Iterable
8
+ from io import IOBase
9
+ from typing import Any, NoReturn
10
+
11
+ from torch.utils._import_utils import dill_available
12
+
13
+
14
+ __all__ = [
15
+ "validate_input_col",
16
+ "StreamWrapper",
17
+ "get_file_binaries_from_pathnames",
18
+ "get_file_pathnames_from_root",
19
+ "match_masks",
20
+ "validate_pathname_binary_tuple",
21
+ ]
22
+
23
+
24
+ # BC for torchdata
25
+ DILL_AVAILABLE = dill_available()
26
+
27
+
28
+ def validate_input_col(fn: Callable, input_col: int | tuple | list | None) -> None:
29
+ """
30
+ Check that function used in a callable datapipe works with the input column.
31
+
32
+ This simply ensures that the number of positional arguments matches the size
33
+ of the input column. The function must not contain any non-default
34
+ keyword-only arguments.
35
+
36
+ Examples:
37
+ >>> # xdoctest: +SKIP("Failing on some CI machines")
38
+ >>> def f(a, b, *, c=1):
39
+ >>> return a + b + c
40
+ >>> def f_def(a, b=1, *, c=1):
41
+ >>> return a + b + c
42
+ >>> assert validate_input_col(f, [1, 2])
43
+ >>> assert validate_input_col(f_def, 1)
44
+ >>> assert validate_input_col(f_def, [1, 2])
45
+
46
+ Notes:
47
+ If the function contains variable positional (`inspect.VAR_POSITIONAL`) arguments,
48
+ for example, f(a, *args), the validator will accept any size of input column
49
+ greater than or equal to the number of positional arguments.
50
+ (in this case, 1).
51
+
52
+ Args:
53
+ fn: The function to check.
54
+ input_col: The input column to check.
55
+
56
+ Raises:
57
+ ValueError: If the function is not compatible with the input column.
58
+ """
59
+ try:
60
+ sig = inspect.signature(fn)
61
+ except (
62
+ ValueError
63
+ ): # Signature cannot be inspected, likely it is a built-in fn or written in C
64
+ return
65
+ if isinstance(input_col, (list, tuple)):
66
+ input_col_size = len(input_col)
67
+ else:
68
+ input_col_size = 1
69
+
70
+ pos = []
71
+ var_positional = False
72
+ non_default_kw_only = []
73
+
74
+ for p in sig.parameters.values():
75
+ if p.kind in (
76
+ inspect.Parameter.POSITIONAL_ONLY,
77
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
78
+ ):
79
+ pos.append(p)
80
+ elif p.kind is inspect.Parameter.VAR_POSITIONAL:
81
+ var_positional = True
82
+ elif p.kind is inspect.Parameter.KEYWORD_ONLY:
83
+ if p.default is p.empty:
84
+ non_default_kw_only.append(p)
85
+ else:
86
+ continue
87
+
88
+ if isinstance(fn, functools.partial):
89
+ fn_name = getattr(fn.func, "__name__", repr(fn.func))
90
+ else:
91
+ fn_name = getattr(fn, "__name__", repr(fn))
92
+
93
+ if len(non_default_kw_only) > 0:
94
+ raise ValueError(
95
+ f"The function {fn_name} takes {len(non_default_kw_only)} "
96
+ f"non-default keyword-only parameters, which is not allowed."
97
+ )
98
+
99
+ if len(sig.parameters) < input_col_size:
100
+ if not var_positional:
101
+ raise ValueError(
102
+ f"The function {fn_name} takes {len(sig.parameters)} "
103
+ f"parameters, but {input_col_size} are required."
104
+ )
105
+ else:
106
+ if len(pos) > input_col_size:
107
+ if any(p.default is p.empty for p in pos[input_col_size:]):
108
+ raise ValueError(
109
+ f"The function {fn_name} takes {len(pos)} "
110
+ f"positional parameters, but {input_col_size} are required."
111
+ )
112
+ elif len(pos) < input_col_size:
113
+ if not var_positional:
114
+ raise ValueError(
115
+ f"The function {fn_name} takes {len(pos)} "
116
+ f"positional parameters, but {input_col_size} are required."
117
+ )
118
+
119
+
120
+ def _is_local_fn(fn):
121
+ # Functions or Methods
122
+ if hasattr(fn, "__code__"):
123
+ return fn.__code__.co_flags & inspect.CO_NESTED
124
+ # Callable Objects
125
+ else:
126
+ if hasattr(fn, "__qualname__"):
127
+ return "<locals>" in fn.__qualname__
128
+ fn_type = type(fn)
129
+ if hasattr(fn_type, "__qualname__"):
130
+ return "<locals>" in fn_type.__qualname__
131
+ return False
132
+
133
+
134
+ def _check_unpickable_fn(fn: Callable) -> None:
135
+ """
136
+ Check function is pickable or not.
137
+
138
+ If it is a lambda or local function, a UserWarning will be raised. If it's not a callable function, a TypeError will be raised.
139
+ """
140
+ if not callable(fn):
141
+ raise TypeError(f"A callable function is expected, but {type(fn)} is provided.")
142
+
143
+ # Extract function from partial object
144
+ # Nested partial function is automatically expanded as a single partial object
145
+ if isinstance(fn, functools.partial):
146
+ fn = fn.func
147
+
148
+ # Local function
149
+ if _is_local_fn(fn) and not dill_available():
150
+ warnings.warn(
151
+ "Local function is not supported by pickle, please use "
152
+ "regular python function or functools.partial instead.",
153
+ stacklevel=2,
154
+ )
155
+ return
156
+
157
+ # Lambda function
158
+ if hasattr(fn, "__name__") and fn.__name__ == "<lambda>" and not dill_available():
159
+ warnings.warn(
160
+ "Lambda function is not supported by pickle, please use "
161
+ "regular python function or functools.partial instead.",
162
+ stacklevel=2,
163
+ )
164
+ return
165
+
166
+
167
+ def match_masks(name: str, masks: str | list[str]) -> bool:
168
+ # empty mask matches any input name
169
+ if not masks:
170
+ return True
171
+
172
+ if isinstance(masks, str):
173
+ return fnmatch.fnmatch(name, masks)
174
+
175
+ for mask in masks:
176
+ if fnmatch.fnmatch(name, mask):
177
+ return True
178
+ return False
179
+
180
+
181
+ def get_file_pathnames_from_root(
182
+ root: str,
183
+ masks: str | list[str],
184
+ recursive: bool = False,
185
+ abspath: bool = False,
186
+ non_deterministic: bool = False,
187
+ ) -> Iterable[str]:
188
+ # print out an error message and raise the error out
189
+ def onerror(err: OSError) -> NoReturn:
190
+ warnings.warn(err.filename + " : " + err.strerror, stacklevel=2)
191
+ raise err
192
+
193
+ if os.path.isfile(root):
194
+ path = root
195
+ if abspath:
196
+ path = os.path.abspath(path)
197
+ fname = os.path.basename(path)
198
+ if match_masks(fname, masks):
199
+ yield path
200
+ else:
201
+ for path, dirs, files in os.walk(root, onerror=onerror):
202
+ if abspath:
203
+ path = os.path.abspath(path)
204
+ if not non_deterministic:
205
+ files.sort()
206
+ for f in files:
207
+ if match_masks(f, masks):
208
+ yield os.path.join(path, f)
209
+ if not recursive:
210
+ break
211
+ if not non_deterministic:
212
+ # Note that this is in-place modifying the internal list from `os.walk`
213
+ # This only works because `os.walk` doesn't shallow copy before turn
214
+ # https://github.com/python/cpython/blob/f4c03484da59049eb62a9bf7777b963e2267d187/Lib/os.py#L407
215
+ dirs.sort()
216
+
217
+
218
+ def get_file_binaries_from_pathnames(
219
+ pathnames: Iterable, mode: str, encoding: str | None = None
220
+ ):
221
+ if not isinstance(pathnames, Iterable):
222
+ pathnames = [
223
+ pathnames,
224
+ ]
225
+
226
+ if mode in ("b", "t"):
227
+ mode = "r" + mode
228
+
229
+ for pathname in pathnames:
230
+ if not isinstance(pathname, str):
231
+ raise TypeError(
232
+ f"Expected string type for pathname, but got {type(pathname)}"
233
+ )
234
+ yield pathname, StreamWrapper(open(pathname, mode, encoding=encoding)) # noqa:SIM115
235
+
236
+
237
+ def validate_pathname_binary_tuple(data: tuple[str, IOBase]) -> None:
238
+ if not isinstance(data, tuple):
239
+ raise TypeError(
240
+ f"pathname binary data should be tuple type, but it is type {type(data)}"
241
+ )
242
+ if len(data) != 2:
243
+ raise TypeError(
244
+ f"pathname binary stream tuple length should be 2, but got {len(data)}"
245
+ )
246
+ if not isinstance(data[0], str):
247
+ raise TypeError(
248
+ f"pathname within the tuple should have string type pathname, but it is type {type(data[0])}"
249
+ )
250
+ if not isinstance(data[1], IOBase) and not isinstance(data[1], StreamWrapper):
251
+ raise TypeError(
252
+ f"binary stream within the tuple should have IOBase or"
253
+ f"its subclasses as type, but it is type {type(data[1])}"
254
+ )
255
+
256
+
257
+ # Deprecated function names and its corresponding DataPipe type and kwargs for the `_deprecation_warning` function
258
+ _iter_deprecated_functional_names: dict[str, dict] = {}
259
+ _map_deprecated_functional_names: dict[str, dict] = {}
260
+
261
+
262
+ def _deprecation_warning(
263
+ old_class_name: str,
264
+ *,
265
+ deprecation_version: str,
266
+ removal_version: str,
267
+ old_functional_name: str = "",
268
+ old_argument_name: str = "",
269
+ new_class_name: str = "",
270
+ new_functional_name: str = "",
271
+ new_argument_name: str = "",
272
+ deprecate_functional_name_only: bool = False,
273
+ ) -> None:
274
+ if new_functional_name and not old_functional_name:
275
+ raise ValueError(
276
+ "Old functional API needs to be specified for the deprecation warning."
277
+ )
278
+ if new_argument_name and not old_argument_name:
279
+ raise ValueError(
280
+ "Old argument name needs to be specified for the deprecation warning."
281
+ )
282
+
283
+ if old_functional_name and old_argument_name:
284
+ raise ValueError(
285
+ "Deprecating warning for functional API and argument should be separated."
286
+ )
287
+
288
+ msg = f"`{old_class_name}()`"
289
+ if deprecate_functional_name_only and old_functional_name:
290
+ msg = f"{msg}'s functional API `.{old_functional_name}()` is"
291
+ elif old_functional_name:
292
+ msg = f"{msg} and its functional API `.{old_functional_name}()` are"
293
+ elif old_argument_name:
294
+ msg = f"The argument `{old_argument_name}` of {msg} is"
295
+ else:
296
+ msg = f"{msg} is"
297
+ msg = (
298
+ f"{msg} deprecated since {deprecation_version} and will be removed in {removal_version}."
299
+ f"\nSee https://github.com/pytorch/data/issues/163 for details."
300
+ )
301
+
302
+ if new_class_name or new_functional_name:
303
+ msg = f"{msg}\nPlease use"
304
+ if new_class_name:
305
+ msg = f"{msg} `{new_class_name}()`"
306
+ if new_class_name and new_functional_name:
307
+ msg = f"{msg} or"
308
+ if new_functional_name:
309
+ msg = f"{msg} `.{new_functional_name}()`"
310
+ msg = f"{msg} instead."
311
+
312
+ if new_argument_name:
313
+ msg = f"{msg}\nPlease use `{old_class_name}({new_argument_name}=)` instead."
314
+
315
+ warnings.warn(msg, FutureWarning, stacklevel=2)
316
+
317
+
318
+ class StreamWrapper:
319
+ """
320
+ StreamWrapper is introduced to wrap file handler generated by DataPipe operation like `FileOpener`.
321
+
322
+ StreamWrapper would guarantee the wrapped file handler is closed when it's out of scope.
323
+ """
324
+
325
+ session_streams: dict[Any, int] = {}
326
+ debug_unclosed_streams: bool = False
327
+
328
+ def __init__(self, file_obj, parent_stream=None, name=None) -> None:
329
+ self.file_obj = file_obj
330
+ self.child_counter = 0
331
+ self.parent_stream = parent_stream
332
+ self.close_on_last_child = False
333
+ self.name = name
334
+ self.closed = False
335
+ if parent_stream is not None:
336
+ if not isinstance(parent_stream, StreamWrapper):
337
+ raise RuntimeError(
338
+ f"Parent stream should be StreamWrapper, {type(parent_stream)} was given"
339
+ )
340
+ parent_stream.child_counter += 1
341
+ self.parent_stream = parent_stream
342
+ if StreamWrapper.debug_unclosed_streams:
343
+ StreamWrapper.session_streams[self] = 1
344
+
345
+ @classmethod
346
+ def close_streams(cls, v, depth=0) -> None:
347
+ """Traverse structure and attempts to close all found StreamWrappers on best effort basis."""
348
+ if depth > 10:
349
+ return
350
+ if isinstance(v, StreamWrapper):
351
+ v.close()
352
+ else:
353
+ # Traverse only simple structures
354
+ if isinstance(v, dict):
355
+ for vv in v.values():
356
+ cls.close_streams(vv, depth=depth + 1)
357
+ elif isinstance(v, (list, tuple)):
358
+ for vv in v:
359
+ cls.close_streams(vv, depth=depth + 1)
360
+
361
+ def __getattr__(self, name):
362
+ file_obj = self.__dict__["file_obj"]
363
+ return getattr(file_obj, name)
364
+
365
+ def close(self, *args, **kwargs) -> None:
366
+ if self.closed:
367
+ return
368
+ if StreamWrapper.debug_unclosed_streams:
369
+ del StreamWrapper.session_streams[self]
370
+ if hasattr(self, "parent_stream") and self.parent_stream is not None:
371
+ self.parent_stream.child_counter -= 1
372
+ if (
373
+ not self.parent_stream.child_counter
374
+ and self.parent_stream.close_on_last_child
375
+ ):
376
+ self.parent_stream.close()
377
+ try:
378
+ self.file_obj.close(*args, **kwargs)
379
+ except AttributeError:
380
+ pass
381
+ self.closed = True
382
+
383
+ def autoclose(self) -> None:
384
+ """Automatically close stream when all child streams are closed or if there are none."""
385
+ self.close_on_last_child = True
386
+ if self.child_counter == 0:
387
+ self.close()
388
+
389
+ def __dir__(self):
390
+ attrs = list(self.__dict__.keys()) + list(StreamWrapper.__dict__.keys())
391
+ attrs += dir(self.file_obj)
392
+ return list(set(attrs))
393
+
394
+ def __del__(self) -> None:
395
+ if not self.closed:
396
+ self.close()
397
+
398
+ def __iter__(self):
399
+ yield from self.file_obj
400
+
401
+ def __next__(self):
402
+ return next(self.file_obj)
403
+
404
+ def __repr__(self) -> str:
405
+ if self.name is None:
406
+ return f"StreamWrapper<{self.file_obj!r}>"
407
+ else:
408
+ return f"StreamWrapper<{self.name},{self.file_obj!r}>"
409
+
410
+ def __getstate__(self):
411
+ return self.file_obj
412
+
413
+ def __setstate__(self, obj):
414
+ self.file_obj = obj
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/decoder.py ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ # This file takes partial of the implementation from NVIDIA's webdataset at here:
3
+ # https://github.com/tmbdev/webdataset/blob/master/webdataset/autodecode.py
4
+
5
+ import io
6
+ import json
7
+ import os.path
8
+ import pickle
9
+ import tempfile
10
+
11
+ import torch
12
+ from torch.utils.data.datapipes.utils.common import StreamWrapper
13
+
14
+
15
+ __all__ = [
16
+ "Decoder",
17
+ "ImageHandler",
18
+ "MatHandler",
19
+ "audiohandler",
20
+ "basichandlers",
21
+ "extension_extract_fn",
22
+ "handle_extension",
23
+ "imagehandler",
24
+ "mathandler",
25
+ "videohandler",
26
+ ]
27
+
28
+
29
+ ################################################################
30
+ # handle basic datatypes
31
+ ################################################################
32
+ def basichandlers(extension: str, data):
33
+ """Transforms raw data (byte stream) into python objects.
34
+
35
+ Looks at the extension and loads the data into a python object supporting
36
+ the corresponding extension.
37
+
38
+ Args:
39
+ extension (str): The file extension
40
+ data (byte stream): Data to load into a python object.
41
+
42
+ Returns:
43
+ object: The data loaded into a corresponding python object
44
+ supporting the extension.
45
+
46
+ Example:
47
+ >>> import pickle
48
+ >>> data = pickle.dumps("some data")
49
+ >>> new_data = basichandlers("pickle", data)
50
+ >>> new_data
51
+ some data
52
+
53
+ The transformation of data for extensions are:
54
+ - txt, text, transcript: utf-8 decoded data of str format
55
+ - cls, cls2, class, count, index, inx, id: int
56
+ - json, jsn: json loaded data
57
+ - pickle, pyd: pickle loaded data
58
+ - pt: torch loaded data
59
+ """
60
+
61
+ if extension in "txt text transcript":
62
+ return data.decode("utf-8")
63
+
64
+ if extension in ["cls", "cls2", "class", "count", "index", "inx", "id"]:
65
+ try:
66
+ return int(data)
67
+ except ValueError:
68
+ return None
69
+
70
+ if extension in "json jsn":
71
+ return json.loads(data)
72
+
73
+ if extension in ["pyd", "pickle"]:
74
+ return pickle.loads(data)
75
+
76
+ if extension == "pt":
77
+ stream = io.BytesIO(data)
78
+ return torch.load(stream)
79
+
80
+ # if extension in "ten tb".split():
81
+ # from . import tenbin
82
+ # return tenbin.decode_buffer(data)
83
+
84
+ # if extension in "mp msgpack msg".split():
85
+ # import msgpack
86
+ # return msgpack.unpackb(data)
87
+
88
+ return None
89
+
90
+
91
+ ################################################################
92
+ # handle images
93
+ ################################################################
94
+ imagespecs = {
95
+ "l8": ("numpy", "uint8", "l"),
96
+ "rgb8": ("numpy", "uint8", "rgb"),
97
+ "rgba8": ("numpy", "uint8", "rgba"),
98
+ "l": ("numpy", "float", "l"),
99
+ "rgb": ("numpy", "float", "rgb"),
100
+ "rgba": ("numpy", "float", "rgba"),
101
+ "torchl8": ("torch", "uint8", "l"),
102
+ "torchrgb8": ("torch", "uint8", "rgb"),
103
+ "torchrgba8": ("torch", "uint8", "rgba"),
104
+ "torchl": ("torch", "float", "l"),
105
+ "torchrgb": ("torch", "float", "rgb"),
106
+ "torch": ("torch", "float", "rgb"),
107
+ "torchrgba": ("torch", "float", "rgba"),
108
+ "pill": ("pil", None, "l"),
109
+ "pil": ("pil", None, "rgb"),
110
+ "pilrgb": ("pil", None, "rgb"),
111
+ "pilrgba": ("pil", None, "rgba"),
112
+ }
113
+
114
+
115
+ def handle_extension(extensions, f):
116
+ """
117
+ Return a decoder handler function for the list of extensions.
118
+
119
+ Extensions can be a space separated list of extensions.
120
+ Extensions can contain dots, in which case the corresponding number
121
+ of extension components must be present in the key given to f.
122
+ Comparisons are case insensitive.
123
+ Examples:
124
+ handle_extension("jpg jpeg", my_decode_jpg) # invoked for any file.jpg
125
+ handle_extension("seg.jpg", special_case_jpg) # invoked only for file.seg.jpg
126
+ """
127
+ extensions = extensions.lower().split()
128
+
129
+ def g(key, data):
130
+ extension = key.lower().split(".")
131
+
132
+ for target in extensions:
133
+ target = target.split(".")
134
+ if len(target) > len(extension):
135
+ continue
136
+
137
+ if extension[-len(target) :] == target:
138
+ return f(data)
139
+ return None
140
+
141
+ return g
142
+
143
+
144
+ class ImageHandler:
145
+ """
146
+ Decode image data using the given `imagespec`.
147
+
148
+ The `imagespec` specifies whether the image is decoded
149
+ to numpy/torch/pi, decoded to uint8/float, and decoded
150
+ to l/rgb/rgba:
151
+
152
+ - l8: numpy uint8 l
153
+ - rgb8: numpy uint8 rgb
154
+ - rgba8: numpy uint8 rgba
155
+ - l: numpy float l
156
+ - rgb: numpy float rgb
157
+ - rgba: numpy float rgba
158
+ - torchl8: torch uint8 l
159
+ - torchrgb8: torch uint8 rgb
160
+ - torchrgba8: torch uint8 rgba
161
+ - torchl: torch float l
162
+ - torchrgb: torch float rgb
163
+ - torch: torch float rgb
164
+ - torchrgba: torch float rgba
165
+ - pill: pil None l
166
+ - pil: pil None rgb
167
+ - pilrgb: pil None rgb
168
+ - pilrgba: pil None rgba
169
+ """
170
+
171
+ def __init__(self, imagespec) -> None:
172
+ if imagespec not in list(imagespecs.keys()):
173
+ raise AssertionError(f"unknown image specification: {imagespec}")
174
+ self.imagespec = imagespec.lower()
175
+
176
+ def __call__(self, extension, data):
177
+ if extension.lower() not in ["jpg", "jpeg", "png", "ppm", "pgm", "pbm", "pnm"]:
178
+ return None
179
+
180
+ try:
181
+ import numpy as np
182
+ except ModuleNotFoundError as e:
183
+ raise ModuleNotFoundError(
184
+ "Package `numpy` is required to be installed for default image decoder."
185
+ "Please use `pip install numpy` to install the package"
186
+ ) from e
187
+
188
+ try:
189
+ import PIL.Image
190
+ except ModuleNotFoundError as e:
191
+ raise ModuleNotFoundError(
192
+ "Package `PIL` is required to be installed for default image decoder."
193
+ "Please use `pip install Pillow` to install the package"
194
+ ) from e
195
+
196
+ imagespec = self.imagespec
197
+ atype, etype, mode = imagespecs[imagespec]
198
+
199
+ with io.BytesIO(data) as stream:
200
+ img = PIL.Image.open(stream)
201
+ img.load()
202
+ img = img.convert(mode.upper())
203
+ if atype == "pil":
204
+ return img
205
+ elif atype == "numpy":
206
+ result = np.asarray(img)
207
+ if result.dtype != np.uint8:
208
+ raise AssertionError(
209
+ f"numpy image array should be type uint8, but got {result.dtype}"
210
+ )
211
+ if etype == "uint8":
212
+ return result
213
+ else:
214
+ return result.astype("f") / 255.0
215
+ elif atype == "torch":
216
+ result = np.asarray(img)
217
+ if result.dtype != np.uint8:
218
+ raise AssertionError(
219
+ f"numpy image array should be type uint8, but got {result.dtype}"
220
+ )
221
+
222
+ if etype == "uint8":
223
+ result = np.array(result.transpose(2, 0, 1))
224
+ return torch.tensor(result)
225
+ else:
226
+ result = np.array(result.transpose(2, 0, 1))
227
+ return torch.tensor(result) / 255.0
228
+ return None
229
+
230
+
231
+ def imagehandler(imagespec):
232
+ return ImageHandler(imagespec)
233
+
234
+
235
+ ################################################################
236
+ # torch video
237
+ ################################################################
238
+ def videohandler(extension, data):
239
+ if extension not in [
240
+ "mp4",
241
+ "ogv",
242
+ "mjpeg",
243
+ "avi",
244
+ "mov",
245
+ "h264",
246
+ "mpg",
247
+ "webm",
248
+ "wmv",
249
+ ]:
250
+ return None
251
+
252
+ try:
253
+ import torchvision.io
254
+ except ImportError as e:
255
+ raise ModuleNotFoundError(
256
+ "Package `torchvision` is required to be installed for default video file loader."
257
+ "Please use `pip install torchvision`"
258
+ "to install the package"
259
+ ) from e
260
+
261
+ with tempfile.TemporaryDirectory() as dirname:
262
+ fname = os.path.join(dirname, f"file.{extension}")
263
+ with open(fname, "wb") as stream:
264
+ stream.write(data)
265
+ return torchvision.io.read_video(fname)
266
+
267
+
268
+ ################################################################
269
+ # torchaudio
270
+ ################################################################
271
+ def audiohandler(extension, data):
272
+ if extension not in ["flac", "mp3", "sox", "wav", "m4a", "ogg", "wma"]:
273
+ return None
274
+
275
+ try:
276
+ import torchaudio # type: ignore[import]
277
+ except ImportError as e:
278
+ raise ModuleNotFoundError(
279
+ "Package `torchaudio` is required to be installed for default audio file loader."
280
+ "Please use `pip install torchaudio`"
281
+ "to install the package"
282
+ ) from e
283
+
284
+ with tempfile.TemporaryDirectory() as dirname:
285
+ fname = os.path.join(dirname, f"file.{extension}")
286
+ with open(fname, "wb") as stream:
287
+ stream.write(data)
288
+ return torchaudio.load(fname)
289
+
290
+
291
+ ################################################################
292
+ # mat
293
+ ################################################################
294
+ class MatHandler:
295
+ def __init__(self, **loadmat_kwargs) -> None:
296
+ try:
297
+ import scipy.io as sio
298
+ except ImportError as e:
299
+ raise ModuleNotFoundError(
300
+ "Package `scipy` is required to be installed for mat file."
301
+ "Please use `pip install scipy`"
302
+ "to install the package"
303
+ ) from e
304
+ self.sio = sio
305
+ self.loadmat_kwargs = loadmat_kwargs
306
+
307
+ def __call__(self, extension, data):
308
+ if extension != "mat":
309
+ return None
310
+ with io.BytesIO(data) as stream:
311
+ return self.sio.loadmat(stream, **self.loadmat_kwargs)
312
+
313
+
314
+ def mathandler(**loadmat_kwargs):
315
+ return MatHandler(**loadmat_kwargs)
316
+
317
+
318
+ ################################################################
319
+ # a sample decoder
320
+ ################################################################
321
+ # Extract extension from pathname
322
+ def extension_extract_fn(pathname):
323
+ ext = os.path.splitext(pathname)[1]
324
+ # Remove dot
325
+ if ext:
326
+ ext = ext[1:]
327
+ return ext
328
+
329
+
330
+ class Decoder:
331
+ """
332
+ Decode key/data sets using a list of handlers.
333
+
334
+ For each key/data item, this iterates through the list of
335
+ handlers until some handler returns something other than None.
336
+ """
337
+
338
+ def __init__(self, *handler, key_fn=extension_extract_fn) -> None:
339
+ self.handlers = list(handler) if handler else []
340
+ self.key_fn = key_fn
341
+
342
+ # Insert new handler from the beginning of handlers list to make sure the new
343
+ # handler having the highest priority
344
+ def add_handler(self, *handler) -> None:
345
+ if not handler:
346
+ return
347
+ self.handlers = list(handler) + self.handlers
348
+
349
+ @staticmethod
350
+ def _is_stream_handle(data):
351
+ obj_to_check = data.file_obj if isinstance(data, StreamWrapper) else data
352
+ return isinstance(obj_to_check, (io.BufferedIOBase, io.RawIOBase))
353
+
354
+ def decode1(self, key, data):
355
+ if not data:
356
+ return data
357
+
358
+ # if data is a stream handle, we need to read all the content before decoding
359
+ if Decoder._is_stream_handle(data):
360
+ ds = data
361
+ # The behavior of .read can differ between streams (e.g. HTTPResponse), hence this is used instead
362
+ data = b"".join(data)
363
+ ds.close()
364
+
365
+ for f in self.handlers:
366
+ result = f(key, data)
367
+ if result is not None:
368
+ return result
369
+ return data
370
+
371
+ def decode(self, data):
372
+ result = {}
373
+ # single data tuple(pathname, data stream)
374
+ if isinstance(data, tuple):
375
+ data = [data]
376
+
377
+ if data is not None:
378
+ for k, v in data:
379
+ # TODO: xinyu, figure out why Nvidia do this?
380
+ if k[0] == "_":
381
+ if isinstance(v, bytes):
382
+ v = v.decode("utf-8")
383
+ result[k] = v
384
+ continue
385
+ result[k] = self.decode1(self.key_fn(k), v)
386
+ return result
387
+
388
+ def __call__(self, data):
389
+ return self.decode(data)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/datapipes/utils/snapshot.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from torch.utils.data.datapipes._hook_iterator import _SnapshotState
3
+ from torch.utils.data.datapipes.datapipe import IterDataPipe
4
+ from torch.utils.data.graph_settings import apply_random_seed
5
+
6
+
7
+ # TODO: Caveats
8
+ # 1. Caller (either the ReadingService or DataLoader) must pass in the initial RNG
9
+ # 2. `in_batch_shuffle` and `bucketbatch` are not compatible with this because they currently
10
+ # lack the option to `set_seed`.
11
+ def _simple_graph_snapshot_restoration(
12
+ datapipe: IterDataPipe, n_iterations: int, rng=None
13
+ ) -> None:
14
+ r"""
15
+ Fast-forward the given DataPipe and its parents by ``n_iterations``, re-doing computations to restore a snapshot.
16
+
17
+ For instance, applying this function to the final DataPipe of a graph will restore the snapshot
18
+ (via fast-forward) every DataPipe within the graph.
19
+
20
+ After you deserialize a DataPipe, you can use its `_number_of_samples_yielded` attribute as the input
21
+ to this function to forward the DataPipe.
22
+
23
+ A DataPipe cannot be restored twice in a row unless there is an iteration started between the restoration
24
+ attempts.
25
+
26
+ Note:
27
+ This is the simplest but least efficient way to fast-forward a DataPipe. Usage of other fast-forwarding
28
+ methods (custom ones if necessary) are recommended.
29
+
30
+ Args:
31
+ datapipe: IterDataPipe to be fast-forwarded
32
+ n_iterations: number of iterations to fast-forward
33
+ rng: ``Optional[torch.Generator]``. If not ``None``, this RNG will be used for shuffling. The generator
34
+ should be in its `initial` state as it was first passed into ``DataLoader`` or ``ReadingService``.
35
+ """
36
+ if datapipe._snapshot_state == _SnapshotState.Restored:
37
+ raise RuntimeError(
38
+ "Snapshot restoration cannot be applied. You can only restore simple snapshot to the graph "
39
+ "if your graph has not been restored."
40
+ )
41
+
42
+ # For this snapshot restoration function, we want the DataPipe to be at its initial state prior to
43
+ # simple fast-forwarding. Therefore, we need to call `reset` twice, because if `SnapshotState` is `Restored`,
44
+ # the first reset will not actually reset.
45
+ datapipe.reset() # This ensures `SnapshotState` is `Iterating` by this point, even if it was `Restored`.
46
+ # pyrefly: ignore [bad-argument-type]
47
+ apply_random_seed(datapipe, rng)
48
+
49
+ remainder = n_iterations
50
+ it = iter(datapipe) # This always reset the DataPipe if it hasn't already.
51
+ while remainder > 0:
52
+ try:
53
+ next(it)
54
+ remainder -= 1
55
+ except StopIteration as e:
56
+ raise RuntimeError(
57
+ f"Fast-forward {datapipe} by {n_iterations} iterations "
58
+ "exceeds the number of samples available."
59
+ ) from e
60
+ datapipe._fast_forward_iterator = it
61
+ # While the DataPipe has `_fast_forward_iterator`, `next()` will get result from there instead of elsewhere.
62
+
63
+ # This will prevent the DataPipe from resetting in the `iter()` call
64
+ # If another DataPipe is consuming it, it won't have to start over again
65
+ datapipe._snapshot_state = _SnapshotState.Restored
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/dataset.py ADDED
@@ -0,0 +1,511 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import bisect
3
+ import itertools
4
+ import math
5
+ import warnings
6
+ from collections.abc import Sequence
7
+
8
+ # UP006 wants 'Iterable' to be imported from collections.abc but it needs to
9
+ # stay from typing for now due to BC concerns. In particular several internal
10
+ # targets fail to typecheck with:
11
+ # TypeError: Cannot create a consistent method resolution order (MRO) for
12
+ # bases Iterable, Generic
13
+ from typing import cast, Generic, Iterable, TypeVar # noqa: UP035
14
+ from typing_extensions import deprecated
15
+
16
+ # No 'default_generator' in torch/__init__.pyi
17
+ from torch import default_generator, Generator, randperm, Tensor
18
+
19
+
20
+ __all__ = [
21
+ "Dataset",
22
+ "IterableDataset",
23
+ "TensorDataset",
24
+ "StackDataset",
25
+ "ConcatDataset",
26
+ "ChainDataset",
27
+ "Subset",
28
+ "random_split",
29
+ ]
30
+
31
+
32
+ _T = TypeVar("_T")
33
+ _T_co = TypeVar("_T_co", covariant=True)
34
+ _T_dict = dict[str, _T_co]
35
+ _T_tuple = tuple[_T_co, ...]
36
+ _T_stack = TypeVar("_T_stack", _T_tuple, _T_dict)
37
+
38
+
39
+ class Dataset(Generic[_T_co]):
40
+ r"""An abstract class representing a :class:`Dataset`.
41
+
42
+ All datasets that represent a map from keys to data samples should subclass
43
+ it. All subclasses should overwrite :meth:`__getitem__`, supporting fetching a
44
+ data sample for a given key. Subclasses could also optionally overwrite
45
+ :meth:`__len__`, which is expected to return the size of the dataset by many
46
+ :class:`~torch.utils.data.Sampler` implementations and the default options
47
+ of :class:`~torch.utils.data.DataLoader`. Subclasses could also
48
+ optionally implement :meth:`__getitems__`, for speedup batched samples
49
+ loading. This method accepts list of indices of samples of batch and returns
50
+ list of samples.
51
+
52
+ .. note::
53
+ :class:`~torch.utils.data.DataLoader` by default constructs an index
54
+ sampler that yields integral indices. To make it work with a map-style
55
+ dataset with non-integral indices/keys, a custom sampler must be provided.
56
+ """
57
+
58
+ def __getitem__(self, index) -> _T_co:
59
+ raise NotImplementedError("Subclasses of Dataset should implement __getitem__.")
60
+
61
+ # def __getitems__(self, indices: List) -> List[_T_co]:
62
+ # Not implemented to prevent false-positives in fetcher check in
63
+ # torch.utils.data._utils.fetch._MapDatasetFetcher
64
+
65
+ def __add__(self, other: "Dataset[_T_co]") -> "ConcatDataset[_T_co]":
66
+ return ConcatDataset([self, other])
67
+
68
+ # No `def __len__(self)` default?
69
+ # See NOTE [ Lack of Default `__len__` in Python Abstract Base Classes ]
70
+ # in pytorch/torch/utils/data/sampler.py
71
+
72
+
73
+ class IterableDataset(Dataset[_T_co], Iterable[_T_co]):
74
+ r"""An iterable Dataset.
75
+
76
+ All datasets that represent an iterable of data samples should subclass it.
77
+ Such form of datasets is particularly useful when data come from a stream.
78
+
79
+ All subclasses should overwrite :meth:`__iter__`, which would return an
80
+ iterator of samples in this dataset.
81
+
82
+ When a subclass is used with :class:`~torch.utils.data.DataLoader`, each
83
+ item in the dataset will be yielded from the :class:`~torch.utils.data.DataLoader`
84
+ iterator. When :attr:`num_workers > 0`, each worker process will have a
85
+ different copy of the dataset object, so it is often desired to configure
86
+ each copy independently to avoid having duplicate data returned from the
87
+ workers. :func:`~torch.utils.data.get_worker_info`, when called in a worker
88
+ process, returns information about the worker. It can be used in either the
89
+ dataset's :meth:`__iter__` method or the :class:`~torch.utils.data.DataLoader` 's
90
+ :attr:`worker_init_fn` option to modify each copy's behavior.
91
+
92
+ Example 1: splitting workload across all workers in :meth:`__iter__`::
93
+
94
+ >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_DATALOADER)
95
+ >>> # xdoctest: +SKIP("Fails on MacOS12")
96
+ >>> class MyIterableDataset(torch.utils.data.IterableDataset):
97
+ ... def __init__(self, start, end):
98
+ ... super(MyIterableDataset).__init__()
99
+ ... assert end > start, "this example only works with end >= start"
100
+ ... self.start = start
101
+ ... self.end = end
102
+ ...
103
+ ... def __iter__(self):
104
+ ... worker_info = torch.utils.data.get_worker_info()
105
+ ... if worker_info is None: # single-process data loading, return the full iterator
106
+ ... iter_start = self.start
107
+ ... iter_end = self.end
108
+ ... else: # in a worker process
109
+ ... # split workload
110
+ ... per_worker = int(math.ceil((self.end - self.start) / float(worker_info.num_workers)))
111
+ ... worker_id = worker_info.id
112
+ ... iter_start = self.start + worker_id * per_worker
113
+ ... iter_end = min(iter_start + per_worker, self.end)
114
+ ... return iter(range(iter_start, iter_end))
115
+ ...
116
+ >>> # should give same set of data as range(3, 7), i.e., [3, 4, 5, 6].
117
+ >>> ds = MyIterableDataset(start=3, end=7)
118
+
119
+ >>> # Single-process loading
120
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=0)))
121
+ [tensor([3]), tensor([4]), tensor([5]), tensor([6])]
122
+
123
+ >>> # xdoctest: +REQUIRES(POSIX)
124
+ >>> # Multi-process loading with two worker processes
125
+ >>> # Worker 0 fetched [3, 4]. Worker 1 fetched [5, 6].
126
+ >>> # xdoctest: +IGNORE_WANT("non deterministic")
127
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=2)))
128
+ [tensor([3]), tensor([5]), tensor([4]), tensor([6])]
129
+
130
+ >>> # With even more workers
131
+ >>> # xdoctest: +IGNORE_WANT("non deterministic")
132
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=12)))
133
+ [tensor([3]), tensor([5]), tensor([4]), tensor([6])]
134
+
135
+ Example 2: splitting workload across all workers using :attr:`worker_init_fn`::
136
+
137
+ >>> # xdoctest: +REQUIRES(env:TORCH_DOCTEST_DATALOADER)
138
+ >>> class MyIterableDataset(torch.utils.data.IterableDataset):
139
+ ... def __init__(self, start, end):
140
+ ... super(MyIterableDataset).__init__()
141
+ ... assert end > start, "this example only works with end >= start"
142
+ ... self.start = start
143
+ ... self.end = end
144
+ ...
145
+ ... def __iter__(self):
146
+ ... return iter(range(self.start, self.end))
147
+ ...
148
+ >>> # should give same set of data as range(3, 7), i.e., [3, 4, 5, 6].
149
+ >>> ds = MyIterableDataset(start=3, end=7)
150
+
151
+ >>> # Single-process loading
152
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=0)))
153
+ [3, 4, 5, 6]
154
+ >>>
155
+ >>> # Directly doing multi-process loading yields duplicate data
156
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=2)))
157
+ [3, 3, 4, 4, 5, 5, 6, 6]
158
+
159
+ >>> # Define a `worker_init_fn` that configures each dataset copy differently
160
+ >>> def worker_init_fn(worker_id):
161
+ ... worker_info = torch.utils.data.get_worker_info()
162
+ ... dataset = worker_info.dataset # the dataset copy in this worker process
163
+ ... overall_start = dataset.start
164
+ ... overall_end = dataset.end
165
+ ... # configure the dataset to only process the split workload
166
+ ... per_worker = int(math.ceil((overall_end - overall_start) / float(worker_info.num_workers)))
167
+ ... worker_id = worker_info.id
168
+ ... dataset.start = overall_start + worker_id * per_worker
169
+ ... dataset.end = min(dataset.start + per_worker, overall_end)
170
+ ...
171
+
172
+ >>> # Mult-process loading with the custom `worker_init_fn`
173
+ >>> # Worker 0 fetched [3, 4]. Worker 1 fetched [5, 6].
174
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=2, worker_init_fn=worker_init_fn)))
175
+ [3, 5, 4, 6]
176
+
177
+ >>> # With even more workers
178
+ >>> print(list(torch.utils.data.DataLoader(ds, num_workers=12, worker_init_fn=worker_init_fn)))
179
+ [3, 4, 5, 6]
180
+ """
181
+
182
+ def __add__(self, other: Dataset[_T_co]):
183
+ return ChainDataset([self, other])
184
+
185
+ # No `def __len__(self)` default? Subclasses raise `TypeError` when needed.
186
+ # See NOTE [ Lack of Default `__len__` in Python Abstract Base Classes ]
187
+
188
+
189
+ class TensorDataset(Dataset[tuple[Tensor, ...]]):
190
+ r"""Dataset wrapping tensors.
191
+
192
+ Each sample will be retrieved by indexing tensors along the first dimension.
193
+
194
+ Args:
195
+ *tensors (Tensor): tensors that have the same size of the first dimension.
196
+ """
197
+
198
+ tensors: tuple[Tensor, ...]
199
+
200
+ def __init__(self, *tensors: Tensor) -> None:
201
+ if any(tensors[0].size(0) != tensor.size(0) for tensor in tensors):
202
+ raise AssertionError("Size mismatch between tensors")
203
+ self.tensors = tensors
204
+
205
+ def __getitem__(self, index):
206
+ return tuple(tensor[index] for tensor in self.tensors)
207
+
208
+ def __len__(self) -> int:
209
+ return self.tensors[0].size(0)
210
+
211
+
212
+ class StackDataset(Dataset[_T_stack]):
213
+ r"""Dataset as a stacking of multiple datasets.
214
+
215
+ This class is useful to assemble different parts of complex input data, given as datasets.
216
+
217
+ Example:
218
+ >>> # xdoctest: +SKIP
219
+ >>> images = ImageDataset()
220
+ >>> texts = TextDataset()
221
+ >>> tuple_stack = StackDataset(images, texts)
222
+ >>> tuple_stack[0] == (images[0], texts[0])
223
+ >>> dict_stack = StackDataset(image=images, text=texts)
224
+ >>> dict_stack[0] == {"image": images[0], "text": texts[0]}
225
+
226
+ Args:
227
+ *args (Dataset): Datasets for stacking returned as tuple.
228
+ **kwargs (Dataset): Datasets for stacking returned as dict.
229
+ """
230
+
231
+ datasets: tuple | dict
232
+
233
+ def __init__(self, *args: Dataset[_T_co], **kwargs: Dataset[_T_co]) -> None:
234
+ if args:
235
+ if kwargs:
236
+ raise ValueError(
237
+ "Supported either ``tuple``- (via ``args``) or"
238
+ "``dict``- (via ``kwargs``) like input/output, but both types are given."
239
+ )
240
+ self._length = len(args[0]) # type: ignore[arg-type]
241
+ if any(self._length != len(dataset) for dataset in args): # type: ignore[arg-type]
242
+ raise ValueError("Size mismatch between datasets")
243
+ self.datasets = args
244
+ elif kwargs:
245
+ tmp = list(kwargs.values())
246
+ self._length = len(tmp[0]) # type: ignore[arg-type]
247
+ if any(self._length != len(dataset) for dataset in tmp): # type: ignore[arg-type]
248
+ raise ValueError("Size mismatch between datasets")
249
+ self.datasets = kwargs
250
+ else:
251
+ raise ValueError("At least one dataset should be passed")
252
+
253
+ def __getitem__(self, index):
254
+ if isinstance(self.datasets, dict):
255
+ return {k: dataset[index] for k, dataset in self.datasets.items()}
256
+ return tuple(dataset[index] for dataset in self.datasets)
257
+
258
+ def __getitems__(self, indices: list):
259
+ # add batched sampling support when parent datasets supports it.
260
+ if isinstance(self.datasets, dict):
261
+ dict_batch: list[_T_dict] = [{} for _ in indices]
262
+ for k, dataset in self.datasets.items():
263
+ if callable(getattr(dataset, "__getitems__", None)):
264
+ items = dataset.__getitems__(indices) # type: ignore[attr-defined]
265
+ if len(items) != len(indices):
266
+ raise ValueError(
267
+ "Nested dataset's output size mismatch."
268
+ f" Expected {len(indices)}, got {len(items)}"
269
+ )
270
+ for data, d_sample in zip(items, dict_batch, strict=True):
271
+ d_sample[k] = data
272
+ else:
273
+ for idx, d_sample in zip(indices, dict_batch, strict=True):
274
+ d_sample[k] = dataset[idx]
275
+ return dict_batch
276
+
277
+ # tuple data
278
+ list_batch: list[list] = [[] for _ in indices]
279
+ for dataset in self.datasets:
280
+ if callable(getattr(dataset, "__getitems__", None)):
281
+ items = dataset.__getitems__(indices) # type: ignore[attr-defined]
282
+ if len(items) != len(indices):
283
+ raise ValueError(
284
+ "Nested dataset's output size mismatch."
285
+ f" Expected {len(indices)}, got {len(items)}"
286
+ )
287
+ for data, t_sample in zip(items, list_batch, strict=True):
288
+ t_sample.append(data)
289
+ else:
290
+ for idx, t_sample in zip(indices, list_batch, strict=True):
291
+ t_sample.append(dataset[idx])
292
+ tuple_batch: list[_T_tuple] = [tuple(sample) for sample in list_batch]
293
+ return tuple_batch
294
+
295
+ def __len__(self) -> int:
296
+ return self._length
297
+
298
+
299
+ class ConcatDataset(Dataset[_T_co]):
300
+ r"""Dataset as a concatenation of multiple datasets.
301
+
302
+ This class is useful to assemble different existing datasets.
303
+
304
+ Args:
305
+ datasets (sequence): List of datasets to be concatenated
306
+ """
307
+
308
+ datasets: list[Dataset[_T_co]]
309
+ cumulative_sizes: list[int]
310
+
311
+ @staticmethod
312
+ def cumsum(sequence):
313
+ r, s = [], 0
314
+ for e in sequence:
315
+ l = len(e)
316
+ r.append(l + s)
317
+ s += l
318
+ return r
319
+
320
+ def __init__(self, datasets: Iterable[Dataset]) -> None:
321
+ super().__init__()
322
+ self.datasets = list(datasets)
323
+ if len(self.datasets) == 0:
324
+ raise AssertionError("datasets should not be an empty iterable")
325
+ for d in self.datasets:
326
+ if isinstance(d, IterableDataset):
327
+ raise AssertionError("ConcatDataset does not support IterableDataset")
328
+ self.cumulative_sizes = self.cumsum(self.datasets)
329
+
330
+ def __len__(self) -> int:
331
+ return self.cumulative_sizes[-1]
332
+
333
+ def __getitem__(self, idx):
334
+ if idx < 0:
335
+ if -idx > len(self):
336
+ raise ValueError(
337
+ "absolute value of index should not exceed dataset length"
338
+ )
339
+ idx = len(self) + idx
340
+ dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx)
341
+ if dataset_idx == 0:
342
+ sample_idx = idx
343
+ else:
344
+ sample_idx = idx - self.cumulative_sizes[dataset_idx - 1]
345
+ return self.datasets[dataset_idx][sample_idx]
346
+
347
+ @property
348
+ @deprecated(
349
+ "`cummulative_sizes` attribute is renamed to `cumulative_sizes`",
350
+ category=FutureWarning,
351
+ )
352
+ def cummulative_sizes(self):
353
+ return self.cumulative_sizes
354
+
355
+
356
+ class ChainDataset(IterableDataset):
357
+ r"""Dataset for chaining multiple :class:`IterableDataset` s.
358
+
359
+ This class is useful to assemble different existing dataset streams. The
360
+ chaining operation is done on-the-fly, so concatenating large-scale
361
+ datasets with this class will be efficient.
362
+
363
+ Args:
364
+ datasets (iterable of IterableDataset): datasets to be chained together
365
+ """
366
+
367
+ def __init__(self, datasets: Iterable[Dataset]) -> None:
368
+ super().__init__()
369
+ self.datasets = datasets
370
+
371
+ def __iter__(self):
372
+ for d in self.datasets:
373
+ if not isinstance(d, IterableDataset):
374
+ raise AssertionError("ChainDataset only supports IterableDataset")
375
+ yield from d
376
+
377
+ def __len__(self) -> int:
378
+ total = 0
379
+ for d in self.datasets:
380
+ if not isinstance(d, IterableDataset):
381
+ raise AssertionError("ChainDataset only supports IterableDataset")
382
+ total += len(d) # type: ignore[arg-type]
383
+ return total
384
+
385
+
386
+ class Subset(Dataset[_T_co]):
387
+ r"""
388
+ Subset of a dataset at specified indices.
389
+
390
+ .. note::
391
+ When subclassing `Subset` and overriding `__getitem__`, you **must** also
392
+ override `__getitems__` to ensure `DataLoader` works correctly with your
393
+ custom logic. If you override only `__getitem__`, a `NotImplementedError`
394
+ will be raised when using `DataLoader`.
395
+
396
+ A simple implementation of `__getitems__` can delegate to `__getitem__`:
397
+
398
+ .. code-block:: python
399
+
400
+ def __getitems__(self, indices):
401
+ return [self.__getitem__(idx) for idx in indices]
402
+
403
+ For better performance, consider implementing batch-aware logic in
404
+ `__getitems__` instead of calling `__getitem__` multiple times.
405
+
406
+ Args:
407
+ dataset (Dataset): The whole Dataset
408
+ indices (sequence): Indices in the whole set selected for subset
409
+ """
410
+
411
+ dataset: Dataset[_T_co]
412
+ indices: Sequence[int]
413
+
414
+ def __init__(self, dataset: Dataset[_T_co], indices: Sequence[int]) -> None:
415
+ self.dataset = dataset
416
+ self.indices = indices
417
+
418
+ # Check if __getitem__ is overridden but __getitems__ is not
419
+ if (
420
+ type(self).__getitem__ is not Subset.__getitem__
421
+ and type(self).__getitems__ is Subset.__getitems__
422
+ ):
423
+ raise NotImplementedError(
424
+ f"{type(self).__name__} overrides __getitem__ but not __getitems__. "
425
+ "When subclassing Subset and overriding __getitem__, you must also override "
426
+ "__getitems__ to ensure DataLoader works correctly with your custom logic. "
427
+ "A simple implementation:\n\n"
428
+ "def __getitems__(self, indices):\n"
429
+ " return [self.__getitem__(idx) for idx in indices]"
430
+ )
431
+
432
+ def __getitem__(self, idx):
433
+ if isinstance(idx, list):
434
+ return self.dataset[[self.indices[i] for i in idx]]
435
+ return self.dataset[self.indices[idx]]
436
+
437
+ def __getitems__(self, indices: list[int]) -> list[_T_co]:
438
+ # add batched sampling support when parent dataset supports it.
439
+ # see torch.utils.data._utils.fetch._MapDatasetFetcher
440
+ if callable(getattr(self.dataset, "__getitems__", None)):
441
+ return self.dataset.__getitems__([self.indices[idx] for idx in indices]) # type: ignore[attr-defined]
442
+ else:
443
+ return [self.dataset[self.indices[idx]] for idx in indices]
444
+
445
+ def __len__(self) -> int:
446
+ return len(self.indices)
447
+
448
+
449
+ def random_split(
450
+ dataset: Dataset[_T],
451
+ lengths: Sequence[int | float],
452
+ generator: Generator | None = default_generator,
453
+ ) -> list[Subset[_T]]:
454
+ r"""
455
+ Randomly split a dataset into non-overlapping new datasets of given lengths.
456
+
457
+ If a list of fractions that sum up to 1 is given,
458
+ the lengths will be computed automatically as
459
+ floor(frac * len(dataset)) for each fraction provided.
460
+
461
+ After computing the lengths, if there are any remainders, 1 count will be
462
+ distributed in round-robin fashion to the lengths
463
+ until there are no remainders left.
464
+
465
+ Optionally fix the generator for reproducible results, e.g.:
466
+
467
+ Example:
468
+ >>> # xdoctest: +SKIP
469
+ >>> generator1 = torch.Generator().manual_seed(42)
470
+ >>> generator2 = torch.Generator().manual_seed(42)
471
+ >>> random_split(range(10), [3, 7], generator=generator1)
472
+ >>> random_split(range(30), [0.3, 0.3, 0.4], generator=generator2)
473
+
474
+ Args:
475
+ dataset (Dataset): Dataset to be split
476
+ lengths (sequence): lengths or fractions of splits to be produced
477
+ generator (Generator): Generator used for the random permutation.
478
+ """
479
+ if math.isclose(sum(lengths), 1) and sum(lengths) <= 1:
480
+ subset_lengths: list[int] = []
481
+ for i, frac in enumerate(lengths):
482
+ if frac < 0 or frac > 1:
483
+ raise ValueError(f"Fraction at index {i} is not between 0 and 1")
484
+ n_items_in_split = math.floor(len(dataset) * frac) # type: ignore[arg-type]
485
+ subset_lengths.append(n_items_in_split)
486
+ remainder = len(dataset) - sum(subset_lengths) # type: ignore[arg-type]
487
+ # add 1 to all the lengths in round-robin fashion until the remainder is 0
488
+ for i in range(remainder):
489
+ idx_to_add_at = i % len(subset_lengths)
490
+ subset_lengths[idx_to_add_at] += 1
491
+ lengths = subset_lengths
492
+ for i, length in enumerate(lengths):
493
+ if length == 0:
494
+ warnings.warn(
495
+ f"Length of split at index {i} is 0. "
496
+ f"This might result in an empty dataset.",
497
+ stacklevel=2,
498
+ )
499
+
500
+ # Cannot verify that dataset is Sized
501
+ if sum(lengths) != len(dataset): # type: ignore[arg-type]
502
+ raise ValueError(
503
+ "Sum of input lengths does not equal the length of the input dataset!"
504
+ )
505
+
506
+ indices = randperm(sum(lengths), generator=generator).tolist() # type: ignore[arg-type, call-overload]
507
+ lengths = cast(Sequence[int], lengths)
508
+ return [
509
+ Subset(dataset, indices[offset - length : offset])
510
+ for offset, length in zip(itertools.accumulate(lengths), lengths, strict=True)
511
+ ]
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/distributed.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from collections.abc import Iterator
3
+ from typing import TypeVar
4
+
5
+ import torch
6
+ import torch.distributed as dist
7
+ from torch.utils.data.dataset import Dataset
8
+ from torch.utils.data.sampler import Sampler
9
+
10
+
11
+ __all__ = ["DistributedSampler"]
12
+
13
+
14
+ _T_co = TypeVar("_T_co", covariant=True)
15
+
16
+
17
+ class DistributedSampler(Sampler[_T_co]):
18
+ r"""Sampler that restricts data loading to a subset of the dataset.
19
+
20
+ It is especially useful in conjunction with
21
+ :class:`torch.nn.parallel.DistributedDataParallel`. In such a case, each
22
+ process can pass a :class:`~torch.utils.data.DistributedSampler` instance as a
23
+ :class:`~torch.utils.data.DataLoader` sampler, and load a subset of the
24
+ original dataset that is exclusive to it.
25
+
26
+ .. note::
27
+ Dataset is assumed to be of constant size and that any instance of it always
28
+ returns the same elements in the same order.
29
+
30
+ Args:
31
+ dataset: Dataset used for sampling.
32
+ num_replicas (int, optional): Number of processes participating in
33
+ distributed training. By default, :attr:`world_size` is retrieved from the
34
+ current distributed group.
35
+ rank (int, optional): Rank of the current process within :attr:`num_replicas`.
36
+ By default, :attr:`rank` is retrieved from the current distributed
37
+ group.
38
+ shuffle (bool, optional): If ``True`` (default), sampler will shuffle the
39
+ indices.
40
+ seed (int, optional): random seed used to shuffle the sampler if
41
+ :attr:`shuffle=True`. This number should be identical across all
42
+ processes in the distributed group. Default: ``0``.
43
+ drop_last (bool, optional): if ``True``, then the sampler will drop the
44
+ tail of the data to make it evenly divisible across the number of
45
+ replicas. If ``False``, the sampler will add extra indices to make
46
+ the data evenly divisible across the replicas. Default: ``False``.
47
+
48
+ .. warning::
49
+ In distributed mode, calling the :meth:`set_epoch` method at
50
+ the beginning of each epoch **before** creating the :class:`DataLoader` iterator
51
+ is necessary to make shuffling work properly across multiple epochs. Otherwise,
52
+ the same ordering will be always used.
53
+
54
+ Example::
55
+
56
+ >>> # xdoctest: +SKIP
57
+ >>> sampler = DistributedSampler(dataset) if is_distributed else None
58
+ >>> loader = DataLoader(dataset, shuffle=(sampler is None),
59
+ ... sampler=sampler)
60
+ >>> for epoch in range(start_epoch, n_epochs):
61
+ ... if is_distributed:
62
+ ... sampler.set_epoch(epoch)
63
+ ... train(loader)
64
+ """
65
+
66
+ def __init__(
67
+ self,
68
+ dataset: Dataset,
69
+ num_replicas: int | None = None,
70
+ rank: int | None = None,
71
+ shuffle: bool = True,
72
+ seed: int = 0,
73
+ drop_last: bool = False,
74
+ ) -> None:
75
+ if num_replicas is None:
76
+ if not dist.is_available():
77
+ raise RuntimeError("Requires distributed package to be available")
78
+ num_replicas = dist.get_world_size()
79
+ if rank is None:
80
+ if not dist.is_available():
81
+ raise RuntimeError("Requires distributed package to be available")
82
+ rank = dist.get_rank()
83
+ if rank >= num_replicas or rank < 0:
84
+ raise ValueError(
85
+ f"Invalid rank {rank}, rank should be in the interval [0, {num_replicas - 1}]"
86
+ )
87
+ self.dataset = dataset
88
+ self.num_replicas = num_replicas
89
+ self.rank = rank
90
+ self.epoch = 0
91
+ self.drop_last = drop_last
92
+ # If the dataset length is evenly divisible by # of replicas, then there
93
+ # is no need to drop any data, since the dataset will be split equally.
94
+ if self.drop_last and len(self.dataset) % self.num_replicas != 0: # type: ignore[arg-type]
95
+ # Split to nearest available length that is evenly divisible.
96
+ # This is to ensure each rank receives the same amount of data when
97
+ # using this Sampler.
98
+ self.num_samples = math.ceil(
99
+ (len(self.dataset) - self.num_replicas) / self.num_replicas # type: ignore[arg-type]
100
+ )
101
+ else:
102
+ self.num_samples = math.ceil(len(self.dataset) / self.num_replicas) # type: ignore[arg-type]
103
+ self.total_size = self.num_samples * self.num_replicas
104
+ self.shuffle = shuffle
105
+ self.seed = seed
106
+
107
+ def __iter__(self) -> Iterator[_T_co]:
108
+ if self.shuffle:
109
+ # deterministically shuffle based on epoch and seed
110
+ g = torch.Generator()
111
+ g.manual_seed(self.seed + self.epoch)
112
+ indices = torch.randperm(len(self.dataset), generator=g).tolist() # type: ignore[arg-type]
113
+ else:
114
+ indices = list(range(len(self.dataset))) # type: ignore[arg-type]
115
+
116
+ if not self.drop_last:
117
+ # add extra samples to make it evenly divisible
118
+ padding_size = self.total_size - len(indices)
119
+ if padding_size <= len(indices):
120
+ indices += indices[:padding_size]
121
+ else:
122
+ indices += (indices * math.ceil(padding_size / len(indices)))[
123
+ :padding_size
124
+ ]
125
+ else:
126
+ # remove tail of data to make it evenly divisible.
127
+ indices = indices[: self.total_size]
128
+ if len(indices) != self.total_size:
129
+ raise AssertionError(
130
+ f"Number of indices ({len(indices)}) does not match total_size ({self.total_size})"
131
+ )
132
+
133
+ # subsample
134
+ indices = indices[self.rank : self.total_size : self.num_replicas]
135
+ if len(indices) != self.num_samples:
136
+ raise AssertionError(
137
+ f"Number of subsampled indices ({len(indices)}) does not match num_samples ({self.num_samples})"
138
+ )
139
+
140
+ # pyrefly: ignore [bad-return]
141
+ return iter(indices)
142
+
143
+ def __len__(self) -> int:
144
+ return self.num_samples
145
+
146
+ def set_epoch(self, epoch: int) -> None:
147
+ r"""
148
+ Set the epoch for this sampler.
149
+
150
+ When :attr:`shuffle=True`, this ensures all replicas
151
+ use a different random ordering for each epoch. Otherwise, the next iteration of this
152
+ sampler will yield the same ordering.
153
+
154
+ Args:
155
+ epoch (int): Epoch number.
156
+ """
157
+ self.epoch = epoch
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/graph.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import io
3
+ import pickle
4
+ import warnings
5
+ from collections.abc import Collection
6
+
7
+ from torch.utils._import_utils import dill_available
8
+ from torch.utils.data.datapipes.datapipe import IterDataPipe, MapDataPipe
9
+
10
+
11
+ __all__ = ["traverse", "traverse_dps"]
12
+
13
+ DataPipe = IterDataPipe | MapDataPipe
14
+ # pyrefly: ignore [invalid-type-alias]
15
+ DataPipeGraph = dict[int, tuple[DataPipe, "DataPipeGraph"]]
16
+
17
+
18
+ def _stub_unpickler() -> str:
19
+ return "STUB"
20
+
21
+
22
+ # TODO(VitalyFedyunin): Make sure it works without dill module installed
23
+ def _list_connected_datapipes(
24
+ scan_obj: DataPipe, only_datapipe: bool, cache: set[int]
25
+ ) -> list[DataPipe]:
26
+ f = io.BytesIO()
27
+ p = pickle.Pickler(
28
+ f
29
+ ) # Not going to work for lambdas, but dill infinite loops on typing and can't be used as is
30
+ if dill_available():
31
+ from dill import Pickler as dill_Pickler
32
+
33
+ d = dill_Pickler(f)
34
+ else:
35
+ d = None
36
+
37
+ captured_connections = []
38
+
39
+ def getstate_hook(ori_state):
40
+ state = None
41
+ if isinstance(ori_state, dict):
42
+ state = {}
43
+ for k, v in ori_state.items():
44
+ if isinstance(v, (IterDataPipe, MapDataPipe, Collection)):
45
+ state[k] = v
46
+ elif isinstance(ori_state, (tuple, list)):
47
+ state = [] # type: ignore[assignment]
48
+ for v in ori_state:
49
+ if isinstance(v, (IterDataPipe, MapDataPipe, Collection)):
50
+ state.append(v) # type: ignore[attr-defined]
51
+ elif isinstance(ori_state, (IterDataPipe, MapDataPipe, Collection)):
52
+ state = ori_state # type: ignore[assignment]
53
+ return state
54
+
55
+ def reduce_hook(obj):
56
+ if obj == scan_obj or id(obj) in cache:
57
+ raise NotImplementedError
58
+ else:
59
+ captured_connections.append(obj)
60
+ # Adding id to remove duplicate DataPipe serialized at the same level
61
+ cache.add(id(obj))
62
+ return _stub_unpickler, ()
63
+
64
+ datapipe_classes: tuple[type[DataPipe]] = (IterDataPipe, MapDataPipe) # type: ignore[assignment]
65
+
66
+ try:
67
+ for cls in datapipe_classes:
68
+ cls.set_reduce_ex_hook(reduce_hook)
69
+ if only_datapipe:
70
+ cls.set_getstate_hook(getstate_hook)
71
+ try:
72
+ p.dump(scan_obj)
73
+ except (pickle.PickleError, AttributeError, TypeError):
74
+ if dill_available():
75
+ # pyrefly: ignore [missing-attribute]
76
+ d.dump(scan_obj)
77
+ else:
78
+ raise
79
+ finally:
80
+ for cls in datapipe_classes:
81
+ cls.set_reduce_ex_hook(None)
82
+ if only_datapipe:
83
+ cls.set_getstate_hook(None)
84
+ if dill_available():
85
+ from dill import extend as dill_extend
86
+
87
+ dill_extend(False) # Undo change to dispatch table
88
+ return captured_connections
89
+
90
+
91
+ def traverse_dps(datapipe: DataPipe) -> DataPipeGraph:
92
+ r"""
93
+ Traverse the DataPipes and their attributes to extract the DataPipe graph.
94
+
95
+ This only looks into the attribute from each DataPipe that is either a
96
+ DataPipe and a Python collection object such as ``list``, ``tuple``,
97
+ ``set`` and ``dict``.
98
+
99
+ Args:
100
+ datapipe: the end DataPipe of the graph
101
+ Returns:
102
+ A graph represented as a nested dictionary, where keys are ids of DataPipe instances
103
+ and values are tuples of DataPipe instance and the sub-graph
104
+ """
105
+ cache: set[int] = set()
106
+ return _traverse_helper(datapipe, only_datapipe=True, cache=cache)
107
+
108
+
109
+ def traverse(datapipe: DataPipe, only_datapipe: bool | None = None) -> DataPipeGraph:
110
+ r"""
111
+ Traverse the DataPipes and their attributes to extract the DataPipe graph.
112
+
113
+ [Deprecated]
114
+ When ``only_dataPipe`` is specified as ``True``, it would only look into the
115
+ attribute from each DataPipe that is either a DataPipe and a Python collection object
116
+ such as ``list``, ``tuple``, ``set`` and ``dict``.
117
+
118
+ Note:
119
+ This function is deprecated. Please use `traverse_dps` instead.
120
+
121
+ Args:
122
+ datapipe: the end DataPipe of the graph
123
+ only_datapipe: If ``False`` (default), all attributes of each DataPipe are traversed.
124
+ This argument is deprecating and will be removed after the next release.
125
+ Returns:
126
+ A graph represented as a nested dictionary, where keys are ids of DataPipe instances
127
+ and values are tuples of DataPipe instance and the sub-graph
128
+ """
129
+ msg = (
130
+ "`traverse` function and will be removed after 1.13. "
131
+ "Please use `traverse_dps` instead."
132
+ )
133
+ if not only_datapipe:
134
+ msg += " And, the behavior will be changed to the equivalent of `only_datapipe=True`."
135
+ warnings.warn(msg, FutureWarning, stacklevel=2)
136
+ if only_datapipe is None:
137
+ only_datapipe = False
138
+ cache: set[int] = set()
139
+ return _traverse_helper(datapipe, only_datapipe, cache)
140
+
141
+
142
+ # Add cache here to prevent infinite recursion on DataPipe
143
+ def _traverse_helper(
144
+ datapipe: DataPipe, only_datapipe: bool, cache: set[int]
145
+ ) -> DataPipeGraph:
146
+ if not isinstance(datapipe, (IterDataPipe, MapDataPipe)):
147
+ raise RuntimeError(
148
+ f"Expected `IterDataPipe` or `MapDataPipe`, but {type(datapipe)} is found"
149
+ )
150
+
151
+ dp_id = id(datapipe)
152
+ if dp_id in cache:
153
+ return {}
154
+ cache.add(dp_id)
155
+ # Using cache.copy() here is to prevent the same DataPipe pollutes the cache on different paths
156
+ items = _list_connected_datapipes(datapipe, only_datapipe, cache.copy())
157
+ d: DataPipeGraph = {dp_id: (datapipe, {})}
158
+ for item in items:
159
+ # Using cache.copy() here is to prevent recursion on a single path rather than global graph
160
+ # Single DataPipe can present multiple times in different paths in graph
161
+ # pyrefly: ignore [no-matching-overload]
162
+ d[dp_id][1].update(_traverse_helper(item, only_datapipe, cache.copy()))
163
+ return d
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/graph_settings.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import inspect
3
+ import warnings
4
+ from typing import Any
5
+ from typing_extensions import deprecated
6
+
7
+ import torch
8
+ from torch.utils.data.datapipes.iter.sharding import (
9
+ _ShardingIterDataPipe,
10
+ SHARDING_PRIORITIES,
11
+ )
12
+ from torch.utils.data.graph import DataPipe, DataPipeGraph, traverse_dps
13
+
14
+
15
+ __all__ = [
16
+ "apply_random_seed",
17
+ "apply_sharding",
18
+ "apply_shuffle_seed",
19
+ "apply_shuffle_settings",
20
+ "get_all_graph_pipes",
21
+ ]
22
+
23
+
24
+ def get_all_graph_pipes(graph: DataPipeGraph) -> list[DataPipe]:
25
+ return _get_all_graph_pipes_helper(graph, set())
26
+
27
+
28
+ def _get_all_graph_pipes_helper(
29
+ graph: DataPipeGraph, id_cache: set[int]
30
+ ) -> list[DataPipe]:
31
+ results: list[DataPipe] = []
32
+ for dp_id, (datapipe, sub_graph) in graph.items():
33
+ if dp_id in id_cache:
34
+ continue
35
+ id_cache.add(dp_id)
36
+ results.append(datapipe)
37
+ results.extend(_get_all_graph_pipes_helper(sub_graph, id_cache))
38
+ return results
39
+
40
+
41
+ def _is_sharding_datapipe(datapipe: DataPipe) -> bool:
42
+ return isinstance(datapipe, _ShardingIterDataPipe) or (
43
+ hasattr(datapipe, "apply_sharding")
44
+ and inspect.ismethod(datapipe.apply_sharding)
45
+ )
46
+
47
+
48
+ def apply_sharding(
49
+ datapipe: DataPipe,
50
+ num_of_instances: int,
51
+ instance_id: int,
52
+ sharding_group=SHARDING_PRIORITIES.DEFAULT,
53
+ ) -> DataPipe:
54
+ r"""
55
+ Apply dynamic sharding over the ``sharding_filter`` DataPipe that has a method ``apply_sharding``.
56
+
57
+ RuntimeError will be raised when multiple ``sharding_filter`` are presented in the same branch.
58
+ """
59
+ graph = traverse_dps(datapipe)
60
+
61
+ def _helper(graph, prev_applied=None) -> None:
62
+ for dp, sub_graph in graph.values():
63
+ applied = None
64
+ if _is_sharding_datapipe(dp):
65
+ if prev_applied is not None:
66
+ raise RuntimeError(
67
+ "Sharding twice on a single pipeline is likely unintended and will cause data loss. "
68
+ f"Sharding already applied to {prev_applied} while trying to apply to {dp}"
69
+ )
70
+ # For BC, only provide sharding_group if accepted
71
+ sig = inspect.signature(dp.apply_sharding)
72
+ if len(sig.parameters) < 3:
73
+ dp.apply_sharding(num_of_instances, instance_id)
74
+ else:
75
+ dp.apply_sharding(
76
+ num_of_instances, instance_id, sharding_group=sharding_group
77
+ )
78
+ applied = dp
79
+ if applied is None:
80
+ applied = prev_applied
81
+ _helper(sub_graph, applied)
82
+
83
+ _helper(graph)
84
+
85
+ return datapipe
86
+
87
+
88
+ def _is_shuffle_datapipe(datapipe: DataPipe) -> bool:
89
+ return (
90
+ hasattr(datapipe, "set_shuffle")
91
+ and hasattr(datapipe, "set_seed")
92
+ and inspect.ismethod(datapipe.set_shuffle)
93
+ and inspect.ismethod(datapipe.set_seed)
94
+ )
95
+
96
+
97
+ def apply_shuffle_settings(datapipe: DataPipe, shuffle: bool | None = None) -> DataPipe:
98
+ r"""
99
+ Traverse the graph of ``DataPipes`` to find and set shuffle attribute.
100
+
101
+ Apply the method to each `DataPipe` that has APIs of ``set_shuffle``
102
+ and ``set_seed``.
103
+
104
+ Args:
105
+ datapipe: DataPipe that needs to set shuffle attribute
106
+ shuffle: Shuffle option (default: ``None`` and no-op to the graph)
107
+ """
108
+ if shuffle is None:
109
+ return datapipe
110
+
111
+ graph = traverse_dps(datapipe)
112
+ all_pipes = get_all_graph_pipes(graph)
113
+ shufflers = [pipe for pipe in all_pipes if _is_shuffle_datapipe(pipe)]
114
+ if not shufflers and shuffle:
115
+ warnings.warn(
116
+ "`shuffle=True` was set, but the datapipe does not contain a `Shuffler`. Adding one at the end. "
117
+ "Be aware that the default buffer size might not be sufficient for your task.",
118
+ stacklevel=2,
119
+ )
120
+ datapipe = datapipe.shuffle()
121
+ shufflers = [
122
+ datapipe,
123
+ ]
124
+
125
+ for shuffler in shufflers:
126
+ shuffler.set_shuffle(shuffle)
127
+
128
+ return datapipe
129
+
130
+
131
+ @deprecated(
132
+ "`apply_shuffle_seed` is deprecated since 1.12 and will be removed in the future releases. "
133
+ "Please use `apply_random_seed` instead.",
134
+ category=FutureWarning,
135
+ )
136
+ def apply_shuffle_seed(datapipe: DataPipe, rng: Any) -> DataPipe:
137
+ return apply_random_seed(datapipe, rng)
138
+
139
+
140
+ def _is_random_datapipe(datapipe: DataPipe) -> bool:
141
+ return hasattr(datapipe, "set_seed") and inspect.ismethod(datapipe.set_seed)
142
+
143
+
144
+ def apply_random_seed(datapipe: DataPipe, rng: torch.Generator) -> DataPipe:
145
+ r"""
146
+ Traverse the graph of ``DataPipes`` to find random ``DataPipe`` with an API of ``set_seed``.
147
+
148
+ Then set the random seed based on the provided RNG to those ``DataPipe``.
149
+
150
+ Args:
151
+ datapipe: DataPipe that needs to set randomness
152
+ rng: Random number generator to generate random seeds
153
+ """
154
+ graph = traverse_dps(datapipe)
155
+ all_pipes = get_all_graph_pipes(graph)
156
+ # Using a set to track id of DataPipe to prevent setting randomness per DataPipe more than once.
157
+ # And, `id` is used in case of unhashable DataPipe
158
+ cache = set()
159
+ random_datapipes = []
160
+ for pipe in all_pipes:
161
+ if id(pipe) in cache:
162
+ continue
163
+ if _is_random_datapipe(pipe):
164
+ random_datapipes.append(pipe)
165
+ cache.add(id(pipe))
166
+
167
+ for pipe in random_datapipes:
168
+ random_seed = int(
169
+ torch.empty((), dtype=torch.int64).random_(generator=rng).item()
170
+ )
171
+ pipe.set_seed(random_seed)
172
+
173
+ return datapipe
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/data/sampler.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import itertools
3
+ from collections.abc import Iterable, Iterator, Sequence, Sized
4
+ from typing import Generic, TypeVar
5
+
6
+ import torch
7
+
8
+
9
+ # Note: For benchmarking changes to samplers, see:
10
+ # /benchmarks/data/samplers_bench.py
11
+ # This benchmark compares the performance of different sampler implementations
12
+ # and can be used to evaluate the impact of optimizations.
13
+
14
+
15
+ __all__ = [
16
+ "BatchSampler",
17
+ "RandomSampler",
18
+ "Sampler",
19
+ "SequentialSampler",
20
+ "SubsetRandomSampler",
21
+ "WeightedRandomSampler",
22
+ ]
23
+
24
+
25
+ _T_co = TypeVar("_T_co", covariant=True)
26
+
27
+
28
+ class Sampler(Generic[_T_co]):
29
+ r"""Base class for all Samplers.
30
+
31
+ Every Sampler subclass has to provide an :meth:`__iter__` method, providing a
32
+ way to iterate over indices or lists of indices (batches) of dataset elements,
33
+ and may provide a :meth:`__len__` method that returns the length of the returned iterators.
34
+
35
+ Example:
36
+ >>> # xdoctest: +SKIP
37
+ >>> class AccedingSequenceLengthSampler(Sampler[int]):
38
+ >>> def __init__(self, data: List[str]) -> None:
39
+ >>> self.data = data
40
+ >>>
41
+ >>> def __len__(self) -> int:
42
+ >>> return len(self.data)
43
+ >>>
44
+ >>> def __iter__(self) -> Iterator[int]:
45
+ >>> sizes = torch.tensor([len(x) for x in self.data])
46
+ >>> yield from torch.argsort(sizes).tolist()
47
+ >>>
48
+ >>> class AccedingSequenceLengthBatchSampler(Sampler[List[int]]):
49
+ >>> def __init__(self, data: List[str], batch_size: int) -> None:
50
+ >>> self.data = data
51
+ >>> self.batch_size = batch_size
52
+ >>>
53
+ >>> def __len__(self) -> int:
54
+ >>> return (len(self.data) + self.batch_size - 1) // self.batch_size
55
+ >>>
56
+ >>> def __iter__(self) -> Iterator[List[int]]:
57
+ >>> sizes = torch.tensor([len(x) for x in self.data])
58
+ >>> for batch in torch.chunk(torch.argsort(sizes), len(self)):
59
+ >>> yield batch.tolist()
60
+
61
+ .. note:: The :meth:`__len__` method isn't strictly required by
62
+ :class:`~torch.utils.data.DataLoader`, but is expected in any
63
+ calculation involving the length of a :class:`~torch.utils.data.DataLoader`.
64
+ """
65
+
66
+ def __iter__(self) -> Iterator[_T_co]:
67
+ raise NotImplementedError
68
+
69
+ # NOTE [ Lack of Default `__len__` in Python Abstract Base Classes ]
70
+ #
71
+ # Many times we have an abstract class representing a collection/iterable of
72
+ # data, e.g., `torch.utils.data.Sampler`, with its subclasses optionally
73
+ # implementing a `__len__` method. In such cases, we must make sure to not
74
+ # provide a default implementation, because both straightforward default
75
+ # implementations have their issues:
76
+ #
77
+ # + `return NotImplemented`:
78
+ # Calling `len(subclass_instance)` raises:
79
+ # TypeError: 'NotImplementedType' object cannot be interpreted as an integer
80
+ #
81
+ # + `raise NotImplementedError`:
82
+ # This prevents triggering some fallback behavior. E.g., the built-in
83
+ # `list(X)` tries to call `len(X)` first, and executes a different code
84
+ # path if the method is not found or `NotImplemented` is returned, while
85
+ # raising a `NotImplementedError` will propagate and make the call fail
86
+ # where it could have used `__iter__` to complete the call.
87
+ #
88
+ # Thus, the only two sensible things to do are
89
+ #
90
+ # + **not** provide a default `__len__`.
91
+ #
92
+ # + raise a `TypeError` instead, which is what Python uses when users call
93
+ # a method that is not defined on an object.
94
+ # (@ssnl verifies that this works on at least Python 3.7.)
95
+
96
+
97
+ class SequentialSampler(Sampler[int]):
98
+ r"""Samples elements sequentially, always in the same order.
99
+
100
+ Args:
101
+ data_source (Sized): data source to sample from. Must implement __len__.
102
+ """
103
+
104
+ data_source: Sized
105
+
106
+ def __init__(self, data_source: Sized) -> None:
107
+ self.data_source = data_source
108
+
109
+ def __iter__(self) -> Iterator[int]:
110
+ return iter(range(len(self.data_source)))
111
+
112
+ def __len__(self) -> int:
113
+ return len(self.data_source)
114
+
115
+
116
+ class RandomSampler(Sampler[int]):
117
+ r"""Samples elements randomly. If without replacement, then sample from a shuffled dataset.
118
+
119
+ If with replacement, then user can specify :attr:`num_samples` to draw.
120
+
121
+ Args:
122
+ data_source (Sized): data source to sample from. Must implement __len__.
123
+ replacement (bool): samples are drawn on-demand with replacement if ``True``, default=``False``
124
+ num_samples (int): number of samples to draw, default=`len(dataset)`.
125
+ generator (Generator): Generator used in sampling.
126
+ """
127
+
128
+ data_source: Sized
129
+ replacement: bool
130
+
131
+ def __init__(
132
+ self,
133
+ data_source: Sized,
134
+ replacement: bool = False,
135
+ num_samples: int | None = None,
136
+ generator=None,
137
+ ) -> None:
138
+ self.data_source = data_source
139
+ self.replacement = replacement
140
+ self._num_samples = num_samples
141
+ self.generator = generator
142
+
143
+ if not isinstance(self.replacement, bool):
144
+ raise TypeError(
145
+ f"replacement should be a boolean value, but got replacement={self.replacement}"
146
+ )
147
+
148
+ if not isinstance(self.num_samples, int) or self.num_samples <= 0:
149
+ raise ValueError(
150
+ f"num_samples should be a positive integer value, but got num_samples={self.num_samples}"
151
+ )
152
+
153
+ @property
154
+ def num_samples(self) -> int:
155
+ # dataset size might change at runtime
156
+ if self._num_samples is None:
157
+ return len(self.data_source)
158
+ return self._num_samples
159
+
160
+ def __iter__(self) -> Iterator[int]:
161
+ n = len(self.data_source)
162
+ if self.generator is None:
163
+ seed = int(torch.empty((), dtype=torch.int64).random_().item())
164
+ generator = torch.Generator()
165
+ generator.manual_seed(seed)
166
+ else:
167
+ generator = self.generator
168
+
169
+ if self.replacement:
170
+ for _ in range(self.num_samples // 32):
171
+ yield from torch.randint(
172
+ high=n, size=(32,), dtype=torch.int64, generator=generator
173
+ ).tolist()
174
+ yield from torch.randint(
175
+ high=n,
176
+ size=(self.num_samples % 32,),
177
+ dtype=torch.int64,
178
+ generator=generator,
179
+ ).tolist()
180
+ else:
181
+ for _ in range(self.num_samples // n):
182
+ yield from torch.randperm(n, generator=generator).tolist()
183
+ yield from torch.randperm(n, generator=generator).tolist()[
184
+ : self.num_samples % n
185
+ ]
186
+
187
+ def __len__(self) -> int:
188
+ return self.num_samples
189
+
190
+
191
+ class SubsetRandomSampler(Sampler[int]):
192
+ r"""Samples elements randomly from a given list of indices, without replacement.
193
+
194
+ Args:
195
+ indices (sequence): a sequence of indices
196
+ generator (Generator): Generator used in sampling.
197
+ """
198
+
199
+ indices: Sequence[int]
200
+
201
+ def __init__(self, indices: Sequence[int], generator=None) -> None:
202
+ self.indices = indices
203
+ self.generator = generator
204
+
205
+ def __iter__(self) -> Iterator[int]:
206
+ for i in torch.randperm(len(self.indices), generator=self.generator).tolist():
207
+ yield self.indices[i]
208
+
209
+ def __len__(self) -> int:
210
+ return len(self.indices)
211
+
212
+
213
+ class WeightedRandomSampler(Sampler[int]):
214
+ r"""Samples elements from ``[0,..,len(weights)-1]`` with given probabilities (weights).
215
+
216
+ Args:
217
+ weights (sequence) : a sequence of weights, not necessary summing up to one
218
+ num_samples (int): number of samples to draw
219
+ replacement (bool): if ``True``, samples are drawn with replacement.
220
+ If not, they are drawn without replacement, which means that when a
221
+ sample index is drawn for a row, it cannot be drawn again for that row.
222
+ generator (Generator): Generator used in sampling.
223
+
224
+ Example:
225
+ >>> # xdoctest: +IGNORE_WANT("non-deterministic")
226
+ >>> list(
227
+ ... WeightedRandomSampler(
228
+ ... [0.1, 0.9, 0.4, 0.7, 3.0, 0.6], 5, replacement=True
229
+ ... )
230
+ ... )
231
+ [4, 4, 1, 4, 5]
232
+ >>> list(
233
+ ... WeightedRandomSampler(
234
+ ... [0.9, 0.4, 0.05, 0.2, 0.3, 0.1], 5, replacement=False
235
+ ... )
236
+ ... )
237
+ [0, 1, 4, 3, 2]
238
+ """
239
+
240
+ weights: torch.Tensor
241
+ num_samples: int
242
+ replacement: bool
243
+
244
+ def __init__(
245
+ self,
246
+ weights: Sequence[float],
247
+ num_samples: int,
248
+ replacement: bool = True,
249
+ generator=None,
250
+ ) -> None:
251
+ if (
252
+ not isinstance(num_samples, int)
253
+ or isinstance(num_samples, bool)
254
+ or num_samples <= 0
255
+ ):
256
+ raise ValueError(
257
+ f"num_samples should be a positive integer value, but got num_samples={num_samples}"
258
+ )
259
+ if not isinstance(replacement, bool):
260
+ raise ValueError(
261
+ f"replacement should be a boolean value, but got replacement={replacement}"
262
+ )
263
+
264
+ weights_tensor = torch.as_tensor(weights, dtype=torch.double)
265
+ if len(weights_tensor.shape) != 1:
266
+ raise ValueError(
267
+ "weights should be a 1d sequence but given "
268
+ f"weights have shape {tuple(weights_tensor.shape)}"
269
+ )
270
+
271
+ self.weights = weights_tensor
272
+ self.num_samples = num_samples
273
+ self.replacement = replacement
274
+ self.generator = generator
275
+
276
+ def __iter__(self) -> Iterator[int]:
277
+ rand_tensor = torch.multinomial(
278
+ self.weights, self.num_samples, self.replacement, generator=self.generator
279
+ )
280
+ yield from iter(rand_tensor.tolist())
281
+
282
+ def __len__(self) -> int:
283
+ return self.num_samples
284
+
285
+
286
+ class BatchSampler(Sampler[list[int]]):
287
+ r"""Wraps another sampler to yield a mini-batch of indices.
288
+
289
+ Args:
290
+ sampler (Sampler or Iterable): Base sampler. Can be any iterable object
291
+ batch_size (int): Size of mini-batch.
292
+ drop_last (bool): If ``True``, the sampler will drop the last batch if
293
+ its size would be less than ``batch_size``
294
+
295
+ Example:
296
+ >>> list(
297
+ ... BatchSampler(
298
+ ... SequentialSampler(range(10)), batch_size=3, drop_last=False
299
+ ... )
300
+ ... )
301
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
302
+ >>> list(
303
+ ... BatchSampler(SequentialSampler(range(10)), batch_size=3, drop_last=True)
304
+ ... )
305
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
306
+ """
307
+
308
+ def __init__(
309
+ self,
310
+ sampler: Sampler[int] | Iterable[int],
311
+ batch_size: int,
312
+ drop_last: bool,
313
+ ) -> None:
314
+ # Since collections.abc.Iterable does not check for `__getitem__`, which
315
+ # is one way for an object to be an iterable, we don't do an `isinstance`
316
+ # check here.
317
+ if (
318
+ not isinstance(batch_size, int)
319
+ or isinstance(batch_size, bool)
320
+ or batch_size <= 0
321
+ ):
322
+ raise ValueError(
323
+ f"batch_size should be a positive integer value, but got batch_size={batch_size}"
324
+ )
325
+ if not isinstance(drop_last, bool):
326
+ raise ValueError(
327
+ f"drop_last should be a boolean value, but got drop_last={drop_last}"
328
+ )
329
+ self.sampler = sampler
330
+ self.batch_size = batch_size
331
+ self.drop_last = drop_last
332
+
333
+ def __iter__(self) -> Iterator[list[int]]:
334
+ sampler_iter = iter(self.sampler)
335
+ if self.drop_last:
336
+ # Create multiple references to the same iterator
337
+ args = [sampler_iter] * self.batch_size
338
+ for batch_droplast in zip(*args, strict=False):
339
+ yield [*batch_droplast]
340
+ else:
341
+ batch = [*itertools.islice(sampler_iter, self.batch_size)]
342
+ while batch:
343
+ yield batch
344
+ batch = [*itertools.islice(sampler_iter, self.batch_size)]
345
+
346
+ def __len__(self) -> int:
347
+ # Can only be called if self.sampler has __len__ implemented
348
+ # We cannot enforce this condition, so we turn off typechecking for the
349
+ # implementation below.
350
+ # Somewhat related: see NOTE [ Lack of Default `__len__` in Python Abstract Base Classes ]
351
+ if self.drop_last:
352
+ return len(self.sampler) // self.batch_size # type: ignore[arg-type]
353
+ else:
354
+ return (len(self.sampler) + self.batch_size - 1) // self.batch_size # type: ignore[arg-type]
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/deterministic.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import sys
3
+ import types
4
+
5
+ import torch
6
+
7
+
8
+ class _Deterministic(types.ModuleType):
9
+ @property
10
+ def fill_uninitialized_memory(self):
11
+ """
12
+ Whether to fill uninitialized memory with a known value when
13
+ :meth:`torch.use_deterministic_algorithms()` is set to ``True``.
14
+ """
15
+ return torch._C._get_deterministic_fill_uninitialized_memory()
16
+
17
+ @fill_uninitialized_memory.setter
18
+ def fill_uninitialized_memory(self, mode):
19
+ return torch._C._set_deterministic_fill_uninitialized_memory(mode)
20
+
21
+
22
+ sys.modules[__name__].__class__ = _Deterministic
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/dlpack.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any
2
+
3
+ import torch
4
+ import enum
5
+
6
+ from torch._C import _to_dlpack as to_dlpack
7
+ from torch.types import Device as _Device
8
+
9
+ __all__ = [
10
+ "DLDeviceType",
11
+ "from_dlpack",
12
+ ]
13
+
14
+ class DLDeviceType(enum.IntEnum):
15
+ # Enums as in DLPack specification (aten/src/ATen/dlpack.h)
16
+ kDLCPU = 1,
17
+ kDLCUDA = 2,
18
+ kDLCUDAHost = 3,
19
+ kDLOpenCL = 4,
20
+ kDLVulkan = 7,
21
+ kDLMetal = 8,
22
+ kDLVPI = 9,
23
+ kDLROCM = 10,
24
+ kDLROCMHost = 11,
25
+ kDLExtDev = 12,
26
+ kDLCUDAManaged = 13,
27
+ kDLOneAPI = 14,
28
+ kDLWebGPU = 15,
29
+ kDLHexagon = 16,
30
+ kDLMAIA = 17,
31
+
32
+
33
+ torch._C._add_docstr(to_dlpack, r"""to_dlpack(tensor) -> PyCapsule
34
+
35
+ Returns an opaque object (a "DLPack capsule") representing the tensor.
36
+
37
+ .. note::
38
+ ``to_dlpack`` is a legacy DLPack interface. The capsule it returns
39
+ cannot be used for anything in Python other than use it as input to
40
+ ``from_dlpack``. The more idiomatic use of DLPack is to call
41
+ ``from_dlpack`` directly on the tensor object - this works when that
42
+ object has a ``__dlpack__`` method, which PyTorch and most other
43
+ libraries indeed have now.
44
+
45
+ .. warning::
46
+ Only call ``from_dlpack`` once per capsule produced with ``to_dlpack``.
47
+ Behavior when a capsule is consumed multiple times is undefined.
48
+
49
+ Args:
50
+ tensor: a tensor to be exported
51
+
52
+ The DLPack capsule shares the tensor's memory.
53
+ """)
54
+
55
+
56
+ # TODO: add a typing.Protocol to be able to tell Mypy that only objects with
57
+ # __dlpack__ and __dlpack_device__ methods are accepted.
58
+ def from_dlpack(
59
+ ext_tensor: Any,
60
+ *,
61
+ device: _Device | None = None,
62
+ copy: bool | None = None
63
+ ) -> 'torch.Tensor':
64
+ """from_dlpack(ext_tensor) -> Tensor
65
+
66
+ Converts a tensor from an external library into a ``torch.Tensor``.
67
+
68
+ The returned PyTorch tensor will share the memory with the input tensor
69
+ (which may have come from another library). Note that in-place operations
70
+ will therefore also affect the data of the input tensor. This may lead to
71
+ unexpected issues (e.g., other libraries may have read-only flags or
72
+ immutable data structures), so the user should only do this if they know
73
+ for sure that this is fine.
74
+
75
+ Args:
76
+ ext_tensor (object with ``__dlpack__`` attribute, or a DLPack capsule):
77
+ The tensor or DLPack capsule to convert.
78
+
79
+ If ``ext_tensor`` is a tensor (or ndarray) object, it must support
80
+ the ``__dlpack__`` protocol (i.e., have a ``ext_tensor.__dlpack__``
81
+ method). Otherwise ``ext_tensor`` may be a DLPack capsule, which is
82
+ an opaque ``PyCapsule`` instance, typically produced by a
83
+ ``to_dlpack`` function or method.
84
+
85
+ device (torch.device or str or None): An optional PyTorch device
86
+ specifying where to place the new tensor. If None (default), the
87
+ new tensor will be on the same device as ``ext_tensor``.
88
+
89
+ copy (bool or None): An optional boolean indicating whether or not to copy
90
+ ``self``. If None, PyTorch will copy only if necessary.
91
+
92
+ Examples::
93
+
94
+ >>> import torch.utils.dlpack
95
+ >>> t = torch.arange(4)
96
+
97
+ # Convert a tensor directly (supported in PyTorch >= 1.10)
98
+ >>> t2 = torch.from_dlpack(t)
99
+ >>> t2[:2] = -1 # show that memory is shared
100
+ >>> t2
101
+ tensor([-1, -1, 2, 3])
102
+ >>> t
103
+ tensor([-1, -1, 2, 3])
104
+
105
+ # The old-style DLPack usage, with an intermediate capsule object
106
+ >>> capsule = torch.utils.dlpack.to_dlpack(t)
107
+ >>> capsule
108
+ <capsule object "dltensor" at ...>
109
+ >>> t3 = torch.from_dlpack(capsule)
110
+ >>> t3
111
+ tensor([-1, -1, 2, 3])
112
+ >>> t3[0] = -9 # now we're sharing memory between 3 tensors
113
+ >>> t3
114
+ tensor([-9, -1, 2, 3])
115
+ >>> t2
116
+ tensor([-9, -1, 2, 3])
117
+ >>> t
118
+ tensor([-9, -1, 2, 3])
119
+
120
+ """
121
+
122
+ if hasattr(ext_tensor, '__dlpack__'):
123
+ # Only populate kwargs if any of the optional arguments are, in fact, not None. Otherwise,
124
+ # leave them out, since we might end up falling back to no-extra-kwargs __dlpack__ call.
125
+ kwargs: dict[str, Any] = {}
126
+ kwargs["max_version"] = (1, 0)
127
+
128
+ # Track copy request for potential manual handling
129
+ requested_copy = copy
130
+ producer_handled_copy = True
131
+ cross_device_transfer = False # Will be set to True if device transfer is needed
132
+
133
+ if copy is not None:
134
+ kwargs["copy"] = copy
135
+
136
+ # Parse the device parameter.
137
+ # At this moment, it can either be a torch.device or a str representing
138
+ # a torch.device, e.g. "cpu", "cuda", etc.
139
+ # Get source device first (we need it to detect cross-device transfers)
140
+ ext_device = ext_tensor.__dlpack_device__()
141
+
142
+ if device is not None:
143
+ if isinstance(device, str):
144
+ device = torch.device(device)
145
+ if not isinstance(device, torch.device):
146
+ raise AssertionError(f"from_dlpack: unsupported device type: {type(device)}")
147
+
148
+ # Convert target device to DLPack format
149
+ target_dl_device = torch._C._torchDeviceToDLDevice(device)
150
+
151
+ # Detect cross-device transfer by comparing source and target devices
152
+ # E.g. CPU->CUDA, cuda:0->cuda:1, etc.
153
+ cross_device_transfer = (ext_device != target_dl_device)
154
+
155
+ # Only pass dl_device to producer if NOT cross-device transfer
156
+ if not cross_device_transfer:
157
+ kwargs["dl_device"] = target_dl_device
158
+
159
+ # Cross-device transfer always requires a copy
160
+ if cross_device_transfer and copy is False:
161
+ raise ValueError(
162
+ f"cannot move DLPack tensor from device {ext_device} to {target_dl_device} "
163
+ "without copying. Set copy=None or copy=True."
164
+ )
165
+
166
+ # ext_device is either CUDA or ROCm, we need to pass the current
167
+ # stream
168
+ if ext_device[0] in (DLDeviceType.kDLCUDA, DLDeviceType.kDLROCM):
169
+ stream = torch.cuda.current_stream(f'cuda:{ext_device[1]}')
170
+ # cuda_stream is the pointer to the stream and it is a public
171
+ # attribute, but it is not documented
172
+ # The array API specify that the default legacy stream must be passed
173
+ # with a value of 1 for CUDA
174
+ # https://data-apis.org/array-api/latest/API_specification/array_object.html?dlpack-self-stream-none#dlpack-self-stream-none
175
+ is_cuda = ext_device[0] == DLDeviceType.kDLCUDA
176
+ # Since pytorch is not using PTDS by default, lets directly pass
177
+ # the legacy stream
178
+ stream_ptr = 1 if is_cuda and stream.cuda_stream == 0 else stream.cuda_stream
179
+ kwargs["stream"] = stream_ptr
180
+
181
+ # Try different parameter combinations until one works
182
+ dlpack = None
183
+
184
+ # Attempt 1: Try with all the parameters
185
+ try:
186
+ dlpack = ext_tensor.__dlpack__(**kwargs)
187
+ except TypeError:
188
+ pass
189
+
190
+ # Attempt 2: Remove max_version
191
+ if dlpack is None:
192
+ kwargs.pop("max_version", None)
193
+ try:
194
+ dlpack = ext_tensor.__dlpack__(**kwargs)
195
+ except TypeError:
196
+ pass
197
+
198
+ # Attempt 3: Remove copy
199
+ if dlpack is None:
200
+ kwargs.pop("copy", None)
201
+ producer_handled_copy = False
202
+ try:
203
+ dlpack = ext_tensor.__dlpack__(**kwargs)
204
+ except TypeError:
205
+ pass
206
+
207
+ # Attempt 4: Remove dl_device
208
+ if dlpack is None:
209
+ kwargs.pop("dl_device", None)
210
+ dlpack = ext_tensor.__dlpack__(**kwargs)
211
+
212
+ tensor = torch._C._from_dlpack(dlpack)
213
+
214
+ # Manual copy if producer didn't handle it (cross-device already copies via .to())
215
+ if requested_copy is True and not producer_handled_copy and not cross_device_transfer:
216
+ tensor = tensor.clone()
217
+
218
+ # Handle cross-device transfer by moving tensor to target device
219
+ if cross_device_transfer:
220
+ tensor = tensor.to(device)
221
+
222
+ return tensor
223
+
224
+ else:
225
+ if device is not None or copy is not None:
226
+ raise AssertionError(
227
+ "device and copy kwargs not supported when ext_tensor is already a DLPack capsule."
228
+ )
229
+ # Old versions just call the converter
230
+ dlpack = ext_tensor
231
+ return torch._C._from_dlpack(dlpack)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/file_baton.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import os
3
+ import time
4
+ import warnings
5
+
6
+
7
+ class FileBaton:
8
+ """A primitive, file-based synchronization utility."""
9
+
10
+ def __init__(self, lock_file_path, wait_seconds=0.1, warn_after_seconds=None) -> None:
11
+ """
12
+ Create a new :class:`FileBaton`.
13
+
14
+ Args:
15
+ lock_file_path: The path to the file used for locking.
16
+ wait_seconds: The seconds to periodically sleep (spin) when
17
+ calling ``wait()``.
18
+ warn_after_seconds: The seconds to wait before showing
19
+ lock file path to warn existing lock file.
20
+ """
21
+ self.lock_file_path = lock_file_path
22
+ self.wait_seconds = wait_seconds
23
+ self.fd = None
24
+ self.warn_after_seconds = warn_after_seconds
25
+
26
+ def try_acquire(self) -> bool | None:
27
+ """
28
+ Try to atomically create a file under exclusive access.
29
+
30
+ Returns:
31
+ True if the file could be created, else False.
32
+ """
33
+ try:
34
+ self.fd = os.open(self.lock_file_path, os.O_CREAT | os.O_EXCL)
35
+ return True
36
+ except FileExistsError:
37
+ return False
38
+
39
+ def wait(self) -> None:
40
+ """
41
+ Periodically sleeps for a certain amount until the baton is released.
42
+
43
+ The amount of time slept depends on the ``wait_seconds`` parameter
44
+ passed to the constructor.
45
+ """
46
+ has_warned = False
47
+
48
+ start_time = time.time()
49
+ while os.path.exists(self.lock_file_path):
50
+ time.sleep(self.wait_seconds)
51
+
52
+ if self.warn_after_seconds is not None:
53
+ if time.time() - start_time > self.warn_after_seconds and not has_warned:
54
+ warnings.warn(f'Waited on lock file "{self.lock_file_path}" for '
55
+ f'{self.warn_after_seconds} seconds.', stacklevel=2)
56
+ has_warned = True
57
+
58
+ def release(self) -> None:
59
+ """Release the baton and removes its file."""
60
+ if self.fd is not None:
61
+ os.close(self.fd)
62
+
63
+ os.remove(self.lock_file_path)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/flop_counter.py ADDED
@@ -0,0 +1,1017 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from types import NoneType
3
+ import logging
4
+ import torch
5
+ from torch.utils._pytree import tree_map, tree_flatten, tree_unflatten
6
+ from .module_tracker import ModuleTracker
7
+ from typing import Any, TypeVar
8
+ from collections.abc import Callable
9
+ from collections.abc import Iterator
10
+ from typing_extensions import ParamSpec
11
+ from collections import defaultdict
12
+ from torch.utils._python_dispatch import TorchDispatchMode
13
+ from math import prod
14
+ from functools import wraps
15
+ import warnings
16
+
17
+ __all__ = ["FlopCounterMode", "register_flop_formula"]
18
+
19
+ _T = TypeVar("_T")
20
+ _P = ParamSpec("_P")
21
+
22
+ log = logging.getLogger(__name__)
23
+
24
+
25
+ try:
26
+ from triton.runtime.jit import JITFunction as _JITFunction
27
+ except ImportError:
28
+ if any(getattr(torch.version, attr, None) is not None for attr in ["cuda", "hip", "xpu"]):
29
+ log.warning("triton not found; flop counting will not work for triton kernels")
30
+ _JITFunction = NoneType
31
+
32
+
33
+ aten = torch.ops.aten
34
+
35
+ def get_shape(i):
36
+ if isinstance(i, torch.Tensor):
37
+ return i.shape
38
+ return i
39
+
40
+ flop_registry: dict[Any, Any] = {}
41
+
42
+ def shape_wrapper(f):
43
+ @wraps(f)
44
+ def nf(*args, out_val=None, **kwargs):
45
+ args, kwargs, out_shape = tree_map(get_shape, (args, kwargs, out_val))
46
+ return f(*args, out_shape=out_shape, **kwargs)
47
+ return nf
48
+
49
+ def register_flop_formula(targets, get_raw=False) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]:
50
+
51
+ def register_fun(flop_formula: Callable[_P, _T]) -> Callable[_P, _T]:
52
+ if not get_raw:
53
+ flop_formula = shape_wrapper(flop_formula)
54
+
55
+ def register(target) -> None:
56
+ if not (isinstance(target, (torch._ops.OpOverloadPacket, _JITFunction))):
57
+ raise ValueError(
58
+ f"register_flop_formula(targets): expected each target to be "
59
+ f"OpOverloadPacket (i.e. torch.ops.mylib.foo), or JitFunction"
60
+ f", got {target} which is of type {type(target)}")
61
+ if target in flop_registry:
62
+ raise RuntimeError(f"duplicate registrations for {target}")
63
+ flop_registry[target] = flop_formula
64
+
65
+ # To handle allowing multiple aten_ops at once
66
+ torch.utils._pytree.tree_map_(register, targets)
67
+
68
+ return flop_formula
69
+
70
+ return register_fun
71
+
72
+ @register_flop_formula(aten.mm)
73
+ def mm_flop(a_shape, b_shape, *args, out_shape=None, **kwargs) -> int:
74
+ """Count flops for matmul."""
75
+ # Inputs should be a list of length 2.
76
+ # Inputs contains the shapes of two matrices.
77
+ m, k = a_shape
78
+ k2, n = b_shape
79
+ if k != k2:
80
+ raise AssertionError(f"matmul: inner dimensions must match (k == k2), got {k} and {k2}")
81
+ # NB(chilli): Should be 2 * k - 1 technically for FLOPs.
82
+ return m * n * 2 * k
83
+
84
+ @register_flop_formula(aten.addmm)
85
+ def addmm_flop(self_shape, a_shape, b_shape, out_shape=None, **kwargs) -> int:
86
+ """Count flops for addmm."""
87
+ return mm_flop(a_shape, b_shape)
88
+
89
+ @register_flop_formula(aten.bmm)
90
+ def bmm_flop(a_shape, b_shape, out_shape=None, **kwargs) -> int:
91
+ """Count flops for the bmm operation."""
92
+ # Inputs should be a list of length 2.
93
+ # Inputs contains the shapes of two tensor.
94
+ b, m, k = a_shape
95
+ b2, k2, n = b_shape
96
+ if b != b2:
97
+ raise AssertionError(f"bmm: batch dimensions must match (b == b2), got {b} and {b2}")
98
+ if k != k2:
99
+ raise AssertionError(f"bmm: inner dimensions must match (k == k2), got {k} and {k2}")
100
+ # NB(chilli): Should be 2 * k - 1 technically for FLOPs.
101
+ flop = b * m * n * 2 * k
102
+ return flop
103
+
104
+ @register_flop_formula(aten.baddbmm)
105
+ def baddbmm_flop(self_shape, a_shape, b_shape, out_shape=None, **kwargs) -> int:
106
+ """Count flops for the baddbmm operation."""
107
+ # Inputs should be a list of length 3.
108
+ # Inputs contains the shapes of three tensors.
109
+ return bmm_flop(a_shape, b_shape)
110
+
111
+ @register_flop_formula(aten._scaled_mm)
112
+ def _scaled_mm_flop(
113
+ a_shape,
114
+ b_shape,
115
+ scale_a_shape,
116
+ scale_b_shape,
117
+ bias_shape=None,
118
+ scale_result_shape=None,
119
+ out_dtype=None,
120
+ use_fast_accum=False,
121
+ out_shape=None,
122
+ **kwargs,
123
+ ) -> int:
124
+ """Count flops for _scaled_mm."""
125
+ return mm_flop(a_shape, b_shape)
126
+
127
+
128
+ def conv_flop_count(
129
+ x_shape: list[int],
130
+ w_shape: list[int],
131
+ out_shape: list[int],
132
+ transposed: bool = False,
133
+ ) -> int:
134
+ """Count flops for convolution.
135
+
136
+ Note only multiplication is
137
+ counted. Computation for bias are ignored.
138
+ Flops for a transposed convolution are calculated as
139
+ flops = (x_shape[2:] * prod(w_shape) * batch_size).
140
+ Args:
141
+ x_shape (list(int)): The input shape before convolution.
142
+ w_shape (list(int)): The filter shape.
143
+ out_shape (list(int)): The output shape after convolution.
144
+ transposed (bool): is the convolution transposed
145
+ Returns:
146
+ int: the number of flops
147
+ """
148
+ batch_size = x_shape[0]
149
+ conv_shape = (x_shape if transposed else out_shape)[2:]
150
+ c_out, c_in, *filter_size = w_shape
151
+
152
+ """
153
+ General idea here is that for a regular conv, for each point in the output
154
+ spatial dimension we convolve the filter with something (hence
155
+ `prod(conv_shape) * prod(filter_size)` ops). Then, this gets multiplied by
156
+ 1. batch_size, 2. the cross product of input and weight channels.
157
+
158
+ For the transpose, it's not each point in the *output* spatial dimension but
159
+ each point in the *input* spatial dimension.
160
+ """
161
+ # NB(chilli): I don't think this properly accounts for padding :think:
162
+ # NB(chilli): Should be 2 * c_in - 1 technically for FLOPs.
163
+ flop = prod(conv_shape) * prod(filter_size) * batch_size * c_out * c_in * 2
164
+ return flop
165
+
166
+ @register_flop_formula([aten.convolution,
167
+ aten._convolution,
168
+ aten.cudnn_convolution,
169
+ aten._slow_conv2d_forward,
170
+ aten.convolution_overrideable])
171
+ def conv_flop(x_shape, w_shape, _bias, _stride, _padding, _dilation, transposed, *args, out_shape=None, **kwargs) -> int:
172
+ """Count flops for convolution."""
173
+ # pyrefly: ignore [bad-argument-type]
174
+ return conv_flop_count(x_shape, w_shape, out_shape, transposed=transposed)
175
+
176
+
177
+ @register_flop_formula(aten.convolution_backward)
178
+ def conv_backward_flop(
179
+ grad_out_shape,
180
+ x_shape,
181
+ w_shape,
182
+ _bias,
183
+ _stride,
184
+ _padding,
185
+ _dilation,
186
+ transposed,
187
+ _output_padding,
188
+ _groups,
189
+ output_mask,
190
+ out_shape) -> int:
191
+
192
+ def t(shape):
193
+ return [shape[1], shape[0]] + list(shape[2:])
194
+ flop_count = 0
195
+
196
+ """
197
+ Let's say we have a regular 1D conv
198
+ {A, B, C} [inp]
199
+ {i, j} [weight]
200
+ => (conv)
201
+ {Ai + Bj, Bi + Cj} [out]
202
+
203
+ And as a reminder, the transposed conv of the above is
204
+ => {Ai, Aj + Bi, Bj + Ci, Cj} [transposed conv out]
205
+
206
+ For the backwards of conv, we now have
207
+ {D, E} [grad_out]
208
+ {A, B, C} [inp]
209
+ {i, j} [weight]
210
+
211
+ # grad_inp as conv_transpose(grad_out, weight)
212
+ Let's first compute grad_inp. To do so, we can simply look at all the
213
+ multiplications that each element of inp is involved in. For example, A is
214
+ only involved in the first element of the output (and thus only depends upon
215
+ D in grad_out), and C is only involved in the last element of the output
216
+ (and thus only depends upon E in grad_out)
217
+
218
+ {Di, Dj + Ei, Ej} [grad_inp]
219
+
220
+ Note that this corresponds to the below conv_transpose. This gives us the
221
+ output_mask[0] branch, which is grad_inp.
222
+
223
+ {D, E} [inp (grad_out)]
224
+ {i, j} [weight]
225
+ => (conv_transpose)
226
+ {Di, Dj + Ei, Ej} [out (grad_inp)]
227
+
228
+ I leave the fact that grad_inp for a transposed conv is just conv(grad_out,
229
+ weight) as an exercise for the reader.
230
+
231
+ # grad_weight as conv(inp, grad_out)
232
+ To compute grad_weight, we again look at the terms in the output, which as
233
+ a reminder is:
234
+ => {Ai + Bj, Bi + Cj} [out]
235
+ => {D, E} [grad_out]
236
+ If we manually compute the gradient for the weights, we see it's
237
+ {AD + BE, BD + CE} [grad_weight]
238
+
239
+ This corresponds to the below conv
240
+ {A, B, C} [inp]
241
+ {D, E} [weight (grad_out)]
242
+ => (conv)
243
+ {AD + BE, BD + CE} [out (grad_weight)]
244
+
245
+ # grad_weight of transposed conv as conv(grad_out, inp)
246
+ As a reminder, the terms of the output of a transposed conv are:
247
+ => {Ai, Aj + Bi, Bj + Ci, Cj} [transposed conv out]
248
+ => {D, E, F, G} [grad_out]
249
+
250
+ Manually computing the gradient for the weights, we see it's
251
+ {AD + BE + CF, AE + BF + CG} [grad_weight]
252
+
253
+ This corresponds to the below conv
254
+ {D, E, F, G} [inp (grad_out)]
255
+ {A, B, C} [weight (inp)]
256
+ => (conv)
257
+ {AD + BE + CF, AE + BF + CG} [out (grad_weight)]
258
+
259
+ For the full backwards formula, there are also some details involving
260
+ transpose of the batch/channel dimensions and groups, but I skip those for
261
+ the sake of brevity (and they're pretty similar to matmul backwards)
262
+
263
+ Check [conv backwards decomposition as conv forwards]
264
+ """
265
+ # grad_inp as conv_transpose(grad_out, weight)
266
+ if output_mask[0]:
267
+ grad_input_shape = get_shape(out_shape[0])
268
+ flop_count += conv_flop_count(grad_out_shape, w_shape, grad_input_shape, not transposed)
269
+
270
+ if output_mask[1]:
271
+ grad_weight_shape = get_shape(out_shape[1])
272
+ if transposed:
273
+ # grad_weight of transposed conv as conv(grad_out, inp)
274
+ flop_count += conv_flop_count(t(grad_out_shape), t(x_shape), t(grad_weight_shape), transposed=False)
275
+ else:
276
+ # grad_weight as conv(inp, grad_out)
277
+ flop_count += conv_flop_count(t(x_shape), t(grad_out_shape), t(grad_weight_shape), transposed=False)
278
+
279
+ return flop_count
280
+
281
+ def sdpa_flop_count(query_shape, key_shape, value_shape):
282
+ """
283
+ Count flops for self-attention.
284
+
285
+ Supports GQA (grouped-query attention) where key/value have fewer heads
286
+ than the query. The kernel broadcasts KV heads to match query heads.
287
+ """
288
+ b, h_q, s_q, d_q = query_shape
289
+ _b2, h_kv, s_k, _d2 = key_shape
290
+ _b3, _h3, _s3, d_v = value_shape
291
+ if not (b == _b2 == _b3 and h_kv == _h3 and d_q == _d2 and s_k == _s3):
292
+ raise AssertionError(
293
+ f"sdpa_flop_count: query/key/value shapes are incompatible: "
294
+ f"q={query_shape}, k={key_shape}, v={value_shape}"
295
+ )
296
+ if h_q < h_kv or h_q % h_kv != 0:
297
+ raise AssertionError(
298
+ f"sdpa_flop_count: query heads ({h_q}) must be a multiple of "
299
+ f"key/value heads ({h_kv})"
300
+ )
301
+ total_flops = 0
302
+ # q: [b, h_q, s_q, d_q] @ k: [b, h_q, d_q, s_k] -> scores: [b, h_q, s_q, s_k]
303
+ total_flops += bmm_flop((b * h_q, s_q, d_q), (b * h_q, d_q, s_k))
304
+ # scores: [b, h_q, s_q, s_k] @ v: [b, h_q, s_k, d_v] -> out: [b, h_q, s_q, d_v]
305
+ total_flops += bmm_flop((b * h_q, s_q, s_k), (b * h_q, s_k, d_v))
306
+ return total_flops
307
+
308
+
309
+ @register_flop_formula([aten._scaled_dot_product_efficient_attention,
310
+ aten._scaled_dot_product_flash_attention,
311
+ aten._scaled_dot_product_cudnn_attention])
312
+ def sdpa_flop(query_shape, key_shape, value_shape, *args, out_shape=None, **kwargs) -> int:
313
+ """Count flops for self-attention."""
314
+ # NB: We aren't accounting for causal attention here
315
+ return sdpa_flop_count(query_shape, key_shape, value_shape)
316
+
317
+
318
+ def _offsets_to_lengths(offsets, max_len):
319
+ """
320
+ If the offsets tensor is fake, then we don't know the actual lengths.
321
+ In that case, we can just assume the worst case; each batch has max length.
322
+ """
323
+ from torch._subclasses.fake_tensor import FakeTensor
324
+ from torch._subclasses.functional_tensor import FunctionalTensor
325
+ if not isinstance(offsets, (FakeTensor, FunctionalTensor)) and offsets.device.type != "meta":
326
+ return offsets.diff().tolist()
327
+ return [max_len] * (offsets.size(0) - 1)
328
+
329
+
330
+ def _unpack_flash_attention_nested_shapes(
331
+ *,
332
+ query,
333
+ key,
334
+ value,
335
+ grad_out=None,
336
+ cum_seq_q,
337
+ cum_seq_k,
338
+ max_q,
339
+ max_k,
340
+ ) -> Iterator[tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[int, ...] | None]]:
341
+ """
342
+ Given inputs to a flash_attention_(forward|backward) kernel, this will handle behavior for
343
+ NestedTensor inputs by effectively unbinding the NestedTensor and yielding the shapes for
344
+ each batch element.
345
+
346
+ In the case that this isn't a NestedTensor kernel, then it just yields the original shapes.
347
+ """
348
+ if cum_seq_q is not None:
349
+ # This means we should be dealing with a Nested Jagged Tensor query.
350
+ # The inputs will have shape (sum(sequence len), heads, dimension)
351
+ # In comparison, non-Nested inputs have shape (batch, heads, sequence len, dimension)
352
+ # To deal with this, we convert to a shape of (batch, heads, max_seq_len, dimension)
353
+ # So the flops calculation in this case is an overestimate of the actual flops.
354
+ if len(key.shape) != 3:
355
+ raise AssertionError("sdpa_flop_count: expected key.shape to be 3-dimensional")
356
+ if len(value.shape) != 3:
357
+ raise AssertionError("sdpa_flop_count: expected value.shape to be 3-dimensional")
358
+ if grad_out is not None and grad_out.shape != query.shape:
359
+ raise AssertionError("sdpa_flop_count: grad_out.shape must match query.shape when provided")
360
+ _, h_q, d_q = query.shape
361
+ _, h_k, d_k = key.shape
362
+ _, h_v, d_v = value.shape
363
+ if cum_seq_q is None:
364
+ raise AssertionError("sdpa_flop_count: cum_seq_q must not be None")
365
+ if cum_seq_k is None:
366
+ raise AssertionError("sdpa_flop_count: cum_seq_k must not be None")
367
+ if cum_seq_q.shape != cum_seq_k.shape:
368
+ raise AssertionError("sdpa_flop_count: cum_seq_q and cum_seq_k must have the same shape")
369
+ seq_q_lengths = _offsets_to_lengths(cum_seq_q, max_q)
370
+ seq_k_lengths = _offsets_to_lengths(cum_seq_k, max_k)
371
+ for (seq_q_len, seq_k_len) in zip(seq_q_lengths, seq_k_lengths, strict=True):
372
+ new_query_shape = (1, h_q, seq_q_len, d_q)
373
+ new_key_shape = (1, h_k, seq_k_len, d_k)
374
+ new_value_shape = (1, h_v, seq_k_len, d_v)
375
+ new_grad_out_shape = new_query_shape if grad_out is not None else None
376
+ yield new_query_shape, new_key_shape, new_value_shape, new_grad_out_shape
377
+ return
378
+
379
+ yield query.shape, key.shape, value.shape, grad_out.shape if grad_out is not None else None
380
+
381
+
382
+ def _unpack_efficient_attention_nested_shapes(
383
+ *,
384
+ query,
385
+ key,
386
+ value,
387
+ grad_out=None,
388
+ cu_seqlens_q,
389
+ cu_seqlens_k,
390
+ max_seqlen_q,
391
+ max_seqlen_k,
392
+ ) -> Iterator[tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...], tuple[int, ...] | None]]:
393
+ """
394
+ Given inputs to a efficient_attention_(forward|backward) kernel, this will handle behavior for
395
+ NestedTensor inputs by effectively unbinding the NestedTensor and yielding the shapes for
396
+ each batch element.
397
+
398
+ In the case that this isn't a NestedTensor kernel, then it just yields the original shapes.
399
+ """
400
+ if cu_seqlens_q is not None:
401
+ # Unlike flash_attention_forward, we get a 4D tensor instead of a 3D tensor for efficient attention.
402
+ #
403
+ # This means we should be dealing with a Nested Jagged Tensor query.
404
+ # The inputs will have shape (sum(sequence len), heads, dimension)
405
+ # In comparison, non-Nested inputs have shape (batch, heads, sequence len, dimension)
406
+ # To deal with this, we convert to a shape of (batch, heads, max_seq_len, dimension)
407
+ # So the flops calculation in this case is an overestimate of the actual flops.
408
+ if len(key.shape) != 4:
409
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: expected key.shape to be 4-dimensional")
410
+ if len(value.shape) != 4:
411
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: expected value.shape to be 4-dimensional")
412
+ if grad_out is not None and grad_out.shape != query.shape:
413
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: grad_out.shape must match query.shape when provided")
414
+ _, _, h_q, d_q = query.shape
415
+ _, _, h_k, d_k = key.shape
416
+ _, _, h_v, d_v = value.shape
417
+ if cu_seqlens_q is None:
418
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: cu_seqlens_q must not be None")
419
+ if cu_seqlens_k is None:
420
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: cu_seqlens_k must not be None")
421
+ if cu_seqlens_q.shape != cu_seqlens_k.shape:
422
+ raise AssertionError("_unpack_efficient_attention_nested_shapes: "
423
+ "cu_seqlens_q and cu_seqlens_k must have the same shape")
424
+ seqlens_q = _offsets_to_lengths(cu_seqlens_q, max_seqlen_q)
425
+ seqlens_k = _offsets_to_lengths(cu_seqlens_k, max_seqlen_k)
426
+ for len_q, len_k in zip(seqlens_q, seqlens_k, strict=True):
427
+ new_query_shape = (1, h_q, len_q, d_q)
428
+ new_key_shape = (1, h_k, len_k, d_k)
429
+ new_value_shape = (1, h_v, len_k, d_v)
430
+ new_grad_out_shape = new_query_shape if grad_out is not None else None
431
+ yield new_query_shape, new_key_shape, new_value_shape, new_grad_out_shape
432
+ return
433
+
434
+ yield query.shape, key.shape, value.shape, grad_out.shape if grad_out is not None else None
435
+
436
+
437
+ @register_flop_formula(aten._flash_attention_forward, get_raw=True)
438
+ def _flash_attention_forward_flop(
439
+ query,
440
+ key,
441
+ value,
442
+ cum_seq_q,
443
+ cum_seq_k,
444
+ max_q,
445
+ max_k,
446
+ *args,
447
+ out_shape=None,
448
+ **kwargs
449
+ ) -> int:
450
+ """Count flops for self-attention."""
451
+ # NB: We aren't accounting for causal attention here
452
+ # in case this is a nested tensor, we unpack the individual batch elements
453
+ # and then sum the flops per batch element
454
+ sizes = _unpack_flash_attention_nested_shapes(
455
+ query=query,
456
+ key=key,
457
+ value=value,
458
+ cum_seq_q=cum_seq_q,
459
+ cum_seq_k=cum_seq_k,
460
+ max_q=max_q,
461
+ max_k=max_k,
462
+ )
463
+ return sum(
464
+ sdpa_flop_count(query_shape, key_shape, value_shape)
465
+ for query_shape, key_shape, value_shape, _ in sizes
466
+ )
467
+
468
+
469
+ @register_flop_formula(aten._efficient_attention_forward, get_raw=True)
470
+ def _efficient_attention_forward_flop(
471
+ query,
472
+ key,
473
+ value,
474
+ bias,
475
+ cu_seqlens_q,
476
+ cu_seqlens_k,
477
+ max_seqlen_q,
478
+ max_seqlen_k,
479
+ *args,
480
+ **kwargs
481
+ ) -> int:
482
+ """Count flops for self-attention."""
483
+ # NB: We aren't accounting for causal attention here
484
+ # in case this is a nested tensor, we unpack the individual batch elements
485
+ # and then sum the flops per batch element
486
+ sizes = _unpack_efficient_attention_nested_shapes(
487
+ query=query,
488
+ key=key,
489
+ value=value,
490
+ cu_seqlens_q=cu_seqlens_q,
491
+ cu_seqlens_k=cu_seqlens_k,
492
+ max_seqlen_q=max_seqlen_q,
493
+ max_seqlen_k=max_seqlen_k,
494
+ )
495
+ return sum(
496
+ sdpa_flop_count(query_shape, key_shape, value_shape)
497
+ for query_shape, key_shape, value_shape, _ in sizes
498
+ )
499
+
500
+
501
+ def sdpa_backward_flop_count(grad_out_shape, query_shape, key_shape, value_shape):
502
+ b, h_q, s_q, d_q = query_shape
503
+ _b2, h_kv, s_k, _d2 = key_shape
504
+ _b3, _h3, _s3, d_v = value_shape
505
+ _b4, _h4, _s4, _d4 = grad_out_shape
506
+ if not (b == _b2 == _b3 == _b4 and h_kv == _h3 and h_q == _h4):
507
+ raise AssertionError(
508
+ "sdpa_backward_flop_count: batch/heads mismatch among tensors"
509
+ )
510
+ if h_q < h_kv or h_q % h_kv != 0:
511
+ raise AssertionError(
512
+ f"sdpa_backward_flop_count: query heads ({h_q}) must be a multiple of "
513
+ f"key/value heads ({h_kv})"
514
+ )
515
+ if not (d_q == _d2 and d_v == _d4 and s_k == _s3 and s_q == _s4):
516
+ raise AssertionError(
517
+ "sdpa_backward_flop_count: grad_out/value/key/query shapes are incompatible"
518
+ )
519
+ total_flops = 0
520
+ # Step 1: We recompute the scores matrix.
521
+ # q: [b, h_q, s_q, d_q] @ k: [b, h_q, d_q, s_k] -> scores: [b, h_q, s_q, s_k]
522
+ total_flops += bmm_flop((b * h_q, s_q, d_q), (b * h_q, d_q, s_k))
523
+
524
+ # Step 2: We propagate the gradients through the score @ v operation.
525
+ # gradOut: [b, h_q, s_q, d_v] @ v: [b, h_q, d_v, s_k] -> gradScores: [b, h_q, s_q, s_k]
526
+ total_flops += bmm_flop((b * h_q, s_q, d_v), (b * h_q, d_v, s_k))
527
+ # scores: [b, h_q, s_k, s_q] @ gradOut: [b, h_q, s_q, d_v] -> gradV: [b, h_q, s_k, d_v]
528
+ total_flops += bmm_flop((b * h_q, s_k, s_q), (b * h_q, s_q, d_v))
529
+
530
+ # Step 3: We propagate th gradients through the k @ v operation
531
+ # gradScores: [b, h_q, s_q, s_k] @ k: [b, h_q, s_k, d_q] -> gradQ: [b, h_q, s_q, d_q]
532
+ total_flops += bmm_flop((b * h_q, s_q, s_k), (b * h_q, s_k, d_q))
533
+ # q: [b, h_q, d_q, s_q] @ gradScores: [b, h_q, s_q, s_k] -> gradK: [b, h_q, d_q, s_k]
534
+ total_flops += bmm_flop((b * h_q, d_q, s_q), (b * h_q, s_q, s_k))
535
+ return total_flops
536
+
537
+
538
+ @register_flop_formula([aten._scaled_dot_product_efficient_attention_backward,
539
+ aten._scaled_dot_product_flash_attention_backward,
540
+ aten._scaled_dot_product_cudnn_attention_backward])
541
+ def sdpa_backward_flop(grad_out_shape, query_shape, key_shape, value_shape, *args, out_shape=None, **kwargs) -> int:
542
+ """Count flops for self-attention backward."""
543
+ return sdpa_backward_flop_count(grad_out_shape, query_shape, key_shape, value_shape)
544
+
545
+ @register_flop_formula(aten._flash_attention_backward, get_raw=True)
546
+ def _flash_attention_backward_flop(
547
+ grad_out,
548
+ query,
549
+ key,
550
+ value,
551
+ out, # named _out_shape to avoid kwarg collision with out_shape created in wrapper
552
+ logsumexp,
553
+ cum_seq_q,
554
+ cum_seq_k,
555
+ max_q,
556
+ max_k,
557
+ *args,
558
+ **kwargs,
559
+ ) -> int:
560
+ # in case this is a nested tensor, we unpack the individual batch elements
561
+ # and then sum the flops per batch element
562
+ shapes = _unpack_flash_attention_nested_shapes(
563
+ query=query,
564
+ key=key,
565
+ value=value,
566
+ grad_out=grad_out,
567
+ cum_seq_q=cum_seq_q,
568
+ cum_seq_k=cum_seq_k,
569
+ max_q=max_q,
570
+ max_k=max_k,
571
+ )
572
+ return sum(
573
+ sdpa_backward_flop_count(grad_out_shape, query_shape, key_shape, value_shape)
574
+ for query_shape, key_shape, value_shape, grad_out_shape in shapes
575
+ )
576
+
577
+
578
+ @register_flop_formula(aten._efficient_attention_backward, get_raw=True)
579
+ def _efficient_attention_backward_flop(
580
+ grad_out,
581
+ query,
582
+ key,
583
+ value,
584
+ bias,
585
+ out, # named _out to avoid kwarg collision with out created in wrapper
586
+ cu_seqlens_q,
587
+ cu_seqlens_k,
588
+ max_seqlen_q,
589
+ max_seqlen_k,
590
+ *args,
591
+ **kwargs,
592
+ ) -> int:
593
+ # in case this is a nested tensor, we unpack the individual batch elements
594
+ # and then sum the flops per batch element
595
+ shapes = _unpack_efficient_attention_nested_shapes(
596
+ query=query,
597
+ key=key,
598
+ value=value,
599
+ grad_out=grad_out,
600
+ cu_seqlens_q=cu_seqlens_q,
601
+ cu_seqlens_k=cu_seqlens_k,
602
+ max_seqlen_q=max_seqlen_q,
603
+ max_seqlen_k=max_seqlen_k,
604
+ )
605
+ return sum(
606
+ sdpa_backward_flop_count(grad_out_shape, query_shape, key_shape, value_shape)
607
+ for query_shape, key_shape, value_shape, grad_out_shape in shapes
608
+ )
609
+
610
+
611
+ def _varlen_attn_forward_flop(
612
+ query,
613
+ key,
614
+ value,
615
+ cu_seq_q,
616
+ cu_seq_k,
617
+ max_q,
618
+ max_k,
619
+ *args,
620
+ out_val=None,
621
+ **kwargs,
622
+ ) -> int:
623
+ """Count flops for varlen_attn forward."""
624
+ sizes = _unpack_flash_attention_nested_shapes(
625
+ query=query,
626
+ key=key,
627
+ value=value,
628
+ cum_seq_q=cu_seq_q,
629
+ cum_seq_k=cu_seq_k if cu_seq_k is not None else cu_seq_q,
630
+ max_q=max_q,
631
+ max_k=max_k,
632
+ )
633
+ return sum(
634
+ sdpa_flop_count(query_shape, key_shape, value_shape)
635
+ for query_shape, key_shape, value_shape, _ in sizes
636
+ )
637
+
638
+
639
+ def _varlen_attn_out_flop(
640
+ out,
641
+ query,
642
+ key,
643
+ value,
644
+ cu_seq_q,
645
+ cu_seq_k,
646
+ max_q,
647
+ max_k,
648
+ *args,
649
+ out_val=None,
650
+ **kwargs,
651
+ ) -> int:
652
+ """Count flops for varlen_attn_out forward."""
653
+ return _varlen_attn_forward_flop(
654
+ query, key, value, cu_seq_q, cu_seq_k, max_q, max_k,
655
+ )
656
+
657
+
658
+ def _varlen_attn_backward_flop(
659
+ grad_out,
660
+ query,
661
+ key,
662
+ value,
663
+ out,
664
+ lse,
665
+ cu_seq_q,
666
+ cu_seq_k,
667
+ max_q,
668
+ max_k,
669
+ *args,
670
+ out_val=None,
671
+ **kwargs,
672
+ ) -> int:
673
+ """Count flops for varlen_attn backward."""
674
+ sizes = _unpack_flash_attention_nested_shapes(
675
+ query=query,
676
+ key=key,
677
+ value=value,
678
+ grad_out=grad_out,
679
+ cum_seq_q=cu_seq_q,
680
+ cum_seq_k=cu_seq_k,
681
+ max_q=max_q,
682
+ max_k=max_k,
683
+ )
684
+ return sum(
685
+ sdpa_backward_flop_count(grad_out_shape, query_shape, key_shape, value_shape)
686
+ for query_shape, key_shape, value_shape, grad_out_shape in sizes
687
+ )
688
+
689
+
690
+ flop_registry = {
691
+ aten.mm: mm_flop,
692
+ aten.addmm: addmm_flop,
693
+ aten.bmm: bmm_flop,
694
+ aten.baddbmm: baddbmm_flop,
695
+ aten._scaled_mm: _scaled_mm_flop,
696
+ aten.convolution: conv_flop,
697
+ aten._convolution: conv_flop,
698
+ aten.cudnn_convolution: conv_flop,
699
+ aten.convolution_overrideable: conv_flop,
700
+ aten._slow_conv2d_forward: conv_flop,
701
+ aten.convolution_backward: conv_backward_flop,
702
+ aten._scaled_dot_product_efficient_attention: sdpa_flop,
703
+ aten._scaled_dot_product_flash_attention: sdpa_flop,
704
+ aten._scaled_dot_product_cudnn_attention: sdpa_flop,
705
+ aten._scaled_dot_product_efficient_attention_backward: sdpa_backward_flop,
706
+ aten._scaled_dot_product_flash_attention_backward: sdpa_backward_flop,
707
+ aten._scaled_dot_product_cudnn_attention_backward: sdpa_backward_flop,
708
+ aten._flash_attention_forward: _flash_attention_forward_flop,
709
+ aten._efficient_attention_forward: _efficient_attention_forward_flop,
710
+ aten._flash_attention_backward: _flash_attention_backward_flop,
711
+ aten._efficient_attention_backward: _efficient_attention_backward_flop,
712
+ }
713
+
714
+ def normalize_tuple(x):
715
+ if not isinstance(x, tuple):
716
+ return (x,)
717
+ return x
718
+
719
+
720
+ # Define the suffixes for different orders of magnitude
721
+ suffixes = ["", "K", "M", "B", "T"]
722
+ # Thanks BingChat!
723
+ def get_suffix_str(number):
724
+ # Find the index of the appropriate suffix based on the number of digits
725
+ # with some additional overflow.
726
+ # i.e. 1.01B should be displayed as 1001M, not 1.001B
727
+ index = max(0, min(len(suffixes) - 1, (len(str(number)) - 2) // 3))
728
+ return suffixes[index]
729
+
730
+ def convert_num_with_suffix(number, suffix):
731
+ index = suffixes.index(suffix)
732
+ # Divide the number by 1000^index and format it to two decimal places
733
+ value = f"{number / 1000 ** index:.3f}"
734
+ # Return the value and the suffix as a string
735
+ return value + suffixes[index]
736
+
737
+ def convert_to_percent_str(num, denom) -> str:
738
+ if denom == 0:
739
+ return "0%"
740
+ return f"{num / denom:.2%}"
741
+
742
+ def _pytreeify_preserve_structure(f):
743
+ @wraps(f)
744
+ def nf(args):
745
+ flat_args, spec = tree_flatten(args)
746
+ out = f(*flat_args)
747
+ return tree_unflatten(out, spec)
748
+
749
+ return nf
750
+
751
+
752
+ class FlopCounterMode:
753
+ """
754
+ ``FlopCounterMode`` is a context manager that counts the number of flops within its context.
755
+
756
+ It does this using a ``TorchDispatchMode``.
757
+
758
+ It also supports hierarchical output by passing a module (or list of
759
+ modules) to FlopCounterMode on construction. If you do not need hierarchical
760
+ output, you do not need to use it with a module.
761
+
762
+ Example usage
763
+
764
+ .. code-block:: python
765
+
766
+ mod = ...
767
+ with FlopCounterMode(mod) as flop_counter:
768
+ mod.sum().backward()
769
+
770
+ """
771
+
772
+ def __init__(
773
+ self,
774
+ mods: torch.nn.Module | list[torch.nn.Module] | None = None,
775
+ depth: int = 2,
776
+ display: bool = True,
777
+ custom_mapping: dict[Any, Any] | None = None) -> None:
778
+ super().__init__()
779
+ self.flop_counts: dict[str, dict[Any, int]] = defaultdict(lambda: defaultdict(int))
780
+ self.depth = depth
781
+ self.display = display
782
+ self.mode: _FlopCounterMode | None = None
783
+ if custom_mapping is None:
784
+ custom_mapping = {}
785
+ if mods is not None:
786
+ warnings.warn("mods argument is not needed anymore, you can stop passing it", stacklevel=2)
787
+ self.flop_registry = {
788
+ **flop_registry,
789
+ **{k: v if getattr(v, "_get_raw", False) else shape_wrapper(v) for k, v in custom_mapping.items()}
790
+ }
791
+ self.mod_tracker = ModuleTracker()
792
+
793
+ def get_total_flops(self) -> int:
794
+ return sum(self.flop_counts['Global'].values())
795
+
796
+ def get_flop_counts(self) -> dict[str, dict[Any, int]]:
797
+ """Return the flop counts as a dictionary of dictionaries.
798
+
799
+ The outer
800
+ dictionary is keyed by module name, and the inner dictionary is keyed by
801
+ operation name.
802
+
803
+ Returns:
804
+ Dict[str, Dict[Any, int]]: The flop counts as a dictionary.
805
+ """
806
+ return {k: dict(v) for k, v in self.flop_counts.items()}
807
+
808
+ def get_table(self, depth=None):
809
+ if depth is None:
810
+ depth = self.depth
811
+ if depth is None:
812
+ depth = 999999
813
+
814
+
815
+ import tabulate
816
+
817
+ tabulate.PRESERVE_WHITESPACE = True
818
+ header = ["Module", "FLOP", "% Total"]
819
+ values = []
820
+ global_flops = self.get_total_flops()
821
+ global_suffix = get_suffix_str(global_flops)
822
+ is_global_subsumed = False
823
+
824
+ def process_mod(mod_name, depth):
825
+ nonlocal is_global_subsumed
826
+
827
+ total_flops = sum(self.flop_counts[mod_name].values())
828
+
829
+ is_global_subsumed |= total_flops >= global_flops
830
+
831
+ padding = " " * depth
832
+ values = []
833
+ values.append([
834
+ padding + mod_name,
835
+ convert_num_with_suffix(total_flops, global_suffix),
836
+ convert_to_percent_str(total_flops, global_flops)
837
+ ])
838
+ for k, v in self.flop_counts[mod_name].items():
839
+ values.append([
840
+ padding + " - " + str(k),
841
+ convert_num_with_suffix(v, global_suffix),
842
+ convert_to_percent_str(v, global_flops)
843
+ ])
844
+ return values
845
+
846
+ for mod in sorted(self.flop_counts.keys()):
847
+ if mod == 'Global':
848
+ continue
849
+ mod_depth = mod.count(".") + 1
850
+ if mod_depth > depth:
851
+ continue
852
+
853
+ cur_values = process_mod(mod, mod_depth - 1)
854
+ values.extend(cur_values)
855
+
856
+ # We do a bit of messing around here to only output the "Global" value
857
+ # if there are any FLOPs in there that aren't already fully contained by
858
+ # a module.
859
+ if 'Global' in self.flop_counts and not is_global_subsumed:
860
+ for value in values:
861
+ value[0] = " " + value[0]
862
+
863
+ values = process_mod('Global', 0) + values
864
+
865
+ if len(values) == 0:
866
+ values = [["Global", "0", "0%"]]
867
+
868
+ return tabulate.tabulate(values, headers=header, colalign=("left", "right", "right"))
869
+
870
+ # NB: This context manager is NOT reentrant
871
+ def __enter__(self):
872
+ self.flop_counts.clear()
873
+ self.mod_tracker.__enter__()
874
+ self.mode = _FlopCounterMode(self)
875
+ self.mode.__enter__()
876
+ return self
877
+
878
+ def __exit__(self, *args):
879
+ if self.mode is None:
880
+ raise AssertionError("Internal error: FlopCounter.__exit__ called but mode is None")
881
+ b = self.mode.__exit__(*args)
882
+ self.mode = None # break cycles
883
+ self.mod_tracker.__exit__()
884
+ if self.display:
885
+ print(self.get_table(self.depth))
886
+ return b
887
+
888
+ def _count_flops(self, func_packet, out, args, kwargs):
889
+ if func_packet in self.flop_registry:
890
+ flop_count_func = self.flop_registry[func_packet]
891
+ flop_count = flop_count_func(*args, **kwargs, out_val=out) # type: ignore[operator]
892
+ for par in set(self.mod_tracker.parents):
893
+ self.flop_counts[par][func_packet] += flop_count
894
+ return out
895
+
896
+ class _FlopCounterMode(TorchDispatchMode):
897
+ supports_higher_order_operators = True
898
+
899
+ def __init__(self, counter: FlopCounterMode) -> None:
900
+ self.counter = counter
901
+
902
+ def _execute_with_isolated_flop_counting(self, branch_fn, operands):
903
+ """Execute a branch function and capture its FLOP counts without
904
+ affecting self.counter.flop_counts
905
+
906
+ Args:
907
+ branch_fn: The branch function to execute
908
+ operands: Arguments to pass to the branch function
909
+
910
+ Returns:
911
+ Tuple of (result, flop_counts) where result is the branch output
912
+ and flop_counts is a copy of the FLOP counts after execution
913
+ """
914
+ import copy
915
+ checkpointed_flop_counts = copy.copy(self.counter.flop_counts)
916
+ with self:
917
+ result = branch_fn(*operands)
918
+ flop_counts = copy.copy(self.counter.flop_counts)
919
+ self.counter.flop_counts = checkpointed_flop_counts
920
+ return result, flop_counts
921
+
922
+ def _handle_higher_order_ops(self, func, types, args, kwargs):
923
+ is_triton = func in {torch.ops.higher_order.triton_kernel_wrapper_mutation,
924
+ torch.ops.higher_order.triton_kernel_wrapper_functional}
925
+ if is_triton:
926
+ from torch._higher_order_ops.triton_kernel_wrap import get_kernel
927
+ # Special case - look in the triton flop registry for the kernel
928
+ from triton.runtime.jit import JITFunction
929
+ kernel_name = get_kernel(kwargs["kernel_idx"])
930
+ # Unwrap heuristics if they are present
931
+ while not isinstance(kernel_name, JITFunction):
932
+ if hasattr(kernel_name, "fn"):
933
+ kernel_name = kernel_name.fn
934
+ else:
935
+ break
936
+ return self.counter._count_flops(kernel_name, None, args, kwargs)
937
+ elif func is torch.ops.higher_order.cond:
938
+ # The flop counter for cond counts the upper bound of flops.
939
+ # For example, if a matmul is executed 2 times in true branch
940
+ # but only 1 time in the false branch, the flop counter will
941
+ # record the larger number of flops, i.e. 2 times.
942
+ pred, true_branch, false_branch, operands = args
943
+ # Step 1: Count flops for true branch and false branch separately
944
+ true_out, true_flop_counts = self._execute_with_isolated_flop_counting(
945
+ true_branch, operands
946
+ )
947
+ if true_out is NotImplemented:
948
+ return NotImplemented
949
+
950
+ false_out, false_flop_counts = self._execute_with_isolated_flop_counting(
951
+ false_branch, operands
952
+ )
953
+ if false_out is NotImplemented:
954
+ return NotImplemented
955
+
956
+ # Step 2: merge flop counts
957
+ all_mod_keys = set(true_flop_counts.keys()) | set(false_flop_counts.keys())
958
+ merged_flop_counts = {}
959
+ for outer_key in all_mod_keys:
960
+ true_func_counts = true_flop_counts[outer_key]
961
+ false_func_counts = false_flop_counts[outer_key]
962
+
963
+ merged_func_counts = {}
964
+ all_func_keys = set(true_func_counts.keys()) | set(false_func_counts.keys())
965
+
966
+ for func_key in all_func_keys:
967
+ true_val = true_func_counts.get(func_key, 0)
968
+ false_val = false_func_counts.get(func_key, 0)
969
+ merged_func_counts[func_key] = max(true_val, false_val)
970
+
971
+ merged_flop_counts[outer_key] = merged_func_counts
972
+
973
+ # Step 3: update the counter with merged counts
974
+ for outer_key, inner_dict in merged_flop_counts.items():
975
+ self.counter.flop_counts[outer_key].update(inner_dict)
976
+
977
+ # It doesn't matter which one we return since true_fn and false_fn return
978
+ # output with the same structure.
979
+ return true_out
980
+ else:
981
+ return NotImplemented
982
+
983
+ def __torch_dispatch__(self, func, types, args=(), kwargs=None):
984
+ kwargs = kwargs if kwargs else {}
985
+
986
+ # Skip ops from non-standard dispatch_sizes_strides_policy such as NJT
987
+ if func in {torch.ops.aten.sym_is_contiguous.default,
988
+ torch.ops.aten.is_contiguous.default,
989
+ torch.ops.aten.is_contiguous.memory_format,
990
+ torch.ops.aten.is_strides_like_format.default,
991
+ torch.ops.aten.is_non_overlapping_and_dense.default,
992
+ torch.ops.aten.size.default,
993
+ torch.ops.aten.sym_size.default,
994
+ torch.ops.aten.stride.default,
995
+ torch.ops.aten.sym_stride.default,
996
+ torch.ops.aten.storage_offset.default,
997
+ torch.ops.aten.sym_storage_offset.default,
998
+ torch.ops.aten.numel.default,
999
+ torch.ops.aten.sym_numel.default,
1000
+ torch.ops.aten.dim.default,
1001
+ torch.ops.prim.layout.default}:
1002
+
1003
+ return NotImplemented
1004
+
1005
+ if isinstance(func, torch._ops.HigherOrderOperator):
1006
+ return self._handle_higher_order_ops(func, types, args, kwargs)
1007
+
1008
+ # If we don't have func in flop_registry, see if it can decompose
1009
+ if func not in self.counter.flop_registry and func is not torch.ops.prim.device.default:
1010
+ with self:
1011
+ r = func.decompose(*args, **kwargs)
1012
+ if r is not NotImplemented:
1013
+ return r
1014
+
1015
+ # no further decomposition; execute & count flops
1016
+ out = func(*args, **kwargs)
1017
+ return self.counter._count_flops(func._overloadpacket, out, args, kwargs)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .version import __version__
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/constants.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Constants for annotations in the mapping.
2
+
3
+ The constants defined here are used to annotate the mapping tuples in cuda_to_hip_mappings.py.
4
+ They are based on
5
+ https://github.com/ROCm/HIPIFY/blob/master/src/Statistics.h
6
+ and fall in three categories: 1) type of mapping, 2) API of mapping, 3) unsupported
7
+ mapping.
8
+ """
9
+
10
+ import warnings
11
+ warnings.warn("hipify's constants.py is no longer used as of version 2.0.0", FutureWarning)
12
+
13
+ CONV_VERSION = 0,
14
+ CONV_INIT = 1
15
+ CONV_DEVICE = 2
16
+ CONV_MEM = 3
17
+ CONV_KERN = 4
18
+ CONV_COORD_FUNC = 5
19
+ CONV_MATH_FUNC = 6
20
+ CONV_DEVICE_FUNC = 7
21
+ CONV_SPECIAL_FUNC = 8
22
+ CONV_STREAM = 9
23
+ CONV_EVENT = 10
24
+ CONV_OCCUPANCY = 11
25
+ CONV_CONTEXT = 12
26
+ CONV_PEER = 13
27
+ CONV_MODULE = 14
28
+ CONV_CACHE = 15
29
+ CONV_EXEC = 16
30
+ CONV_ERROR = 17
31
+ CONV_DEF = 18
32
+ CONV_TEX = 19
33
+ CONV_GL = 20
34
+ CONV_GRAPHICS = 21
35
+ CONV_SURFACE = 22
36
+ CONV_JIT = 23
37
+ CONV_D3D9 = 24
38
+ CONV_D3D10 = 25
39
+ CONV_D3D11 = 26
40
+ CONV_VDPAU = 27
41
+ CONV_EGL = 28
42
+ CONV_THREAD = 29
43
+ CONV_OTHER = 30
44
+ CONV_INCLUDE = 31
45
+ CONV_INCLUDE_CUDA_MAIN_H = 32
46
+ CONV_TYPE = 33
47
+ CONV_LITERAL = 34
48
+ CONV_NUMERIC_LITERAL = 35
49
+ CONV_LAST = 36
50
+
51
+ API_DRIVER = 37
52
+ API_RUNTIME = 38
53
+ API_BLAS = 39
54
+ API_SPECIAL = 40
55
+ API_RAND = 41
56
+ API_LAST = 42
57
+ API_FFT = 43
58
+ API_RTC = 44
59
+ API_ROCTX = 45
60
+ API_PYT_EXT = 46
61
+
62
+ HIP_UNSUPPORTED = 47
63
+ API_PYTORCH = 1337
64
+ API_CAFFE2 = 1338
65
+ API_C10 = 1339
66
+ API_ROCMSMI = 1340
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/cuda_to_hip_mappings.py ADDED
The diff for this file is too large to render. See raw diff
 
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/hipify_python.py ADDED
@@ -0,0 +1,1175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # mypy: allow-untyped-defs
3
+ """ The Python Hipify script.
4
+ ##
5
+ # Copyright (c) 2015-2016 Advanced Micro Devices, Inc. All rights reserved.
6
+ # 2017-2018 Advanced Micro Devices, Inc. and
7
+ # Facebook Inc. All rights reserved.
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in
17
+ # all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ # THE SOFTWARE.
26
+ """
27
+ import argparse
28
+ import fnmatch
29
+ import re
30
+ import shutil
31
+ import sys
32
+ import os
33
+ import warnings
34
+
35
+ from .cuda_to_hip_mappings import CUDA_TO_HIP_MAPPINGS
36
+ from .cuda_to_hip_mappings import MATH_TRANSPILATIONS
37
+ from .cuda_to_hip_mappings import CAFFE2_PATH_MAPPINGS
38
+
39
+ from collections.abc import Iterator
40
+ from collections.abc import Mapping, Iterable
41
+ from enum import Enum
42
+ import functools
43
+ import hashlib
44
+
45
+ def _deprecated(name):
46
+ warnings.warn(f"hipify version 2.0.0 no longer uses function {name}", FutureWarning, stacklevel=2)
47
+
48
+ class CurrentState(Enum):
49
+ INITIALIZED = 1
50
+ DONE = 2
51
+
52
+ class HipifyResult:
53
+ def __init__(self, current_state, hipified_path) -> None:
54
+ self.current_state = current_state
55
+ self.hipified_path = hipified_path
56
+ self.status = ""
57
+
58
+ def __str__(self) -> str:
59
+ return (f"HipifyResult:: current_state: {self.current_state}, hipified_path : {self.hipified_path}, status: {self.status}")
60
+
61
+ HipifyFinalResult = dict[str, HipifyResult]
62
+ HIPIFY_C_BREADCRUMB = "// !!! This is a file automatically generated by hipify!!!\n"
63
+ HIPIFY_FINAL_RESULT: HipifyFinalResult = {}
64
+
65
+ # Hardcode the PyTorch template map
66
+ """This dictionary provides the mapping from PyTorch kernel template types
67
+ to their actual types."""
68
+ PYTORCH_TEMPLATE_MAP = {"Dtype": "scalar_t", "T": "scalar_t"}
69
+
70
+ __all__ = ['InputError', 'openf', 'bcolors', 'GeneratedFileCleaner', 'match_extensions', 'matched_files_iter',
71
+ 'preprocess_file_and_save_result', 'compute_stats', 'add_dim3', 'processKernelLaunches', 'find_closure_group',
72
+ 'find_bracket_group', 'find_parentheses_group', 'replace_math_functions', 'hip_header_magic', 'replace_extern_shared',
73
+ 'get_hip_file_path', 'is_out_of_place', 'is_pytorch_file', 'is_cusparse_file', 'is_special_file', 'is_caffe2_gpu_file',
74
+ 'Trie', 'preprocessor', 'file_specific_replacement', 'file_add_header',
75
+ 'fix_static_global_kernels', 'extract_arguments', 'str2bool', 'CurrentState', 'HipifyResult', 'hipify']
76
+
77
+
78
+ class InputError(Exception):
79
+ # Exception raised for errors in the input.
80
+
81
+ def __init__(self, message) -> None:
82
+ super().__init__(message)
83
+ self.message = message
84
+
85
+ def __str__(self) -> str:
86
+ return f"Input error: {self.message}"
87
+
88
+
89
+ def openf(filename, mode):
90
+ return open(filename, mode, errors='ignore')
91
+
92
+
93
+ # Color coding for printing
94
+ class bcolors:
95
+ HEADER = '\033[95m'
96
+ OKBLUE = '\033[94m'
97
+ OKGREEN = '\033[92m'
98
+ WARNING = '\033[93m'
99
+ FAIL = '\033[91m'
100
+ ENDC = '\033[0m'
101
+ BOLD = '\033[1m'
102
+ UNDERLINE = '\033[4m'
103
+
104
+
105
+ # To the programmer, the output of hipify most likely are intermediates.
106
+ # This class allows users of hipify to ask for a cleanup by running the
107
+ # hipify and compilation in a with instantiating this context manager class
108
+ # with keep_intermediates=False.
109
+ # The main usecase is the cpp_extensions, specifically the load method.
110
+ # It is a good idea to keep intermediates (in case of errors or to
111
+ # not recompile unchanged files), but in cases where you don't want to
112
+ # keep them (e.g. in the CI), this can be used to remove files.
113
+ class GeneratedFileCleaner:
114
+ """Context Manager to clean up generated files"""
115
+ def __init__(self, keep_intermediates=False) -> None:
116
+ self.keep_intermediates = keep_intermediates
117
+ self.files_to_clean = set()
118
+ self.dirs_to_clean = []
119
+
120
+ def __enter__(self):
121
+ return self
122
+
123
+ def open(self, fn, *args, **kwargs):
124
+ if not os.path.exists(fn):
125
+ self.files_to_clean.add(os.path.abspath(fn))
126
+
127
+ return open(fn, *args, **kwargs)
128
+
129
+ def makedirs(self, dn, exist_ok=False) -> None:
130
+ parent, n = os.path.split(dn)
131
+ if not n:
132
+ parent, n = os.path.split(parent)
133
+ if parent and n and not os.path.exists(parent):
134
+ self.makedirs(parent, exist_ok=True)
135
+ if not os.path.isdir(dn) or not exist_ok:
136
+ os.mkdir(dn)
137
+ self.dirs_to_clean.append(os.path.abspath(dn))
138
+
139
+ def __exit__(self, type, value, traceback):
140
+ if not self.keep_intermediates:
141
+ for f in self.files_to_clean:
142
+ os.unlink(f)
143
+ for d in self.dirs_to_clean[::-1]:
144
+ os.rmdir(d)
145
+
146
+ # Follow UNIX convention for paths to use '/' instead of '\\' on Windows
147
+ def _to_unix_path(path: str) -> str:
148
+ return path.replace(os.sep, '/')
149
+
150
+ def match_extensions(filename: str, extensions: Iterable) -> bool:
151
+ """Helper method to see if filename ends with certain extension"""
152
+ return any(filename.endswith(e) for e in extensions)
153
+
154
+
155
+ def _fnmatch(filepath, patterns):
156
+ return any(fnmatch.fnmatch(filepath, pattern) for pattern in patterns)
157
+
158
+
159
+ def matched_files_iter(
160
+ root_path: str,
161
+ includes: Iterable = (),
162
+ ignores: Iterable = (),
163
+ extensions: Iterable = (),
164
+ out_of_place_only: bool = False,
165
+ is_pytorch_extension: bool = False) -> Iterator[str]:
166
+
167
+ exact_matches = set(includes)
168
+
169
+ # This is a very rough heuristic; really, we want to avoid scanning
170
+ # any file which is not checked into source control, but this script
171
+ # needs to work even if you're in a Git or Hg checkout, so easier to
172
+ # just block the biggest time sinks that won't matter in the
173
+ # end.
174
+ for (abs_dirpath, dirs, filenames) in os.walk(root_path, topdown=True):
175
+ rel_dirpath = os.path.relpath(abs_dirpath, root_path)
176
+ if rel_dirpath == '.':
177
+ # Blah blah blah O(n) blah blah
178
+ if ".git" in dirs:
179
+ dirs.remove(".git")
180
+ if "build" in dirs:
181
+ dirs.remove("build")
182
+ if "third_party" in dirs:
183
+ dirs.remove("third_party")
184
+ dirs.append("third_party/nvfuser")
185
+ for filename in filenames:
186
+ filepath = _to_unix_path(os.path.join(abs_dirpath, filename))
187
+ # We respect extensions, UNLESS you wrote the entire
188
+ # filename verbatim, in which case we always accept it
189
+ if (
190
+ _fnmatch(filepath, includes)
191
+ and (not _fnmatch(filepath, ignores))
192
+ and (match_extensions(filepath, extensions) or filepath in exact_matches)
193
+ ):
194
+ yield filepath
195
+
196
+
197
+ def preprocess_file_and_save_result(
198
+ output_directory: str,
199
+ filepath: str,
200
+ all_files: Iterable,
201
+ header_include_dirs: Iterable,
202
+ stats: dict[str, list],
203
+ hip_clang_launch: bool,
204
+ is_pytorch_extension: bool,
205
+ clean_ctx: GeneratedFileCleaner,
206
+ show_progress: bool) -> None:
207
+ fin_path = os.path.abspath(os.path.join(output_directory, filepath))
208
+ hipify_result = HipifyResult(current_state=CurrentState.INITIALIZED, hipified_path=fin_path)
209
+ HIPIFY_FINAL_RESULT[fin_path] = hipify_result
210
+ result = preprocessor(output_directory, filepath, all_files, header_include_dirs, stats,
211
+ hip_clang_launch, is_pytorch_extension, clean_ctx, show_progress)
212
+
213
+ # Show what happened
214
+ if show_progress and "ignored" not in result.status:
215
+ print(
216
+ fin_path, "->",
217
+ result.hipified_path, result.status, flush=True)
218
+
219
+ HIPIFY_FINAL_RESULT[fin_path] = result
220
+
221
+
222
+ def compute_stats(stats) -> None:
223
+ unsupported_calls = {cuda_call for (cuda_call, _filepath) in stats["unsupported_calls"]}
224
+
225
+ # Print the number of unsupported calls
226
+ print(f"Total number of unsupported CUDA function calls: {len(unsupported_calls):d}")
227
+
228
+ # Print the list of unsupported calls
229
+ print(", ".join(unsupported_calls))
230
+
231
+ # Print the number of kernel launches
232
+ print(f"\nTotal number of replaced kernel launches: {len(stats['kernel_launches']):d}")
233
+
234
+
235
+ def add_dim3(kernel_string, cuda_kernel):
236
+ '''adds dim3() to the second and third arguments in the kernel launch'''
237
+ count = 0
238
+ closure = 0
239
+ kernel_string = kernel_string.replace("<<<", "").replace(">>>", "")
240
+ arg_locs: list[dict[str, int]] = [{} for _ in range(2)]
241
+ arg_locs[count]['start'] = 0
242
+ for ind, c in enumerate(kernel_string):
243
+ if count > 1:
244
+ break
245
+ if c == "(":
246
+ closure += 1
247
+ elif c == ")":
248
+ closure -= 1
249
+ if (c == "," or ind == len(kernel_string) - 1) and closure == 0:
250
+ arg_locs[count]['end'] = ind + (c != ",")
251
+ count += 1
252
+ if count < 2:
253
+ arg_locs[count]['start'] = ind + 1
254
+
255
+ first_arg_raw = kernel_string[arg_locs[0]['start']:arg_locs[0]['end'] + 1]
256
+ second_arg_raw = kernel_string[arg_locs[1]['start']:arg_locs[1]['end']]
257
+
258
+ first_arg_clean = kernel_string[arg_locs[0]['start']:arg_locs[0]['end']].replace("\n", "").strip(" ")
259
+ second_arg_clean = kernel_string[arg_locs[1]['start']:arg_locs[1]['end']].replace("\n", "").strip(" ")
260
+
261
+ first_arg_dim3 = f"dim3({first_arg_clean})"
262
+ second_arg_dim3 = f"dim3({second_arg_clean})"
263
+
264
+ first_arg_raw_dim3 = first_arg_raw.replace(first_arg_clean, first_arg_dim3)
265
+ second_arg_raw_dim3 = second_arg_raw.replace(second_arg_clean, second_arg_dim3)
266
+ cuda_kernel = cuda_kernel.replace(first_arg_raw + second_arg_raw, first_arg_raw_dim3 + second_arg_raw_dim3)
267
+ return cuda_kernel
268
+
269
+
270
+ RE_KERNEL_LAUNCH = re.compile(r'([ ]+)(detail?)::[ ]+\\\n[ ]+')
271
+
272
+
273
+ def processKernelLaunches(string, stats):
274
+ """ Replace the CUDA style Kernel launches with the HIP style kernel launches."""
275
+ # Concat the namespace with the kernel names. (Find cleaner way of doing this later).
276
+ string = RE_KERNEL_LAUNCH.sub(lambda inp: f"{inp.group(1)}{inp.group(2)}::", string)
277
+
278
+ def grab_method_and_template(in_kernel):
279
+ # The positions for relevant kernel components.
280
+ pos = {
281
+ "kernel_launch": {"start": in_kernel["start"], "end": in_kernel["end"]},
282
+ "kernel_name": {"start": -1, "end": -1},
283
+ "template": {"start": -1, "end": -1}
284
+ }
285
+
286
+ # Count for balancing template
287
+ count = {"<>": 0}
288
+
289
+ # Status for whether we are parsing a certain item.
290
+ START = 0
291
+ AT_TEMPLATE = 1
292
+ AFTER_TEMPLATE = 2
293
+ AT_KERNEL_NAME = 3
294
+
295
+ status = START
296
+
297
+ # Parse the string character by character
298
+ for i in range(pos["kernel_launch"]["start"] - 1, -1, -1):
299
+ char = string[i]
300
+
301
+ # Handle Templating Arguments
302
+ if status in (START, AT_TEMPLATE):
303
+ if char == ">":
304
+ if status == START:
305
+ status = AT_TEMPLATE
306
+ pos["template"]["end"] = i
307
+ count["<>"] += 1
308
+
309
+ if char == "<":
310
+ count["<>"] -= 1
311
+ if count["<>"] == 0 and (status == AT_TEMPLATE):
312
+ pos["template"]["start"] = i
313
+ status = AFTER_TEMPLATE
314
+
315
+ # Handle Kernel Name
316
+ if status != AT_TEMPLATE:
317
+ if string[i].isalnum() or string[i] in {'(', ')', '_', ':', '#'}:
318
+ if status != AT_KERNEL_NAME:
319
+ status = AT_KERNEL_NAME
320
+ pos["kernel_name"]["end"] = i
321
+
322
+ # Case: Kernel name starts the string.
323
+ if i == 0:
324
+ pos["kernel_name"]["start"] = 0
325
+
326
+ # Finished
327
+ return [(pos["kernel_name"]), (pos["template"]), (pos["kernel_launch"])]
328
+
329
+ else:
330
+ # Potential ending point if we're already traversing a kernel's name.
331
+ if status == AT_KERNEL_NAME:
332
+ pos["kernel_name"]["start"] = i
333
+
334
+ # Finished
335
+ return [(pos["kernel_name"]), (pos["template"]), (pos["kernel_launch"])]
336
+
337
+ def find_kernel_bounds(string):
338
+ """Finds the starting and ending points for all kernel launches in the string."""
339
+ kernel_end = 0
340
+ kernel_positions = []
341
+
342
+ # Continue until we cannot find any more kernels anymore.
343
+ while string.find("<<<", kernel_end) != -1:
344
+ # Get kernel starting position (starting from the previous ending point)
345
+ kernel_start = string.find("<<<", kernel_end)
346
+
347
+ # Get kernel ending position (adjust end point past the >>>)
348
+ kernel_end = string.find(">>>", kernel_start) + 3
349
+ if kernel_end <= 0:
350
+ raise InputError("no kernel end found")
351
+
352
+ # Add to list of traversed kernels
353
+ kernel_positions.append({"start": kernel_start, "end": kernel_end,
354
+ "group": string[kernel_start: kernel_end]})
355
+
356
+ return kernel_positions
357
+
358
+ # Replace comments and string literals from the code so that find_kernel_bounds does not
359
+ # wrongly capture kernels in comments and string literals.
360
+ # This function replaces them with "x" to keep positions.
361
+ def mask_comments(string):
362
+ in_comment = ''
363
+ prev_c = ''
364
+ new_string = ''
365
+ for c in string:
366
+ if in_comment == '':
367
+ # Outside comments
368
+ if c == '/' and prev_c == '/':
369
+ in_comment = '//'
370
+ elif c == '*' and prev_c == '/':
371
+ in_comment = '/*'
372
+ elif c == '"' and prev_c != '\\' and prev_c != "'":
373
+ in_comment = '"'
374
+ elif in_comment == '//':
375
+ # In // xxx
376
+ if c == '\r' or c == '\n':
377
+ in_comment = ''
378
+ elif in_comment == '/*':
379
+ # In /* xxx */
380
+ if c == '/' and prev_c == '*':
381
+ in_comment = ''
382
+ elif in_comment == '"':
383
+ # In ""
384
+ if c == '"' and prev_c != '\\':
385
+ in_comment = ''
386
+ prev_c = c
387
+ if in_comment == '':
388
+ new_string += c
389
+ else:
390
+ new_string += 'x'
391
+ return new_string
392
+
393
+ # Grab positional ranges of all kernel launches
394
+ get_kernel_positions = list(find_kernel_bounds(mask_comments(string)))
395
+ output_string = string
396
+
397
+ # Replace each CUDA kernel with a HIP kernel.
398
+ for kernel in get_kernel_positions:
399
+ # Get kernel components
400
+ params = grab_method_and_template(kernel)
401
+
402
+ # Find parenthesis after kernel launch
403
+ parenthesis = string.find("(", kernel["end"])
404
+
405
+ # Extract cuda kernel
406
+ cuda_kernel = string[params[0]["start"]:parenthesis + 1]
407
+ kernel_string = string[kernel['start']:kernel['end']]
408
+ end_param_index = 0 if params[1]['end'] == -1 else 1
409
+ kernel_name_with_template = string[params[0]['start']:params[end_param_index]['end'] + 1]
410
+ cuda_kernel_dim3 = add_dim3(kernel_string, cuda_kernel)
411
+ # Keep number of kernel launch params consistent (grid dims, group dims, stream, dynamic shared size)
412
+ num_klp = len(extract_arguments(0, kernel["group"].replace("<<<", "(").replace(">>>", ")")))
413
+
414
+ hip_kernel = "hipLaunchKernelGGL(" + cuda_kernel_dim3[0:-1].replace(
415
+ ">>>", ", 0" * (4 - num_klp) + ">>>").replace("<<<", ", ").replace(
416
+ ">>>", ", ").replace(kernel_name_with_template, "(" + kernel_name_with_template + ")")
417
+
418
+ # Replace cuda kernel with hip kernel
419
+ output_string = output_string.replace(cuda_kernel, hip_kernel)
420
+
421
+ # Update the statistics
422
+ stats["kernel_launches"].append(hip_kernel)
423
+
424
+ return output_string
425
+
426
+
427
+ def find_closure_group(input_string, start, group):
428
+ """Generalization for finding a balancing closure group
429
+
430
+ if group = ["(", ")"], then finds the first balanced parentheses.
431
+ if group = ["{", "}"], then finds the first balanced bracket.
432
+
433
+ Given an input string, a starting position in the input string, and the group type,
434
+ find_closure_group returns the positions of group[0] and group[1] as a tuple.
435
+
436
+ Example:
437
+ >>> find_closure_group("(hi)", 0, ["(", ")"])
438
+ (0, 3)
439
+ """
440
+
441
+ inside_parenthesis = False
442
+ parens = 0
443
+ pos = start
444
+ p_start, p_end = -1, -1
445
+
446
+ while pos < len(input_string):
447
+ if input_string[pos] == group[0]:
448
+ if inside_parenthesis is False:
449
+ inside_parenthesis = True
450
+ parens = 1
451
+ p_start = pos
452
+ else:
453
+ parens += 1
454
+ elif input_string[pos] == group[1] and inside_parenthesis:
455
+ parens -= 1
456
+
457
+ if parens == 0:
458
+ p_end = pos
459
+ return p_start, p_end
460
+
461
+ pos += 1
462
+ return None, None
463
+
464
+
465
+ def find_bracket_group(input_string, start):
466
+ """Finds the first balanced parentheses."""
467
+ return find_closure_group(input_string, start, group=["{", "}"])
468
+
469
+
470
+ def find_parentheses_group(input_string, start):
471
+ """Finds the first balanced bracket."""
472
+ return find_closure_group(input_string, start, group=["(", ")"])
473
+
474
+
475
+ RE_ASSERT = re.compile(r"\bassert[ ]*\(")
476
+
477
+
478
+ def replace_math_functions(input_string):
479
+ """FIXME: Temporarily replace std:: invocations of math functions
480
+ with non-std:: versions to prevent linker errors NOTE: This
481
+ can lead to correctness issues when running tests, since the
482
+ correct version of the math function (exp/expf) might not get
483
+ called. Plan is to remove this function once HIP supports
484
+ std:: math function calls inside device code
485
+
486
+ """
487
+ output_string = input_string
488
+ for func in MATH_TRANSPILATIONS:
489
+ output_string = output_string.replace(fr'{func}(', f'{MATH_TRANSPILATIONS[func]}(')
490
+
491
+ return output_string
492
+
493
+
494
+ RE_SYNCTHREADS = re.compile(r":?:?\b(__syncthreads)\b(\w*\()")
495
+
496
+
497
+ def hip_header_magic(input_string):
498
+ """If the file makes kernel builtin calls and does not include the cuda_runtime.h header,
499
+ then automatically add an #include to match the "magic" includes provided by NVCC.
500
+ TODO:
501
+ Update logic to ignore cases where the cuda_runtime.h is included by another file.
502
+ """
503
+
504
+ # Copy the input.
505
+ output_string = input_string
506
+
507
+ # Check if one of the following headers is already included.
508
+ headers = ["hip/hip_runtime.h", "hip/hip_runtime_api.h"]
509
+ if any(re.search(fr'#include ("{ext}"|<{ext}>)', output_string) for ext in headers):
510
+ return output_string
511
+
512
+ # Rough logic to detect if we're inside device code
513
+ hasDeviceLogic: int
514
+ hasDeviceLogic = "hipLaunchKernelGGL" in output_string
515
+ hasDeviceLogic += "__global__" in output_string
516
+ hasDeviceLogic += "__shared__" in output_string
517
+ hasDeviceLogic += RE_SYNCTHREADS.search(output_string) is not None
518
+
519
+ # If device logic found, provide the necessary header.
520
+ if hasDeviceLogic:
521
+ output_string = '#include "hip/hip_runtime.h"\n' + input_string
522
+
523
+ return output_string
524
+
525
+
526
+ RE_EXTERN_SHARED = re.compile(r"extern\s+([\w\(\)]+)?\s*__shared__\s+([\w:<>\s]+)\s+(\w+)\s*\[\s*\]\s*;")
527
+
528
+
529
+ def replace_extern_shared(input_string):
530
+ """
531
+ Match 'extern __shared__ type foo[];' syntax and use HIP_DYNAMIC_SHARED() MACRO instead.
532
+ See: https://github.com/ROCm/hip/blob/master/docs/markdown/hip_kernel_language.md#__shared__
533
+ Examples:
534
+ "extern __shared__ char smemChar[];"
535
+ => "HIP_DYNAMIC_SHARED( char, smemChar)"
536
+ "extern __shared__ unsigned char smem[];"
537
+ => "HIP_DYNAMIC_SHARED( unsigned char, my_smem)"
538
+ """
539
+ output_string = input_string
540
+ output_string = RE_EXTERN_SHARED.sub(
541
+ lambda inp: f"HIP_DYNAMIC_SHARED({inp.group(1) or ''} {inp.group(2)}, {inp.group(3)})", output_string)
542
+
543
+ return output_string
544
+
545
+
546
+ def get_hip_file_path(rel_filepath, is_pytorch_extension=False):
547
+ """
548
+ Returns the new name of the hipified file
549
+ """
550
+ # At the moment, some PyTorch source files are HIPified in place. The predicate
551
+ # is_out_of_place tells us if this is the case or not.
552
+ if os.path.isabs(rel_filepath):
553
+ raise AssertionError("rel_filepath must be a relative path")
554
+ if not is_pytorch_extension and not is_out_of_place(rel_filepath):
555
+ return rel_filepath
556
+
557
+ dirpath, filename = os.path.split(rel_filepath)
558
+ root, ext = os.path.splitext(filename)
559
+
560
+ # Here's the plan:
561
+ #
562
+ # In general, we need to disambiguate the HIPified filename so that
563
+ # it gets a different name from the original filename, so
564
+ # that we don't overwrite the original file
565
+ #
566
+ # There's a lot of different naming conventions across PyTorch,
567
+ # but the general recipe is to convert occurrences
568
+ # of cuda/gpu to hip, and add hip if there are no occurrences
569
+ # of cuda/gpu anywhere.
570
+ #
571
+ # Concretely, we do the following:
572
+ #
573
+ # - If there is a directory component named "cuda", replace
574
+ # it with "hip", AND
575
+ #
576
+ # - If the file name contains "CUDA", replace it with "HIP", AND
577
+ #
578
+ # - ALWAYS replace '.cu' with '.hip', because those files
579
+ # contain CUDA kernels that needs to be hipified and processed with
580
+ # hip compiler
581
+ #
582
+ # - If we are not hipifying a PyTorch extension, and the parent
583
+ # directory name did not change as a result of the above
584
+ # transformations, insert "hip" in the file path
585
+ # as the direct parent folder of the file
586
+ #
587
+ # - If we are hipifying a PyTorch extension, and the parent directory
588
+ # name as well as the filename (incl. extension) did not change as
589
+ # a result of the above transformations, insert "_hip" in the filename
590
+ #
591
+ # This isn't set in stone; we might adjust this to support other
592
+ # naming conventions.
593
+
594
+ if ext == '.cu':
595
+ ext = '.hip'
596
+
597
+ orig_filename = filename
598
+ orig_dirpath = dirpath
599
+
600
+ dirpath = dirpath.replace('cuda', 'hip')
601
+ dirpath = dirpath.replace('CUDA', 'HIP')
602
+ dirpath = dirpath.replace('THC', 'THH')
603
+
604
+ root = root.replace('cuda', 'hip')
605
+ root = root.replace('CUDA', 'HIP')
606
+ # Special case to handle caffe2/core/THCCachingAllocator
607
+ if dirpath != "caffe2/core":
608
+ root = root.replace('THC', 'THH')
609
+
610
+ if not is_pytorch_extension and dirpath == orig_dirpath:
611
+ dirpath = os.path.join(dirpath, 'hip')
612
+
613
+ if is_pytorch_extension and dirpath == orig_dirpath and (root + ext) == orig_filename:
614
+ root = root + "_hip"
615
+
616
+ return os.path.join(dirpath, root + ext)
617
+
618
+
619
+ def is_out_of_place(rel_filepath) -> bool:
620
+ if os.path.isabs(rel_filepath):
621
+ raise AssertionError("rel_filepath must be a relative path")
622
+ if rel_filepath.startswith("torch/"):
623
+ return False
624
+ if rel_filepath.startswith("third_party/nvfuser/"):
625
+ return False
626
+ if rel_filepath.startswith("tools/autograd/templates/"):
627
+ return False
628
+ return True
629
+
630
+
631
+ # Keep this synchronized with includes/ignores in build_amd.py
632
+ def is_pytorch_file(rel_filepath) -> bool:
633
+ _deprecated("is_pytorch_file")
634
+ if os.path.isabs(rel_filepath):
635
+ raise AssertionError("rel_filepath must be a relative path")
636
+ if rel_filepath.startswith("aten/"):
637
+ if rel_filepath.startswith("aten/src/ATen/core/"):
638
+ return False
639
+ return True
640
+ if rel_filepath.startswith("torch/"):
641
+ return True
642
+ if rel_filepath.startswith("third_party/nvfuser/"):
643
+ return True
644
+ if rel_filepath.startswith("third_party/fbgemm/"):
645
+ return True
646
+ if rel_filepath.startswith("third_party/mslk/"):
647
+ return True
648
+ if rel_filepath.startswith("tools/autograd/templates/"):
649
+ return True
650
+ if rel_filepath.startswith("test/cpp/c10d/"):
651
+ return True
652
+ return False
653
+
654
+
655
+ def is_cusparse_file(rel_filepath):
656
+ _deprecated("is_cusparse_file")
657
+ if is_pytorch_file(rel_filepath):
658
+ return "sparse" in rel_filepath.lower()
659
+ return False
660
+
661
+
662
+ def is_special_file(rel_filepath) -> bool:
663
+ _deprecated("is_special_file")
664
+ if is_pytorch_file(rel_filepath):
665
+ if "sparse" in rel_filepath.lower():
666
+ return True
667
+ elif "linalg" in rel_filepath.lower():
668
+ if "batchlinearalgebralibblas" in rel_filepath.lower():
669
+ return False # don't use "special" mappings for this specific linalg cublas file
670
+ return True
671
+ return False
672
+
673
+
674
+ def is_caffe2_gpu_file(rel_filepath):
675
+ _deprecated("is_caffe2_gpu_file")
676
+ if os.path.isabs(rel_filepath):
677
+ raise AssertionError("rel_filepath must be a relative path")
678
+ if rel_filepath.startswith("c10/cuda"):
679
+ return True
680
+ filename = os.path.basename(rel_filepath)
681
+ _, ext = os.path.splitext(filename)
682
+
683
+ return ('gpu' in filename or ext in ['.cu', '.cuh']) and ('cudnn' not in filename)
684
+
685
+
686
+ class TrieNode:
687
+ """A Trie node whose children are represented as a directory of char: TrieNode.
688
+ A special char '' represents end of word
689
+ """
690
+
691
+ def __init__(self) -> None:
692
+ self.children = {}
693
+
694
+
695
+ class Trie:
696
+ """Creates a Trie out of a list of words. The trie can be exported to a Regex pattern.
697
+ The corresponding Regex should match much faster than a simple Regex union."""
698
+
699
+ def __init__(self) -> None:
700
+ """Initialize the trie with an empty root node."""
701
+ self.root = TrieNode()
702
+ self._hash = hashlib.md5(usedforsecurity=False)
703
+ self._digest = self._hash.digest()
704
+
705
+ def add(self, word) -> None:
706
+ """Add a word to the Trie. """
707
+ self._hash.update(word.encode())
708
+ self._digest = self._hash.digest()
709
+ node = self.root
710
+
711
+ for char in word:
712
+ node.children.setdefault(char, TrieNode())
713
+ node = node.children[char]
714
+ node.children[''] = True # Mark the end of the word
715
+
716
+ def dump(self):
717
+ """Return the root node of Trie. """
718
+ return self.root
719
+
720
+ def quote(self, char):
721
+ """ Escape a char for regex. """
722
+ return re.escape(char)
723
+
724
+ def search(self, word):
725
+ """Search whether word is present in the Trie.
726
+ Returns True if yes, else return False"""
727
+ node = self.root
728
+ for char in word:
729
+ if char in node.children:
730
+ node = node.children[char]
731
+ else:
732
+ return False
733
+
734
+ # make sure to check the end-of-word marker present
735
+ return '' in node.children
736
+
737
+ @functools.lru_cache # noqa: B019
738
+ def _pattern(self, root, digest):
739
+ """Convert a Trie into a regular expression pattern
740
+
741
+ Memoized on the hash digest of the trie, which is built incrementally
742
+ during add().
743
+ """
744
+ node = root
745
+
746
+ if "" in node.children and len(node.children.keys()) == 1:
747
+ return None
748
+
749
+ alt = [] # store alternative patterns
750
+ cc = [] # store char to char classes
751
+ q = 0 # for node representing the end of word
752
+ for char in sorted(node.children.keys()):
753
+ if isinstance(node.children[char], TrieNode):
754
+ try:
755
+ recurse = self._pattern(node.children[char], self._digest)
756
+ alt.append(self.quote(char) + recurse)
757
+ except Exception:
758
+ cc.append(self.quote(char))
759
+ else:
760
+ q = 1
761
+ cconly = not len(alt) > 0
762
+
763
+ if len(cc) > 0:
764
+ if len(cc) == 1:
765
+ alt.append(cc[0])
766
+ else:
767
+ alt.append('[' + ''.join(cc) + ']')
768
+
769
+ if len(alt) == 1:
770
+ result = alt[0]
771
+ else:
772
+ result = "(?:" + "|".join(alt) + ")"
773
+
774
+ if q:
775
+ if cconly:
776
+ result += "?"
777
+ else:
778
+ result = f"(?:{result})?"
779
+ return result
780
+
781
+ def pattern(self):
782
+ """Export the Trie to a regex pattern."""
783
+ return self._pattern(self.root, self._digest)
784
+
785
+ def export_to_regex(self):
786
+ """Export the Trie to a regex pattern."""
787
+ return self._pattern(self.root, self._digest)
788
+
789
+ PYTORCH_TRIE = Trie()
790
+ PYTORCH_MAP: dict[str, object] = {}
791
+
792
+ for mapping in CUDA_TO_HIP_MAPPINGS:
793
+ if not isinstance(mapping, Mapping):
794
+ raise TypeError("Expected each mapping in CUDA_TO_HIP_MAPPINGS to be a Mapping")
795
+ for src, dst in mapping.items():
796
+ PYTORCH_TRIE.add(src)
797
+ PYTORCH_MAP[src] = dst
798
+
799
+ RE_PYTORCH_PREPROCESSOR = re.compile(fr'(?<=\W)({PYTORCH_TRIE.export_to_regex()})(?=\W)')
800
+
801
+ RE_QUOTE_HEADER = re.compile(r'#include "([^"]+)"')
802
+ RE_ANGLE_HEADER = re.compile(r'#include <([^>]+)>')
803
+ RE_THC_GENERIC_FILE = re.compile(r'#define THC_GENERIC_FILE "([^"]+)"')
804
+ RE_CU_SUFFIX = re.compile(r'\.cu\b') # be careful not to pick up .cuh
805
+
806
+ """
807
+ Returns a HipifyResult object with the following details:
808
+ "hipified_path" : absolute path of hipified source file
809
+ "status" : "ok" if hipified file was written out
810
+ "skipped" if an identical hipified file already existed or hipified file couldn't be written out
811
+ "ignored" if the source file was a hipified file itself or not meant to be hipified
812
+ "current_state" : CurrentState.INITIALIZED if source file is first ready to be hipified
813
+ CurrentState.DONE if source file is done with hipification process
814
+ """
815
+
816
+
817
+ def preprocessor(
818
+ output_directory: str,
819
+ filepath: str,
820
+ all_files: Iterable,
821
+ header_include_dirs: Iterable,
822
+ stats: dict[str, list],
823
+ hip_clang_launch: bool,
824
+ is_pytorch_extension: bool,
825
+ clean_ctx: GeneratedFileCleaner,
826
+ show_progress: bool) -> HipifyResult:
827
+ """ Executes the CUDA -> HIP conversion on the specified file. """
828
+ fin_path = os.path.abspath(os.path.join(output_directory, filepath))
829
+ filepath = _to_unix_path(filepath)
830
+ hipify_result = HIPIFY_FINAL_RESULT[fin_path]
831
+ if filepath not in all_files:
832
+ hipify_result.hipified_path = None
833
+ hipify_result.status = "[ignored, not to be hipified]"
834
+ hipify_result.current_state = CurrentState.DONE
835
+ return hipify_result
836
+
837
+ rel_filepath = _to_unix_path(os.path.relpath(filepath, output_directory))
838
+
839
+ with open(fin_path, encoding='utf-8') as fin:
840
+ if fin.readline() == HIPIFY_C_BREADCRUMB:
841
+ hipify_result.hipified_path = None
842
+ hipify_result.status = "[ignored, input is hipified output]"
843
+ hipify_result.current_state = CurrentState.DONE
844
+ return hipify_result
845
+ fin.seek(0)
846
+ output_source = fin.read()
847
+
848
+ orig_output_source = output_source
849
+
850
+ # get_hip_file_path needs a relative path to work correctly
851
+ fout_path = os.path.abspath(os.path.join(output_directory, get_hip_file_path(rel_filepath, is_pytorch_extension)))
852
+ if not os.path.exists(os.path.dirname(fout_path)):
853
+ clean_ctx.makedirs(os.path.dirname(fout_path))
854
+
855
+ # unsupported_calls statistics reporting is broken atm
856
+ def pt_repl(m):
857
+ return PYTORCH_MAP[m.group(0)]
858
+
859
+ output_source = RE_PYTORCH_PREPROCESSOR.sub(pt_repl, output_source)
860
+
861
+ # TODO: Remove CAFFE2_PATH_MAPPINGS. They were necessary for Meta-internal builds.
862
+ # Apply CAFFE2 path mappings (simple string replacement for paths containing slashes)
863
+ # Need to be careful to avoid double-transformations when source file has #ifdef blocks
864
+ # with HIP-specific paths already in them (e.g., caffe2/core/hip/context_gpu.h)
865
+ for cuda_path, hip_path in CAFFE2_PATH_MAPPINGS.items():
866
+ # Use regex to ensure we don't match paths that already have been hipified
867
+ # We need to avoid transforming "caffe2/core/hip/context_gpu.h" when looking for "caffe2/core/context_gpu.h"
868
+ # The key insight: if hip_path contains /hip/ and cuda_path doesn't, we need to be careful
869
+ if "/hip/" in hip_path and "/hip/" not in cuda_path:
870
+ # Only replace cuda_path if it's not preceded by "/hip/"
871
+ # Use negative lookbehind to prevent matching already-hipified paths
872
+ # The pattern checks that the cuda_path is not immediately preceded by "/hip/"
873
+ pattern = r'(?<!/hip/)' + re.escape(cuda_path)
874
+ output_source = re.sub(pattern, hip_path, output_source)
875
+ else:
876
+ # Simple replacement when no /hip/ involved or both have it
877
+ output_source = output_source.replace(cuda_path, hip_path)
878
+
879
+ # Header rewrites
880
+ def mk_repl(templ, include_current_dir=True):
881
+ def repl(m):
882
+ f = m.group(1)
883
+ filename = os.path.basename(f)
884
+ if (
885
+ f.startswith(("ATen/cuda",
886
+ "ATen/native/cuda",
887
+ "ATen/native/nested/cuda",
888
+ "ATen/native/quantized/cuda",
889
+ "ATen/native/sparse/cuda",
890
+ "ATen/native/transformers/cuda",
891
+ "THC/")) or
892
+ (f.startswith("THC") and not f.startswith("THCP"))
893
+ ):
894
+ return templ.format(get_hip_file_path(m.group(1), is_pytorch_extension))
895
+ # if filename is one of the files being hipified for this extension
896
+ if (is_pytorch_extension and any(s.endswith(filename) for s in all_files)):
897
+ header_dir = None
898
+ header_filepath = None
899
+ # If include_current_dir True, look first in same dir as the including source file
900
+ if include_current_dir:
901
+ header_dir_to_check = os.path.dirname(fin_path)
902
+ header_path_to_check = os.path.abspath(os.path.join(header_dir_to_check, f))
903
+ if os.path.exists(header_path_to_check):
904
+ header_dir = header_dir_to_check
905
+ header_filepath = header_path_to_check
906
+ # If not found, look in include dirs one by one and first match wins
907
+ if header_filepath is None:
908
+ for header_include_dir in header_include_dirs:
909
+ header_dir_to_check = os.path.join(output_directory, header_include_dir)
910
+ header_path_to_check = os.path.abspath(os.path.join(header_dir_to_check, f))
911
+ if os.path.exists(header_path_to_check):
912
+ header_dir = header_dir_to_check
913
+ header_filepath = header_path_to_check
914
+ # If header file not found, keep as is
915
+ if header_filepath is None:
916
+ return m.group(0)
917
+ # Hipify header file first if needed
918
+ if header_filepath not in HIPIFY_FINAL_RESULT:
919
+ preprocess_file_and_save_result(output_directory,
920
+ header_filepath,
921
+ all_files, header_include_dirs, stats, hip_clang_launch,
922
+ is_pytorch_extension, clean_ctx, show_progress)
923
+ elif header_filepath in HIPIFY_FINAL_RESULT:
924
+ header_result = HIPIFY_FINAL_RESULT[header_filepath]
925
+ if header_result.current_state == CurrentState.INITIALIZED:
926
+ # get_hip_file_path needs a relative path to work correctly
927
+ header_rel_path = os.path.relpath(header_filepath, output_directory)
928
+ header_fout_path = os.path.abspath(os.path.join(output_directory,
929
+ get_hip_file_path(header_rel_path, is_pytorch_extension)))
930
+ header_result.hipified_path = header_fout_path
931
+ HIPIFY_FINAL_RESULT[header_filepath] = header_result
932
+ return templ.format(os.path.relpath(header_fout_path if header_fout_path is not None
933
+ else header_filepath, header_dir))
934
+ hipified_header_filepath = HIPIFY_FINAL_RESULT[header_filepath].hipified_path
935
+ return templ.format(_to_unix_path(os.path.relpath(hipified_header_filepath if hipified_header_filepath is not None
936
+ else header_filepath, header_dir)))
937
+
938
+ return m.group(0)
939
+ return repl
940
+ output_source = RE_QUOTE_HEADER.sub(mk_repl('#include "{0}"', True), output_source)
941
+ output_source = RE_ANGLE_HEADER.sub(mk_repl('#include <{0}>', False), output_source)
942
+ output_source = RE_THC_GENERIC_FILE.sub(mk_repl('#define THC_GENERIC_FILE "{0}"'), output_source)
943
+
944
+ # CMakeLists.txt rewrites
945
+ if filepath.endswith('CMakeLists.txt'):
946
+ output_source = output_source.replace('CUDA', 'HIP')
947
+ output_source = output_source.replace('THC', 'THH')
948
+ output_source = RE_CU_SUFFIX.sub('.hip', output_source)
949
+
950
+ # Perform Kernel Launch Replacements
951
+ if not hip_clang_launch:
952
+ output_source = processKernelLaunches(output_source, stats)
953
+
954
+ # Replace std:: with non-std:: versions
955
+ if (filepath.endswith((".cu", ".cuh"))) and "PowKernel" not in filepath:
956
+ output_source = replace_math_functions(output_source)
957
+
958
+ # Include header if device code is contained.
959
+ output_source = hip_header_magic(output_source)
960
+
961
+ # Replace the extern __shared__
962
+ # NOTE: No longer needed after transition from hcc to hipclang.
963
+ # output_source = replace_extern_shared(output_source)
964
+
965
+ # Don't write out identical hipified files for extensions if dirpath has not changed
966
+ if (
967
+ is_pytorch_extension
968
+ and orig_output_source == output_source
969
+ and os.path.dirname(fin_path) == os.path.dirname(fout_path)
970
+ ):
971
+ hipify_result.hipified_path = fin_path
972
+ hipify_result.status = "[skipped, no changes]"
973
+ hipify_result.current_state = CurrentState.DONE
974
+ return hipify_result
975
+
976
+ # Add hipify breadcrumb for C-style files to avoid re-hipification
977
+ if fin_path != fout_path and match_extensions(fin_path, (".cu", ".cuh", ".c", ".cc", ".cpp", ".h", ".hpp")):
978
+ output_source = HIPIFY_C_BREADCRUMB + output_source
979
+
980
+ do_write = True
981
+ if os.path.exists(fout_path):
982
+ with open(fout_path, encoding='utf-8') as fout_old:
983
+ do_write = fout_old.read() != output_source
984
+ if do_write:
985
+ try:
986
+ with clean_ctx.open(fout_path, 'w', encoding='utf-8') as fout:
987
+ fout.write(output_source)
988
+ hipify_result.hipified_path = fout_path
989
+ hipify_result.status = "[ok]"
990
+ hipify_result.current_state = CurrentState.DONE
991
+ return hipify_result
992
+ except OSError as e:
993
+ print(f'{bcolors.WARNING}Failed to save {fout_path} with "{e.strerror}", leaving {fin_path} unchanged.{bcolors.ENDC}',
994
+ file=sys.stderr)
995
+ hipify_result.hipified_path = fin_path
996
+ hipify_result.status = "[skipped, no permissions]"
997
+ hipify_result.current_state = CurrentState.DONE
998
+ return hipify_result
999
+ else:
1000
+ hipify_result.hipified_path = fout_path
1001
+ hipify_result.status = "[skipped, already hipified]"
1002
+ hipify_result.current_state = CurrentState.DONE
1003
+ return hipify_result
1004
+
1005
+ def file_specific_replacement(filepath, search_string, replace_string, strict=False) -> None:
1006
+ with openf(filepath, "r+") as f:
1007
+ contents = f.read()
1008
+ if strict:
1009
+ contents = re.sub(fr'\b({re.escape(search_string)})\b', lambda x: replace_string, contents)
1010
+ else:
1011
+ contents = contents.replace(search_string, replace_string)
1012
+ f.seek(0)
1013
+ f.write(contents)
1014
+ f.truncate()
1015
+
1016
+
1017
+ def file_add_header(filepath, header) -> None:
1018
+ with openf(filepath, "r+") as f:
1019
+ contents = f.read()
1020
+ if header[0] != "<" and header[-1] != ">":
1021
+ header = f'"{header}"'
1022
+ contents = (f'#include {header} \n') + contents
1023
+ f.seek(0)
1024
+ f.write(contents)
1025
+ f.truncate()
1026
+
1027
+
1028
+ def fix_static_global_kernels(in_txt):
1029
+ """Static global kernels in HIP results in a compilation error."""
1030
+ in_txt = in_txt.replace(" __global__ static", "__global__")
1031
+ return in_txt
1032
+
1033
+
1034
+ RE_INCLUDE = re.compile(r"#include .*\n")
1035
+
1036
+
1037
+ def extract_arguments(start, string):
1038
+ """
1039
+ Return the list of arguments in the upcoming function parameter closure.
1040
+ Example:
1041
+ string (input): '(blocks, threads, 0, THCState_getCurrentStream(state))'
1042
+ arguments (output): [{'start': 1, 'end': 7}, {'start': 8, 'end': 16}, \
1043
+ {'start': 17, 'end': 19}, {'start': 20, 'end': 53}]
1044
+ """
1045
+
1046
+ arguments = []
1047
+ closures = {
1048
+ "<": 0,
1049
+ "(": 0
1050
+ }
1051
+ current_position = start
1052
+ argument_start_pos = current_position + 1
1053
+
1054
+ # Search for final parenthesis
1055
+ while current_position < len(string):
1056
+ if string[current_position] == "(":
1057
+ closures["("] += 1
1058
+ elif string[current_position] == ")":
1059
+ closures["("] -= 1
1060
+ elif string[current_position] == "<":
1061
+ closures["<"] += 1
1062
+ elif string[current_position] == ">" and string[current_position - 1] != "-" and closures["<"] > 0:
1063
+ closures["<"] -= 1
1064
+
1065
+ # Finished all arguments
1066
+ if closures["("] == 0 and closures["<"] == 0:
1067
+ # Add final argument
1068
+ arguments.append({"start": argument_start_pos, "end": current_position})
1069
+ break
1070
+
1071
+ # Finished current argument
1072
+ if closures["("] == 1 and closures["<"] == 0 and string[current_position] == ",":
1073
+ arguments.append({"start": argument_start_pos, "end": current_position})
1074
+ argument_start_pos = current_position + 1
1075
+
1076
+ current_position += 1
1077
+
1078
+ return arguments
1079
+
1080
+
1081
+ def str2bool(v : str) -> bool:
1082
+ """ArgumentParser doesn't support type=bool. Thus, this helper method will convert
1083
+ from possible string types to True / False."""
1084
+ if v.lower() in ('yes', 'true', 't', 'y', '1'):
1085
+ return True
1086
+ elif v.lower() in ('no', 'false', 'f', 'n', '0'):
1087
+ return False
1088
+ else:
1089
+ raise argparse.ArgumentTypeError('Boolean value expected.')
1090
+
1091
+
1092
+ def hipify(
1093
+ project_directory: str,
1094
+ show_detailed: bool = False,
1095
+ extensions: Iterable = (".cu", ".cuh", ".c", ".cc", ".cpp", ".h", ".in", ".hpp"),
1096
+ header_extensions: Iterable = (".cuh", ".h", ".hpp"),
1097
+ output_directory: str = "",
1098
+ header_include_dirs: Iterable = (),
1099
+ includes: Iterable = ('*',),
1100
+ extra_files: Iterable = (),
1101
+ out_of_place_only: bool = False,
1102
+ ignores: Iterable = (),
1103
+ show_progress: bool = True,
1104
+ hip_clang_launch: bool = False,
1105
+ is_pytorch_extension: bool = False,
1106
+ hipify_extra_files_only: bool = False,
1107
+ clean_ctx: GeneratedFileCleaner | None = None
1108
+ ) -> HipifyFinalResult:
1109
+ if project_directory == "":
1110
+ project_directory = os.getcwd()
1111
+
1112
+ # Verify the project directory exists.
1113
+ if not os.path.exists(project_directory):
1114
+ print("The project folder specified does not exist.")
1115
+ sys.exit(1)
1116
+
1117
+ # If no output directory, provide a default one.
1118
+ if not output_directory:
1119
+ project_directory.rstrip("/")
1120
+ output_directory = project_directory + "_amd"
1121
+
1122
+ if project_directory != output_directory:
1123
+ includes = [include.replace(project_directory, output_directory) for include in includes]
1124
+ ignores = [ignore.replace(project_directory, output_directory) for ignore in ignores]
1125
+
1126
+ # Copy from project directory to output directory if not done already.
1127
+ if not os.path.exists(output_directory):
1128
+ shutil.copytree(project_directory, output_directory)
1129
+
1130
+ includes = list(map(_to_unix_path, includes))
1131
+ ignores = list(map(_to_unix_path, ignores))
1132
+
1133
+ all_files = list(matched_files_iter(output_directory, includes=includes,
1134
+ ignores=ignores, extensions=extensions,
1135
+ out_of_place_only=out_of_place_only,
1136
+ is_pytorch_extension=is_pytorch_extension))
1137
+ all_files_set = set(all_files)
1138
+
1139
+ for f in extra_files:
1140
+ if not os.path.isabs(f):
1141
+ f = os.path.join(output_directory, f)
1142
+ if f not in all_files_set:
1143
+ all_files.append(f)
1144
+
1145
+ # List all files in header_include_paths to ensure they are hipified
1146
+ from pathlib import Path
1147
+ for header_include_dir in header_include_dirs:
1148
+ if os.path.isabs(header_include_dir):
1149
+ header_include_dir_path = Path(header_include_dir)
1150
+ else:
1151
+ header_include_dir_path = Path(os.path.join(output_directory, header_include_dir))
1152
+ all_files.extend(
1153
+ str(path) for path in header_include_dir_path.rglob('*') if path.is_file()
1154
+ and _fnmatch(str(path), includes)
1155
+ and (not _fnmatch(str(path), ignores))
1156
+ and match_extensions(path.name, header_extensions)
1157
+ )
1158
+
1159
+ if clean_ctx is None:
1160
+ clean_ctx = GeneratedFileCleaner(keep_intermediates=True)
1161
+
1162
+ # Preprocessing statistics.
1163
+ stats: dict[str, list] = {"unsupported_calls": [], "kernel_launches": []}
1164
+
1165
+ for filepath in (all_files if not hipify_extra_files_only else extra_files):
1166
+ preprocess_file_and_save_result(output_directory, filepath, all_files, header_include_dirs,
1167
+ stats, hip_clang_launch, is_pytorch_extension, clean_ctx, show_progress)
1168
+
1169
+ print(bcolors.OKGREEN + "Successfully preprocessed all matching files." + bcolors.ENDC, file=sys.stderr)
1170
+
1171
+ # Show detailed summary
1172
+ if show_detailed:
1173
+ compute_stats(stats)
1174
+
1175
+ return HIPIFY_FINAL_RESULT
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hipify/version.py ADDED
@@ -0,0 +1 @@
 
 
1
+ __version__ = '2.0.0'
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/hooks.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import torch
3
+ from collections import OrderedDict
4
+ import weakref
5
+ import warnings
6
+ from typing import Any
7
+
8
+ __all__ = ["RemovableHandle", "unserializable_hook", "warn_if_has_hooks", "BackwardHook"]
9
+
10
+ class RemovableHandle:
11
+ r"""
12
+ A handle which provides the capability to remove a hook.
13
+
14
+ Args:
15
+ hooks_dict (dict): A dictionary of hooks, indexed by hook ``id``.
16
+ extra_dict (Union[dict, List[dict]]): An additional dictionary or list of
17
+ dictionaries whose keys will be deleted when the same keys are
18
+ removed from ``hooks_dict``.
19
+ """
20
+
21
+ id: int
22
+ next_id: int = 0
23
+
24
+ def __init__(self, hooks_dict: Any, *, extra_dict: Any = None) -> None:
25
+ self.hooks_dict_ref = weakref.ref(hooks_dict)
26
+ self.id = RemovableHandle.next_id
27
+ RemovableHandle.next_id += 1
28
+
29
+ self.extra_dict_ref: tuple = ()
30
+ if isinstance(extra_dict, dict):
31
+ self.extra_dict_ref = (weakref.ref(extra_dict),)
32
+ elif isinstance(extra_dict, list):
33
+ self.extra_dict_ref = tuple(weakref.ref(d) for d in extra_dict)
34
+
35
+ def remove(self) -> None:
36
+ hooks_dict = self.hooks_dict_ref()
37
+ if hooks_dict is not None and self.id in hooks_dict:
38
+ del hooks_dict[self.id]
39
+
40
+ for ref in self.extra_dict_ref:
41
+ extra_dict = ref()
42
+ if extra_dict is not None and self.id in extra_dict:
43
+ del extra_dict[self.id]
44
+
45
+ def __getstate__(self):
46
+ if self.extra_dict_ref is None:
47
+ return (self.hooks_dict_ref(), self.id)
48
+ else:
49
+ return (self.hooks_dict_ref(), self.id, tuple(ref() for ref in self.extra_dict_ref))
50
+
51
+ def __setstate__(self, state) -> None:
52
+ if state[0] is None:
53
+ # create a dead reference
54
+ self.hooks_dict_ref = weakref.ref(OrderedDict())
55
+ else:
56
+ self.hooks_dict_ref = weakref.ref(state[0])
57
+ self.id = state[1]
58
+ RemovableHandle.next_id = max(RemovableHandle.next_id, self.id + 1)
59
+
60
+ if len(state) < 3 or state[2] is None:
61
+ self.extra_dict_ref = ()
62
+ else:
63
+ self.extra_dict_ref = tuple(weakref.ref(d) for d in state[2])
64
+
65
+ def __enter__(self) -> "RemovableHandle":
66
+ return self
67
+
68
+ def __exit__(self, type: Any, value: Any, tb: Any) -> None:
69
+ self.remove()
70
+
71
+
72
+ def unserializable_hook(f):
73
+ """
74
+ Mark a function as an unserializable hook with this decorator.
75
+
76
+ This suppresses warnings that would otherwise arise if you attempt
77
+ to serialize a tensor that has a hook.
78
+ """
79
+ f.__torch_unserializable__ = True
80
+ return f
81
+
82
+
83
+ def warn_if_has_hooks(tensor) -> None:
84
+ if tensor._backward_hooks:
85
+ for k in tensor._backward_hooks:
86
+ hook = tensor._backward_hooks[k]
87
+ if not hasattr(hook, "__torch_unserializable__"):
88
+ warnings.warn(f"backward hook {repr(hook)} on tensor will not be "
89
+ "serialized. If this is expected, you can "
90
+ "decorate the function with @torch.utils.hooks.unserializable_hook "
91
+ "to suppress this warning", stacklevel=2)
92
+
93
+ class BackwardHook:
94
+ """
95
+ A wrapper class to implement nn.Module backward hooks.
96
+
97
+ It handles:
98
+ - Ignoring non-Tensor inputs and replacing them by None before calling the user hook
99
+ - Generating the proper Node to capture a set of Tensor's gradients
100
+ - Linking the gradients captures for the outputs with the gradients captured for the input
101
+ - Calling the user hook once both output and input gradients are available
102
+ """
103
+
104
+ def __init__(self, module, user_hooks, user_pre_hooks) -> None:
105
+ self.user_hooks = user_hooks
106
+ self.user_pre_hooks = user_pre_hooks
107
+ self.module = module
108
+
109
+ self.grad_outputs = None
110
+ self.n_outputs = -1
111
+ self.output_tensors_index = None
112
+ self.n_inputs = -1
113
+ self.input_tensors_index = None
114
+
115
+ def _pack_with_none(self, indices, values, size):
116
+ res = [None] * size
117
+ for idx, val in zip(indices, values, strict=True):
118
+ res[idx] = val
119
+
120
+ return tuple(res)
121
+
122
+ def _unpack_none(self, indices, values):
123
+ res = [values[idx] for idx in indices]
124
+
125
+ return tuple(res)
126
+
127
+ def _set_user_hook(self, grad_fn) -> None:
128
+ def hook(grad_input, _):
129
+ if self.grad_outputs is None:
130
+ # This happens because the gradient in your nn.Module flows to
131
+ # the Module's input without " passing through the Module's
132
+ # output, e.g. when you're doing double backward.
133
+ return
134
+ res = self._pack_with_none(self.input_tensors_index, grad_input, self.n_inputs)
135
+
136
+ for hook in self.user_hooks:
137
+ out = hook(self.module, res, self.grad_outputs)
138
+
139
+ if out is None:
140
+ continue
141
+
142
+ if len(out) != len(res):
143
+ raise RuntimeError("Backward hook returned an invalid number of grad_input, "
144
+ f"got {len(out)}, but expected {len(res)}")
145
+
146
+ res = out
147
+
148
+ self.grad_outputs = None
149
+
150
+ return self._unpack_none(self.input_tensors_index, res)
151
+
152
+ grad_fn.register_hook(hook)
153
+
154
+ def _apply_on_tensors(self, fn, args):
155
+ # Can be used to apply the given function to the tensors contained in the
156
+ # args. Will return updated args and the tensors indices
157
+ tensors_idx = []
158
+ tensors = []
159
+
160
+ requires_grad = False
161
+ for i, arg in enumerate(args):
162
+ if isinstance(arg, torch.Tensor):
163
+ tensors_idx.append(i)
164
+ tensors.append(arg)
165
+ requires_grad |= arg.requires_grad
166
+
167
+ if not (requires_grad and torch.is_grad_enabled()):
168
+ return args, None
169
+
170
+ new_tensors = torch.nn.modules._functions.BackwardHookFunction.apply(*tensors)
171
+ if len(new_tensors) == 0:
172
+ raise RuntimeError("Cannot set Module backward hook for a Module with no input Tensors.")
173
+
174
+ grad_fns = [t.grad_fn for t in new_tensors if t.grad_fn is not None and t.grad_fn.name() == "BackwardHookFunctionBackward"]
175
+ if len(grad_fns) == 0:
176
+ raise RuntimeError("Error while setting up backward hooks. Please open "
177
+ "an issue with a code sample to reproduce this.")
178
+
179
+ fn(grad_fns[0])
180
+
181
+ arg_list = list(args)
182
+ for idx, val in zip(tensors_idx, new_tensors, strict=True):
183
+ arg_list[idx] = val
184
+
185
+ if type(args) is tuple:
186
+ out = tuple(arg_list)
187
+ else:
188
+ out = type(args)(*arg_list)
189
+ return out, tensors_idx
190
+
191
+ def setup_input_hook(self, args):
192
+ def fn(grad_fn) -> None:
193
+ self._set_user_hook(grad_fn)
194
+
195
+ res, input_idx = self._apply_on_tensors(fn, args)
196
+ self.n_inputs = len(args)
197
+ self.input_tensors_index = input_idx
198
+ return res
199
+
200
+ def setup_output_hook(self, args):
201
+ def fn(grad_fn) -> None:
202
+ def hook(_, grad_output):
203
+ self.grad_outputs = self._pack_with_none(self.output_tensors_index,
204
+ grad_output,
205
+ self.n_outputs)
206
+
207
+ if self.user_pre_hooks:
208
+ expected_len = len(self.grad_outputs)
209
+ for user_pre_hook in self.user_pre_hooks:
210
+ hook_grad_outputs = user_pre_hook(self.module, self.grad_outputs)
211
+ if hook_grad_outputs is None:
212
+ continue
213
+
214
+ actual_len = len(hook_grad_outputs)
215
+ if actual_len != expected_len:
216
+ raise RuntimeError("Backward pre hook returned an invalid number of grad_output, "
217
+ f"got {actual_len}, but expected {expected_len}")
218
+ self.grad_outputs = hook_grad_outputs
219
+
220
+ # We need to be able to clear self.grad_outputs but also return it
221
+ local_grad_outputs = self.grad_outputs
222
+
223
+ # Special case if no input required gradients, this hook should call the user
224
+ # hook directly
225
+ if self.input_tensors_index is None:
226
+ warnings.warn("Full backward hook is firing when gradients are computed "
227
+ "with respect to module outputs since no inputs require gradients. See "
228
+ "https://docs.pytorch.org/docs/main/generated/torch.nn.Module.html#torch.nn.Module.register_full_backward_hook " # noqa: B950
229
+ "for more details.",
230
+ stacklevel=5)
231
+ grad_inputs = self._pack_with_none([], [], self.n_inputs)
232
+ for user_hook in self.user_hooks:
233
+ res = user_hook(self.module, grad_inputs, self.grad_outputs)
234
+ if res is not None and not (isinstance(res, tuple) and all(el is None for el in res)):
235
+ raise RuntimeError("Backward hook for Modules where no input requires "
236
+ "gradient should always return None or None for all gradients.")
237
+ self.grad_outputs = None
238
+
239
+ if local_grad_outputs is not None:
240
+ if self.output_tensors_index is None:
241
+ raise AssertionError("output_tensors_index should not be None when grad_outputs is not None")
242
+ return tuple(local_grad_outputs[i] for i in self.output_tensors_index)
243
+
244
+ grad_fn.register_hook(hook)
245
+
246
+ is_tuple = True
247
+ if not isinstance(args, tuple):
248
+ args = (args,)
249
+ is_tuple = False
250
+
251
+ res, output_idx = self._apply_on_tensors(fn, args)
252
+ self.n_outputs = len(args)
253
+ self.output_tensors_index = output_idx
254
+
255
+ if not is_tuple:
256
+ res = res[0]
257
+ return res
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/jit/__init__.py ADDED
File without changes
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/jit/log_extract.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ from contextlib import contextmanager
3
+ from typing import Any, cast
4
+ import random
5
+ import torch
6
+ import time
7
+ from torch.utils.benchmark import Timer
8
+
9
+ def extract_ir(filename: str) -> list[str]:
10
+ BEGIN = "<GRAPH_EXPORT>"
11
+ END = "</GRAPH_EXPORT>"
12
+ pfx = None
13
+ graphs = []
14
+ with open(filename) as f:
15
+ split_strs = f.read().split(BEGIN)
16
+ for i, split_str in enumerate(split_strs):
17
+ if i == 0:
18
+ continue
19
+ end_loc = split_str.find(END)
20
+ if end_loc == -1:
21
+ continue
22
+ s = split_str[:end_loc]
23
+ pfx = split_strs[i - 1].splitlines()[-1]
24
+ lines = [x[len(pfx):] for x in s.splitlines(keepends=True)]
25
+ graphs.append(''.join(lines))
26
+
27
+ return graphs
28
+
29
+
30
+ def make_tensor_from_type(inp_type: torch._C.TensorType):
31
+ size = inp_type.sizes()
32
+ stride = inp_type.strides()
33
+ device = inp_type.device()
34
+ dtype = inp_type.dtype()
35
+ if size is None:
36
+ raise AssertionError("make_tensor_from_type: 'size' is None (inp_type.sizes() returned None)")
37
+ if stride is None:
38
+ raise AssertionError("make_tensor_from_type: 'stride' is None (inp_type.strides() returned None)")
39
+ if device is None:
40
+ raise AssertionError("make_tensor_from_type: 'device' is None (inp_type.device() returned None)")
41
+ if dtype is None:
42
+ raise AssertionError("make_tensor_from_type: 'dtype' is None (inp_type.dtype() returned None)")
43
+ return torch.empty_strided(size=size, stride=stride, device=device, dtype=dtype)
44
+
45
+ def load_graph_and_inputs(ir: str) -> tuple[Any, list[Any]]:
46
+ graph = torch._C.parse_ir(ir, parse_tensor_constants=True)
47
+ graph.makeMultiOutputIntoTuple()
48
+ inputs = []
49
+ for inp in graph.inputs():
50
+ if isinstance(inp.type(), torch._C.FloatType):
51
+ inputs.append(random.uniform(.1, 100))
52
+ elif isinstance(inp.type(), torch._C.IntType):
53
+ inputs.append(random.randint(1, 100))
54
+ elif isinstance(inp.type(), torch._C.TensorType):
55
+ tensorType = cast(torch._C.TensorType, inp.type())
56
+ inputs.append(make_tensor_from_type(tensorType))
57
+ elif isinstance(inp.type(), torch._C.BoolType):
58
+ inputs.append(random.randint(0, 1) == 1)
59
+ else:
60
+ raise NotImplementedError(f"A default value is not implemented for type {inp.type()}")
61
+
62
+ func = torch._C._create_function_from_graph("forward", graph)
63
+ torch._C._jit_pass_erase_shape_information(func.graph)
64
+ return (func, inputs)
65
+
66
+ def time_cuda(fn, inputs, test_runs):
67
+ t = Timer(stmt="fn(*inputs)", globals={"fn": fn, "inputs" : inputs})
68
+ times = t.blocked_autorange()
69
+ return times.median * 1000 # time in ms
70
+
71
+ def time_cpu(fn, inputs, test_runs):
72
+ s = time.perf_counter()
73
+ for _ in range(test_runs):
74
+ fn(*inputs)
75
+ e = time.perf_counter()
76
+ return (e - s) / test_runs * 1000 # time in ms
77
+
78
+ def run_test(ir, inputs, *, warmup_runs=10, test_runs=20) -> float:
79
+ graph, _ = load_graph_and_inputs(ir)
80
+ for _ in range(warmup_runs):
81
+ graph(*inputs)
82
+
83
+ is_cpu = None
84
+ for input in inputs:
85
+ if isinstance(input, torch.Tensor):
86
+ is_cpu = input.device.type == "cpu"
87
+ break
88
+ if is_cpu is None:
89
+ raise AssertionError("No tensor found in inputs")
90
+
91
+ out = time_cpu(graph, inputs, test_runs) if is_cpu else time_cuda(graph, inputs, test_runs)
92
+ return out
93
+
94
+ @contextmanager
95
+ def no_fuser(*args, **kwargs):
96
+ old_optimize = torch._C._get_graph_executor_optimize(False)
97
+ try:
98
+ yield
99
+ finally:
100
+ torch._C._get_graph_executor_optimize(old_optimize)
101
+
102
+ def run_baseline_no_fusion(ir, inputs) -> float:
103
+ with no_fuser():
104
+ return run_test(ir, inputs)
105
+
106
+
107
+ def run_nnc(ir, inputs, dynamic) -> float:
108
+ try:
109
+ strat = [("DYNAMIC", 10)] if dynamic else [("STATIC", 10)]
110
+ old_strat = torch.jit.set_fusion_strategy(strat)
111
+ with torch.jit.fuser("fuser1"):
112
+ return run_test(ir, inputs)
113
+ finally:
114
+ torch.jit.set_fusion_strategy(old_strat)
115
+
116
+ def run_nvfuser(ir, inputs) -> float:
117
+ with torch.jit.fuser("fuser2"):
118
+ return run_test(ir, inputs)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/mkldnn.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import torch
3
+
4
+
5
+ class MkldnnLinear(torch.jit.ScriptModule):
6
+ def __init__(self, dense_module, dtype) -> None:
7
+ super().__init__()
8
+ self.register_buffer('weight', dense_module.weight.to_mkldnn(dtype))
9
+ if dense_module.bias is not None:
10
+ # Bias can be fp32 or bf16 for OneDNN bf16 path, but for good accuracy,
11
+ # we use fp32 dtype.
12
+ self.register_buffer('bias', dense_module.bias.to_mkldnn())
13
+ else:
14
+ # TODO: Remove this once ScriptModule supports registering None buffer
15
+ self.register_buffer(
16
+ 'bias',
17
+ torch.zeros([dense_module.weight.size(0)], dtype=torch.float).to_mkldnn())
18
+
19
+ @torch.jit.script_method
20
+ def __getstate__(self):
21
+ return (self.weight.to_dense(), self.bias.to_dense(), self.training)
22
+
23
+ @torch.jit.script_method
24
+ def __setstate__(self, state):
25
+ self.weight = state[0].to_mkldnn()
26
+ self.bias = state[1].to_mkldnn()
27
+ self.training = state[2]
28
+
29
+ @torch.jit.script_method
30
+ def forward(self, x):
31
+ x_mkldnn = x if x.is_mkldnn else x.to_mkldnn()
32
+ y_mkldnn = torch._C._nn.mkldnn_linear(x_mkldnn, self.weight, self.bias)
33
+ y = y_mkldnn if x.is_mkldnn else y_mkldnn.to_dense()
34
+ return y
35
+
36
+
37
+ class _MkldnnConvNd(torch.jit.ScriptModule):
38
+ """Common base of MkldnnConv1d and MkldnnConv2d."""
39
+
40
+ __constants__ = ['stride', 'padding', 'dilation', 'groups']
41
+
42
+ def __init__(self, dense_module) -> None:
43
+ super().__init__()
44
+
45
+ self.stride = dense_module.stride
46
+ self.padding = dense_module.padding
47
+ self.dilation = dense_module.dilation
48
+ self.groups = dense_module.groups
49
+
50
+ if dense_module.bias is not None:
51
+ self.register_buffer('bias', dense_module.bias.to_mkldnn())
52
+ else:
53
+ # Bias can be fp32 or bf16 for OneDNN bf16 path, but for good accuracy,
54
+ # we use fp32 dtype.
55
+ # TODO: Remove this once ScriptModule supports registering None buffer
56
+ self.register_buffer(
57
+ 'bias',
58
+ torch.zeros([dense_module.weight.size(0)], dtype=torch.float).to_mkldnn())
59
+
60
+ @torch.jit.script_method
61
+ def __getstate__(self):
62
+ return (self.weight.to_dense(), self.bias.to_dense(), self.training)
63
+
64
+ @torch.jit.script_method
65
+ def forward(self, x):
66
+ return torch.mkldnn_convolution(
67
+ x,
68
+ self.weight,
69
+ self.bias,
70
+ self.padding,
71
+ self.stride,
72
+ self.dilation,
73
+ self.groups)
74
+
75
+
76
+ class MkldnnConv1d(_MkldnnConvNd):
77
+ def __init__(self, dense_module, dtype) -> None:
78
+ super().__init__(dense_module)
79
+
80
+ self.register_buffer('weight', dense_module.weight.to_mkldnn(dtype))
81
+
82
+ @torch.jit.script_method
83
+ def __setstate__(self, state):
84
+ self.weight = state[0].to_mkldnn()
85
+ self.bias = state[1].to_mkldnn()
86
+ self.training = state[2]
87
+
88
+
89
+ class MkldnnConv2d(_MkldnnConvNd):
90
+ def __init__(self, dense_module, dtype) -> None:
91
+ super().__init__(dense_module)
92
+
93
+ self.register_buffer('weight', torch._C._nn.mkldnn_reorder_conv2d_weight(
94
+ dense_module.weight.to_mkldnn(dtype),
95
+ self.padding,
96
+ self.stride,
97
+ self.dilation,
98
+ self.groups))
99
+
100
+ @torch.jit.script_method
101
+ def __setstate__(self, state):
102
+ self.weight = torch._C._nn.mkldnn_reorder_conv2d_weight(
103
+ state[0].to_mkldnn(),
104
+ self.padding,
105
+ self.stride,
106
+ self.dilation,
107
+ self.groups)
108
+ self.bias = state[1].to_mkldnn()
109
+ self.training = state[2]
110
+
111
+ class MkldnnConv3d(_MkldnnConvNd):
112
+ def __init__(self, dense_module, dtype) -> None:
113
+ super().__init__(dense_module)
114
+
115
+ self.register_buffer('weight', torch._C._nn.mkldnn_reorder_conv3d_weight(
116
+ dense_module.weight.to_mkldnn(dtype),
117
+ self.padding,
118
+ self.stride,
119
+ self.dilation,
120
+ self.groups))
121
+
122
+ @torch.jit.script_method
123
+ def __setstate__(self, state):
124
+ self.weight = torch._C._nn.mkldnn_reorder_conv3d_weight(
125
+ state[0].to_mkldnn(),
126
+ self.padding,
127
+ self.stride,
128
+ self.dilation,
129
+ self.groups)
130
+ self.bias = state[1].to_mkldnn()
131
+ self.training = state[2]
132
+
133
+
134
+ class MkldnnBatchNorm(torch.jit.ScriptModule):
135
+ __constants__ = ['exponential_average_factor', 'eps']
136
+
137
+ def __init__(self, dense_module) -> None:
138
+ super().__init__()
139
+
140
+ if dense_module.training:
141
+ raise AssertionError("Only support eval mode batchnorm for mkldnn path now")
142
+ if not dense_module.track_running_stats:
143
+ raise AssertionError("Only support track_running_stats=True for mkldnn path now")
144
+ if not dense_module.affine:
145
+ raise AssertionError("Only support affine=True for mkldnn path now")
146
+
147
+ if dense_module.momentum is None:
148
+ self.exponential_average_factor = 0.0
149
+ else:
150
+ self.exponential_average_factor = dense_module.momentum
151
+ self.eps = dense_module.eps
152
+
153
+ self.register_buffer('weight', dense_module.weight.to_mkldnn())
154
+ self.register_buffer('bias', dense_module.bias.to_mkldnn())
155
+ self.register_buffer('running_mean', dense_module.running_mean.to_mkldnn())
156
+ self.register_buffer('running_var', dense_module.running_var.to_mkldnn())
157
+
158
+ @torch.jit.script_method
159
+ def __getstate__(self):
160
+ weight = self.weight.to_dense()
161
+ bias = self.bias.to_dense()
162
+ running_mean = self.running_mean.to_dense()
163
+ running_var = self.running_var.to_dense()
164
+ return (weight, bias, running_mean, running_var, self.training)
165
+
166
+ @torch.jit.script_method
167
+ def __setstate__(self, state):
168
+ self.weight = state[0].to_mkldnn()
169
+ self.bias = state[1].to_mkldnn()
170
+ self.running_mean = state[2].to_mkldnn()
171
+ self.running_var = state[3].to_mkldnn()
172
+ self.training = state[4]
173
+
174
+ @torch.jit.script_method
175
+ def forward(self, x):
176
+ return torch.batch_norm(
177
+ x,
178
+ self.weight,
179
+ self.bias,
180
+ self.running_mean,
181
+ self.running_var,
182
+ False, # training
183
+ self.exponential_average_factor,
184
+ self.eps,
185
+ False, # cuda_enabled
186
+ )
187
+
188
+ class MkldnnPrelu(torch.jit.ScriptModule):
189
+ def __init__(self, dense_module, dtype) -> None:
190
+ super().__init__()
191
+ self.register_buffer('weight', dense_module.weight.to_mkldnn(dtype))
192
+
193
+ @torch.jit.script_method
194
+ def __getstate__(self):
195
+ return (self.weight.to_dense(), self.training)
196
+
197
+ @torch.jit.script_method
198
+ def __setstate__(self, state):
199
+ self.weight = state[0].to_mkldnn()
200
+ self.training = state[1]
201
+
202
+ @torch.jit.script_method
203
+ def forward(self, x):
204
+ x_mkldnn = x if x.is_mkldnn else x.to_mkldnn()
205
+ y_mkldnn = torch.prelu(x_mkldnn, self.weight)
206
+ y = y_mkldnn if x.is_mkldnn else y_mkldnn.to_dense()
207
+ return y
208
+
209
+ def to_mkldnn(module, dtype=torch.float):
210
+ if dtype not in (torch.float, torch.bfloat16, torch.half):
211
+ raise AssertionError("MKLDNN only support float, bfloat16, and half path now")
212
+
213
+
214
+ def m_fn(m, d):
215
+ if isinstance(m, torch.nn.Linear):
216
+ return MkldnnLinear(m, d)
217
+ elif isinstance(m, torch.nn.Conv1d):
218
+ return MkldnnConv1d(m, d)
219
+ elif isinstance(m, torch.nn.Conv2d):
220
+ return MkldnnConv2d(m, d)
221
+ elif isinstance(m, torch.nn.Conv3d):
222
+ return MkldnnConv3d(m, d)
223
+ elif isinstance(m, (torch.nn.BatchNorm2d, torch.nn.BatchNorm3d)):
224
+ # For batchnorm bf16 path, OneDNN requires weight and bias need fp32 dtype.
225
+ # so it doesn't need dtype argument.
226
+ return MkldnnBatchNorm(m)
227
+ elif isinstance(m, torch.nn.PReLU):
228
+ return MkldnnPrelu(m, d)
229
+ else:
230
+ return m
231
+
232
+ def m_fn_rec(m, d):
233
+ new_m = m_fn(m, d)
234
+ for name, sub_m in m.named_children():
235
+ setattr(new_m, name, m_fn_rec(sub_m, d))
236
+ return new_m
237
+
238
+ return m_fn_rec(module, dtype)
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/mobile_optimizer.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ """This module contains utility method for mobile model optimization and lint."""
3
+
4
+ import torch
5
+ from enum import Enum
6
+ from torch._C import _MobileOptimizerType as MobileOptimizerType
7
+ from typing import AnyStr
8
+
9
+ class LintCode(Enum):
10
+ BUNDLED_INPUT = 1
11
+ REQUIRES_GRAD = 2
12
+ DROPOUT = 3
13
+ BATCHNORM = 4
14
+
15
+ def optimize_for_mobile(
16
+ script_module: torch.jit.ScriptModule,
17
+ optimization_blocklist: set[MobileOptimizerType] | None = None,
18
+ preserved_methods: list[AnyStr] | None = None,
19
+ backend: str = 'CPU') -> torch.jit.RecursiveScriptModule:
20
+ """
21
+ Optimize a torch script module for mobile deployment.
22
+
23
+ Args:
24
+ script_module: An instance of torch script module with type of ScriptModule.
25
+ optimization_blocklist: A set with type of MobileOptimizerType. When set is not passed,
26
+ optimization method will run all the optimizer pass; otherwise, optimizer
27
+ method will run the optimization pass that is not included inside optimization_blocklist.
28
+ preserved_methods: A list of methods that needed to be preserved when freeze_module pass is invoked
29
+ backend: Device type to use for running the result model ('CPU'(default), 'Vulkan' or 'Metal').
30
+ Returns:
31
+ A new optimized torch script module
32
+ """
33
+ if not isinstance(script_module, torch.jit.ScriptModule):
34
+ raise TypeError(
35
+ f'Got {type(script_module)}, but ScriptModule is expected.')
36
+
37
+ if optimization_blocklist is None:
38
+ optimization_blocklist = set()
39
+
40
+ if preserved_methods is None:
41
+ preserved_methods = []
42
+
43
+ # Convert potential byte arrays into strings (if there is any) to pass type checking
44
+ # Here we use a new name as assigning it back to preserved_methods will invoke
45
+ # mypy errors (i.e. List[AnyStr] = List[str])
46
+ preserved_methods_str: list[str] = [str(method) for method in preserved_methods]
47
+
48
+ bundled_inputs_attributes = _get_bundled_inputs_preserved_attributes(script_module, preserved_methods_str)
49
+ if all(hasattr(script_module, method) for method in bundled_inputs_attributes):
50
+ preserved_methods_str = list(set(preserved_methods_str + bundled_inputs_attributes))
51
+
52
+ non_exist_methods = [method for method in preserved_methods_str if not hasattr(script_module, method)]
53
+ if non_exist_methods:
54
+ raise AttributeError(
55
+ f"The following methods to preserve do not exist in script_module: {', '.join(non_exist_methods)}")
56
+
57
+ backend = backend.lower()
58
+ if backend == 'cpu':
59
+ optimized_cpp_module = torch._C._jit_pass_optimize_for_mobile(
60
+ script_module._c,
61
+ optimization_blocklist,
62
+ preserved_methods_str)
63
+ elif backend == 'vulkan':
64
+ optimized_cpp_module = torch._C._jit_pass_vulkan_optimize_for_mobile(
65
+ script_module._c,
66
+ optimization_blocklist,
67
+ preserved_methods_str)
68
+ elif backend == 'metal':
69
+ optimized_cpp_module = torch._C._jit_pass_metal_optimize_for_mobile(script_module._c, preserved_methods_str)
70
+ else:
71
+ raise TypeError("Unknown backend, must be one of 'CPU', 'Vulkan' or 'Metal'")
72
+
73
+ return torch.jit._recursive.wrap_cpp_module(optimized_cpp_module)
74
+
75
+
76
+ def generate_mobile_module_lints(script_module: torch.jit.ScriptModule):
77
+ """
78
+ Generate a list of lints for a given torch script module.
79
+
80
+ Args:
81
+ script_module: An instance of torch script module with type of ScriptModule.
82
+
83
+ Returns:
84
+ lint_map: A list of dictionary that contains modules lints
85
+ """
86
+ if not isinstance(script_module, torch.jit.ScriptModule):
87
+ raise TypeError(
88
+ f'Got {type(script_module)}, but ScriptModule is expected.')
89
+
90
+ lint_list = []
91
+
92
+ if not hasattr(script_module, "_generate_bundled_inputs_for_forward"):
93
+ lint_list.append({"name": LintCode.BUNDLED_INPUT.name, "message": "No bundled input for forward, please add bundled inputs "
94
+ "before saving the module using torch.utils.bundled_inputs.augment_model_with_bundled_inputs."})
95
+
96
+ for name, param in script_module.named_parameters():
97
+ if param.requires_grad:
98
+ lint_list.append({"name": LintCode.REQUIRES_GRAD.name, "message": f"Param {name} requires grad, "
99
+ "please set torch.no_grad() to reduce memory usage and improve computation speed during "
100
+ "inference phase."})
101
+
102
+ op_names = torch.jit.export_opnames(script_module)
103
+ for op_name in op_names:
104
+ if "dropout" in op_name:
105
+ lint_list.append({"name": LintCode.DROPOUT.name,
106
+ "message": f"Operator {op_name} exists, remember to call eval() before "
107
+ "saving the module.and call torch.utils.mobile_optimizer.optimize_for_mobile to drop dropout "
108
+ "operator."})
109
+ if "batch_norm" in op_name:
110
+ lint_list.append({"name": LintCode.BATCHNORM.name,
111
+ "message": f"Operator {op_name} exists, remember to call eval() before "
112
+ "saving the module and call torch.utils.mobile_optimizer.optimize_for_mobile to drop batch_norm "
113
+ "operator."})
114
+
115
+ return lint_list
116
+
117
+ def _get_bundled_inputs_preserved_attributes(script_module: torch.jit.ScriptModule, preserved_methods: list[str]) -> list[str]:
118
+
119
+ bundled_inputs_attributes = []
120
+ # Has bundled inputs for forward
121
+ if hasattr(script_module, 'get_all_bundled_inputs'):
122
+ bundled_inputs_attributes.append('get_all_bundled_inputs')
123
+ bundled_inputs_attributes.append('get_num_bundled_inputs')
124
+
125
+ # Bundled inputs in module after the change that introduced bundled inputs for multiple functions
126
+ if hasattr(script_module, 'get_bundled_inputs_functions_and_info'):
127
+ bundled_inputs_attributes.append('get_bundled_inputs_functions_and_info')
128
+ all_info = script_module.get_bundled_inputs_functions_and_info()
129
+ for function_name in all_info:
130
+ if function_name not in preserved_methods:
131
+ bundled_inputs_attributes.append(function_name)
132
+ bundled_inputs_attributes.append("get_all_bundled_inputs_for_" + function_name)
133
+ bundled_inputs_attributes.append("_bundled_inputs_deflated_" + function_name)
134
+
135
+ return bundled_inputs_attributes
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/__init__.py ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # mypy: allow-untyped-defs
3
+ """
4
+ model_dump: a one-stop shop for TorchScript model inspection.
5
+
6
+ The goal of this tool is to provide a simple way to extract lots of
7
+ useful information from a TorchScript model and make it easy for humans
8
+ to consume. It (mostly) replaces zipinfo, common uses of show_pickle,
9
+ and various ad-hoc analysis notebooks.
10
+
11
+ The tool extracts information from the model and serializes it as JSON.
12
+ That JSON can then be rendered by an HTML+JS page, either by
13
+ loading the JSON over HTTP or producing a fully self-contained page
14
+ with all of the code and data burned-in.
15
+ """
16
+
17
+ # Maintainer notes follow.
18
+ """
19
+ The implementation strategy has tension between 3 goals:
20
+ - Small file size.
21
+ - Fully self-contained.
22
+ - Easy, modern JS environment.
23
+ Using Preact and HTM achieves 1 and 2 with a decent result for 3.
24
+ However, the models I tested with result in ~1MB JSON output,
25
+ so even using something heavier like full React might be tolerable
26
+ if the build process can be worked out.
27
+
28
+ One principle I have followed that I think is very beneficial
29
+ is to keep the JSON data as close as possible to the model
30
+ and do most of the rendering logic on the client.
31
+ This makes for easier development (just refresh, usually),
32
+ allows for more laziness and dynamism, and lets us add more
33
+ views of the same data without bloating the HTML file.
34
+
35
+ Currently, this code doesn't actually load the model or even
36
+ depend on any part of PyTorch. I don't know if that's an important
37
+ feature to maintain, but it's probably worth preserving the ability
38
+ to run at least basic analysis on models that cannot be loaded.
39
+
40
+ I think the easiest way to develop this code is to cd into model_dump and
41
+ run "python -m http.server", then load http://localhost:8000/skeleton.html
42
+ in the browser. In another terminal, run
43
+ "python -m torch.utils.model_dump --style=json FILE > \
44
+ torch/utils/model_dump/model_info.json"
45
+ every time you update the Python code or model.
46
+ When you update JS, just refresh.
47
+
48
+ Possible improvements:
49
+ - Fix various TODO comments in this file and the JS.
50
+ - Make the HTML much less janky, especially the auxiliary data panel.
51
+ - Make the auxiliary data panel start small, expand when
52
+ data is available, and have a button to clear/contract.
53
+ - Clean up the JS. There's a lot of copypasta because
54
+ I don't really know how to use Preact.
55
+ - Make the HTML render and work nicely inside a Jupyter notebook.
56
+ - Add the ability for JS to choose the URL to load the JSON based
57
+ on the page URL (query or hash). That way we could publish the
58
+ inlined skeleton once and have it load various JSON blobs.
59
+ - Add a button to expand all expandable sections so ctrl-F works well.
60
+ - Add hyperlinking from data to code, and code to code.
61
+ - Add hyperlinking from debug info to Diffusion.
62
+ - Make small tensor contents available.
63
+ - Do something nice for quantized models
64
+ (they probably don't work at all right now).
65
+ """
66
+
67
+ import argparse
68
+ import io
69
+ import itertools
70
+ import json
71
+ import os
72
+ import pickle
73
+ import pprint
74
+ import re
75
+ import sys
76
+ import urllib.parse
77
+ import zipfile
78
+ from pathlib import Path
79
+ import warnings
80
+
81
+ import torch.utils.show_pickle
82
+
83
+
84
+ DEFAULT_EXTRA_FILE_SIZE_LIMIT = 16 * 1024
85
+
86
+ __all__ = ['get_storage_info', 'hierarchical_pickle', 'get_model_info', 'get_inline_skeleton',
87
+ 'burn_in_info', 'get_info_and_burn_skeleton']
88
+
89
+ def get_storage_info(storage):
90
+ if not isinstance(storage, torch.utils.show_pickle.FakeObject):
91
+ raise AssertionError(f"storage is not FakeObject: {type(storage)}")
92
+ if storage.module != "pers":
93
+ raise AssertionError(f"storage.module is not 'pers': {storage.module!r}")
94
+ if storage.name != "obj":
95
+ raise AssertionError(f"storage.name is not 'obj': {storage.name!r}")
96
+ if storage.state is not None:
97
+ raise AssertionError(f"storage.state is not None: {storage.state!r}")
98
+ if not isinstance(storage.args, tuple):
99
+ raise AssertionError(f"storage.args is not a tuple: {type(storage.args)}")
100
+ if len(storage.args) != 1:
101
+ raise AssertionError(f"len(storage.args) is not 1: {len(storage.args)}")
102
+ sa = storage.args[0]
103
+ if not isinstance(sa, tuple):
104
+ raise AssertionError(f"sa is not a tuple: {type(sa)}")
105
+ if len(sa) != 5:
106
+ raise AssertionError(f"len(sa) is not 5: {len(sa)}")
107
+ if sa[0] != "storage":
108
+ raise AssertionError(f"sa[0] is not 'storage': {sa[0]!r}")
109
+ if not isinstance(sa[1], torch.utils.show_pickle.FakeClass):
110
+ raise AssertionError(f"sa[1] is not FakeClass: {type(sa[1])}")
111
+ if sa[1].module != "torch":
112
+ raise AssertionError(f"sa[1].module is not 'torch': {sa[1].module!r}")
113
+ if not sa[1].name.endswith("Storage"):
114
+ raise AssertionError(f"sa[1].name does not end with 'Storage': {sa[1].name!r}")
115
+ storage_info = [sa[1].name.replace("Storage", "")] + list(sa[2:])
116
+ return storage_info
117
+
118
+
119
+ def hierarchical_pickle(data):
120
+ if isinstance(data, (bool, int, float, str, type(None))):
121
+ return data
122
+ if isinstance(data, list):
123
+ return [hierarchical_pickle(d) for d in data]
124
+ if isinstance(data, tuple):
125
+ return {
126
+ "__tuple_values__": hierarchical_pickle(list(data)),
127
+ }
128
+ if isinstance(data, dict):
129
+ return {
130
+ "__is_dict__": True,
131
+ "keys": hierarchical_pickle(list(data.keys())),
132
+ "values": hierarchical_pickle(list(data.values())),
133
+ }
134
+ if isinstance(data, torch.utils.show_pickle.FakeObject):
135
+ typename = f"{data.module}.{data.name}"
136
+ if (
137
+ typename.startswith(('__torch__.', 'torch.jit.LoweredWrapper.', 'torch.jit.LoweredModule.'))
138
+ ):
139
+ if data.args != ():
140
+ raise AssertionError("data.args is not ()")
141
+ return {
142
+ "__module_type__": typename,
143
+ "state": hierarchical_pickle(data.state),
144
+ }
145
+ if typename == "torch._utils._rebuild_tensor_v2":
146
+ if data.state is not None:
147
+ raise AssertionError("data.state is not None")
148
+ storage, offset, size, stride, requires_grad, *_ = data.args
149
+ storage_info = get_storage_info(storage)
150
+ return {"__tensor_v2__": [storage_info, offset, size, stride, requires_grad]}
151
+ if typename == "torch._utils._rebuild_qtensor":
152
+ if data.state is not None:
153
+ raise AssertionError("data.state is not None")
154
+ storage, offset, size, stride, quantizer, requires_grad, *_ = data.args
155
+ storage_info = get_storage_info(storage)
156
+ if not isinstance(quantizer, tuple):
157
+ raise AssertionError("quantizer is not a tuple")
158
+ if not isinstance(quantizer[0], torch.utils.show_pickle.FakeClass):
159
+ raise AssertionError("quantizer[0] is not a FakeClass")
160
+ if quantizer[0].module != "torch":
161
+ raise AssertionError("quantizer[0].module is not torch")
162
+ if quantizer[0].name == "per_tensor_affine":
163
+ if len(quantizer) != 3:
164
+ raise AssertionError("len(quantizer) is not 3")
165
+ if not isinstance(quantizer[1], float):
166
+ raise AssertionError("quantizer[1] is not a float")
167
+ if not isinstance(quantizer[2], int):
168
+ raise AssertionError("quantizer[2] is not an int")
169
+ quantizer_extra = list(quantizer[1:3])
170
+ else:
171
+ quantizer_extra = []
172
+ quantizer_json = [quantizer[0].name] + quantizer_extra
173
+ return {"__qtensor__": [storage_info, offset, size, stride, quantizer_json, requires_grad]}
174
+ if typename == "torch.jit._pickle.restore_type_tag":
175
+ if data.state is not None:
176
+ raise AssertionError("data.state is not None")
177
+ obj, typ = data.args
178
+ if not isinstance(typ, str):
179
+ raise AssertionError("typ is not a string")
180
+ return hierarchical_pickle(obj)
181
+ if re.fullmatch(r"torch\.jit\._pickle\.build_[a-z]+list", typename):
182
+ if data.state is not None:
183
+ raise AssertionError("data.state is not None")
184
+ ls, = data.args
185
+ if not isinstance(ls, list):
186
+ raise AssertionError("ls is not a list")
187
+ return hierarchical_pickle(ls)
188
+ if typename == "torch.device":
189
+ if data.state is not None:
190
+ raise AssertionError("data.state is not None")
191
+ name, = data.args
192
+ if not isinstance(name, str):
193
+ raise AssertionError("name is not a string")
194
+ # Just forget that it was a device and return the name.
195
+ return name
196
+ if typename == "builtin.UnicodeDecodeError":
197
+ if data.state is not None:
198
+ raise AssertionError("data.state is not None")
199
+ msg, = data.args
200
+ if not isinstance(msg, str):
201
+ raise AssertionError("msg is not a string")
202
+ # Hack: Pretend this is a module so we don't need custom serialization.
203
+ # Hack: Wrap the message in a tuple so it looks like a nice state object.
204
+ # TODO: Undo at least that second hack. We should support string states.
205
+ return {
206
+ "__module_type__": typename,
207
+ "state": hierarchical_pickle((msg,)),
208
+ }
209
+ raise Exception(f"Can't prepare fake object of type for JS: {typename}") # noqa: TRY002
210
+ raise Exception(f"Can't prepare data of type for JS: {type(data)}") # noqa: TRY002
211
+
212
+
213
+ def get_model_info(
214
+ path_or_file,
215
+ title=None,
216
+ extra_file_size_limit=DEFAULT_EXTRA_FILE_SIZE_LIMIT):
217
+ """Get JSON-friendly information about a model.
218
+
219
+ The result is suitable for being saved as model_info.json,
220
+ or passed to burn_in_info.
221
+ """
222
+
223
+ if isinstance(path_or_file, os.PathLike):
224
+ default_title = os.fspath(path_or_file)
225
+ file_size = path_or_file.stat().st_size # type: ignore[attr-defined]
226
+ elif isinstance(path_or_file, str):
227
+ default_title = path_or_file
228
+ file_size = Path(path_or_file).stat().st_size
229
+ else:
230
+ default_title = "buffer"
231
+ path_or_file.seek(0, io.SEEK_END)
232
+ file_size = path_or_file.tell()
233
+ path_or_file.seek(0)
234
+
235
+ title = title or default_title
236
+
237
+ with zipfile.ZipFile(path_or_file) as zf:
238
+ path_prefix = None
239
+ zip_files = []
240
+ # pyrefly: ignore [bad-assignment]
241
+ for zi in zf.infolist():
242
+ prefix = re.sub("/.*", "", zi.filename)
243
+ if path_prefix is None:
244
+ path_prefix = prefix
245
+ elif prefix != path_prefix:
246
+ raise Exception(f"Mismatched prefixes: {path_prefix} != {prefix}") # noqa: TRY002
247
+ zip_files.append(
248
+ {
249
+ "filename": zi.filename,
250
+ "compression": zi.compress_type,
251
+ "compressed_size": zi.compress_size,
252
+ "file_size": zi.file_size,
253
+ }
254
+ )
255
+ if path_prefix is None:
256
+ raise AssertionError("path_prefix is None")
257
+ version = zf.read(path_prefix + "/version").decode("utf-8").strip()
258
+
259
+ def get_pickle(name):
260
+ if path_prefix is None:
261
+ raise AssertionError("path_prefix is None")
262
+ with zf.open(path_prefix + f"/{name}.pkl") as handle:
263
+ raw = torch.utils.show_pickle.DumpUnpickler(handle, catch_invalid_utf8=True).load()
264
+ return hierarchical_pickle(raw)
265
+
266
+ model_data = get_pickle("data")
267
+ constants = get_pickle("constants")
268
+
269
+ # Intern strings that are likely to be reused.
270
+ # Pickle automatically detects shared structure,
271
+ # so reused strings are stored efficiently.
272
+ # However, JSON has no way of representing this,
273
+ # so we have to do it manually.
274
+ interned_strings : dict[str, int] = {}
275
+
276
+ def intern(s):
277
+ if s not in interned_strings:
278
+ interned_strings[s] = len(interned_strings)
279
+ return interned_strings[s]
280
+
281
+ code_files = {}
282
+ for zi in zf.infolist():
283
+ if not zi.filename.endswith(".py"):
284
+ continue
285
+ with zf.open(zi) as handle:
286
+ raw_code = handle.read()
287
+ with zf.open(zi.filename + ".debug_pkl") as handle:
288
+ raw_debug = handle.read()
289
+
290
+ # Parse debug info and add begin/end markers if not present
291
+ # to ensure that we cover the entire source code.
292
+ debug_info_t = pickle.loads(raw_debug)
293
+ text_table = None
294
+
295
+ if (len(debug_info_t) == 3 and
296
+ isinstance(debug_info_t[0], str) and
297
+ debug_info_t[0] == 'FORMAT_WITH_STRING_TABLE'):
298
+ _, text_table, content = debug_info_t
299
+
300
+ def parse_new_format(line):
301
+ # (0, (('', '', 0), 0, 0))
302
+ num, ((text_indexes, fname_idx, offset), start, end), tag = line
303
+ text = ''.join(text_table[x] for x in text_indexes) # type: ignore[index]
304
+ fname = text_table[fname_idx] # type: ignore[index]
305
+ return num, ((text, fname, offset), start, end), tag
306
+
307
+ debug_info_t = map(parse_new_format, content)
308
+
309
+ debug_info = list(debug_info_t)
310
+ if not debug_info:
311
+ debug_info.append((0, (('', '', 0), 0, 0)))
312
+ if debug_info[-1][0] != len(raw_code):
313
+ debug_info.append((len(raw_code), (('', '', 0), 0, 0)))
314
+
315
+ code_parts = []
316
+ for di, di_next in itertools.pairwise(debug_info):
317
+ start, source_range, *_ = di
318
+ end = di_next[0]
319
+ if end <= start:
320
+ raise AssertionError("end is not greater than start")
321
+ source, s_start, s_end = source_range
322
+ s_text, s_file, s_line = source
323
+ # TODO: Handle this case better. TorchScript ranges are in bytes,
324
+ # but JS doesn't really handle byte strings.
325
+ # if bytes and chars are not equivalent for this string,
326
+ # zero out the ranges so we don't highlight the wrong thing.
327
+ if len(s_text) != len(s_text.encode("utf-8")):
328
+ s_start = 0
329
+ s_end = 0
330
+ text = raw_code[start:end]
331
+ code_parts.append([text.decode("utf-8"), intern(s_file), s_line, intern(s_text), s_start, s_end])
332
+ code_files[zi.filename] = code_parts
333
+
334
+ extra_files_json_pattern = re.compile(re.escape(path_prefix) + "/extra/.*\\.json")
335
+ extra_files_jsons = {}
336
+ for zi in zf.infolist():
337
+ if not extra_files_json_pattern.fullmatch(zi.filename):
338
+ continue
339
+ if zi.file_size > extra_file_size_limit:
340
+ continue
341
+ with zf.open(zi) as handle:
342
+ try:
343
+ json_content = json.load(handle)
344
+ extra_files_jsons[zi.filename] = json_content
345
+ except json.JSONDecodeError:
346
+ extra_files_jsons[zi.filename] = "INVALID JSON"
347
+
348
+ always_render_pickles = {
349
+ "bytecode.pkl",
350
+ }
351
+ extra_pickles = {}
352
+ for zi in zf.infolist():
353
+ if not zi.filename.endswith(".pkl"):
354
+ continue
355
+ with zf.open(zi) as handle:
356
+ # TODO: handle errors here and just ignore the file?
357
+ # NOTE: For a lot of these files (like bytecode),
358
+ # we could get away with just unpickling, but this should be safer.
359
+ obj = torch.utils.show_pickle.DumpUnpickler(handle, catch_invalid_utf8=True).load()
360
+ buf = io.StringIO()
361
+ pprint.pprint(obj, buf)
362
+ contents = buf.getvalue()
363
+ # Checked the rendered length instead of the file size
364
+ # because pickles with shared structure can explode in size during rendering.
365
+ if os.path.basename(zi.filename) not in always_render_pickles and \
366
+ len(contents) > extra_file_size_limit:
367
+ continue
368
+ extra_pickles[zi.filename] = contents
369
+
370
+ return {
371
+ "model": {
372
+ "title": title,
373
+ "file_size": file_size,
374
+ "version": version,
375
+ "zip_files": zip_files,
376
+ "interned_strings": list(interned_strings),
377
+ "code_files": code_files,
378
+ "model_data": model_data,
379
+ "constants": constants,
380
+ "extra_files_jsons": extra_files_jsons,
381
+ "extra_pickles": extra_pickles,
382
+ }
383
+ }
384
+
385
+
386
+ def get_inline_skeleton():
387
+ """Get a fully-inlined skeleton of the frontend.
388
+
389
+ The returned HTML page has no external network dependencies for code.
390
+ It can load model_info.json over HTTP, or be passed to burn_in_info.
391
+ """
392
+
393
+ import importlib.resources
394
+
395
+ # pyrefly: ignore [bad-argument-type]
396
+ skeleton = importlib.resources.read_text(__package__, "skeleton.html")
397
+ # pyrefly: ignore [bad-argument-type]
398
+ js_code = importlib.resources.read_text(__package__, "code.js")
399
+ for js_module in ["preact", "htm"]:
400
+ # pyrefly: ignore [bad-argument-type]
401
+ js_lib = importlib.resources.read_binary(__package__, f"{js_module}.mjs")
402
+ js_url = "data:application/javascript," + urllib.parse.quote(js_lib)
403
+ js_code = js_code.replace(f"https://unpkg.com/{js_module}?module", js_url)
404
+ skeleton = skeleton.replace(' src="./code.js">', ">\n" + js_code)
405
+ return skeleton
406
+
407
+
408
+ def burn_in_info(skeleton, info):
409
+ """Burn model info into the HTML skeleton.
410
+
411
+ The result will render the hard-coded model info and
412
+ have no external network dependencies for code or data.
413
+ """
414
+
415
+ # Note that Python's json serializer does not escape slashes in strings.
416
+ # Since we're inlining this JSON directly into a script tag, a string
417
+ # containing "</script>" would end the script prematurely and
418
+ # mess up our page. Unconditionally escape fixes that.
419
+ return skeleton.replace(
420
+ "BURNED_IN_MODEL_INFO = null",
421
+ "BURNED_IN_MODEL_INFO = " + json.dumps(info, sort_keys=True).replace("/", "\\/"))
422
+
423
+
424
+ def get_info_and_burn_skeleton(path_or_bytesio, **kwargs):
425
+ model_info = get_model_info(path_or_bytesio, **kwargs)
426
+ skeleton = get_inline_skeleton()
427
+ page = burn_in_info(skeleton, model_info)
428
+ return page
429
+
430
+
431
+ def main(argv, *, stdout=None) -> None:
432
+ warnings.warn("torch.utils.model_dump is deprecated and will be removed in a future PyTorch release.", stacklevel=2)
433
+ parser = argparse.ArgumentParser()
434
+ parser.add_argument("--style", choices=["json", "html"])
435
+ parser.add_argument("--title")
436
+ parser.add_argument("model")
437
+ args = parser.parse_args(argv[1:])
438
+
439
+ info = get_model_info(args.model, title=args.title)
440
+
441
+ output = stdout or sys.stdout
442
+
443
+ if args.style == "json":
444
+ output.write(json.dumps(info, sort_keys=True) + "\n")
445
+ elif args.style == "html":
446
+ skeleton = get_inline_skeleton()
447
+ page = burn_in_info(skeleton, info)
448
+ output.write(page)
449
+ else:
450
+ raise Exception("Invalid style") # noqa: TRY002
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/__main__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import sys
3
+ from . import main
4
+
5
+ sys.exit(main(sys.argv))
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/code.js ADDED
@@ -0,0 +1,689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { h, Component, render } from 'https://unpkg.com/preact?module';
2
+ import htm from 'https://unpkg.com/htm?module';
3
+
4
+ const html = htm.bind(h);
5
+
6
+ const BURNED_IN_MODEL_INFO = null;
7
+
8
+ // https://stackoverflow.com/a/20732091
9
+ function humanFileSize(size) {
10
+ if (size == 0) { return "0 B"; }
11
+ var i = Math.floor( Math.log(size) / Math.log(1024) );
12
+ return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
13
+ }
14
+
15
+ function caret(down) {
16
+ return down ? "\u25BE" : "\u25B8";
17
+ }
18
+
19
+ class Blamer {
20
+ constructor() {
21
+ this.blame_on_click = false;
22
+ this.aux_content_pane = null;
23
+ }
24
+
25
+ setAuxContentPane(pane) {
26
+ this.aux_content_pane = pane;
27
+ }
28
+
29
+ readyBlame() {
30
+ this.blame_on_click = true;
31
+ }
32
+
33
+ maybeBlame(arg) {
34
+ if (!this.blame_on_click) {
35
+ return;
36
+ }
37
+ this.blame_on_click = false;
38
+ if (!this.aux_content_pane) {
39
+ return;
40
+ }
41
+ this.aux_content_pane.doBlame(arg);
42
+ }
43
+ }
44
+
45
+ let blame = new Blamer();
46
+
47
+ class Hider extends Component {
48
+ constructor() {
49
+ super();
50
+ this.state = { shown: null };
51
+ }
52
+
53
+ componentDidMount() {
54
+ this.setState({ shown: this.props.shown === "true" });
55
+ }
56
+
57
+ render({name, children}, {shown}) {
58
+ let my_caret = html`<span class=caret onClick=${() => this.click()} >${caret(shown)}</span>`;
59
+ return html`<div data-hider-title=${name} data-shown=${shown}>
60
+ <h2>${my_caret} ${name}</h2>
61
+ <div>${shown ? this.props.children : []}</div></div>`;
62
+ }
63
+
64
+ click() {
65
+ this.setState({shown: !this.state.shown});
66
+ }
67
+ }
68
+
69
+ function ModelSizeSection({model: {file_size, zip_files}}) {
70
+ let store_size = 0;
71
+ let compr_size = 0;
72
+ for (const zi of zip_files) {
73
+ if (zi.compression === 0) {
74
+ // TODO: Maybe check that compressed_size === file_size.
75
+ store_size += zi.compressed_size;
76
+ } else {
77
+ compr_size += zi.compressed_size;
78
+ }
79
+ }
80
+ let zip_overhead = file_size - store_size - compr_size;
81
+ // TODO: Better formatting. Right-align this.
82
+ return html`
83
+ <${Hider} name="Model Size" shown=true>
84
+ <pre>.
85
+ Model size: ${file_size} (${humanFileSize(file_size)})
86
+ Stored files: ${store_size} (${humanFileSize(store_size)})
87
+ Compressed files: ${compr_size} (${humanFileSize(compr_size)})
88
+ Zip overhead: ${zip_overhead} (${humanFileSize(zip_overhead)})
89
+ </pre><//>`;
90
+ }
91
+
92
+ function StructuredDataSection({name, data, shown}) {
93
+ return html`
94
+ <${Hider} name=${name} shown=${shown}>
95
+ <div style="font-family:monospace;">
96
+ <${StructuredData} data=${data} indent="" prefix=""/>
97
+ </div><//>`;
98
+ }
99
+
100
+ class StructuredData extends Component {
101
+ constructor() {
102
+ super();
103
+ this.state = { shown: false };
104
+
105
+ this.INLINE_TYPES = new Set(["boolean", "number", "string"])
106
+ this.IGNORED_STATE_KEYS = new Set(["training", "_is_full_backward_hook"])
107
+ }
108
+
109
+ click() {
110
+ this.setState({shown: !this.state.shown});
111
+ }
112
+
113
+ expando(data) {
114
+ if (data === null || this.INLINE_TYPES.has(typeof(data))) {
115
+ return false;
116
+ }
117
+ if (typeof(data) != "object") {
118
+ throw new Error("Not an object");
119
+ }
120
+ if (Array.isArray(data)) {
121
+ // TODO: Maybe show simple lists and tuples on one line.
122
+ return true;
123
+ }
124
+ if (data.__tuple_values__) {
125
+ // TODO: Maybe show simple lists and tuples on one line.
126
+ return true;
127
+ }
128
+ if (data.__is_dict__) {
129
+ // TODO: Maybe show simple (empty?) dicts on one line.
130
+ return true;
131
+ }
132
+ if (data.__module_type__) {
133
+ return true;
134
+ }
135
+ if (data.__tensor_v2__) {
136
+ return false;
137
+ }
138
+ if (data.__qtensor__) {
139
+ return false;
140
+ }
141
+ throw new Error("Can't handle data type.", data);
142
+ }
143
+
144
+ renderHeadline(data) {
145
+ if (data === null) {
146
+ return "None";
147
+ }
148
+ if (typeof(data) == "boolean") {
149
+ const sd = String(data);
150
+ return sd.charAt(0).toUpperCase() + sd.slice(1);
151
+ }
152
+ if (typeof(data) == "number") {
153
+ return JSON.stringify(data);
154
+ }
155
+ if (typeof(data) == "string") {
156
+ return JSON.stringify(data);
157
+ }
158
+ if (typeof(data) != "object") {
159
+ throw new Error("Not an object");
160
+ }
161
+ if (Array.isArray(data)) {
162
+ return "list([";
163
+ }
164
+ if (data.__tuple_values__) {
165
+ return "tuple((";
166
+ }
167
+ if (data.__is_dict__) {
168
+ return "dict({";
169
+ }
170
+ if (data.__module_type__) {
171
+ return data.__module_type__ + "()";
172
+ }
173
+ if (data.__tensor_v2__) {
174
+ const [storage, offset, size, stride, grad] = data.__tensor_v2__;
175
+ const [dtype, key, device, numel] = storage;
176
+ return this.renderTensor(
177
+ "tensor", dtype, key, device, numel, offset, size, stride, grad, []);
178
+ }
179
+ if (data.__qtensor__) {
180
+ const [storage, offset, size, stride, quantizer, grad] = data.__qtensor__;
181
+ const [dtype, key, device, numel] = storage;
182
+ let extra_parts = [];
183
+ if (quantizer[0] == "per_tensor_affine") {
184
+ extra_parts.push(`scale=${quantizer[1]}`);
185
+ extra_parts.push(`zero_point=${quantizer[2]}`);
186
+ } else {
187
+ extra_parts.push(`quantizer=${quantizer[0]}`);
188
+ }
189
+ return this.renderTensor(
190
+ "qtensor", dtype, key, device, numel, offset, size, stride, grad, extra_parts);
191
+ }
192
+ throw new Error("Can't handle data type.", data);
193
+ }
194
+
195
+ renderTensor(
196
+ prefix,
197
+ dtype,
198
+ storage_key,
199
+ device,
200
+ storage_numel,
201
+ offset,
202
+ size,
203
+ stride,
204
+ grad,
205
+ extra_parts) {
206
+ let parts = [
207
+ "(" + size.join(",") + ")",
208
+ dtype,
209
+ ];
210
+ parts.push(...extra_parts);
211
+ if (device != "cpu") {
212
+ parts.push(device);
213
+ }
214
+ if (grad) {
215
+ parts.push("grad");
216
+ }
217
+ // TODO: Check stride and indicate if the tensor is channels-last or non-contiguous
218
+ // TODO: Check size, stride, offset, and numel and indicate if
219
+ // the tensor doesn't use all data in storage.
220
+ // TODO: Maybe show key?
221
+ void(offset);
222
+ void(stride);
223
+ void(storage_key);
224
+ void(storage_numel);
225
+ return prefix + "(" + parts.join(", ") + ")";
226
+ }
227
+
228
+ renderBody(indent, data) {
229
+ if (data === null || this.INLINE_TYPES.has(typeof(data))) {
230
+ throw "Should not reach here."
231
+ }
232
+ if (typeof(data) != "object") {
233
+ throw new Error("Not an object");
234
+ }
235
+ if (Array.isArray(data)) {
236
+ let new_indent = indent + "\u00A0\u00A0";
237
+ let parts = [];
238
+ for (let idx = 0; idx < data.length; idx++) {
239
+ // Does it make sense to put explicit index numbers here?
240
+ parts.push(html`<br/><${StructuredData} prefix=${idx + ": "} indent=${new_indent} data=${data[idx]} />`);
241
+ }
242
+ return parts;
243
+ }
244
+ if (data.__tuple_values__) {
245
+ // Handled the same as lists.
246
+ return this.renderBody(indent, data.__tuple_values__);
247
+ }
248
+ if (data.__is_dict__) {
249
+ let new_indent = indent + "\u00A0\u00A0";
250
+ let parts = [];
251
+ for (let idx = 0; idx < data.keys.length; idx++) {
252
+ if (typeof(data.keys[idx]) != "string") {
253
+ parts.push(html`<br/>${new_indent}Non-string key`);
254
+ } else {
255
+ parts.push(html`<br/><${StructuredData} prefix=${data.keys[idx] + ": "} indent=${new_indent} data=${data.values[idx]} />`);
256
+ }
257
+ }
258
+ return parts;
259
+ }
260
+ if (data.__module_type__) {
261
+ const mstate = data.state;
262
+ if (mstate === null || typeof(mstate) != "object") {
263
+ throw new Error("Bad module state");
264
+ }
265
+ let new_indent = indent + "\u00A0\u00A0";
266
+ let parts = [];
267
+ if (mstate.__is_dict__) {
268
+ // TODO: Less copy/paste between this and normal dicts.
269
+ for (let idx = 0; idx < mstate.keys.length; idx++) {
270
+ if (typeof(mstate.keys[idx]) != "string") {
271
+ parts.push(html`<br/>${new_indent}Non-string key`);
272
+ } else if (this.IGNORED_STATE_KEYS.has(mstate.keys[idx])) {
273
+ // Do nothing.
274
+ } else {
275
+ parts.push(html`<br/><${StructuredData} prefix=${mstate.keys[idx] + ": "} indent=${new_indent} data=${mstate.values[idx]} />`);
276
+ }
277
+ }
278
+ } else if (mstate.__tuple_values__) {
279
+ parts.push(html`<br/><${StructuredData} prefix="" indent=${new_indent} data=${mstate} />`);
280
+ } else if (mstate.__module_type__) {
281
+ // We normally wouldn't have the state of a module be another module,
282
+ // but we use "modules" to encode special values (like Unicode decode
283
+ // errors) that might be valid states. Just go with it.
284
+ parts.push(html`<br/><${StructuredData} prefix="" indent=${new_indent} data=${mstate} />`);
285
+ } else {
286
+ throw new Error("Bad module state");
287
+ }
288
+ return parts;
289
+ }
290
+ if (data.__tensor_v2__) {
291
+ throw "Should not reach here."
292
+ }
293
+ if (data.__qtensor__) {
294
+ throw "Should not reach here."
295
+ }
296
+ throw new Error("Can't handle data type.", data);
297
+ }
298
+
299
+ render({data, indent, prefix}, {shown}) {
300
+ const exp = this.expando(data) ? html`<span class=caret onClick=${() => this.click()} >${caret(shown)} </span>` : "";
301
+ const headline = this.renderHeadline(data);
302
+ const body = shown ? this.renderBody(indent, data) : "";
303
+ return html`${indent}${exp}${prefix}${headline}${body}`;
304
+ }
305
+ }
306
+
307
+ function ZipContentsSection({model: {zip_files}}) {
308
+ // TODO: Add human-readable sizes?
309
+ // TODO: Add sorting options?
310
+ // TODO: Add hierarchical collapsible tree?
311
+ return html`
312
+ <${Hider} name="Zip Contents" shown=false>
313
+ <table>
314
+ <thead>
315
+ <tr>
316
+ <th>Mode</th>
317
+ <th>Size</th>
318
+ <th>Compressed</th>
319
+ <th>Name</th>
320
+ </tr>
321
+ </thead>
322
+ <tbody style="font-family:monospace;">
323
+ ${zip_files.map(zf => html`<tr>
324
+ <td>${{0: "store", 8: "deflate"}[zf.compression] || zf.compression}</td>
325
+ <td>${zf.file_size}</td>
326
+ <td>${zf.compressed_size}</td>
327
+ <td>${zf.filename}</td>
328
+ </tr>`)}
329
+ </tbody>
330
+ </table><//>`;
331
+ }
332
+
333
+ function CodeSection({model: {code_files}}) {
334
+ return html`
335
+ <${Hider} name="Code" shown=false>
336
+ <div>
337
+ ${Object.entries(code_files).map(([fn, code]) => html`<${OneCodeSection}
338
+ filename=${fn} code=${code} />`)}
339
+ </div><//>`;
340
+ }
341
+
342
+ class OneCodeSection extends Component {
343
+ constructor() {
344
+ super();
345
+ this.state = { shown: false };
346
+ }
347
+
348
+ click() {
349
+ const shown = !this.state.shown;
350
+ this.setState({shown: shown});
351
+ }
352
+
353
+ render({filename, code}, {shown}) {
354
+ const header = html`
355
+ <h3 style="font-family:monospace;">
356
+ <span class=caret onClick=${() => this.click()} >${caret(shown)} </span>
357
+ ${filename}</h3>
358
+ `;
359
+ if (!shown) {
360
+ return header;
361
+ }
362
+ return html`
363
+ ${header}
364
+ <pre>${code.map(c => this.renderBlock(c))}</pre>
365
+ `;
366
+ }
367
+
368
+ renderBlock([text, ist_file, line, ist_s_text, s_start, s_end]) {
369
+ return html`<span
370
+ onClick=${() => blame.maybeBlame({ist_file, line, ist_s_text, s_start, s_end})}
371
+ >${text}</span>`;
372
+ }
373
+ }
374
+
375
+ function ExtraJsonSection({files}) {
376
+ return html`
377
+ <${Hider} name="Extra files (JSON)" shown=false>
378
+ <div>
379
+ <p>Use "Log Raw Model Info" for hierarchical view in browser console.</p>
380
+ ${Object.entries(files).map(([fn, json]) => html`<${OneJsonSection}
381
+ filename=${fn} json=${json} />`)}
382
+ </div><//>`;
383
+ }
384
+
385
+ class OneJsonSection extends Component {
386
+ constructor() {
387
+ super();
388
+ this.state = { shown: false };
389
+ }
390
+
391
+ click() {
392
+ const shown = !this.state.shown;
393
+ this.setState({shown: shown});
394
+ }
395
+
396
+ render({filename, json}, {shown}) {
397
+ const header = html`
398
+ <h3 style="font-family:monospace;">
399
+ <span class=caret onClick=${() => this.click()} >${caret(shown)} </span>
400
+ ${filename}</h3>
401
+ `;
402
+ if (!shown) {
403
+ return header;
404
+ }
405
+ return html`
406
+ ${header}
407
+ <pre>${JSON.stringify(json, null, 2)}</pre>
408
+ `;
409
+ }
410
+ }
411
+
412
+ function ExtraPicklesSection({files}) {
413
+ return html`
414
+ <${Hider} name="Extra Pickles" shown=false>
415
+ <div>
416
+ ${Object.entries(files).map(([fn, content]) => html`<${OnePickleSection}
417
+ filename=${fn} content=${content} />`)}
418
+ </div><//>`;
419
+ }
420
+
421
+ class OnePickleSection extends Component {
422
+ constructor() {
423
+ super();
424
+ this.state = { shown: false };
425
+ }
426
+
427
+ click() {
428
+ const shown = !this.state.shown;
429
+ this.setState({shown: shown});
430
+ }
431
+
432
+ render({filename, content}, {shown}) {
433
+ const header = html`
434
+ <h3 style="font-family:monospace;">
435
+ <span class=caret onClick=${() => this.click()} >${caret(shown)} </span>
436
+ ${filename}</h3>
437
+ `;
438
+ if (!shown) {
439
+ return header;
440
+ }
441
+ return html`
442
+ ${header}
443
+ <pre>${content}</pre>
444
+ `;
445
+ }
446
+ }
447
+
448
+ function assertStorageAreEqual(key, lhs, rhs) {
449
+ if (lhs.length !== rhs.length ||
450
+ !lhs.every((val, idx) => val === rhs[idx])) {
451
+ throw new Error("Storage mismatch for key '" + key + "'");
452
+ }
453
+ }
454
+
455
+ function computeTensorMemory(numel, dtype) {
456
+ const sizes = {
457
+ "Byte": 1,
458
+ "Char": 1,
459
+ "Short": 2,
460
+ "Int": 4,
461
+ "Long": 8,
462
+ "Half": 2,
463
+ "Float": 4,
464
+ "Double": 8,
465
+ "ComplexHalf": 4,
466
+ "ComplexFloat": 8,
467
+ "ComplexDouble": 16,
468
+ "Bool": 1,
469
+ "QInt8": 1,
470
+ "QUInt8": 1,
471
+ "QInt32": 4,
472
+ "BFloat16": 2,
473
+ };
474
+ let dtsize = sizes[dtype];
475
+ if (!dtsize) {
476
+ throw new Error("Unrecognized dtype: " + dtype);
477
+ }
478
+ return numel * dtsize;
479
+ }
480
+
481
+ // TODO: Maybe track by dtype as well.
482
+ // TODO: Maybe distinguish between visible size and storage size.
483
+ function getTensorStorages(data) {
484
+ if (data === null) {
485
+ return new Map();
486
+ }
487
+ if (typeof(data) == "boolean") {
488
+ return new Map();
489
+ }
490
+ if (typeof(data) == "number") {
491
+ return new Map();
492
+ }
493
+ if (typeof(data) == "string") {
494
+ return new Map();
495
+ }
496
+ if (typeof(data) != "object") {
497
+ throw new Error("Not an object");
498
+ }
499
+ if (Array.isArray(data)) {
500
+ let result = new Map();
501
+ for (const item of data) {
502
+ const tensors = getTensorStorages(item);
503
+ for (const [key, storage] of tensors.entries()) {
504
+ if (!result.has(key)) {
505
+ result.set(key, storage);
506
+ } else {
507
+ const old_storage = result.get(key);
508
+ assertStorageAreEqual(key, old_storage, storage);
509
+ }
510
+ }
511
+ }
512
+ return result;
513
+ }
514
+ if (data.__tuple_values__) {
515
+ return getTensorStorages(data.__tuple_values__);
516
+ }
517
+ if (data.__is_dict__) {
518
+ return getTensorStorages(data.values);
519
+ }
520
+ if (data.__module_type__) {
521
+ return getTensorStorages(data.state);
522
+ }
523
+ if (data.__tensor_v2__) {
524
+ const [storage, offset, size, stride, grad] = data.__tensor_v2__;
525
+ const [dtype, key, device, numel] = storage;
526
+ return new Map([[key, storage]]);
527
+ }
528
+ if (data.__qtensor__) {
529
+ const [storage, offset, size, stride, quantizer, grad] = data.__qtensor__;
530
+ const [dtype, key, device, numel] = storage;
531
+ return new Map([[key, storage]]);
532
+ }
533
+ throw new Error("Can't handle data type.", data);
534
+ }
535
+
536
+ function getTensorMemoryByDevice(pickles) {
537
+ let all_tensors = [];
538
+ for (const [name, pickle] of pickles) {
539
+ const tensors = getTensorStorages(pickle);
540
+ all_tensors.push(...tensors.values());
541
+ }
542
+ let result = {};
543
+ for (const storage of all_tensors.values()) {
544
+ const [dtype, key, device, numel] = storage;
545
+ const size = computeTensorMemory(numel, dtype);
546
+ result[device] = (result[device] || 0) + size;
547
+ }
548
+ return result;
549
+ }
550
+
551
+ // Make this a separate component so it is rendered lazily.
552
+ class OpenTensorMemorySection extends Component {
553
+ render({model: {model_data, constants}}) {
554
+ let sizes = getTensorMemoryByDevice(new Map([
555
+ ["data", model_data],
556
+ ["constants", constants],
557
+ ]));
558
+ return html`
559
+ <table>
560
+ <thead>
561
+ <tr>
562
+ <th>Device</th>
563
+ <th>Bytes</th>
564
+ <th>Human</th>
565
+ </tr>
566
+ </thead>
567
+ <tbody style="font-family:monospace;">
568
+ ${Object.entries(sizes).map(([dev, size]) => html`<tr>
569
+ <td>${dev}</td>
570
+ <td>${size}</td>
571
+ <td>${humanFileSize(size)}</td>
572
+ </tr>`)}
573
+ </tbody>
574
+ </table>`;
575
+ }
576
+ }
577
+
578
+ function TensorMemorySection({model}) {
579
+ return html`
580
+ <${Hider} name="Tensor Memory" shown=false>
581
+ <${OpenTensorMemorySection} model=${model} /><//>`;
582
+ }
583
+
584
+ class AuxContentPane extends Component {
585
+ constructor() {
586
+ super();
587
+ this.state = {
588
+ blame_info: null,
589
+ };
590
+ }
591
+
592
+ doBlame(arg) {
593
+ this.setState({...this.state, blame_info: arg});
594
+ }
595
+
596
+ render({model: {interned_strings}}, {blame_info}) {
597
+ let blame_content = "";
598
+ if (blame_info) {
599
+ const {ist_file, line, ist_s_text, s_start, s_end} = blame_info;
600
+ let s_text = interned_strings[ist_s_text];
601
+ if (s_start != 0 || s_end != s_text.length) {
602
+ let prefix = s_text.slice(0, s_start);
603
+ let main = s_text.slice(s_start, s_end);
604
+ let suffix = s_text.slice(s_end);
605
+ s_text = html`${prefix}<strong>${main}</strong>${suffix}`;
606
+ }
607
+ blame_content = html`
608
+ <h3>${interned_strings[ist_file]}:${line}</h3>
609
+ <pre>${s_start}:${s_end}</pre>
610
+ <pre>${s_text}</pre><br/>
611
+ `;
612
+ }
613
+ return html`
614
+ <button onClick=${() => blame.readyBlame()}>Blame Code</button>
615
+ <br/>
616
+ ${blame_content}
617
+ `;
618
+ }
619
+ }
620
+
621
+ class App extends Component {
622
+ constructor() {
623
+ super();
624
+ this.state = {
625
+ err: false,
626
+ model: null,
627
+ };
628
+ }
629
+
630
+ componentDidMount() {
631
+ const app = this;
632
+ if (BURNED_IN_MODEL_INFO !== null) {
633
+ app.setState({model: BURNED_IN_MODEL_INFO});
634
+ } else {
635
+ fetch("./model_info.json").then(function(response) {
636
+ if (!response.ok) {
637
+ throw new Error("Response not ok.");
638
+ }
639
+ return response.json();
640
+ }).then(function(body) {
641
+ app.setState({model: body});
642
+ }).catch(function(error) {
643
+ console.log("Top-level error: ", error);
644
+ });
645
+ }
646
+ }
647
+
648
+ componentDidCatch(error) {
649
+ void(error);
650
+ this.setState({...this.state, err: true});
651
+ }
652
+
653
+ render(_, {err}) {
654
+ if (this.state.model === null) {
655
+ return html`<h1>Loading...</h1>`;
656
+ }
657
+
658
+ const model = this.state.model.model;
659
+
660
+ let error_msg = "";
661
+ if (err) {
662
+ error_msg = html`<h2 style="background:red">An error occurred. Check console</h2>`;
663
+ }
664
+
665
+ return html`
666
+ ${error_msg}
667
+ <div id=main_content style="position:absolute;width:99%;height:79%;overflow:scroll">
668
+ <h1>TorchScript Model (version ${model.version}): ${model.title}</h1>
669
+ <button onClick=${() => console.log(model)}>Log Raw Model Info</button>
670
+ <${ModelSizeSection} model=${model}/>
671
+ <${StructuredDataSection} name="Model Data" data=${model.model_data} shown=true/>
672
+ <${StructuredDataSection} name="Constants" data=${model.constants} shown=false/>
673
+ <${ZipContentsSection} model=${model}/>
674
+ <${CodeSection} model=${model}/>
675
+ <${ExtraJsonSection} files=${model.extra_files_jsons}/>
676
+ <${ExtraPicklesSection} files=${model.extra_pickles}/>
677
+ <${TensorMemorySection} model=${model}/>
678
+ </div>
679
+ <div id=aux_content style="position:absolute;width:99%;top:80%;height:20%;overflow:scroll">
680
+ <${AuxContentPane}
681
+ err=${this.state.error}
682
+ model=${model}
683
+ ref=${(p) => blame.setAuxContentPane(p)}/>
684
+ </div>
685
+ `;
686
+ }
687
+ }
688
+
689
+ render(h(App), document.body);
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/htm.mjs ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ // HTM, Apache License
2
+ var n=function(t,s,r,e){var u;s[0]=0;for(var h=1;h<s.length;h++){var p=s[h++],a=s[h]?(s[0]|=p?1:2,r[s[h++]]):s[++h];3===p?e[0]=a:4===p?e[1]=Object.assign(e[1]||{},a):5===p?(e[1]=e[1]||{})[s[++h]]=a:6===p?e[1][s[++h]]+=a+"":p?(u=t.apply(a,n(t,a,r,["",null])),e.push(u),a[0]?s[0]|=2:(s[h-2]=0,s[h]=u)):e.push(a)}return e},t=new Map;export default function(s){var r=t.get(this);return r||(r=new Map,t.set(this,r)),(r=n(this,r.get(s)||(r.set(s,r=function(n){for(var t,s,r=1,e="",u="",h=[0],p=function(n){1===r&&(n||(e=e.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?h.push(0,n,e):3===r&&(n||e)?(h.push(3,n,e),r=2):2===r&&"..."===e&&n?h.push(4,n,0):2===r&&e&&!n?h.push(5,0,!0,e):r>=5&&((e||!n&&5===r)&&(h.push(r,0,e,s),r=6),n&&(h.push(r,n,0,s),r=6)),e=""},a=0;a<n.length;a++){a&&(1===r&&p(),p(a));for(var l=0;l<n[a].length;l++)t=n[a][l],1===r?"<"===t?(p(),h=[h],r=3):e+=t:4===r?"--"===e&&">"===t?(r=1,e=""):e=t+e[0]:u?t===u?u="":e+=t:'"'===t||"'"===t?u=t:">"===t?(p(),r=1):r&&("="===t?(r=5,s=e,e=""):"/"===t&&(r<5||">"===n[a][l+1])?(p(),3===r&&(h=h[0]),r=h,(h=h[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(p(),r=2):e+=t),3===r&&"!--"===e&&(r=4,h=h[0])}return p(),h}(s)),r),arguments,[])).length>1?r:r[0]}
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/preact.mjs ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ // Preact, MIT License
2
+ var n,l,u,i,t,o,r={},f=[],e=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function c(e,n){for(var t in n)e[t]=n[t];return e}function s(e){var n=e.parentNode;n&&n.removeChild(e)}function a(e,n,t){var _,l,o,r=arguments,i={};for(o in n)"key"==o?_=n[o]:"ref"==o?l=n[o]:i[o]=n[o];if(arguments.length>3)for(t=[t],o=3;o<arguments.length;o++)t.push(r[o]);if(null!=t&&(i.children=t),"function"==typeof e&&null!=e.defaultProps)for(o in e.defaultProps)void 0===i[o]&&(i[o]=e.defaultProps[o]);return v(e,i,_,l,null)}function v(e,t,_,l,o){var r={type:e,props:t,key:_,ref:l,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++n.__v:o};return null!=n.vnode&&n.vnode(r),r}function h(){return{current:null}}function y(e){return e.children}function p(e,n){this.props=e,this.context=n}function d(e,n){if(null==n)return e.__?d(e.__,e.__.__k.indexOf(e)+1):null;for(var t;n<e.__k.length;n++)if(null!=(t=e.__k[n])&&null!=t.__e)return t.__e;return"function"==typeof e.type?d(e):null}function _(e){var n,t;if(null!=(e=e.__)&&null!=e.__c){for(e.__e=e.__c.base=null,n=0;n<e.__k.length;n++)if(null!=(t=e.__k[n])&&null!=t.__e){e.__e=e.__c.base=t.__e;break}return _(e)}}function k(e){(!e.__d&&(e.__d=!0)&&u.push(e)&&!b.__r++||t!==n.debounceRendering)&&((t=n.debounceRendering)||i)(b)}function b(){for(var e;b.__r=u.length;)e=u.sort(function(e,n){return e.__v.__b-n.__v.__b}),u=[],e.some(function(e){var n,t,l,o,r,i;e.__d&&(r=(o=(n=e).__v).__e,(i=n.__P)&&(t=[],(l=c({},o)).__v=o.__v+1,I(i,o,l,n.__n,void 0!==i.ownerSVGElement,null!=o.__h?[r]:null,t,null==r?d(o):r,o.__h),T(t,o),o.__e!=r&&_(o)))})}function m(e,n,t,_,l,o,i,u,s,c){var p,a,h,m,k,b,C,P=_&&_.__k||f,S=P.length;for(t.__k=[],p=0;p<n.length;p++)if(null!=(m=t.__k[p]=null==(m=n[p])||"boolean"==typeof m?null:"string"==typeof m||"number"==typeof m||"bigint"==typeof m?v(null,m,null,null,m):Array.isArray(m)?v(y,{children:m},null,null,null):m.__b>0?v(m.type,m.props,m.key,null,m.__v):m)){if(m.__=t,m.__b=t.__b+1,null===(h=P[p])||h&&m.key==h.key&&m.type===h.type)P[p]=void 0;else for(a=0;a<S;a++){if((h=P[a])&&m.key==h.key&&m.type===h.type){P[a]=void 0;break}h=null}I(e,m,h=h||r,l,o,i,u,s,c),k=m.__e,(a=m.ref)&&h.ref!=a&&(C||(C=[]),h.ref&&C.push(h.ref,null,m),C.push(a,m.__c||k,m)),null!=k?(null==b&&(b=k),"function"==typeof m.type&&null!=m.__k&&m.__k===h.__k?m.__d=s=g(m,s,e):s=x(e,m,h,P,k,s),c||"option"!==t.type?"function"==typeof t.type&&(t.__d=s):e.value=""):s&&h.__e==s&&s.parentNode!=e&&(s=d(h))}for(t.__e=b,p=S;p--;)null!=P[p]&&("function"==typeof t.type&&null!=P[p].__e&&P[p].__e==t.__d&&(t.__d=d(_,p+1)),L(P[p],P[p]));if(C)for(p=0;p<C.length;p++)z(C[p],C[++p],C[++p])}function g(e,n,t){var _,l;for(_=0;_<e.__k.length;_++)(l=e.__k[_])&&(l.__=e,n="function"==typeof l.type?g(l,n,t):x(t,l,l,e.__k,l.__e,n));return n}function w(e,n){return n=n||[],null==e||"boolean"==typeof e||(Array.isArray(e)?e.some(function(e){w(e,n)}):n.push(e)),n}function x(e,n,t,_,l,o){var r,i,u;if(void 0!==n.__d)r=n.__d,n.__d=void 0;else if(null==t||l!=o||null==l.parentNode)e:if(null==o||o.parentNode!==e)e.appendChild(l),r=null;else{for(i=o,u=0;(i=i.nextSibling)&&u<_.length;u+=2)if(i==l)break e;e.insertBefore(l,o),r=o}return void 0!==r?r:l.nextSibling}function A(e,n,t,_,l){var o;for(o in t)"children"===o||"key"===o||o in n||C(e,o,null,t[o],_);for(o in n)l&&"function"!=typeof n[o]||"children"===o||"key"===o||"value"===o||"checked"===o||t[o]===n[o]||C(e,o,n[o],t[o],_)}function P(n,t,_){"-"===t[0]?n.setProperty(t,_):n[t]=null==_?"":"number"!=typeof _||e.test(t)?_:_+"px"}function C(e,n,t,_,l){var o;e:if("style"===n)if("string"==typeof t)e.style.cssText=t;else{if("string"==typeof _&&(e.style.cssText=_=""),_)for(n in _)t&&n in t||P(e.style,n,"");if(t)for(n in t)_&&t[n]===_[n]||P(e.style,n,t[n])}else if("o"===n[0]&&"n"===n[1])o=n!==(n=n.replace(/Capture$/,"")),n=n.toLowerCase()in e?n.toLowerCase().slice(2):n.slice(2),e.l||(e.l={}),e.l[n+o]=t,t?_||e.addEventListener(n,o?H:$,o):e.removeEventListener(n,o?H:$,o);else if("dangerouslySetInnerHTML"!==n){if(l)n=n.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==n&&"list"!==n&&"form"!==n&&"tabIndex"!==n&&"download"!==n&&n in e)try{e[n]=null==t?"":t;break e}catch(e){}"function"==typeof t||(null!=t&&(!1!==t||"a"===n[0]&&"r"===n[1])?e.setAttribute(n,t):e.removeAttribute(n))}}function $(e){this.l[e.type+!1](n.event?n.event(e):e)}function H(e){this.l[e.type+!0](n.event?n.event(e):e)}function I(e,t,_,l,o,r,i,u,s){var f,a,d,h,v,k,g,b,C,x,P,S=t.type;if(void 0!==t.constructor)return null;null!=_.__h&&(s=_.__h,u=t.__e=_.__e,t.__h=null,r=[u]),(f=n.__b)&&f(t);try{e:if("function"==typeof S){if(b=t.props,C=(f=S.contextType)&&l[f.__c],x=f?C?C.props.value:f.__:l,_.__c?g=(a=t.__c=_.__c).__=a.__E:("prototype"in S&&S.prototype.render?t.__c=a=new S(b,x):(t.__c=a=new p(b,x),a.constructor=S,a.render=M),C&&C.sub(a),a.props=b,a.state||(a.state={}),a.context=x,a.__n=l,d=a.__d=!0,a.__h=[]),null==a.__s&&(a.__s=a.state),null!=S.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=c({},a.__s)),c(a.__s,S.getDerivedStateFromProps(b,a.__s))),h=a.props,v=a.state,d)null==S.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else{if(null==S.getDerivedStateFromProps&&b!==h&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,x),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldComponentUpdate(b,a.__s,x)||t.__v===_.__v){a.props=b,a.state=a.__s,t.__v!==_.__v&&(a.__d=!1),a.__v=t,t.__e=_.__e,t.__k=_.__k,t.__k.forEach(function(e){e&&(e.__=t)}),a.__h.length&&i.push(a);break e}null!=a.componentWillUpdate&&a.componentWillUpdate(b,a.__s,x),null!=a.componentDidUpdate&&a.__h.push(function(){a.componentDidUpdate(h,v,k)})}a.context=x,a.props=b,a.state=a.__s,(f=n.__r)&&f(t),a.__d=!1,a.__v=t,a.__P=e,f=a.render(a.props,a.state,a.context),a.state=a.__s,null!=a.getChildContext&&(l=c(c({},l),a.getChildContext())),d||null==a.getSnapshotBeforeUpdate||(k=a.getSnapshotBeforeUpdate(h,v)),P=null!=f&&f.type===y&&null==f.key?f.props.children:f,m(e,Array.isArray(P)?P:[P],t,_,l,o,r,i,u,s),a.base=t.__e,t.__h=null,a.__h.length&&i.push(a),g&&(a.__E=a.__=null),a.__e=!1}else null==r&&t.__v===_.__v?(t.__k=_.__k,t.__e=_.__e):t.__e=j(_.__e,t,_,l,o,r,i,s);(f=n.diffed)&&f(t)}catch(e){t.__v=null,(s||null!=r)&&(t.__e=u,t.__h=!!s,r[r.indexOf(u)]=null),n.__e(e,t,_)}}function T(e,t){n.__c&&n.__c(t,e),e.some(function(t){try{e=t.__h,t.__h=[],e.some(function(e){e.call(t)})}catch(e){n.__e(e,t.__v)}})}function j(e,n,t,_,l,o,i,u){var c,p,a,d,h=t.props,v=n.props,y=n.type,k=0;if("svg"===y&&(l=!0),null!=o)for(;k<o.length;k++)if((c=o[k])&&(c===e||(y?c.localName==y:3==c.nodeType))){e=c,o[k]=null;break}if(null==e){if(null===y)return document.createTextNode(v);e=l?document.createElementNS("http://www.w3.org/2000/svg",y):document.createElement(y,v.is&&v),o=null,u=!1}if(null===y)h===v||u&&e.data===v||(e.data=v);else{if(o=o&&f.slice.call(e.childNodes),p=(h=t.props||r).dangerouslySetInnerHTML,a=v.dangerouslySetInnerHTML,!u){if(null!=o)for(h={},d=0;d<e.attributes.length;d++)h[e.attributes[d].name]=e.attributes[d].value;(a||p)&&(a&&(p&&a.__html==p.__html||a.__html===e.innerHTML)||(e.innerHTML=a&&a.__html||""))}if(A(e,v,h,l,u),a)n.__k=[];else if(k=n.props.children,m(e,Array.isArray(k)?k:[k],n,t,_,l&&"foreignObject"!==y,o,i,e.firstChild,u),null!=o)for(k=o.length;k--;)null!=o[k]&&s(o[k]);u||("value"in v&&void 0!==(k=v.value)&&(k!==e.value||"progress"===y&&!k)&&C(e,"value",k,h.value,!1),"checked"in v&&void 0!==(k=v.checked)&&k!==e.checked&&C(e,"checked",k,h.checked,!1))}return e}function z(e,t,_){try{"function"==typeof e?e(t):e.current=t}catch(e){n.__e(e,_)}}function L(e,t,_){var l,o,r;if(n.unmount&&n.unmount(e),(l=e.ref)&&(l.current&&l.current!==e.__e||z(l,null,t)),_||"function"==typeof e.type||(_=null!=(o=e.__e)),e.__e=e.__d=void 0,null!=(l=e.__c)){if(l.componentWillUnmount)try{l.componentWillUnmount()}catch(e){n.__e(e,t)}l.base=l.__P=null}if(l=e.__k)for(r=0;r<l.length;r++)l[r]&&L(l[r],t,_);null!=o&&s(o)}function M(e,n,t){return this.constructor(e,t)}function N(e,t,_){var l,o,i;n.__&&n.__(e,t),o=(l="function"==typeof _)?null:_&&_.__k||t.__k,i=[],I(t,e=(!l&&_||t).__k=a(y,null,[e]),o||r,r,void 0!==t.ownerSVGElement,!l&&_?[_]:o?null:t.firstChild?f.slice.call(t.childNodes):null,i,!l&&_?_:o?o.__e:t.firstChild,l),T(i,e)}function O(e,n){N(e,n,O)}function S(e,n,t){var _,l,o,r=arguments,i=c({},e.props);for(o in n)"key"==o?_=n[o]:"ref"==o?l=n[o]:i[o]=n[o];if(arguments.length>3)for(t=[t],o=3;o<arguments.length;o++)t.push(r[o]);return null!=t&&(i.children=t),v(e.type,i,_||e.key,l||e.ref,null)}function q(e,n){var t={__c:n="__cC"+o++,__:e,Consumer:function(e,n){return e.children(n)},Provider:function(e){var t,_;return this.getChildContext||(t=[],(_={})[n]=this,this.getChildContext=function(){return _},this.shouldComponentUpdate=function(e){this.props.value!==e.value&&t.some(k)},this.sub=function(e){t.push(e);var n=e.componentWillUnmount;e.componentWillUnmount=function(){t.splice(t.indexOf(e),1),n&&n.call(e)}}),e.children}};return t.Provider.__=t.Consumer.contextType=t}n={__e:function(e,n){for(var t,_,l;n=n.__;)if((t=n.__c)&&!t.__)try{if((_=t.constructor)&&null!=_.getDerivedStateFromError&&(t.setState(_.getDerivedStateFromError(e)),l=t.__d),null!=t.componentDidCatch&&(t.componentDidCatch(e),l=t.__d),l)return t.__E=t}catch(n){e=n}throw e},__v:0},l=function(e){return null!=e&&void 0===e.constructor},p.prototype.setState=function(e,n){var t;t=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=c({},this.state),"function"==typeof e&&(e=e(c({},t),this.props)),e&&c(t,e),null!=e&&this.__v&&(n&&this.__h.push(n),k(this))},p.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),k(this))},p.prototype.render=y,u=[],i="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,b.__r=0,o=0;export{N as render,O as hydrate,a as createElement,a as h,y as Fragment,h as createRef,l as isValidElement,p as Component,S as cloneElement,q as createContext,w as toChildArray,n as options};
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_dump/skeleton.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>TorchScript Model</title>
5
+ <meta charset="UTF-8">
6
+ <style>
7
+ table, th, td {
8
+ border: 1px solid black;
9
+ border-collapse: collapse;
10
+ }
11
+ .caret {
12
+ cursor: pointer;
13
+ user-select: none;
14
+ }
15
+ </style>
16
+ <script type="module" src="./code.js"></script>
17
+ </head>
18
+
19
+ <body>
20
+ </body>
21
+ </html>
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/model_zoo.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # torchvision imports tqdm from here.
2
+ from torch.hub import tqdm, load_state_dict_from_url as load_url # noqa: F401
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/module_tracker.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # mypy: allow-untyped-defs
2
+ import logging
3
+ import weakref
4
+ from typing import TYPE_CHECKING
5
+
6
+ import torch
7
+ from torch.autograd.graph import register_multi_grad_hook
8
+ from torch.nn.modules.module import (
9
+ register_module_forward_hook,
10
+ register_module_forward_pre_hook,
11
+ )
12
+ from torch.utils._pytree import tree_flatten
13
+
14
+
15
+ if TYPE_CHECKING:
16
+ from torch.utils.hooks import RemovableHandle
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ __all__ = ["ModuleTracker"]
23
+
24
+
25
+ class ModuleTracker:
26
+ """
27
+ ``ModuleTracker`` is a context manager that tracks the nn.Module hierarchy during execution
28
+ so that other system can query which Module is currently being executed (or its backward is being
29
+ executed).
30
+
31
+ You can access the ``parents`` attribute on this context manager to get the set of all the
32
+ Modules currently being executed via their fqn (fully qualified name, also used as the key within
33
+ the state_dict).
34
+ You can access the ``is_bw`` attribute to know if you are currently running in backward or not.
35
+
36
+ Note that ``parents`` is never empty and always contains the "Global" key. The ``is_bw`` flag
37
+ will remain ``True`` after the forward until another Module is executed. If you need it to be
38
+ more accurate, please submit an issue requesting this. Adding a map from fqn to the module instance
39
+ is possible but not done yet, please submit an issue requesting this if you need it.
40
+
41
+ Example usage
42
+
43
+ .. code-block:: python
44
+
45
+ mod = torch.nn.Linear(2, 2)
46
+
47
+ with ModuleTracker() as tracker:
48
+ # Access anything during the forward pass
49
+ def my_linear(m1, m2, bias):
50
+ print(f"Current modules: {tracker.parents}")
51
+ return torch.mm(m1, m2.t()) + bias
52
+
53
+ torch.nn.functional.linear = my_linear
54
+
55
+ mod(torch.rand(2, 2))
56
+
57
+ """
58
+
59
+ parents: set[str]
60
+ """
61
+ A Set containing the fqn for each module currently running their forward
62
+ """
63
+
64
+ def __init__(self) -> None:
65
+ self.parents = {"Global"}
66
+ self._known_modules: weakref.WeakKeyDictionary = weakref.WeakKeyDictionary()
67
+ self._seen_modules: weakref.WeakSet = weakref.WeakSet()
68
+ self._has_callback = False
69
+ self._hooks: list[RemovableHandle] = []
70
+
71
+ def _maybe_set_engine_callback(self) -> None:
72
+ # This assumes no concurrent calls to backward
73
+ if self._has_callback:
74
+ return
75
+
76
+ def callback() -> None:
77
+ self.parents = {"Global"}
78
+ self._has_callback = False
79
+
80
+ torch.autograd.Variable._execution_engine.queue_callback(callback)
81
+ self._has_callback = True
82
+
83
+ @property
84
+ def is_bw(self):
85
+ """
86
+ A boolean marking if this is currently running during the backward pass or not
87
+ """
88
+ return torch._C._current_graph_task_id() != -1
89
+
90
+ def _get_mod_name(self, mod):
91
+ if mod not in self._known_modules:
92
+ self._known_modules[mod] = type(mod).__name__
93
+ mod_name = self._known_modules[mod]
94
+ if mod not in self._seen_modules:
95
+ for name, submod in mod.named_children():
96
+ self._known_modules[submod] = f"{mod_name}.{name}"
97
+ self._get_mod_name(submod)
98
+ self._seen_modules.add(mod)
99
+ return mod_name
100
+
101
+ def _get_append_fn(self, name, is_bw):
102
+ def fn(*args) -> None:
103
+ if is_bw:
104
+ self._maybe_set_engine_callback()
105
+ if name in self.parents:
106
+ logger.info(
107
+ "The module hierarchy tracking seems to be broken as this Module was already entered. %s during %s",
108
+ name,
109
+ "backward" if is_bw else "forward",
110
+ )
111
+ self.parents.add(name)
112
+
113
+ return fn
114
+
115
+ def _get_pop_fn(self, name, is_bw):
116
+ def fn(*args) -> None:
117
+ if name in self.parents:
118
+ self.parents.remove(name)
119
+ else:
120
+ logger.info(
121
+ "The Module hierarchy tracking is confused as we're exiting a Module that was never entered. %s during %s",
122
+ name,
123
+ "backward" if is_bw else "forward",
124
+ )
125
+
126
+ return fn
127
+
128
+ def _fw_pre_hook(self, mod, input) -> None:
129
+ name = self._get_mod_name(mod)
130
+ self._get_append_fn(name, False)()
131
+
132
+ args, _ = tree_flatten(input)
133
+ tensors = [a for a in args if isinstance(a, torch.Tensor) and a.requires_grad]
134
+ if tensors:
135
+ self._hooks.append(
136
+ register_multi_grad_hook(tensors, self._get_pop_fn(name, True))
137
+ )
138
+
139
+ def _fw_post_hook(self, mod, input, output) -> None:
140
+ name = self._get_mod_name(mod)
141
+ self._get_pop_fn(name, False)()
142
+
143
+ args, _ = tree_flatten(output)
144
+ tensors = [a for a in args if isinstance(a, torch.Tensor) and a.requires_grad]
145
+ if tensors:
146
+ self._hooks.append(
147
+ register_multi_grad_hook(tensors, self._get_append_fn(name, True))
148
+ )
149
+
150
+ def __enter__(self):
151
+ self._fw_pre_handle = register_module_forward_pre_hook(self._fw_pre_hook)
152
+ self._fw_post_handle = register_module_forward_hook(self._fw_post_hook)
153
+ return self
154
+
155
+ def __exit__(self, *args):
156
+ self._fw_pre_handle.remove()
157
+ self._fw_post_handle.remove()
158
+ for hook in self._hooks:
159
+ hook.remove()
160
+ self._hooks.clear()
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/serialization/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from . import config
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/serialization/config.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from typing import Optional as _Optional, TYPE_CHECKING as _TYPE_CHECKING
3
+
4
+
5
+ if _TYPE_CHECKING:
6
+ from torch.serialization import LoadEndianness as _LoadEndianess
7
+
8
+ from torch.utils._config_module import install_config_module as _install_config_module
9
+
10
+
11
+ class load:
12
+ mmap: bool = False
13
+ endianness: _Optional["_LoadEndianess"] = None
14
+ # MAP_PRIVATE = 2
15
+ mmap_flags: int | None = None if sys.platform == "win32" else 2
16
+ calculate_storage_offsets: bool = False
17
+
18
+
19
+ class save:
20
+ compute_crc32: bool = True
21
+ use_pinned_memory_for_d2h: bool = False
22
+ storage_alignment: int = 64
23
+
24
+
25
+ _install_config_module(sys.modules[__name__])
outputs/audit_venv/lib/python3.11/site-packages/torch/utils/show_pickle.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # mypy: allow-untyped-defs
3
+ import sys
4
+ import pickle
5
+ import struct
6
+ import pprint
7
+ import zipfile
8
+ import fnmatch
9
+ from typing import Any, IO
10
+
11
+ __all__ = ["FakeObject", "FakeClass", "DumpUnpickler", "main"]
12
+
13
+ class FakeObject:
14
+ def __init__(self, module, name, args) -> None:
15
+ self.module = module
16
+ self.name = name
17
+ self.args = args
18
+ # NOTE: We don't distinguish between state never set and state set to None.
19
+ self.state = None
20
+
21
+ def __repr__(self) -> str:
22
+ state_str = "" if self.state is None else f"(state={self.state!r})"
23
+ return f"{self.module}.{self.name}{self.args!r}{state_str}"
24
+
25
+ def __setstate__(self, state):
26
+ self.state = state
27
+
28
+ @staticmethod
29
+ def pp_format(printer, obj, stream, indent, allowance, context, level) -> None:
30
+ if not obj.args and obj.state is None:
31
+ stream.write(repr(obj))
32
+ return
33
+ if obj.state is None:
34
+ stream.write(f"{obj.module}.{obj.name}")
35
+ printer._format(obj.args, stream, indent + 1, allowance + 1, context, level)
36
+ return
37
+ if not obj.args:
38
+ stream.write(f"{obj.module}.{obj.name}()(state=\n")
39
+ indent += printer._indent_per_level
40
+ stream.write(" " * indent)
41
+ printer._format(obj.state, stream, indent, allowance + 1, context, level + 1)
42
+ stream.write(")")
43
+ return
44
+ raise Exception("Need to implement") # noqa: TRY002
45
+
46
+
47
+ class FakeClass:
48
+ def __init__(self, module, name) -> None:
49
+ self.module = module
50
+ self.name = name
51
+ self.__new__ = self.fake_new # type: ignore[assignment]
52
+
53
+ def __repr__(self) -> str:
54
+ return f"{self.module}.{self.name}"
55
+
56
+ def __call__(self, *args):
57
+ return FakeObject(self.module, self.name, args)
58
+
59
+ def fake_new(self, *args):
60
+ return FakeObject(self.module, self.name, args[1:])
61
+
62
+
63
+ class DumpUnpickler(pickle._Unpickler): # type: ignore[name-defined]
64
+ def __init__(
65
+ self,
66
+ file,
67
+ *,
68
+ catch_invalid_utf8=False,
69
+ **kwargs) -> None:
70
+ super().__init__(file, **kwargs)
71
+ self.catch_invalid_utf8 = catch_invalid_utf8
72
+
73
+ def find_class(self, module, name):
74
+ return FakeClass(module, name)
75
+
76
+ def persistent_load(self, pid):
77
+ return FakeObject("pers", "obj", (pid,))
78
+
79
+ dispatch = dict(pickle._Unpickler.dispatch) # type: ignore[attr-defined]
80
+
81
+ # Custom objects in TorchScript are able to return invalid UTF-8 strings
82
+ # from their pickle (__getstate__) functions. Install a custom loader
83
+ # for strings that catches the decode exception and replaces it with
84
+ # a sentinel object.
85
+ def load_binunicode(self) -> None:
86
+ strlen, = struct.unpack("<I", self.read(4)) # type: ignore[attr-defined]
87
+ if strlen > sys.maxsize:
88
+ raise Exception("String too long.") # noqa: TRY002
89
+ str_bytes = self.read(strlen) # type: ignore[attr-defined]
90
+ obj: Any
91
+ try:
92
+ obj = str(str_bytes, "utf-8", "surrogatepass")
93
+ except UnicodeDecodeError as exn:
94
+ if not self.catch_invalid_utf8:
95
+ raise
96
+ obj = FakeObject("builtin", "UnicodeDecodeError", (str(exn),))
97
+ self.append(obj) # type: ignore[attr-defined]
98
+ dispatch[pickle.BINUNICODE[0]] = load_binunicode # type: ignore[assignment]
99
+
100
+ @classmethod
101
+ def dump(cls, in_stream, out_stream):
102
+ value = cls(in_stream).load()
103
+ pprint.pprint(value, stream=out_stream)
104
+ return value
105
+
106
+
107
+ def main(argv, output_stream=None) -> int | None:
108
+ if len(argv) != 2:
109
+ # Don't spam stderr if not using stdout.
110
+ if output_stream is not None:
111
+ raise Exception("Pass argv of length 2.") # noqa: TRY002
112
+ sys.stderr.write("usage: show_pickle PICKLE_FILE\n")
113
+ sys.stderr.write(" PICKLE_FILE can be any of:\n")
114
+ sys.stderr.write(" path to a pickle file\n")
115
+ sys.stderr.write(" file.zip@member.pkl\n")
116
+ sys.stderr.write(" file.zip@*/pattern.*\n")
117
+ sys.stderr.write(" (shell glob pattern for members)\n")
118
+ sys.stderr.write(" (only first match will be shown)\n")
119
+ return 2
120
+
121
+ fname = argv[1]
122
+ handle: IO[bytes]
123
+ if "@" not in fname:
124
+ with open(fname, "rb") as handle:
125
+ DumpUnpickler.dump(handle, output_stream)
126
+ else:
127
+ zfname, mname = fname.split("@", 1)
128
+ with zipfile.ZipFile(zfname) as zf:
129
+ if "*" not in mname:
130
+ with zf.open(mname) as handle:
131
+ DumpUnpickler.dump(handle, output_stream)
132
+ else:
133
+ found = False
134
+ for info in zf.infolist():
135
+ if fnmatch.fnmatch(info.filename, mname):
136
+ with zf.open(info) as handle:
137
+ DumpUnpickler.dump(handle, output_stream)
138
+ found = True
139
+ break
140
+ if not found:
141
+ raise Exception(f"Could not find member matching {mname} in {zfname}") # noqa: TRY002
142
+
143
+
144
+ if __name__ == "__main__":
145
+ # This hack works on every version of Python I've tested.
146
+ # I've tested on the following versions:
147
+ # 3.7.4
148
+ if True:
149
+ pprint.PrettyPrinter._dispatch[FakeObject.__repr__] = FakeObject.pp_format # type: ignore[attr-defined]
150
+
151
+ sys.exit(main(sys.argv))