mirror of
https://github.com/pytorch/pytorch.git
synced 2025-10-20 12:54:11 +08:00
Pull Request resolved: https://github.com/pytorch/pytorch/pull/157637 Approved by: https://github.com/yewentao256, https://github.com/albanD ghstack dependencies: #156605
862 lines
28 KiB
C++
862 lines
28 KiB
C++
#include <gtest/gtest.h>
|
|
#include <test/cpp/jit/test_utils.h>
|
|
#include <torch/csrc/jit/api/module.h>
|
|
#include <torch/csrc/jit/backends/backend_detail.h>
|
|
#include <torch/csrc/jit/mobile/import.h>
|
|
#include <torch/csrc/jit/serialization/import.h>
|
|
#include <torch/torch.h>
|
|
|
|
// Tests go in torch::jit
|
|
namespace torch {
|
|
namespace jit {
|
|
TEST(BackendTest, ToBackend) {
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return self.accum(x, h), self.sub_accum(x, h)
|
|
|
|
def accum(self, x, h):
|
|
return x + h
|
|
|
|
def sub_accum(self, x, h):
|
|
return x - h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(2.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto ref = m.forward(inputs).toTupleRef().elements().vec();
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"test_backend", m, compile_spec, any_dict_ty);
|
|
// lowered module code:
|
|
/*
|
|
class test_backendLoweredModule(Module):
|
|
__parameters__ = []
|
|
__buffers__ = []
|
|
__processed_module : Any
|
|
__method_compile_spec : Dict[str, Any]
|
|
__backend : __torch__.torch.classes.__backends__.test_backend
|
|
__handles : Dict[str, Any]
|
|
def __create_backend(self: torch.jit.test_backendLoweredModule) -> None:
|
|
_0 =
|
|
__torch__.torch.classes.__backends__.test_backend.__new__(__torch__.torch.classes.__backends__.test_backend)
|
|
_1 = (_0).__init__()
|
|
self.__backend = _0
|
|
return None
|
|
def __getstate__(self: torch.jit.test_backendLoweredModule) ->
|
|
Tuple[Dict[str, Any], Any]: _2 = (self.__method_compile_spec,
|
|
self.__processed_module) return _2 def __setstate__(self:
|
|
torch.jit.test_backendLoweredModule, state: Tuple[Dict[str, Any], Any]) ->
|
|
None: self.__method_compile_spec = (state)[0] self.__processed_module =
|
|
(state)[1] _3 = (self).__create_backend() _4 =
|
|
(self.__backend).compile(self.__processed_module,
|
|
self.__method_compile_spec, ) self.__handles = _4 return None def
|
|
forward(self: torch.jit.test_backendLoweredModule, x: Tensor, h: Tensor) ->
|
|
Tuple[Tensor, Tensor]: _5 = uninitialized(Tensor) typed_inputs =
|
|
annotate(List[Any], [x, h]) _6 =
|
|
(self.__backend).execute((self.__handles)["forward"], typed_inputs, ) _7,
|
|
_8, = _6 _9 = isinstance(_7, Tensor) if _9: _10 = unchecked_cast(Tensor, _7)
|
|
else:
|
|
ops.prim.RaiseException("AssertionError: ")
|
|
_10 = _5
|
|
_11 = isinstance(_8, Tensor)
|
|
if _11:
|
|
_12 = unchecked_cast(Tensor, _8)
|
|
else:
|
|
ops.prim.RaiseException("AssertionError: ")
|
|
_12 = _5
|
|
return (_10, _12)
|
|
|
|
*/
|
|
auto res = lm.forward(inputs).toTupleRef().elements().vec();
|
|
AT_ASSERT(res[0].toTensor().equal(ref[0].toTensor()));
|
|
AT_ASSERT(res[1].toTensor().equal(ref[1].toTensor()));
|
|
}
|
|
|
|
TEST(BackendTest, ToBackendNotAvailable) {
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return self.accum(x, h), self.sub_accum(x, h)
|
|
|
|
def accum(self, x, h):
|
|
return x + h
|
|
|
|
def sub_accum(self, x, h):
|
|
return x - h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(2.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto ref = m.forward(inputs).toTupleRef().elements().vec();
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// Produce lowered module (backend not available).
|
|
// Exception is not thrown at this point.
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"test_backend_unavailable", m, compile_spec, any_dict_ty);
|
|
// Validate exception is thrown when trying to execute and
|
|
// the backend is not available.
|
|
ASSERT_THROWS_WITH_MESSAGE(
|
|
lm.forward(inputs).toTupleRef().elements(), "Backend is not available.");
|
|
}
|
|
|
|
TEST(BackendTest, TestCompiler) {
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return x + h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(2.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto ref = m.forward(inputs);
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m, compile_spec, any_dict_ty);
|
|
auto res = lm.forward(inputs);
|
|
AT_ASSERT(res.toTensor().equal(ref.toTensor()));
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss);
|
|
auto mlm = _load_for_mobile(ss);
|
|
auto mres = mlm.forward(inputs);
|
|
AT_ASSERT(mres.toTensor().equal(ref.toTensor()));
|
|
}
|
|
|
|
TEST(BackendTest, TestCompilerWithStringTable) {
|
|
setShouldUseFormatWithStringTable(true);
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return x + h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(2.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto ref = m.forward(inputs);
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m, compile_spec, any_dict_ty);
|
|
auto res = lm.forward(inputs);
|
|
AT_ASSERT(res.toTensor().equal(ref.toTensor()));
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss);
|
|
auto mlm = _load_for_mobile(ss);
|
|
auto mres = mlm.forward(inputs);
|
|
setShouldUseFormatWithStringTable(false);
|
|
AT_ASSERT(mres.toTensor().equal(ref.toTensor()));
|
|
}
|
|
|
|
TEST(BackendTest, TestComposite) {
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
|
|
Module m_add("m_add");
|
|
m_add.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
auto lm_add = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m_add, compile_spec, any_dict_ty);
|
|
|
|
Module m_sub("m_sub");
|
|
m_sub.define(R"(
|
|
def forward(self, x, y):
|
|
return x - y
|
|
)");
|
|
auto lm_sub = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m_sub, compile_spec, any_dict_ty);
|
|
|
|
Module c("C");
|
|
c.register_module("Add", lm_add);
|
|
c.register_module("Sub", lm_sub);
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
return self.Add.forward(x, y) * self.Sub.forward(x, y)
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(3.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto res_jit = c.forward(inputs);
|
|
|
|
std::stringstream ss;
|
|
c._save_for_mobile(ss);
|
|
auto mc = _load_for_mobile(ss);
|
|
auto res_mobile = mc.forward(inputs);
|
|
|
|
AT_ASSERT(res_jit.toTensor().equal(res_mobile.toTensor()));
|
|
}
|
|
|
|
TEST(BackendTest, TestPrimDtype) {
|
|
Module c("name");
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
c = y.dtype
|
|
return c
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(3.0 * torch::ones({}));
|
|
inputs.emplace_back(1.0 * torch::ones({}));
|
|
auto res_jit = c.forward(inputs);
|
|
|
|
std::stringstream ss;
|
|
c._save_for_mobile(ss);
|
|
auto mc = _load_for_mobile(ss);
|
|
auto res_mobile = mc.forward(inputs);
|
|
|
|
ASSERT_EQ(res_jit.toInt(), res_mobile.toInt());
|
|
}
|
|
|
|
Module getCompositeModuleWithSameNameSubModules() {
|
|
// Two submodules with same module name but different forward and other
|
|
// functions should be serialized and loaded correctly.
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
|
|
Module sub1("m_add");
|
|
sub1.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
auto lowered_sub1 = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", sub1, compile_spec, any_dict_ty);
|
|
|
|
Module sub2("m_add");
|
|
sub2.define(R"(
|
|
def forward(self, x, y):
|
|
return x - y
|
|
)");
|
|
auto lowered_sub2 = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", sub2, compile_spec, any_dict_ty);
|
|
|
|
Module c("C");
|
|
c.register_module("Add", lowered_sub1);
|
|
c.register_module("Sub", lowered_sub2);
|
|
c.define(R"(
|
|
def forward(self, a, b, s:int):
|
|
c = self.Add.forward(a, b)
|
|
d = self.Sub.forward(a, b)
|
|
y = s * (c * d)
|
|
return y
|
|
)");
|
|
|
|
return c;
|
|
}
|
|
|
|
TEST(BackendTest, TestCompositeWithSetStates) {
|
|
Module c = getCompositeModuleWithSameNameSubModules();
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::ones({}));
|
|
inputs.emplace_back(3.0 * torch::ones({}));
|
|
inputs.emplace_back(3);
|
|
auto res_jit = c.forward(inputs);
|
|
|
|
std::stringstream ss;
|
|
c._save_for_mobile(ss);
|
|
auto mc = _load_for_mobile(ss);
|
|
auto res_mobile = mc.forward(inputs);
|
|
AT_ASSERT(res_jit.toTensor().equal(res_mobile.toTensor()));
|
|
}
|
|
|
|
TEST(BackendTest, TestConsistencyOfCompositeWithSetStates) {
|
|
Module c = getCompositeModuleWithSameNameSubModules();
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::ones({}));
|
|
inputs.emplace_back(3.0 * torch::ones({}));
|
|
inputs.emplace_back(3);
|
|
|
|
std::stringstream ss, ss_resave;
|
|
c._save_for_mobile(ss);
|
|
auto mc = _load_for_mobile(ss);
|
|
auto res_mobile = mc.forward(inputs);
|
|
ss.seekg(0, ss.beg);
|
|
|
|
// check if the methods names are always the same
|
|
// by reloading the script module and saving it back as mobile
|
|
// The below checks ensure that the names of Methods
|
|
// and numerical outputs of mobile and reloaded mobile
|
|
// modules are same.
|
|
auto script_module_load = torch::jit::load(ss);
|
|
script_module_load._save_for_mobile(ss_resave);
|
|
auto mc_reload = _load_for_mobile(ss_resave);
|
|
auto res_mobile_reload = mc_reload.forward(inputs);
|
|
|
|
AT_ASSERT(res_mobile_reload.toTensor().equal(res_mobile.toTensor()));
|
|
|
|
auto mc_methods = mc.get_methods();
|
|
auto mc_reload_methods = mc_reload.get_methods();
|
|
|
|
std::vector<std::string> mc_method_qns, mc_reload_method_qns;
|
|
|
|
auto get_qual_name = [](mobile::Method method) -> std::string {
|
|
return method.function().qualname().qualifiedName();
|
|
};
|
|
|
|
std::transform(
|
|
mc_methods.begin(),
|
|
mc_methods.end(),
|
|
std::back_inserter(mc_method_qns),
|
|
get_qual_name);
|
|
|
|
std::transform(
|
|
mc_reload_methods.begin(),
|
|
mc_reload_methods.end(),
|
|
std::back_inserter(mc_reload_method_qns),
|
|
get_qual_name);
|
|
|
|
AT_ASSERT(std::equal(
|
|
mc_method_qns.begin(),
|
|
mc_method_qns.end(),
|
|
mc_reload_method_qns.begin()));
|
|
}
|
|
|
|
TEST(BackendTest, TestCompilerNotSupport) {
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return x * h
|
|
)");
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
ASSERT_THROWS_WITH_MESSAGE(
|
|
torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m, compile_spec, any_dict_ty),
|
|
"The node of aten::mul is not supported in this compiler. Source code:");
|
|
}
|
|
|
|
TEST(BackendTestDebugInfo, TestCompiler) {
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return x + h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m, compile_spec, any_dict_ty);
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto mlm = _load_for_mobile(ss);
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(m)::<unknown>.__loweredModule__(m)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x: Tensor, h: Tensor):
|
|
return self.__loweredModule__.forward(x, h)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, h, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, h):
|
|
return x + h
|
|
~~~~~ <--- HERE
|
|
)";
|
|
ASSERT_THROWS_WITH_MESSAGE(mlm.forward(inputs), error_pattern);
|
|
}
|
|
|
|
TEST(BackendTestDebugInfo, TestCompilerWithStringTable) {
|
|
setShouldUseFormatWithStringTable(true);
|
|
Module m("m");
|
|
m.define(R"(
|
|
def forward(self, x, h):
|
|
return x + h
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", m, compile_spec, any_dict_ty);
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto mlm = _load_for_mobile(ss);
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(m)::<unknown>.__loweredModule__(m)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x: Tensor, h: Tensor):
|
|
return self.__loweredModule__.forward(x, h)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, h, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, h):
|
|
return x + h
|
|
~~~~~ <--- HERE
|
|
)";
|
|
setShouldUseFormatWithStringTable(false);
|
|
ASSERT_THROWS_WITH_MESSAGE(mlm.forward(inputs), error_pattern);
|
|
}
|
|
|
|
TEST(BackendTestDebugInfo, TestExceptionStackForCompilerWithModuleHierarchy) {
|
|
Module a("A");
|
|
a.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
Module b("B");
|
|
b.define(R"(
|
|
def forward(self, x):
|
|
return x + 2
|
|
)");
|
|
Module c("C");
|
|
c.register_module("A0", a);
|
|
c.register_module("B0", b);
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", c, compile_spec, any_dict_ty);
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto mlm = _load_for_mobile(ss);
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(C)::<unknown>.__loweredModule__(C)::forward.A0(A)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x: Tensor, y: Tensor):
|
|
return self.__loweredModule__.forward(x, y)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, y, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x, y):
|
|
return x + y
|
|
~~~~~ <--- HERE
|
|
)";
|
|
ASSERT_THROWS_WITH_MESSAGE(mlm.forward(inputs), error_pattern);
|
|
}
|
|
|
|
TEST(
|
|
BackendTestDebugInfo,
|
|
TestExceptionStackForCompilerWithTwoLevelModuleHierarchy) {
|
|
Module a("A");
|
|
a.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
Module b("B");
|
|
b.register_module("A0", a);
|
|
b.define(R"(
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + 2
|
|
)");
|
|
Module c("C");
|
|
c.register_module("B0", b);
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
return self.B0.forward(x, y) + 3
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lm = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", c, compile_spec, any_dict_ty);
|
|
|
|
std::stringstream ss;
|
|
lm._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto mlm = _load_for_mobile(ss);
|
|
/*
|
|
* Error stack throw will look like this:
|
|
* Module hierarchy:top(backend_with_compiler_demoLoweredModule).B0(B).A0(A)
|
|
* Traceback of TorchScript (most recent call last):
|
|
* File "<string>", line 5, in FunctionName_UNKNOWN
|
|
* typed_inputs: List[Any] = [x, y, ]
|
|
* if self.__backend.is_available() :
|
|
* _0, = self.__backend.execute(self.__handles["forward"],
|
|
* typed_inputs)
|
|
* ~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
* assert isinstance(_0, Tensor)
|
|
* return _0
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return self.B0.forward(x, y) + 3
|
|
* ~~~~~~~~~~~~~~~ <--- HERE
|
|
*
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return self.A0.forward(x, y) + 2
|
|
* ~~~~~~~~~~~~~~~ <--- HERE
|
|
*
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return x + y
|
|
* ~~~~~ <--- HERE
|
|
*
|
|
*/
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(C)::<unknown>.__loweredModule__(C)::forward.B0(B)::forward.A0(A)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x: Tensor, y: Tensor):
|
|
return self.__loweredModule__.forward(x, y)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, y, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return self.B0.forward(x, y) + 3
|
|
~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + 2
|
|
~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x, y):
|
|
return x + y
|
|
~~~~~ <--- HERE
|
|
)";
|
|
ASSERT_THROWS_WITH_MESSAGE(mlm.forward(inputs), error_pattern);
|
|
}
|
|
|
|
TEST(BackendTestDebugInfo, TestExceptionStackForCompilerWithLoweredSubModule) {
|
|
std::shared_ptr<CompilationUnit> cu = std::make_shared<CompilationUnit>();
|
|
Module a("A");
|
|
a.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
Module b("B");
|
|
b.define(R"(
|
|
def forward(self, x):
|
|
return x + 2
|
|
)");
|
|
Module c("C");
|
|
c.register_module("A0", a);
|
|
c.register_module("B0", b);
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
IValue submodule = c.attr("A0");
|
|
Module current_sm = submodule.toModule();
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lowered_submodule = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", current_sm, compile_spec, any_dict_ty);
|
|
|
|
c.type()->unsafeChangeAttributeType("A0", lowered_submodule.type());
|
|
c.setattr("A0", lowered_submodule._ivalue());
|
|
std::unordered_map<TypePtr, TypePtr> type_remap;
|
|
type_remap[a.type()] = lowered_submodule.type();
|
|
auto type_remap_fn = [&type_remap](TypePtr in) {
|
|
auto it = type_remap.find(in);
|
|
if (it == type_remap.end())
|
|
return in;
|
|
return it->second;
|
|
};
|
|
for (auto& fn : c.type()->methods()) {
|
|
auto method = c.get_method(fn->name());
|
|
auto graph = method.graph();
|
|
graph->remapTypes(type_remap_fn);
|
|
auto new_schema = fn->getSchema().cloneWithRemappedTypes(type_remap_fn);
|
|
fn->setSchema(new_schema);
|
|
}
|
|
|
|
std::stringstream ss;
|
|
c._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto c_loaded = _load_for_mobile(ss);
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(C)::<unknown>.A0(A)::forward.__loweredModule__(A)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x: Tensor, y: Tensor):
|
|
return self.__loweredModule__.forward(x, y)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, y, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return x + y
|
|
~~~~~ <--- HERE
|
|
)";
|
|
ASSERT_THROWS_WITH_MESSAGE(c_loaded.forward(inputs), error_pattern);
|
|
}
|
|
|
|
TEST(
|
|
BackendTestDebugInfo,
|
|
TestExceptionStackForCompilerWithSelectiveLoweredSubModule) {
|
|
std::shared_ptr<CompilationUnit> cu = std::make_shared<CompilationUnit>();
|
|
Module aa("AA");
|
|
aa.define(R"(
|
|
def forward(self, x, y):
|
|
return x + y
|
|
)");
|
|
Module a("A");
|
|
a.register_module("AA0", aa);
|
|
a.define(R"(
|
|
def forward(self, x, y):
|
|
return self.AA0.forward(x, y) + 3
|
|
)");
|
|
Module b("B");
|
|
b.define(R"(
|
|
def forward(self, x):
|
|
return x + 2
|
|
)");
|
|
Module c("C");
|
|
c.register_module("A0", a);
|
|
c.register_module("B0", b);
|
|
c.define(R"(
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
)");
|
|
|
|
std::vector<IValue> inputs;
|
|
inputs.emplace_back(torch::rand({2, 4}));
|
|
inputs.emplace_back(torch::rand({13, 9}));
|
|
|
|
c10::Dict<IValue, IValue> compile_spec(StringType::get(), AnyType::get());
|
|
c10::Dict<IValue, IValue> fake_dict(StringType::get(), AnyType::get());
|
|
fake_dict.insert("", "");
|
|
compile_spec.insert("forward", fake_dict);
|
|
IValue submodule = c.attr("A0");
|
|
Module current_sm = submodule.toModule();
|
|
auto any_dict_ty = DictType::create(StringType::get(), AnyType::get());
|
|
// lowered module
|
|
auto lowered_submodule = torch::jit::detail::codegen_backend_module(
|
|
"backend_with_compiler_demo", current_sm, compile_spec, any_dict_ty);
|
|
|
|
c.type()->unsafeChangeAttributeType("A0", lowered_submodule.type());
|
|
c.setattr("A0", lowered_submodule._ivalue());
|
|
std::unordered_map<TypePtr, TypePtr> type_remap;
|
|
type_remap[a.type()] = lowered_submodule.type();
|
|
auto type_remap_fn = [&type_remap](TypePtr in) {
|
|
auto it = type_remap.find(in);
|
|
if (it == type_remap.end())
|
|
return in;
|
|
return it->second;
|
|
};
|
|
for (auto& fn : c.type()->methods()) {
|
|
auto method = c.get_method(fn->name());
|
|
auto graph = method.graph();
|
|
graph->remapTypes(type_remap_fn);
|
|
auto new_schema = fn->getSchema().cloneWithRemappedTypes(type_remap_fn);
|
|
fn->setSchema(new_schema);
|
|
}
|
|
|
|
std::stringstream ss;
|
|
c._save_for_mobile(ss, ExtraFilesMap(), true);
|
|
auto c_loaded = _load_for_mobile(ss);
|
|
/*
|
|
* Error stack trace will look like this:
|
|
* Module hierarchy:top(C).A0(backend_with_compiler_demoLoweredModule).AA0(AA)
|
|
* Traceback of TorchScript (most recent call last):
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return self.A0.forward(x, y) + self.B0.forward(x)
|
|
* ~~~~~~~~~~~~~~~ <--- HERE
|
|
*
|
|
* File "<string>", line 5, in FunctionName_UNKNOWN
|
|
* typed_inputs: List[Any] = [x, y, ]
|
|
* if self.__backend.is_available() :
|
|
* _0, = self.__backend.execute(self.__handles["forward"],
|
|
* typed_inputs)
|
|
* ~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
* assert isinstance(_0, Tensor)
|
|
* return _0
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return self.AA0.forward(x, y) + 3
|
|
* ~~~~~~~~~~~~~~~~ <--- HERE
|
|
*
|
|
* File "<string>", line 3, in FunctionName_UNKNOWN
|
|
*
|
|
* def forward(self, x, y):
|
|
* return x + y
|
|
* ~~~~~ <--- HERE
|
|
*
|
|
*
|
|
* */
|
|
std::string error_pattern = R"(
|
|
Module hierarchy:top(C)::<unknown>.A0(A)::forward.__loweredModule__(A)::forward.AA0(AA)::forward.aten::add
|
|
Traceback of TorchScript (most recent call last):
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return self.A0.forward(x, y) + self.B0.forward(x)
|
|
~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x: Tensor, y: Tensor):
|
|
return self.__loweredModule__.forward(x, y)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 5, in forward
|
|
typed_inputs: List[Any] = [x, y, ]
|
|
if self.__backend.is_available() :
|
|
_0, = self.__backend.execute(self.__handles["forward"], typed_inputs)
|
|
~~~~~~~~~~~~~~~~~~~~~~ <--- HERE
|
|
assert isinstance(_0, Tensor)
|
|
return _0
|
|
File "<string>", line 3, in <unknown>
|
|
|
|
def forward(self, x, y):
|
|
return self.AA0.forward(x, y) + 3
|
|
~~~~~~~~~~~~~~~~ <--- HERE
|
|
|
|
File "<string>", line 3, in forward
|
|
|
|
def forward(self, x, y):
|
|
return x + y
|
|
~~~~~ <--- HERE
|
|
)";
|
|
ASSERT_THROWS_WITH_MESSAGE(c_loaded.forward(inputs), error_pattern);
|
|
}
|
|
|
|
} // namespace jit
|
|
} // namespace torch
|