Op support 2024.0 (#22745)

### Details:
 - Improved op support for 2024.0 release
 - Includes ops required for enabling customer model lightglue
 - Also added ops for lora fine tuning
 - Added support for options in aot autograd backend
- Added a new option to specify whether to use aot autograd
functionality
 - Added a new option to specify decompositions that need to be added
- Added a new option to specify ops that need to be disabled from
executing on OpenVINO


### Tickets:
 - https://jira.devtools.intel.com/browse/CVS-116702
 - https://jira.devtools.intel.com/browse/CVS-120276

---------

Co-authored-by: ynimmaga <yamini.nimmagadda@intel.com>
This commit is contained in:
Surya Siddharth Pemmaraju 2024-02-13 04:13:42 -08:00 committed by GitHub
parent 4f0012dff0
commit 19ba148b4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 383 additions and 51 deletions

View File

@ -97,9 +97,9 @@ jobs:
run: |
# Install the core OV wheel
python3 -m pip install ${INSTALL_DIR}/tools/openvino-*.whl
extras_to_install="caffe,kaldi,onnx,tensorflow2,pytorch"
if [[ "${{ runner.arch }}" != "ARM64" ]]; then
extras_to_install="mxnet,$extras_to_install"
fi
@ -144,14 +144,14 @@ jobs:
# Skips under Ticket: 122666
skip_filter='--ignore-glob=**/mo/unit_tests/mo/front/mxnet/**'
fi
python3 -m pytest -s ${INSTALL_TEST_DIR}/mo/unit_tests \
--junitxml=${INSTALL_TEST_DIR}/TEST-ModelOptimizer.xml \
"$skip_filter"
- name: Python ONNX operators tests
if: (fromJSON(inputs.affected-components).Python_API.test ||
fromJSON(inputs.affected-components).ONNX_FE.test) &&
fromJSON(inputs.affected-components).ONNX_FE.test) &&
runner.arch != 'ARM64' # Ticket: 123325
run: |
# Skip test_onnx/test_zoo_models and test_onnx/test_backend due to long execution time - ONNX Model Zoo tests are run separately
@ -174,13 +174,13 @@ jobs:
# Import 'test_utils' installed in '<package_test>/tests/python/openvino'
export LD_LIBRARY_PATH=${PIP_INSTALL_PATH}/openvino/libs:$LD_LIBRARY_PATH
export PYTHONPATH=${INSTALL_TEST_DIR}/python
if [[ "${{ runner.os }}" == "Linux" ]] && [[ "${{ runner.arch }}" == "ARM64" ]]; then
# Find gomp lib
GOMP_LIB=$(find "${PIP_INSTALL_PATH}/torch/lib/../../torch.libs/" -name '*libgomp-*so*')
export LD_PRELOAD=${GOMP_LIB}
fi
python3 -m pytest ${LAYER_TESTS_INSTALL_DIR}/mo_python_api_tests --junitxml=${INSTALL_TEST_DIR}/TEST-test_mo_convert.xml
env:
TEST_DEVICE: CPU
@ -192,13 +192,13 @@ jobs:
# Import 'test_utils' installed in '<package_test>/tests/python/openvino'
export PYTHONPATH=${INSTALL_TEST_DIR}/python
export LD_LIBRARY_PATH=${PIP_INSTALL_PATH}/openvino/libs:$LD_LIBRARY_PATH
if [[ "${{ runner.os }}" == "Linux" ]] && [[ "${{ runner.arch }}" == "ARM64" ]]; then
# Find gomp lib
GOMP_LIB=$(find "${PIP_INSTALL_PATH}/torch/lib/../../torch.libs/" -name '*libgomp-*so*')
export LD_PRELOAD=${GOMP_LIB}
fi
python3 -m pytest ${LAYER_TESTS_INSTALL_DIR}/ovc_python_api_tests --junitxml=${INSTALL_TEST_DIR}/TEST-test_ovc_convert.xml
env:
TEST_DEVICE: CPU
@ -229,7 +229,7 @@ jobs:
PYTORCH_TRACING_MODE: EXPORT
- name: PyTorch torch.compile TORCHFX Layer Tests
if: ${{ fromJSON(inputs.affected-components).PyTorch_FE.test && runner.os != 'macOS' }}
if: ${{ fromJSON(inputs.affected-components).PyTorch_FE.test && runner.os != 'macOS' && runner.arch != 'ARM64' }} # Ticket: 126287
run: |
python3 -m pytest ${LAYER_TESTS_INSTALL_DIR}/pytorch_tests -m precommit_fx_backend --junitxml=${INSTALL_TEST_DIR}/TEST-pytorch.xml
env:
@ -238,7 +238,7 @@ jobs:
PYTORCH_TRACING_MODE: TORCHFX
- name: PyTorch torch.compile TORCHSCRIPT Layer Tests
if: ${{ fromJSON(inputs.affected-components).PyTorch_FE.test && runner.os != 'macOS' }}
if: ${{ fromJSON(inputs.affected-components).PyTorch_FE.test && runner.os != 'macOS' && runner.arch != 'ARM64' }} # Ticket: 126287
run: |
python3 -m pytest ${LAYER_TESTS_INSTALL_DIR}/pytorch_tests -m precommit_ts_backend --junitxml=${INSTALL_TEST_DIR}/TEST-pytorch.xml
env:

View File

@ -10,18 +10,21 @@ from functools import partial
from hashlib import sha256
import torch
from torch._dynamo.backends.common import fake_tensor_unsupported
from torch._dynamo.backends.common import fake_tensor_unsupported, aot_autograd
from torch._dynamo.backends.registry import register_backend
from torch._inductor.compile_fx import compile_fx
from torch.fx.experimental.proxy_tensor import make_fx
from torch._decomp import decomposition_table, get_decompositions
from openvino.frontend import FrontEndManager
from openvino.runtime import Core, Type, PartialShape
from openvino.frontend.pytorch.ts_decoder import TorchScriptPythonDecoder
from openvino.frontend.pytorch.torchdynamo import decompositions
from openvino.frontend.pytorch.torchdynamo.decompositions import get_aot_decomposition_list
from openvino.frontend.pytorch.torchdynamo.partition import Partitioner
from openvino.frontend.pytorch.torchdynamo.execute import execute, execute_cached
from openvino.frontend.pytorch.torchdynamo.compile import cached_model_name, openvino_compile_cached_model
from openvino.frontend.pytorch.torchdynamo.backend_utils import _get_cache_dir, _get_device, _get_model_caching
from openvino.frontend.pytorch.torchdynamo.backend_utils import _get_cache_dir, _get_device, _get_model_caching, _get_decompositions, _get_aot_autograd
from openvino.runtime import Core, Type, PartialShape
@ -42,10 +45,15 @@ log = logging.getLogger(__name__)
2) model = torch.compile(model, backend="openvino")
"""
openvino_options = {}
@register_backend
@fake_tensor_unsupported
def openvino(subgraph, example_inputs, options=None):
if (_get_aot_autograd(options)):
global openvino_options
openvino_options = options
return aot_autograd(fw_compiler=fx_openvino, bw_compiler=fx_openvino)(subgraph, example_inputs)
return fx_openvino(subgraph, example_inputs, options)
@register_backend
@ -111,9 +119,10 @@ def ts_openvino(subgraph, example_inputs):
log.debug(f"Failed in compilation: {e}")
return compile_fx(subgraph, example_inputs)
def fx_openvino(subgraph, example_inputs, options):
def fx_openvino(subgraph, example_inputs, options=None):
try:
if len(openvino_options) != 0:
options = openvino_options
executor_parameters = None
inputs_reversed = False
openvino_model_caching = _get_model_caching(options)
@ -134,11 +143,18 @@ def fx_openvino(subgraph, example_inputs, options):
return _call
if inputs_reversed:
example_inputs.reverse()
model = make_fx(subgraph)(*example_inputs)
from torch._subclasses.fake_tensor import FakeTensorMode
decompositions = _get_decompositions(options)
if (_get_aot_autograd(options)):
decompositions = decompositions + get_aot_decomposition_list()
with FakeTensorMode(allow_non_fake_inputs=True):
model = make_fx(subgraph, decomposition_table=get_decompositions(decompositions))(*example_inputs)
with torch.no_grad():
model.eval()
partitioner = Partitioner()
compiled_model = partitioner.make_partitions(model)
partitioner = Partitioner(options)
compiled_model = partitioner.make_partitions(model, options)
if executor_parameters is not None and 'model_hash_str' in executor_parameters:
# Check if the model is fully supported.

View File

@ -49,6 +49,15 @@ def _get_cache_dir(options) -> Optional[Any]:
return cache_dir
def _get_aot_autograd(options) -> Optional[Any]:
if options is not None and "aot_autograd" in options:
aot_autograd = options["aot_autograd"]
if bool(aot_autograd) and str(aot_autograd).lower() not in ["false", "0"]:
return True
else:
return False
def _get_model_caching(options) -> Optional[Any]:
if options is not None and "model_caching" in options:
caching = options["model_caching"]
@ -67,4 +76,24 @@ def _get_model_caching(options) -> Optional[Any]:
def _get_config(options) -> Optional[Any]:
if options is not None and "config" in options:
return options["config"]
return {}
return {}
def _get_decompositions(options) -> Optional[Any]:
decompositions = []
if options is not None and "decompositions" in options:
decompositions = options["decompositions"]
return decompositions
def _get_disabled_ops(options) -> Optional[Any]:
disabled_ops = []
if options is not None and "disabled_ops" in options:
disabled_ops = options["disabled_ops"]
return disabled_ops
def _is_testing(options) -> Optional[Any]:
if options is not None and "testing" in options:
is_testing = options["testing"]
if bool(is_testing) and str(is_testing).lower not in ["false", "0"]:
return True
return False

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import torch
from torch._decomp.decompositions import aten, pw_cast_for_opmath
from torch._decomp import register_decomposition, get_decompositions
@register_decomposition(aten.convolution_backward)
@pw_cast_for_opmath
def convolution_backward(
grad_output,
inp,
weight,
bias,
stride,
padding,
dilation,
transposed,
output_padding,
groups,
output_mask,
):
if stride == [2, 2]:
output_padding = [1, 1]
# Compute the gradient of the input tensor
grad_input = torch.nn.functional.conv_transpose2d(
grad_output, weight, stride=stride, padding=padding, dilation=dilation, groups=groups, output_padding=output_padding
)
# Compute the gradient of the weight tensor
grad_weight = torch.nn.functional.conv_transpose2d(
inp, weight.transpose(0, 1), stride=stride, padding=padding, dilation=dilation, groups=groups, output_padding=output_padding
)
# Compute the gradient of the bias tensor
if bias is not None:
grad_bias = grad_output.sum([0, 2, 3], keepdim=True)
else:
grad_bias = None
return grad_input, grad_weight, grad_bias
if len(get_decompositions([aten._scaled_dot_product_flash_attention.default])) == 0:
@register_decomposition(aten._scaled_dot_product_flash_attention.default)
def scaled_dot_product_flash_attention(
query,
key,
value,
dropout_p=0.0,
is_causal=False,
*,
return_debug_mask=False,
scale=None,
):
batch_size, num_head, q_size, head_size = (
query.shape[0],
query.shape[1],
query.shape[2],
query.shape[3],
)
logsumexp = torch.empty([batch_size, q_size, num_head, head_size], dtype=torch.float)
cum_seq_q, cum_seq_k = torch.empty([], dtype=torch.long), torch.empty(
[], dtype=torch.long
)
max_q, max_k = 0, 0
philox_seed, philox_offset = torch.empty([], dtype=torch.long), torch.empty(
[], dtype=torch.long
)
debug_attn_mask = torch.empty(
[],
dtype=query.dtype,
device=query.device,
requires_grad=query.requires_grad,
)
output, _ = aten._scaled_dot_product_attention_math.default(
query, key, value, None, dropout_p, is_causal, None, scale=scale
)
scores = torch.matmul(query, key.transpose(-2, -1)) / (key.size(-1) ** 0.5)
logsumexp = torch.logsumexp(scores, dim=-1)
output = output.transpose(1, 2).contiguous(memory_format=torch.contiguous_format)
return (
output.transpose(1, 2),
logsumexp,
cum_seq_q,
cum_seq_k,
max_q,
max_k,
philox_seed,
philox_offset,
debug_attn_mask,
)
def get_aot_decomposition_list():
return ([torch.ops.aten._scaled_dot_product_flash_attention.default,
torch.ops.aten._softmax.default,
torch.ops.aten._softmax_backward_data.default,
torch.ops.aten.convolution_backward.default,
torch.ops.aten.gelu_backward.default,
torch.ops.aten.native_group_norm.default,
torch.ops.aten.native_group_norm_backward.default,
torch.ops.aten.native_layer_norm.default,
torch.ops.aten.native_layer_norm_backward.default,
torch.ops.aten.slice_backward.default])

View File

@ -13,9 +13,9 @@ from torch._ops import OpOverload
from torch.fx.node import Node, _get_qualified_name
from torch.fx.passes.operator_support import OperatorSupport
from torch.fx.passes.tools_common import CALLABLE_NODE_OPS
from openvino.frontend.pytorch.torchdynamo.backend_utils import _get_disabled_ops
import typing as t
import logging
logger = logging.getLogger(__name__)
@ -26,17 +26,20 @@ class OperatorSupport(OperatorSupport):
Operator support for OpenVINO backend.
"""
def __init__(self):
def __init__(self, options):
support_dict = {
"_operator.getitem": None,
"torch.ops.aten._adaptive_avg_pool2d.default": None,
"torch.ops.aten._log_softmax.default": None,
"torch.ops.aten._softmax.default": None,
"torch.ops.aten._to_copy.default": None,
"torch.ops.aten._unsafe_view.default": None,
"torch.ops.aten._unsafe_view.default": None,
"torch.ops.aten.add.Scalar": None,
"torch.ops.aten.add.Tensor": None,
"torch.ops.aten.add_.Tensor": None,
"torch.ops.aten.addmm.default": None,
"torch.ops.aten.amax.default": None,
"torch.ops.aten.arange.start": None,
"torch.ops.aten.arange.default": None,
"torch.ops.aten.argmax.default": None,
@ -56,10 +59,12 @@ class OperatorSupport(OperatorSupport):
"torch.ops.aten.div.Tensor": None,
"torch.ops.aten.embedding.default": None,
"torch.ops.aten.empty.memory_format": None,
"torch.ops.aten.erf.default": None,
"torch.ops.aten.eq.Scalar": None,
"torch.ops.aten.eq.Tensor": None,
"torch.ops.aten.exp.default": None,
"torch.ops.aten.expand.default": None,
"torch.ops.aten.fill.Scalar": None,
"torch.ops.aten.full.default": None,
"torch.ops.aten.gather.default": None,
"torch.ops.aten.gelu.default": None,
@ -73,9 +78,11 @@ class OperatorSupport(OperatorSupport):
"torch.ops.aten.linalg_vector_norm.default": None,
"torch.ops.aten.lt.Tensor": None,
"torch.ops.aten.log.default": None,
"torch.ops.aten.log_sigmoid_forward.default": None,
"torch.ops.aten.logsumexp.default": None,
"torch.ops.aten.masked_fill_.Scalar": None,
"torch.ops.aten.masked_fill.Tensor": None,
"torch.ops.aten.max.dim": None,
"torch.ops.aten.max_pool2d_with_indices.default": None,
"torch.ops.aten.mean.dim": None,
"torch.ops.aten.mm.default": None,
@ -86,12 +93,14 @@ class OperatorSupport(OperatorSupport):
"torch.ops.aten._native_batch_norm_legit_no_training.default": None,
"torch.ops.aten.native_group_norm.default": None,
"torch.ops.aten.native_layer_norm.default": None,
"torch.ops.aten.new_full.default": None,
"torch.ops.aten.neg.default": None,
"torch.ops.aten.new_ones.default": None,
"torch.ops.aten.permute.default": None,
"torch.ops.aten.pow.Tensor_Scalar": None,
"torch.ops.aten.relu.default": None,
"torch.ops.aten.relu_.default": None,
"torch.ops.aten.rsqrt.default": None,
"torch.ops.aten.rsub.Scalar": None,
"torch.ops.aten._scaled_dot_product_flash_attention.default": None,
"torch.ops.aten.select.int": None,
@ -101,18 +110,26 @@ class OperatorSupport(OperatorSupport):
"torch.ops.aten.sin.default": None,
"torch.ops.aten.slice.Tensor": None,
"torch.ops.aten.split.Tensor": None,
"torch.ops.aten.squeeze.dim": None,
"torch.ops.aten.squeeze.dims": None,
"torch.ops.aten.sub.default": None,
"torch.ops.aten.sub.Tensor": None,
"torch.ops.aten.sum.dim_IntList": None,
"torch.ops.aten.t.default": None,
"torch.ops.aten.tanh.default": None,
"torch.ops.aten.transpose.int": None,
"torch.ops.aten.unbind.int": None,
"torch.ops.aten.unsqueeze.default": None,
"torch.ops.aten.upsample_nearest2d.default": None,
"torch.ops.aten.var_mean.correction": None,
"torch.ops.aten.view.default": None,
"torch.ops.aten.where.self": None,
"torch.ops.aten.zeros_like.default": None,
}
for op in _get_disabled_ops(options):
del support_dict[op]
super().__init__(support_dict)
def is_node_supported(self, submodules: t.Mapping[str, Module], node: Node) -> bool:

View File

@ -16,6 +16,7 @@ from torch._decomp import decomposition_table
from torch.fx.experimental.proxy_tensor import make_fx
from torch.utils._pytree import tree_flatten, tree_map, tree_unflatten
from openvino.frontend.pytorch.torchdynamo.op_support import OperatorSupport
from openvino.frontend.pytorch.torchdynamo.backend_utils import _is_testing
import typing as t
import logging
@ -25,8 +26,8 @@ logger.setLevel(logging.WARNING)
class Partitioner:
def __init__(self):
self.supported_ops = OperatorSupport()
def __init__(self, options):
self.supported_ops = OperatorSupport(options)
def fx_serialize(self, graph_module: GraphModule, *args, **kwargs):
fx_gm = make_fx(graph_module)(*args)
@ -55,9 +56,10 @@ class Partitioner:
return True
return False
def make_partitions(self, graph_module: GraphModule) -> GraphModule:
def make_partitions(self, graph_module: GraphModule, options) -> GraphModule:
allow_single_node_partition = _is_testing(options)
partitioner = CapabilityBasedPartitioner(
graph_module, self.supported_ops, allows_single_node_partition=False)
graph_module, self.supported_ops, allows_single_node_partition=allow_single_node_partition)
partitions = partitioner.propose_partitions()
self.add_get_attr_inputs(partitions)
fused_graph_module = partitioner.fuse_partitions(partitions)

View File

@ -9,6 +9,7 @@
#include "openvino/op/scatter_elements_update.hpp"
#include "openvino/op/shape_of.hpp"
#include "openvino/op/slice.hpp"
#include "openvino/op/unsqueeze.hpp"
#include "pt_framework_node.hpp"
#include "utils.hpp"
#include "utils_quantize.hpp"
@ -94,6 +95,24 @@ OutputVector translate_quantized_cat(const NodeContext& context) {
list_elems.front())};
};
OutputVector translate_stack_fx(const NodeContext& context) {
num_inputs_check(context, 2, context.get_input_size());
auto dim = context.mark_node(v0::Constant::create(element::i32, Shape{}, {0}));
std::deque<Output<Node>> list_elems;
auto num_elements = context.get_input_size();
if (num_elements > 2)
num_elements = num_elements - 1;
for (size_t i = 0; i < num_elements; i++) {
auto stack_input =
context.mark_node(std::make_shared<v0::Unsqueeze>(context.get_input(static_cast<int>(i)), dim));
list_elems.push_back(stack_input);
}
int64_t axis = 0;
if (context.get_input_size() > 2)
axis = context.const_input<int64_t>(context.get_input_size() - 1);
return translate_cat_common(context, list_elems, axis, true);
}
} // namespace op
} // namespace pytorch
} // namespace frontend

View File

@ -11,6 +11,7 @@
#include "openvino/op/divide.hpp"
#include "openvino/op/exp.hpp"
#include "openvino/op/reduce_sum.hpp"
#include "openvino/op/sigmoid.hpp"
#include "utils.hpp"
namespace ov {
@ -29,6 +30,15 @@ OutputVector translate_log(const NodeContext& context) {
return {log};
};
OutputVector translate_log_sigmoid(const NodeContext& context) {
num_inputs_check(context, 1, 1);
auto x = context.get_input(0);
x = context.mark_node(std::make_shared<v0::Convert>(x, element::f32));
auto sigmoid = context.mark_node(std::make_shared<v0::Sigmoid>(x));
auto log = context.mark_node(std::make_shared<v0::Log>(sigmoid));
return {log};
};
OutputVector translate_log2(const NodeContext& context) {
// torch.log2 returns a tensor with the logarithm to the base 2 of the elements of input.
num_inputs_check(context, 1, 1);

View File

@ -13,7 +13,7 @@ namespace op {
using namespace ov::op;
OutputVector translate_log_softmax(const NodeContext& context) {
OutputVector translate_log_softmax_common(const NodeContext& context, bool is_fx) {
/*
aten::log_softmax(
Tensor input,
@ -25,7 +25,7 @@ OutputVector translate_log_softmax(const NodeContext& context) {
auto input = context.get_input(0);
auto const dim = context.const_input<int64_t>(1);
if (!context.input_is_none(2)) {
if (!context.input_is_none(2) && !is_fx) {
const auto elem_type = input.get_element_type();
const auto target_dtype_i64 = context.const_input<int64_t>(2);
const auto target_dtype = convert_dtype(target_dtype_i64);
@ -38,6 +38,14 @@ OutputVector translate_log_softmax(const NodeContext& context) {
return {log_softmax};
};
OutputVector translate_log_softmax(const NodeContext& context) {
return translate_log_softmax_common(context, false);
}
OutputVector translate_log_softmax_fx(const NodeContext& context) {
return translate_log_softmax_common(context, true);
}
} // namespace op
} // namespace pytorch
} // namespace frontend

View File

@ -11,6 +11,7 @@
#include "openvino/op/reduce_min.hpp"
#include "openvino/op/squeeze.hpp"
#include "openvino/op/topk.hpp"
#include "openvino/op/util/framework_node.hpp"
#include "utils.hpp"
namespace ov {
@ -36,7 +37,7 @@ OutputVector translate_max(const NodeContext& context) {
align_eltwise_input_types(context, x, y, true);
return {context.mark_node(std::make_shared<v1::Maximum>(x, y))};
}
// torch.max(input, dim, keepdim), returns values and indicies
// torch.max(input, dim, keepdim), returns values and indices
auto axes_node = context.get_input(1);
auto axis_const = context.const_input<int64_t>(1);
auto keepdims = context.const_input<bool>(2);
@ -44,11 +45,38 @@ OutputVector translate_max(const NodeContext& context) {
auto k = context.mark_node(std::make_shared<v0::Constant>(element::i32, Shape{}, 1));
auto topk =
context.mark_node(std::make_shared<v3::TopK>(x, k, axis_const, v3::TopK::Mode::MAX, v3::TopK::SortType::NONE));
auto indicies = context.mark_node(std::make_shared<v0::Convert>(topk->output(1), element::i64));
auto indices = context.mark_node(std::make_shared<v0::Convert>(topk->output(1), element::i64));
if (!keepdims) {
indicies = context.mark_node(std::make_shared<v0::Squeeze>(indicies, axes_node));
indices = context.mark_node(std::make_shared<v0::Squeeze>(indices, axes_node));
}
return {values, indicies};
return {values, indices};
};
OutputVector translate_max_dim(const NodeContext& context) {
// torch.max.dim(x, dim, keepdim)
num_inputs_check(context, 2, 3);
auto x = context.get_input(0);
auto axes_node = context.get_input(1);
auto axis_const = context.const_input<int64_t>(1);
bool keepdims = false;
if (!context.input_is_none(2)) {
keepdims = context.const_input<bool>(2);
}
auto values = context.mark_node(std::make_shared<v1::ReduceMax>(x, axes_node, keepdims));
auto k = context.mark_node(std::make_shared<v0::Constant>(element::i32, Shape{}, 1));
auto topk = std::make_shared<v3::TopK>(x, k, axis_const, v3::TopK::Mode::MAX, v3::TopK::SortType::NONE);
auto indices = context.mark_node(std::make_shared<v0::Convert>(topk->output(1), element::i64));
if (!keepdims) {
indices = std::make_shared<v0::Squeeze>(indices, axes_node);
}
return {values, indices};
};
OutputVector translate_max_dim_fx(const NodeContext& context) {
ov::OutputVector out_vec = translate_max_dim(context);
return {context.mark_node(make_list_construct(out_vec))};
};
OutputVector translate_min(const NodeContext& context) {
@ -67,7 +95,7 @@ OutputVector translate_min(const NodeContext& context) {
align_eltwise_input_types(context, x, y, true);
return {context.mark_node(std::make_shared<v1::Minimum>(x, y))};
}
// torch.min(input, dim, keepdim), returns values and indicies
// torch.min(input, dim, keepdim), returns values and indices
auto axes_node = context.get_input(1);
auto axis_const = context.const_input<int64_t>(1);
auto keepdims = context.const_input<bool>(2);
@ -75,11 +103,11 @@ OutputVector translate_min(const NodeContext& context) {
auto k = context.mark_node(std::make_shared<v0::Constant>(element::i32, Shape{}, 1));
auto topk =
context.mark_node(std::make_shared<v3::TopK>(x, k, axis_const, v3::TopK::Mode::MIN, v3::TopK::SortType::NONE));
auto indicies = context.mark_node(std::make_shared<v0::Convert>(topk->output(1), element::i64));
auto indices = context.mark_node(std::make_shared<v0::Convert>(topk->output(1), element::i64));
if (!keepdims) {
indicies = context.mark_node(std::make_shared<v0::Squeeze>(indicies, axes_node));
indices = context.mark_node(std::make_shared<v0::Squeeze>(indices, axes_node));
}
return {values, indicies};
return {values, indices};
};
OutputVector translate_maximum(const NodeContext& context) {

View File

@ -3,6 +3,7 @@
//
#include "openvino/frontend/pytorch/node_context.hpp"
#include "openvino/op/unsqueeze.hpp"
#include "utils_quantize.hpp"
namespace ov {
@ -31,6 +32,22 @@ OutputVector translate_quantize_per_channel(const NodeContext& context) {
return {quantize(context, input, scales, zero_points, axis, dtype, QuantizedPtNodeType::QUANTIZE_PER_CHANNEL)};
}
OutputVector translate_fake_quantize_per_tensor_affine_fx(const NodeContext& context) {
num_inputs_check(context, 6, 6);
auto out = translate_quantize_per_tensor(context);
auto axis_0 = context.mark_node(v0::Constant::create(element::i32, Shape{}, {0}));
return {context.mark_node(std::make_shared<v0::Unsqueeze>(out[0], axis_0))};
}
OutputVector translate_fake_quantize_per_channel_affine_fx(const NodeContext& context) {
num_inputs_check(context, 6, 6);
auto out = translate_quantize_per_channel(context);
auto axis_0 = context.mark_node(v0::Constant::create(element::i32, Shape{}, {0}));
return {context.mark_node(std::make_shared<v0::Unsqueeze>(out[0], axis_0))};
}
} // namespace op
} // namespace pytorch
} // namespace frontend

View File

@ -36,6 +36,23 @@ OutputVector translate_chunk_fx(const NodeContext& context) {
return {context.mark_node(make_list_construct(chunk->outputs()))};
}
OutputVector translate_unbind_int_fx(const NodeContext& context) {
num_inputs_check(context, 2, 3);
auto input = context.get_input(0);
auto dim = context.get_input(1);
auto dim_val = context.const_input<int>(1);
auto shape = input.get_shape();
if (dim_val < 0) {
dim_val = static_cast<int>(shape.size()) + dim_val;
}
auto num_splits = static_cast<int>(shape[dim_val]);
auto chunk = context.mark_node(std::make_shared<v1::Split>(input, dim, num_splits));
return {context.mark_node(make_list_construct(chunk->outputs()))};
}
OutputVector translate_split_with_sizes_fx(const NodeContext& context) {
num_inputs_check(context, 3, 3);
auto data = context.get_input(0);

View File

@ -12,6 +12,7 @@
#include "openvino/op/shape_of.hpp"
#include "openvino/op/sqrt.hpp"
#include "openvino/op/subtract.hpp"
#include "openvino/op/util/framework_node.hpp"
#include "utils.hpp"
namespace ov {
@ -73,6 +74,27 @@ OutputVector translate_var_mean(const NodeContext& context) {
return {var, mean};
};
OutputVector translate_var_mean_fx(const NodeContext& context) {
num_inputs_check(context, 2, 2);
auto data = context.get_input(0);
auto num_elements = numel(context, data);
std::shared_ptr<ov::Node> mean;
ov::Output<ov::Node> axes;
axes = context.get_input(1);
mean = context.mark_node(std::make_shared<v1::ReduceMean>(data, axes, true));
auto sub_v = context.mark_node(std::make_shared<v1::Subtract>(data, mean));
auto sqr_sub = context.mark_node(std::make_shared<v1::Multiply>(sub_v, sub_v));
auto var = context.mark_node(std::make_shared<v1::ReduceMean>(sqr_sub, axes, true));
ov::OutputVector out_vec;
out_vec.push_back(var);
out_vec.push_back(mean);
return {context.mark_node(make_list_construct(out_vec))};
};
OutputVector translate_var(const NodeContext& context) {
auto res = translate_var_mean(context);
return {res[0]};

View File

@ -114,6 +114,7 @@ OP_CONVERTER(translate_list_construct);
OP_CONVERTER(translate_list_unpack);
OP_CONVERTER(translate_log);
OP_CONVERTER(translate_log1p);
OP_CONVERTER(translate_log_sigmoid);
OP_CONVERTER(translate_log_softmax);
OP_CONVERTER(translate_log2);
OP_CONVERTER(translate_log10);
@ -123,6 +124,7 @@ OP_CONVERTER(translate_lstm);
OP_CONVERTER(translate_masked_fill);
OP_CONVERTER(translate_masked_scatter);
OP_CONVERTER(translate_max);
OP_CONVERTER(translate_max_dim);
OP_CONVERTER(translate_maximum);
OP_CONVERTER(translate_max_poolnd);
OP_CONVERTER(translate_mean);
@ -247,23 +249,30 @@ OP_CONVERTER(translate_constant_pad_nd_fx);
OP_CONVERTER(translate_chunk_fx);
OP_CONVERTER(translate_div_fx);
OP_CONVERTER(translate_expand_fx);
OP_CONVERTER(translate_fake_quantize_per_channel_affine_fx);
OP_CONVERTER(translate_fake_quantize_per_tensor_affine_fx);
OP_CONVERTER(translate_full_fx);
OP_CONVERTER(translate_gelu_fx);
OP_CONVERTER(translate_group_norm_fx);
OP_CONVERTER(translate_index_fx);
OP_CONVERTER(translate_layer_norm_fx);
OP_CONVERTER(translate_leaky_relu_fx);
OP_CONVERTER(translate_log_softmax_fx);
OP_CONVERTER(translate_max_dim_fx);
OP_CONVERTER(translate_max_poolnd_fx);
OP_CONVERTER(translate_mean_fx);
OP_CONVERTER(translate_split_with_sizes_fx);
OP_CONVERTER(translate_scalar_tensor_fx);
OP_CONVERTER(translate_scaled_dot_product_attention_fx);
OP_CONVERTER(translate_slice_fx);
OP_CONVERTER(translate_slice_scatter_fx);
OP_CONVERTER(translate_softmax_fx);
OP_CONVERTER(translate_split_with_sizes_fx);
OP_CONVERTER(translate_stack_fx);
OP_CONVERTER(translate_sub_fx);
OP_CONVERTER(translate_to_fx);
OP_CONVERTER(translate_transpose_fx);
OP_CONVERTER(translate_var_mean_fx);
OP_CONVERTER(translate_unbind_int_fx);
} // namespace op
@ -660,15 +669,20 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.adaptive_max_pool1d.default", op::translate_adaptive_max_pool1d_fx},
{"aten.adaptive_max_pool2d.default", op::translate_adaptive_max_pool2d_fx},
{"aten.adaptive_max_pool3d.default", op::translate_adaptive_max_pool3d_fx},
{"aten._fake_quantize_per_tensor_affine_cachemask_tensor_qparams.default",
op::translate_fake_quantize_per_tensor_affine_fx},
{"aten._local_scalar_dense.default", op::skip_node},
{"aten._log_softmax.default", op::translate_log_softmax_fx},
{"aten._softmax.default", op::translate_softmax_fx},
{"aten._to_copy.default", op::translate_to_fx},
{"aten._unsafe_view.default", op::translate_reshape},
{"aten.add.Scalar", op::translate_add},
{"aten.add.Tensor", op::translate_add},
{"aten.add_.Tensor", op::translate_add},
{"aten.addcmul.default", op::translate_addcmul_fx},
{"aten.addmm.default", op::translate_addmm_fx},
{"aten.alias.default", op::skip_node},
{"aten.amax.default", op::translate_amax},
{"aten.arange.start", op::translate_arange_fx},
{"aten.arange.start_step", op::translate_arange_fx},
{"aten.arange.default", op::translate_arange_fx},
@ -697,10 +711,13 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.div.Tensor_mode", op::translate_div_fx},
{"aten.embedding.default", op::translate_embedding},
{"aten.empty.memory_format", op::translate_empty},
{"aten.erf.default", op::translate_erf},
{"aten.eq.Scalar", op::translate_1to1_match_2_inputs_align_types<opset10::Equal>},
{"aten.eq.Tensor", op::translate_1to1_match_2_inputs_align_types<opset10::Equal>},
{"aten.exp.default", op::translate_1to1_match_1_inputs<opset10::Exp>},
{"aten.expand.default", op::translate_expand_fx},
{"aten.fake_quantize_per_channel_affine_cachemask.default", op::translate_fake_quantize_per_channel_affine_fx},
{"aten.fill.Scalar", op::translate_fill},
{"aten.floor.default", op::translate_1to1_match_1_inputs<opset10::Floor>},
{"aten.floor_divide.default", op::translate_floor_divide},
{"aten.full.default", op::translate_full_fx},
@ -721,11 +738,13 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.lift_fresh_copy.default", op::skip_node},
{"aten.linalg_vector_norm.default", op::translate_linalg_vector_norm},
{"aten.log.default", op::translate_log},
{"aten.log_sigmoid_forward.default", op::translate_log_sigmoid},
{"aten.logsumexp.default", op::translate_logsumexp},
{"aten.lt.Scalar", op::translate_1to1_match_2_inputs_align_types<opset10::Less>},
{"aten.lt.Tensor", op::translate_1to1_match_2_inputs_align_types<opset10::Less>},
{"aten.masked_fill_.Scalar", op::inplace_op<op::translate_masked_fill>},
{"aten.masked_fill.Tensor", op::translate_masked_fill},
{"aten.max.dim", op::translate_max_dim_fx},
{"aten.max_pool2d_with_indices.default", op::translate_max_poolnd_fx},
{"aten.max_pool3d_with_indices.default", op::translate_max_poolnd_fx},
{"aten.mean.dim", op::translate_mean_fx},
@ -742,12 +761,14 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.native_layer_norm.default", op::translate_layer_norm_fx},
{"aten.ne.Scalar", op::translate_1to1_match_2_inputs_align_types<opset10::NotEqual>},
{"aten.neg.default", op::translate_neg},
{"aten.new_full.default", op::translate_new_full},
{"aten.new_ones.default", op::translate_new_ones},
{"aten.permute.default", op::translate_1to1_match_2_inputs<opset10::Transpose>},
{"aten.pow.Tensor_Scalar", op::translate_pow},
{"aten.relu.default", op::translate_1to1_match_1_inputs<opset10::Relu>},
{"aten.relu_.default", op::inplace_op<op::translate_1to1_match_1_inputs<opset10::Relu>>},
{"aten.repeat.default", op::translate_1to1_match_2_inputs<opset10::Tile>},
{"aten.rsqrt.default", op::translate_rsqrt},
{"aten.rsub.Scalar", op::translate_rsub},
{"aten.roll.default", op::translate_roll},
{"aten._scaled_dot_product_flash_attention.default", op::translate_scaled_dot_product_attention_fx},
@ -764,6 +785,7 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.split_with_sizes.default", op::translate_split_with_sizes_fx},
{"aten.squeeze.dim", op::translate_squeeze},
{"aten.squeeze.dims", op::translate_squeeze},
{"aten.stack.default", op::translate_stack_fx},
{"aten.sub.default", op::translate_sub_fx},
{"aten.sub.Tensor", op::translate_sub_fx},
{"aten.sum.dim_IntList", op::translate_sum},
@ -771,8 +793,10 @@ const std::map<std::string, CreatorFunction> get_supported_ops_fx() {
{"aten.tanh.default", op::translate_1to1_match_1_inputs<opset10::Tanh>},
{"aten.unfold.default", op::translate_unfold},
{"aten.transpose.int", op::translate_transpose},
{"aten.unbind.int", op::translate_unbind_int_fx},
{"aten.unsqueeze.default", op::translate_1to1_match_2_inputs<opset10::Unsqueeze>},
{"aten.upsample_nearest2d.default", op::translate_upsample_nearest2d},
{"aten.var_mean.correction", op::translate_var_mean_fx},
{"aten.view.default", op::translate_reshape},
{"aten.where.self", op::translate_where},
{"aten.zeros_like.default", op::translate_zeros_like},

View File

@ -15,7 +15,7 @@ from openvino.frontend import FrontEndManager
from openvino.runtime import Core, Type, PartialShape
import torch
from packaging import version
import openvino.frontend.pytorch.torchdynamo.backend
import openvino.torch
class PytorchLayerTest:
@ -263,10 +263,13 @@ class PytorchLayerTest:
torch._dynamo.reset()
with torch.no_grad():
model.eval()
fw_model = torch.compile(model)
ov_model = torch.compile(model, backend="openvino")
ov_res = ov_model(*inputs)
fw_res = fw_model(*inputs)
fw_res = model(*inputs)
torch._dynamo.reset()
with torch.no_grad():
model.eval()
ov_model = torch.compile(model, backend="openvino", options={"testing" : 1})
ov_res = ov_model(*inputs)
if not isinstance(fw_res, (tuple)):
fw_res = (fw_res,)

View File

@ -17,7 +17,7 @@ class TestErf(PytorchLayerTest):
def create_model(self, mode="", input_dtype="float32"):
import torch
dtypes = {
"float32": torch.float32,
"float32": torch.float32,
"float64": torch.float64,
"int32": torch.int32
}
@ -48,10 +48,11 @@ class TestErf(PytorchLayerTest):
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_fx_backend
@pytest.mark.parametrize("mode,input_dtype", [
("", "float32"), ("", "float64"), ("", "int32"),
("out", "float32"), ("out", "float64"),
("inplace", "float32"), ("inplace", "float64")])
def test_erf(self, mode, input_dtype, ie_device, precision, ir_version):
self._test(*self.create_model(mode, input_dtype), ie_device, precision, ir_version,
self._test(*self.create_model(mode, input_dtype), ie_device, precision, ir_version,
kwargs_to_prepare_input={"input_dtype": input_dtype, "out": mode == "out"} )

View File

@ -34,12 +34,13 @@ class TestLogSoftmax(PytorchLayerTest):
])
@pytest.mark.parametrize("dim", [
0,
1,
1,
-1
])
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_fx_backend
def test_log_softmax(self, input_dtype, convert_dtype, dim, ie_device, precision, ir_version):
self.input_dtype = input_dtype
self._test(aten_log_softmax(dim, convert_dtype), None, "aten::log_softmax",
self._test(aten_log_softmax(dim, convert_dtype), None, "aten::log_softmax",
ie_device, precision, ir_version)

View File

@ -245,13 +245,13 @@ class TestMinimumMaximum(PytorchLayerTest):
self.r_dtype = r_dtype
if out:
self.forward = self.forward_out
def forward_out(self, x, y, z):
return self.op(x.to(self.l_dtype), y.to(self.r_dtype), out=z), z
def forward(self, x, y):
return self.op(x.to(self.l_dtype), y.to(self.r_dtype))
l_dtype = dtypes_map[dtypes[0]]
r_dtype = dtypes_map[dtypes[1]]
model_cls = aten_minimum_maximum(op, l_dtype, r_dtype, out)
@ -281,7 +281,7 @@ class TestMinimumMaximum(PytorchLayerTest):
):
self._test(*self.create_model(op_type, dtypes=(input_dtype, input_dtype), out=True),
ie_device, precision, ir_version, kwargs_to_prepare_input=
{"input_dtype": input_dtype, "second_input_dtype": input_dtype,
{"input_dtype": input_dtype, "second_input_dtype": input_dtype,
"out": True}
)
@ -315,13 +315,13 @@ class TestAminAmax(PytorchLayerTest):
self.keep_dims = keep_dims
if out:
self.forward = self.forward_out
def forward_out(self, x, y):
return self.op(x, self.axis, self.keep_dims, out=y), y
return self.op(x, self.axis, self.keep_dims, out=y), y
def forward(self, x):
return self.op(x, self.axis, self.keep_dims)
model_cls = aten_amin_amax(op, axis, keep_dims, out)
@ -332,6 +332,7 @@ class TestAminAmax(PytorchLayerTest):
@pytest.mark.parametrize("keep_dims", [True, False])
@pytest.mark.parametrize("out", [True, False])
@pytest.mark.parametrize("input_dtype", ['float32', 'int32', 'int64', 'float64'])
@pytest.mark.precommit_fx_backend
def test_amin_amax(self, op_type, input_dtype, axis, keep_dims, out, ie_device, precision, ir_version):
self._test(*self.create_model(op_type, axis, keep_dims, out),
ie_device, precision, ir_version, kwargs_to_prepare_input=

View File

@ -50,6 +50,7 @@ class TestSoftmax(PytorchLayerTest):
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_torch_export
@pytest.mark.precommit_fx_backend
def test_softmax(self, dim, ie_device, precision, ir_version):
self._test(*self.create_model(dim), ie_device, precision, ir_version)
@ -59,6 +60,7 @@ class TestSoftmax(PytorchLayerTest):
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_torch_export
@pytest.mark.precommit_fx_backend
def test_softmax(self, dim, dtype, use_prim_dtype, ie_device, precision, ir_version):
input_kwargs = {}
if use_prim_dtype:

View File

@ -32,6 +32,7 @@ class TestSqueeze(PytorchLayerTest):
@pytest.mark.parametrize("dim,dynamic_shapes", [(-2, True), (0, True), (None, False)])
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_fx_backend
def test_squeeze(self, dim, dynamic_shapes, ie_device, precision, ir_version):
self._test(*self.create_model(dim), ie_device, precision, ir_version, dynamic_shapes=dynamic_shapes)
@ -39,6 +40,7 @@ class TestSqueeze(PytorchLayerTest):
@pytest.mark.parametrize("dim", [-1, 2])
@pytest.mark.nightly
@pytest.mark.precommit
@pytest.mark.precommit_fx_backend
def test_squeeze_non_1(self, dim, ie_device, precision, ir_version):
# Dynamic shapes are introducing dynamic rank, with is not suppoerted by Squeeze operation.
self._test(*self.create_model(dim), ie_device, precision, ir_version, dynamic_shapes=False)

View File

@ -57,7 +57,7 @@ class TestSum(PytorchLayerTest):
else:
return torch.sum(x, self.axes, dtype=self.dtype)
if self.dtype is not None:
if self.dtype is not None:
return torch.sum(x, self.axes, self.keep_dims, dtype=self.dtype)
else:
return torch.sum(x, self.axes, self.keep_dims)