# Copyright (C) 2018-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # flake8: noqa # mypy: ignore-errors import logging import os from functools import partial from hashlib import sha256 import torch 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._inductor.freezing import replace_params_with_constants from torch.fx.experimental.proxy_tensor import make_fx from torch._decomp import decomposition_table, get_decompositions from openvino.frontend import FrontEndManager from openvino 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, get_inf_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, _get_decompositions, _get_aot_autograd from openvino import Core, Type, PartialShape logger = logging.getLogger(__name__) logger.setLevel(logging.WARNING) """ This is a preview feature in OpenVINO. This feature enables users to compile PyTorch models using torch.compile with OpenVINO as a target backend in PyTorch applications Sample usage: This sample code loads resnet50 torchvision model and compiles it using torch dynamo. We can then use this model for inference. We only need to add two lines of code to the Pytorch applications which are marked in the code below 1) import openvino.torch model = torchvision.models.resnet50() 2) model = torch.compile(model, backend="openvino") """ openvino_options = {} # Disable regional compilation which was enabled by default from Torch 2.5.0 if hasattr(torch._dynamo.config, "inline_inbuilt_nn_modules"): torch._dynamo.config.inline_inbuilt_nn_modules=False @fake_tensor_unsupported def openvino(subgraph, example_inputs, options=None): if _get_aot_autograd(options): global openvino_options openvino_options = options decompositions = _get_decompositions(options) + get_inf_decomposition_list() + get_aot_decomposition_list() return aot_autograd(fw_compiler=fx_openvino, bw_compiler=fx_openvino, decompositions=get_decompositions(decompositions))(subgraph, example_inputs) return fx_openvino(subgraph, example_inputs, options) if "openvino" not in torch.compiler.list_backends(): register_backend(compiler_fn=openvino, name="openvino") 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) if openvino_model_caching is not None and openvino_model_caching: # Create a hash to be used for caching model_hash_str = sha256(subgraph.code.encode("utf-8")).hexdigest() executor_parameters = {"model_hash_str": model_hash_str} # Check if the model was fully supported and already cached example_inputs.reverse() inputs_reversed = True maybe_fs_cached_name = cached_model_name(model_hash_str + "_fs", _get_device(options), example_inputs, _get_cache_dir(options)) if os.path.isfile(maybe_fs_cached_name + ".xml") and os.path.isfile(maybe_fs_cached_name + ".bin"): # Model is fully supported and already cached. Run the cached OV model directly. compiled_model = openvino_compile_cached_model(maybe_fs_cached_name, options, *example_inputs) def _call(*args): res = execute_cached(compiled_model, *args) return res return _call if inputs_reversed: example_inputs.reverse() preserved_arg_indices = [] if _get_aot_autograd(options): if tracing_context := torch._guards.TracingContext.try_get(): fw_metadata = tracing_context.fw_metadata params_flat = tracing_context.params_flat assert fw_metadata is not None and params_flat is not None preserved_arg_indices = replace_params_with_constants(subgraph, params_flat, fw_metadata) example_inputs = [example_inputs[ind] for ind in preserved_arg_indices] model = subgraph else: from torch._subclasses.fake_tensor import FakeTensorMode decompositions = _get_decompositions(options) + get_inf_decomposition_list() with FakeTensorMode(allow_non_fake_inputs=True) as fakemode: fake_inputs = [fakemode.from_tensor(x) for x in example_inputs] model = make_fx(subgraph, decomposition_table=get_decompositions(decompositions))(*fake_inputs) with torch.no_grad(): model.eval() 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. fully_supported = partitioner.check_fully_supported(compiled_model) if fully_supported: executor_parameters["model_hash_str"] += "_fs" def _call(*args): if _get_aot_autograd(options): args_list = args[0] args_new = [args_list[i] for i in preserved_arg_indices] args = args_new res = execute(compiled_model, *args, executor="openvino", executor_parameters=executor_parameters, options=options) return res if _get_aot_autograd(options): _call._boxed_call = True # type: ignore[attr-defined] return _call except Exception as e: logger.debug(f"Failed in OpenVINO execution: {e}") return compile_fx(subgraph, example_inputs) def reset(): clear_caches()