ai-edge-torch-nightly 0.2.0.dev20240625__py3-none-any.whl → 0.2.0.dev20240627__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ai-edge-torch-nightly might be problematic. Click here for more details.

ai_edge_torch/__init__.py CHANGED
@@ -15,6 +15,7 @@
15
15
 
16
16
  from .convert.converter import convert
17
17
  from .convert.converter import signature
18
+ from .convert.to_channel_last_io import to_channel_last_io
18
19
  from .model import Model
19
20
 
20
21
 
@@ -17,10 +17,22 @@ import functools
17
17
 
18
18
  import torch
19
19
 
20
- from ai_edge_torch.convert.fx_passes import FxPassBase
21
- from ai_edge_torch.convert.fx_passes import FxPassResult
20
+ from ai_edge_torch.convert.fx_passes._pass_base import ExportedProgramPassBase
21
+ from ai_edge_torch.convert.fx_passes._pass_base import ExportedProgramPassResult # NOQA
22
22
  from ai_edge_torch.hlfb import mark_pattern
23
23
 
24
+ # For torch nightly released after mid June 2024,
25
+ # torch.nn.functional.interpolate no longer gets exported into decomposed graph
26
+ # but single aten op torch.ops.aten.upsample_nearest2d.vec/torch.ops.aten.upsample_bilinear2d.vec.
27
+ # This behavior would our pattern matching based composite builder.
28
+ # It requires the pattern and model graph to get decomposed first for backward compatibility.
29
+ _INTERPOLATE_DECOMPOSITIONS = torch._decomp.get_decompositions(
30
+ [
31
+ torch.ops.aten.upsample_bilinear2d.vec,
32
+ torch.ops.aten.upsample_nearest2d.vec,
33
+ ]
34
+ )
35
+
24
36
 
25
37
  @functools.cache
26
38
  def _get_upsample_bilinear2d_pattern():
@@ -30,6 +42,7 @@ def _get_upsample_bilinear2d_pattern():
30
42
  x, scale_factor=2, mode="bilinear", align_corners=False
31
43
  ),
32
44
  export_args=(torch.rand(1, 3, 100, 100),),
45
+ decomp_table=_INTERPOLATE_DECOMPOSITIONS,
33
46
  )
34
47
 
35
48
  @pattern.register_attr_builder
@@ -52,6 +65,7 @@ def _get_upsample_bilinear2d_align_corners_pattern():
52
65
  x, scale_factor=2, mode="bilinear", align_corners=True
53
66
  ),
54
67
  export_args=(torch.rand(1, 3, 100, 100),),
68
+ decomp_table=_INTERPOLATE_DECOMPOSITIONS,
55
69
  )
56
70
 
57
71
  @pattern.register_attr_builder
@@ -72,6 +86,7 @@ def _get_interpolate_nearest2d_pattern():
72
86
  "tfl.resize_nearest_neighbor",
73
87
  lambda x: torch.nn.functional.interpolate(x, scale_factor=2, mode="nearest"),
74
88
  export_args=(torch.rand(1, 3, 100, 100),),
89
+ decomp_table=_INTERPOLATE_DECOMPOSITIONS,
75
90
  )
76
91
 
77
92
  @pattern.register_attr_builder
@@ -86,7 +101,7 @@ def _get_interpolate_nearest2d_pattern():
86
101
  return pattern
87
102
 
88
103
 
89
- class BuildInterpolateCompositePass(FxPassBase):
104
+ class BuildInterpolateCompositePass(ExportedProgramPassBase):
90
105
 
91
106
  def __init__(self):
92
107
  super().__init__()
@@ -96,10 +111,13 @@ class BuildInterpolateCompositePass(FxPassBase):
96
111
  _get_interpolate_nearest2d_pattern(),
97
112
  ]
98
113
 
99
- def call(self, graph_module: torch.fx.GraphModule):
114
+ def call(self, exported_program: torch.export.ExportedProgram):
115
+ exported_program = exported_program.run_decompositions(_INTERPOLATE_DECOMPOSITIONS)
116
+
117
+ graph_module = exported_program.graph_module
100
118
  for pattern in self._patterns:
101
119
  graph_module = mark_pattern.mark_pattern(graph_module, pattern)
102
120
 
103
121
  graph_module.graph.lint()
104
122
  graph_module.recompile()
105
- return FxPassResult(graph_module, True)
123
+ return ExportedProgramPassResult(exported_program, True)
@@ -0,0 +1,96 @@
1
+ # Copyright 2024 The AI Edge Torch Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ import unittest
17
+
18
+ import torch
19
+
20
+ import ai_edge_torch
21
+
22
+
23
+ class Identity(torch.nn.Module):
24
+
25
+ def forward(self, x):
26
+ return x
27
+
28
+
29
+ class TestToChannelLastIO(unittest.TestCase):
30
+ """Tests to_channel_last_io API and module wrapper."""
31
+
32
+ def test_no_transformations(self):
33
+ x = torch.rand(1, 3, 10, 10)
34
+ y = ai_edge_torch.to_channel_last_io(Identity())(x)
35
+ self.assertEqual(y.shape, (1, 3, 10, 10))
36
+
37
+ def test_args(self):
38
+ x = torch.rand(1, 10, 10, 3)
39
+ y = ai_edge_torch.to_channel_last_io(Identity(), args=[0])(x)
40
+ self.assertEqual(y.shape, (1, 3, 10, 10))
41
+
42
+ def test_outputs(self):
43
+ x = torch.rand(1, 3, 10, 10)
44
+ y = ai_edge_torch.to_channel_last_io(Identity(), outputs=[0])(x)
45
+ self.assertEqual(y.shape, (1, 10, 10, 3))
46
+
47
+ def test_args_outputs(self):
48
+ x = torch.rand(1, 10, 10, 3)
49
+ y = ai_edge_torch.to_channel_last_io(Identity(), args=[0], outputs=[0])(x)
50
+ self.assertEqual(y.shape, (1, 10, 10, 3))
51
+
52
+ def test_args_5d(self):
53
+ x = torch.rand(1, 10, 10, 10, 3)
54
+ y = ai_edge_torch.to_channel_last_io(Identity(), args=[0])(x)
55
+ self.assertEqual(y.shape, (1, 3, 10, 10, 10))
56
+
57
+ def test_outputs_5d(self):
58
+ x = torch.rand(1, 3, 10, 10, 10)
59
+ y = ai_edge_torch.to_channel_last_io(Identity(), outputs=[0])(x)
60
+ self.assertEqual(y.shape, (1, 10, 10, 10, 3))
61
+
62
+ def test_chained_wrappers(self):
63
+ x = torch.rand(1, 10, 10, 3)
64
+
65
+ m = Identity()
66
+ m = ai_edge_torch.to_channel_last_io(m, args=[0])
67
+ m = ai_edge_torch.to_channel_last_io(m, outputs=[0])
68
+
69
+ y = m(x)
70
+ self.assertEqual(y.shape, (1, 10, 10, 3))
71
+
72
+ def test_list_args(self):
73
+ class Add(torch.nn.Module):
74
+
75
+ def forward(self, x, y):
76
+ return x + y
77
+
78
+ x = (torch.rand(1, 10, 10, 3), torch.rand(1, 10, 10, 3))
79
+ y = ai_edge_torch.to_channel_last_io(Add(), args=[0, 1])(*x)
80
+ self.assertEqual(y.shape, (1, 3, 10, 10))
81
+
82
+ def test_list_outputs(self):
83
+ class TwoIdentity(torch.nn.Module):
84
+
85
+ def forward(self, x):
86
+ return x, x
87
+
88
+ x = torch.rand(1, 3, 10, 10)
89
+ y = ai_edge_torch.to_channel_last_io(TwoIdentity(), outputs=[0])(x)
90
+ self.assertIsInstance(y, tuple)
91
+ self.assertEqual(y[0].shape, (1, 10, 10, 3))
92
+ self.assertEqual(y[1].shape, (1, 3, 10, 10))
93
+
94
+
95
+ if __name__ == "__main__":
96
+ unittest.main()
@@ -0,0 +1,85 @@
1
+ # Copyright 2024 The AI Edge Torch Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ from typing import Optional
17
+
18
+ import torch
19
+ from torch import nn
20
+
21
+
22
+ class ChannelLastIOWrapper(nn.Module):
23
+
24
+ def __init__(self, wrapped, *, args=None, outputs=None):
25
+ super().__init__()
26
+ self.wrapped = wrapped
27
+ self._args = args or []
28
+ self._outputs = outputs or []
29
+
30
+ def _to_channel_last(self, x):
31
+ if not torch.is_tensor(x):
32
+ raise ValueError("Input must be a torch tensor")
33
+ if x.ndim < 3:
34
+ raise ValueError("Input must be a tensor with rank >= 3 in layout (N, C, ...)")
35
+ dims = [0, *range(2, x.ndim), 1]
36
+ return torch.permute(x, dims)
37
+
38
+ def _to_channel_first(self, x):
39
+ if not torch.is_tensor(x):
40
+ raise ValueError("Input must be a torch tensor.")
41
+ if x.ndim < 3:
42
+ raise ValueError("Input must be a tensor with rank >= 3 in layout (N, ..., C)")
43
+ dims = [0, x.ndim - 1, *range(1, x.ndim - 1)]
44
+ return torch.permute(x, dims)
45
+
46
+ def forward(self, *args, **kwargs):
47
+ args = list(args)
48
+ for i in self._args:
49
+ args[i] = self._to_channel_first(args[i])
50
+
51
+ outputs = self.wrapped(*args, **kwargs)
52
+
53
+ if not isinstance(outputs, (list, tuple)):
54
+ outputs_is_list = False
55
+ output_list = [outputs]
56
+ else:
57
+ outputs_is_list = True
58
+ output_list = list(outputs)
59
+
60
+ for i in self._outputs:
61
+ output_list[i] = self._to_channel_last(output_list[i])
62
+
63
+ if not outputs_is_list:
64
+ return output_list[0]
65
+ else:
66
+ return type(outputs)(output_list)
67
+
68
+
69
+ def to_channel_last_io(
70
+ module: nn.Module,
71
+ args: Optional[list[int]] = None,
72
+ outputs: Optional[list[int]] = None,
73
+ ):
74
+ """Wraps the module with channel first to channel last layout transformations.
75
+
76
+ Args:
77
+ args (list[int]): Transform args with indices in the list from channel first
78
+ (N, C, ...) to channel last (N, ..., C).
79
+ outputs (list[int]): Transform outputs with indices in the list from channel
80
+ first (N, C, ...) to channel last (N, ..., C).
81
+ Returns:
82
+ The wrapped nn.Module with additional layout transposes after inputs and/or before
83
+ outputs.
84
+ """
85
+ return ChannelLastIOWrapper(module, args=args, outputs=outputs)
@@ -100,13 +100,18 @@ class ScalarAttrLocation:
100
100
 
101
101
 
102
102
  def _find_scalar_attr(
103
- pattern_module: torch.nn.Module, export_args: tuple[Any], tracker: ScalarAttrTracker
103
+ pattern_module: torch.nn.Module,
104
+ export_args: tuple[Any],
105
+ tracker: ScalarAttrTracker,
106
+ decomp_table=None,
104
107
  ) -> ScalarAttrLocation:
105
108
  scalar_loc_intersections = None
106
109
  for source, target in tracker._source_targets:
107
110
  track_args = list(export_args)
108
111
  track_args[tracker.pattern_arg_pos] = source
109
112
  ep = torch.export.export(pattern_module, tuple(track_args))
113
+ if decomp_table is not None:
114
+ ep = ep.run_decompositions(decomp_table)
110
115
 
111
116
  scalar_locs = set()
112
117
  nodes = ep.graph_module.graph.nodes
@@ -145,6 +150,7 @@ class Pattern:
145
150
  ["Pattern", GraphModule, InternalMatch], Optional[dict[str, Any]]
146
151
  ] = None,
147
152
  scalar_attr_trackers: list[ScalarAttrTracker] = None,
153
+ decomp_table: Optional[dict[torch._ops.OperatorBase, Callable]] = None,
148
154
  ):
149
155
  """The PyTorch computation pattern to match against a model.
150
156
 
@@ -165,6 +171,8 @@ class Pattern:
165
171
  for scalar args in `export_args`, which are used to track
166
172
  the attr occurrence(s) and retrieve their values from the
167
173
  matched subgraph.
174
+ decomp_table (Optional[dict[torch._ops.OperatorBase, Callable]]):
175
+ The decomposition table to be run on the pattern's exported program.
168
176
  """
169
177
  if not isinstance(module, torch.nn.Module):
170
178
 
@@ -180,23 +188,28 @@ class Pattern:
180
188
  module = PatternModule(module).eval()
181
189
 
182
190
  self.name = name
183
- self.exported_program = torch.export.export(module, export_args)
184
- self.graph_module = self.exported_program.graph_module
185
191
  self.attr_builder = attr_builder
186
192
  self._scalar_attr_trackers = scalar_attr_trackers if scalar_attr_trackers else []
187
193
 
188
- # Sanitize graph_module for more precise pattern matching.
189
- # The graph_module to match against this pattern should apply equivalent
190
- # sanitization.
191
- self.graph_module = passes.remove_clone_ops(self.graph_module)
192
- self.graph_module = passes.remove_dangling_args(self.graph_module)
194
+ exported_program = torch.export.export(module, export_args)
195
+ if decomp_table is not None:
196
+ exported_program = exported_program.run_decompositions(decomp_table)
197
+
198
+ self.exported_program = exported_program
199
+ self.graph_module = self.exported_program.graph_module
193
200
 
194
201
  self._scalar_attr_locations = []
195
202
  for tracker in self._scalar_attr_trackers:
196
203
  self._scalar_attr_locations.append(
197
- _find_scalar_attr(module, export_args, tracker)
204
+ _find_scalar_attr(module, export_args, tracker, decomp_table=decomp_table)
198
205
  )
199
206
 
207
+ # Sanitize graph_module for more precise pattern matching.
208
+ # The graph_module to match against this pattern should apply equivalent
209
+ # sanitization.
210
+ self.graph_module = passes.remove_clone_ops(self.graph_module)
211
+ self.graph_module = passes.remove_dangling_args(self.graph_module)
212
+
200
213
  # Builds list of ordered input and output nodes.
201
214
  self.graph_nodes_map = {}
202
215
  for node in self.graph_module.graph.nodes:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ai-edge-torch-nightly
3
- Version: 0.2.0.dev20240625
3
+ Version: 0.2.0.dev20240627
4
4
  Summary: Supporting PyTorch models with the Google AI Edge TFLite runtime.
5
5
  Home-page: https://github.com/google-ai-edge/ai-edge-torch
6
6
  Keywords: On-Device ML,AI,Google,TFLite,PyTorch,LLMs,GenAI
@@ -1,13 +1,14 @@
1
- ai_edge_torch/__init__.py,sha256=FPMmuFU3pyMREtjB_san1fy_0PFtAsgA0VZfOYvDrb4,1008
1
+ ai_edge_torch/__init__.py,sha256=CNDboRP4zQBpz2hznNCQWcQCARvNXUm3DMa1Dw_XXFg,1067
2
2
  ai_edge_torch/model.py,sha256=kmcgELjsYl8YzF8nUF6P7q4i8MWS-pLGpfsy-yTUXmE,4243
3
3
  ai_edge_torch/convert/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
4
4
  ai_edge_torch/convert/conversion.py,sha256=8K8jQuaCjlUWoj7jiimxp_zpN6mYThLOcQ858UDcYnE,4159
5
5
  ai_edge_torch/convert/conversion_utils.py,sha256=9BqCL38DErv1vEVGtT3BIJVhdwZjw2EQ-_m5UpvVVYE,11294
6
6
  ai_edge_torch/convert/converter.py,sha256=bjj5TV5_g4sGyuSh8ThEDydlNMqhkGSY4SzXK6vwhqI,6927
7
+ ai_edge_torch/convert/to_channel_last_io.py,sha256=zo5tY3yDhY_EPCkrL1XSXs2uRFS8B4_qu08dSjNsUGk,2778
7
8
  ai_edge_torch/convert/fx_passes/__init__.py,sha256=EPs4PSIDLuRH5EBETi6deaOvaaf_Q4xD3_9NVcR7x8o,2810
8
9
  ai_edge_torch/convert/fx_passes/_pass_base.py,sha256=ijVyDclPnd6a0DWWUJkwR4igj6f82S-cE1-83QGPvgw,1652
9
10
  ai_edge_torch/convert/fx_passes/build_aten_composite_pass.py,sha256=2yqUwJJ2R233_X9FNMOP9oYRTTzH34TR_BIUj-wfnKw,7080
10
- ai_edge_torch/convert/fx_passes/build_interpolate_composite_pass.py,sha256=jB27GlDC8x36nn35aiq97uKERiq4KXSUZ7tv7yc0Gl4,3223
11
+ ai_edge_torch/convert/fx_passes/build_interpolate_composite_pass.py,sha256=6m_vcycd9f3OQgQLx2hhQjsKfOqdxE5EkjzqrxqyAQM,4168
11
12
  ai_edge_torch/convert/fx_passes/canonicalize_pass.py,sha256=UX6dJsxCqSkftXXvNBV-i7Bjk6H7qTyqzUnE640Itfg,1673
12
13
  ai_edge_torch/convert/fx_passes/inject_mlir_debuginfo_pass.py,sha256=aRT8hTS3n9ie28lgu6mygtFO6Ypwu0qjNb0c81v9HLs,2448
13
14
  ai_edge_torch/convert/fx_passes/optimize_layout_transposes_pass/__init__.py,sha256=VA9bekxPVhLk4MYlIRXnOzrSnbCtUmGj7OQ_fJcKQtc,795
@@ -24,6 +25,7 @@ ai_edge_torch/convert/test/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrK
24
25
  ai_edge_torch/convert/test/test_convert.py,sha256=2qPmmGqnfV_o1gfsSdjGq3-JR1b323ligiy5MdAv9NA,8021
25
26
  ai_edge_torch/convert/test/test_convert_composites.py,sha256=_Ojc-H6GOS5s8ek3_8eRBL_AiCs-k3srziPJ2R4Ulrg,7255
26
27
  ai_edge_torch/convert/test/test_convert_multisig.py,sha256=kMaGnHe9ylfyU68qCifYcaGwJqyejKz--QQt9jS2oUA,4537
28
+ ai_edge_torch/convert/test/test_to_channel_last_io.py,sha256=I8c4ZG3v1vo0yxQYzLK_BTId4AOL9vadHGDtfCUZ4UI,2930
27
29
  ai_edge_torch/debug/__init__.py,sha256=N05Mmvi41KgSuK0JhuMejERESgP8QekiGdp9_PEyuKU,742
28
30
  ai_edge_torch/debug/culprit.py,sha256=urtCKPXORPvn6oyDxDSCSjgvngUnjjcsUMwAOeIl15E,14236
29
31
  ai_edge_torch/debug/utils.py,sha256=hjVmQVVl1dKxEF0D6KB4a3ouQ3wBkTsebOX2YsUObZM,1430
@@ -101,7 +103,7 @@ ai_edge_torch/generative/utilities/t5_loader.py,sha256=h1FQzt4x8wiQMX4NzYNVIaJGL
101
103
  ai_edge_torch/hlfb/__init__.py,sha256=rrje8a2iuKboBoV96bVq7nlS9HsnuEMbHE5JiWmCxFA,752
102
104
  ai_edge_torch/hlfb/mark_pattern/__init__.py,sha256=2VXnHcGf23VOuP-1GriGIpuL98leBB8twp_qaScMnmc,4799
103
105
  ai_edge_torch/hlfb/mark_pattern/passes.py,sha256=YV2YKBkh7y7j7sd7EA81vf_1hUKUvTRiy1pfqZustXc,1539
104
- ai_edge_torch/hlfb/mark_pattern/pattern.py,sha256=H4047w-xwx27rYPKNqmeOSQ9M1Adkpd7drp81YdV7Hw,9206
106
+ ai_edge_torch/hlfb/mark_pattern/pattern.py,sha256=BEMKgkX8IrsX70h2CxwA_tpsBm6qWWe2bOeOufMYNkA,9722
105
107
  ai_edge_torch/hlfb/test/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
106
108
  ai_edge_torch/hlfb/test/test_mark_pattern.py,sha256=qYR3PRGS9W3OG-qX7UFqL69VxXuUSfyDBUJtCXtXcOE,4262
107
109
  ai_edge_torch/hlfb/test/test_stablehlo_composite_builder.py,sha256=aUAPKnH4_Jxpp3mLlD5rzdT1g_VBm7OrwwLJ9DeJlzQ,8190
@@ -112,8 +114,8 @@ ai_edge_torch/quantize/quant_config.py,sha256=eO9Ra160ITjQSyRBEGy6nNIVH3gYacSWDd
112
114
  ai_edge_torch/testing/__init__.py,sha256=hHLluseD2R0Hh4W6XZRIXY_dRQeYudjsrKGf6LZz65g,671
113
115
  ai_edge_torch/testing/model_coverage/__init__.py,sha256=5P8J6Zk5YYtDvTBucFvB9NGSRI7Gw_24WnrbhXgycEE,765
114
116
  ai_edge_torch/testing/model_coverage/model_coverage.py,sha256=EIyKz-HY70DguWuSrJal8LpYXQ5ZSEUf3ZrVl7jikFM,4286
115
- ai_edge_torch_nightly-0.2.0.dev20240625.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
116
- ai_edge_torch_nightly-0.2.0.dev20240625.dist-info/METADATA,sha256=BMjo3IdVwNBpXCIGJC01y1buq5mPWWPyAJV1LFvxsJw,1748
117
- ai_edge_torch_nightly-0.2.0.dev20240625.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
118
- ai_edge_torch_nightly-0.2.0.dev20240625.dist-info/top_level.txt,sha256=5KXRaF2hwkApYxf7Y8y_tVb9aulGTlbOoNdbx1aKRkE,14
119
- ai_edge_torch_nightly-0.2.0.dev20240625.dist-info/RECORD,,
117
+ ai_edge_torch_nightly-0.2.0.dev20240627.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
118
+ ai_edge_torch_nightly-0.2.0.dev20240627.dist-info/METADATA,sha256=9dkJuSBvHoa50nBzs65ALLOMm4i4WWywsB98qgTVaR8,1748
119
+ ai_edge_torch_nightly-0.2.0.dev20240627.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
120
+ ai_edge_torch_nightly-0.2.0.dev20240627.dist-info/top_level.txt,sha256=5KXRaF2hwkApYxf7Y8y_tVb9aulGTlbOoNdbx1aKRkE,14
121
+ ai_edge_torch_nightly-0.2.0.dev20240627.dist-info/RECORD,,