Skip to content

Commit dc2cff4

Browse files
[executorch][native] Parse packed quant metadata and validate PTN method structure (pytorch#23333)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: pytorch#23228 by @digantdesai ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/digantdesai/108/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/digantdesai/108/head Merge bot PR base: https://github.com/pytorch/executorch/tree/gh/digantdesai/107/orig Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/digantdesai/108/orig Differential Revision: [D121704317](https://our.internmc.facebook.com/intern/diff/D121704317/) @diff-train-skip-merge Co-authored-by: Digant Desai <digantdesai@meta.com>
1 parent f1e4992 commit dc2cff4

10 files changed

Lines changed: 678 additions & 12 deletions

File tree

‎backends/native/extension/module/test/TestData.h‎

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,61 @@
1818
namespace executorch::extension::native_module::testing {
1919

2020
// cppcheck-suppress-begin useStlAlgorithm
21+
// Placeholders for input0..N-1, one opaque op producing output0..M-1 from them,
22+
// and the output node.
23+
inline flatbuffers::Offset<
24+
flatbuffers::Vector<flatbuffers::Offset<native_backend::Node>>>
25+
make_signature_nodes(
26+
flatbuffers::FlatBufferBuilder& builder,
27+
size_t num_inputs,
28+
size_t num_outputs) {
29+
const auto tensor_arg = [&builder](const std::string& name) {
30+
return native_backend::CreateNamedArgument(
31+
builder,
32+
/*name=*/0,
33+
native_backend::CreateArgument(
34+
builder,
35+
native_backend::ArgumentValue::TensorArg,
36+
native_backend::CreateTensorArgDirect(builder, name.c_str())
37+
.Union()));
38+
};
39+
std::vector<flatbuffers::Offset<native_backend::Node>> nodes;
40+
std::vector<flatbuffers::Offset<native_backend::NamedArgument>> op_inputs;
41+
for (size_t i = 0; i < num_inputs; ++i) {
42+
const std::string name = "input" + std::to_string(i);
43+
const std::vector<flatbuffers::Offset<native_backend::Output>> outputs = {
44+
native_backend::CreateOutputDirect(builder, name.c_str())};
45+
nodes.push_back(native_backend::CreateNodeDirect(
46+
builder,
47+
name.c_str(),
48+
native_backend::OpKind::PLACEHOLDER,
49+
"",
50+
nullptr,
51+
&outputs));
52+
op_inputs.push_back(tensor_arg(name));
53+
}
54+
std::vector<flatbuffers::Offset<native_backend::Output>> op_outputs;
55+
std::vector<flatbuffers::Offset<native_backend::NamedArgument>> results;
56+
for (size_t i = 0; i < num_outputs; ++i) {
57+
const std::string name = "output" + std::to_string(i);
58+
op_outputs.push_back(
59+
native_backend::CreateOutputDirect(builder, name.c_str()));
60+
results.push_back(tensor_arg(name));
61+
}
62+
if (num_outputs > 0) {
63+
nodes.push_back(native_backend::CreateNodeDirect(
64+
builder,
65+
"op",
66+
native_backend::OpKind::CALL_FUNCTION,
67+
"test.op",
68+
&op_inputs,
69+
&op_outputs));
70+
}
71+
nodes.push_back(native_backend::CreateNodeDirect(
72+
builder, "output", native_backend::OpKind::OUTPUT, "", &results));
73+
return builder.CreateVector(nodes);
74+
}
75+
2176
inline std::vector<uint8_t> make_tensor_package(
2277
const std::vector<std::string>& method_names,
2378
size_t num_inputs = 1,
@@ -95,8 +150,7 @@ inline std::vector<uint8_t> make_tensor_package(
95150
}
96151
const auto graph = native_backend::CreateGraph(
97152
builder,
98-
builder.CreateVector(
99-
std::vector<flatbuffers::Offset<native_backend::Node>>{}),
153+
make_signature_nodes(builder, num_inputs, num_outputs),
100154
builder.CreateVector(input_names),
101155
builder.CreateVector(output_names),
102156
builder.CreateVector(tensor_values));

‎backends/native/runtime/Deserialize.cpp‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,44 @@ TensorMeta build_tensor_meta(
179179
out.dim_order_hint.push_back(static_cast<int32_t>(dord->Get(i)));
180180
}
181181
}
182+
if (const auto* quant = m->quant()) {
183+
switch (quant->scheme_type()) {
184+
case fbs::QuantScheme::AffineGroup: {
185+
const fbs::AffineGroup* affine = quant->scheme_as_AffineGroup();
186+
if (affine == nullptr) {
187+
throw std::runtime_error(
188+
"build_tensor_meta: " + name +
189+
" has a malformed affine quant scheme");
190+
}
191+
out.quant = AffineGroupQuant{
192+
.scale_data_key = str_of(affine->scale_data_key()),
193+
.scale_dtype = map_scalar_type(affine->scale_dtype()),
194+
.quant_min = affine->quant_min(),
195+
.quant_max = affine->quant_max(),
196+
.group_size = affine->group_size(),
197+
.zero_point_data_key = str_of(affine->zero_point_data_key()),
198+
.zero_point_dtype = map_scalar_type(affine->zero_point_dtype()),
199+
};
200+
break;
201+
}
202+
case fbs::QuantScheme::PackedQuant: {
203+
const fbs::PackedQuant* packed = quant->scheme_as_PackedQuant();
204+
if (packed == nullptr) {
205+
throw std::runtime_error(
206+
"build_tensor_meta: " + name +
207+
" has a malformed packed quant scheme");
208+
}
209+
out.quant = PackedQuant{.codec = str_of(packed->codec())};
210+
break;
211+
}
212+
case fbs::QuantScheme::NONE:
213+
throw std::runtime_error(
214+
"build_tensor_meta: " + name + " has an empty quant scheme");
215+
default:
216+
throw std::runtime_error(
217+
"build_tensor_meta: " + name + " has an unsupported quant scheme");
218+
}
219+
}
182220
return out;
183221
}
184222

‎backends/native/runtime/Method.h‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
#pragma once
88

9+
#include <algorithm>
910
#include <string>
11+
#include <string_view>
1012
#include <vector>
1113

1214
#include <executorch/backends/native/runtime/graph/Graph.h>
@@ -59,4 +61,18 @@ struct Method {
5961
std::vector<OutputSpec> output_specs; // aligned to graph.output_ids by index
6062
};
6163

64+
// Returns the binding that ships data under `key` (e.g. a quantized tensor's
65+
// scale_data_key), or nullptr.
66+
inline const DataBinding* find_data_binding(
67+
const Method& method,
68+
std::string_view key) {
69+
const auto it = std::find_if(
70+
method.data_bindings.begin(),
71+
method.data_bindings.end(),
72+
[key](const DataBinding& binding) {
73+
return binding.has_data && binding.key == key;
74+
});
75+
return it == method.data_bindings.end() ? nullptr : &*it;
76+
}
77+
6278
} // namespace ptn

0 commit comments

Comments
 (0)