diff --git a/hls4ml/converters/__init__.py b/hls4ml/converters/__init__.py index 1343907b5..693a76f66 100644 --- a/hls4ml/converters/__init__.py +++ b/hls4ml/converters/__init__.py @@ -1,6 +1,5 @@ import importlib import os -import warnings import yaml @@ -10,33 +9,19 @@ from hls4ml.converters.keras_to_hls import get_supported_keras_layers # noqa: F401 from hls4ml.converters.keras_to_hls import parse_keras_model # noqa: F401 from hls4ml.converters.keras_to_hls import keras_to_hls, register_keras_layer_handler +from hls4ml.converters.onnx_to_hls import get_supported_onnx_layers # noqa: F401 from hls4ml.converters.onnx_to_hls import parse_onnx_model # noqa: F401 +from hls4ml.converters.onnx_to_hls import onnx_to_hls, register_onnx_layer_handler +from hls4ml.converters.pytorch_to_hls import ( # noqa: F401 + get_supported_pytorch_layers, + pytorch_to_hls, + register_pytorch_layer_handler, +) from hls4ml.model import ModelGraph from hls4ml.utils.config import create_config +from hls4ml.utils.dependency import requires from hls4ml.utils.symbolic_utils import LUTFunction -# ----------Make converters available if the libraries can be imported----------# -try: - from hls4ml.converters.pytorch_to_hls import ( # noqa: F401 - get_supported_pytorch_layers, - pytorch_to_hls, - register_pytorch_layer_handler, - ) - - __pytorch_enabled__ = True -except ImportError: - warnings.warn("WARNING: Pytorch converter is not enabled!", stacklevel=1) - __pytorch_enabled__ = False - -try: - from hls4ml.converters.onnx_to_hls import get_supported_onnx_layers # noqa: F401 - from hls4ml.converters.onnx_to_hls import onnx_to_hls, register_onnx_layer_handler - - __onnx_enabled__ = True -except ImportError: - warnings.warn("WARNING: ONNX converter is not enabled!", stacklevel=1) - __onnx_enabled__ = False - # ----------Layer handling register----------# model_types = ['keras', 'pytorch', 'onnx'] @@ -51,7 +36,7 @@ # and has 'handles' attribute # and is defined in this module (i.e., not imported) if callable(func) and hasattr(func, 'handles') and func.__module__ == lib.__name__: - for layer in func.handles: + for layer in func.handles: # type: ignore if model_type == 'keras': register_keras_layer_handler(layer, func) elif model_type == 'pytorch': @@ -124,15 +109,9 @@ def convert_from_config(config): model = None if 'OnnxModel' in yamlConfig: - if __onnx_enabled__: - model = onnx_to_hls(yamlConfig) - else: - raise Exception("ONNX not found. Please install ONNX.") + model = onnx_to_hls(yamlConfig) elif 'PytorchModel' in yamlConfig: - if __pytorch_enabled__: - model = pytorch_to_hls(yamlConfig) - else: - raise Exception("PyTorch not found. Please install PyTorch.") + model = pytorch_to_hls(yamlConfig) else: model = keras_to_hls(yamlConfig) @@ -174,6 +153,7 @@ def _check_model_config(model_config): return model_config +@requires('_keras') def convert_from_keras_model( model, output_dir='my-hls-test', @@ -237,6 +217,7 @@ def convert_from_keras_model( return keras_to_hls(config) +@requires('_torch') def convert_from_pytorch_model( model, output_dir='my-hls-test', @@ -308,6 +289,7 @@ def convert_from_pytorch_model( return pytorch_to_hls(config) +@requires('onnx') def convert_from_onnx_model( model, output_dir='my-hls-test', @@ -371,6 +353,7 @@ def convert_from_onnx_model( return onnx_to_hls(config) +@requires('sr') def convert_from_symbolic_expression( expr, n_symbols=None, diff --git a/hls4ml/converters/onnx_to_hls.py b/hls4ml/converters/onnx_to_hls.py index f3b6acbaf..d51701e72 100644 --- a/hls4ml/converters/onnx_to_hls.py +++ b/hls4ml/converters/onnx_to_hls.py @@ -1,4 +1,5 @@ from hls4ml.model import ModelGraph +from hls4ml.utils.dependency import requires # ----------------------Helpers--------------------- @@ -17,6 +18,7 @@ def replace_char_inconsitency(name): return name.replace('.', '_') +@requires('onnx') def get_onnx_attribute(operation, name, default=None): from onnx import helper @@ -73,6 +75,7 @@ def get_input_shape(graph, node): return rv +@requires('onnx') def get_constant_value(graph, constant_name): tensor = next((x for x in graph.initializer if x.name == constant_name), None) from onnx import numpy_helper @@ -258,6 +261,7 @@ def parse_onnx_model(onnx_model): return layer_list, input_layers, output_layers +@requires('onnx') def onnx_to_hls(config): """Convert onnx model to hls model from configuration. diff --git a/hls4ml/converters/pytorch_to_hls.py b/hls4ml/converters/pytorch_to_hls.py index 3ec5b1769..f279a1970 100644 --- a/hls4ml/converters/pytorch_to_hls.py +++ b/hls4ml/converters/pytorch_to_hls.py @@ -1,4 +1,5 @@ from hls4ml.model import ModelGraph +from hls4ml.utils.dependency import requires class PyTorchModelReader: @@ -22,6 +23,7 @@ def get_weights_data(self, layer_name, var_name): return data +@requires('_torch') class PyTorchFileReader(PyTorchModelReader): # Inherit get_weights_data method def __init__(self, config): import torch @@ -103,6 +105,7 @@ def decorator(function): # ---------------------------------------------------------------- +@requires('_torch') def parse_pytorch_model(config, verbose=True): """Convert PyTorch model to hls4ml ModelGraph. @@ -368,6 +371,7 @@ def parse_pytorch_model(config, verbose=True): return layer_list, input_layers +@requires('_torch') def pytorch_to_hls(config): layer_list, input_layers = parse_pytorch_model(config) print('Creating HLS model') diff --git a/hls4ml/model/__init__.py b/hls4ml/model/__init__.py index fc504392b..4ca72e3cd 100644 --- a/hls4ml/model/__init__.py +++ b/hls4ml/model/__init__.py @@ -1,8 +1 @@ from hls4ml.model.graph import HLSConfig, ModelGraph # noqa: F401 - -try: - from hls4ml.model import profiling # noqa: F401 - - __profiling_enabled__ = True -except ImportError: - __profiling_enabled__ = False diff --git a/hls4ml/model/quantizers.py b/hls4ml/model/quantizers.py index b445c70af..eb313fc4e 100644 --- a/hls4ml/model/quantizers.py +++ b/hls4ml/model/quantizers.py @@ -14,6 +14,7 @@ SaturationMode, XnorPrecisionType, ) +from hls4ml.utils.dependency import requires class Quantizer: @@ -84,6 +85,7 @@ class QKerasQuantizer(Quantizer): config (dict): Config of the QKeras quantizer to wrap. """ + @requires('qkeras') def __init__(self, config): from qkeras.quantizers import get_quantizer @@ -131,6 +133,7 @@ class QKerasBinaryQuantizer(Quantizer): config (dict): Config of the QKeras quantizer to wrap. """ + @requires('qkeras') def __init__(self, config, xnor=False): from qkeras.quantizers import get_quantizer @@ -155,6 +158,7 @@ class QKerasPO2Quantizer(Quantizer): config (dict): Config of the QKeras quantizer to wrap. """ + @requires('qkeras') def __init__(self, config): from qkeras.quantizers import get_quantizer diff --git a/hls4ml/report/quartus_report.py b/hls4ml/report/quartus_report.py index 47fc43c13..677a93140 100644 --- a/hls4ml/report/quartus_report.py +++ b/hls4ml/report/quartus_report.py @@ -2,6 +2,8 @@ import webbrowser from ast import literal_eval +from hls4ml.utils.dependency import requires + def parse_quartus_report(hls_dir, write_to_file=True): ''' @@ -39,6 +41,7 @@ def parse_quartus_report(hls_dir, write_to_file=True): return results +@requires('quantus-report') def read_quartus_report(hls_dir, open_browser=False): ''' Parse and print the Quartus report to print the report. Optionally open a browser. @@ -89,6 +92,7 @@ def _find_project_dir(hls_dir): return top_func_name + '-fpga.prj' +@requires('quantus-report') def read_js_object(js_script): ''' Reads the JavaScript file and return a dictionary of variables definded in the script. diff --git a/hls4ml/utils/config.py b/hls4ml/utils/config.py index 6a356f5f2..8c8ff3a06 100644 --- a/hls4ml/utils/config.py +++ b/hls4ml/utils/config.py @@ -1,6 +1,7 @@ import json import hls4ml +from hls4ml.utils.dependency import requires def create_config(output_dir='my-hls-test', project_name='myproject', backend='Vivado', version='1.0.0', **kwargs): @@ -44,6 +45,7 @@ def create_config(output_dir='my-hls-test', project_name='myproject', backend='V return config +@requires('qkeras') def _get_precision_from_quantizer(quantizer): if isinstance(quantizer, str): import qkeras diff --git a/pyproject.toml b/pyproject.toml index 756e688d5..24175c961 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ optional-dependencies.doc = [ "sphinx-rtd-theme", ] optional-dependencies.HGQ = [ "hgq~=0.2.0" ] +optional-dependencies.onnx = [ "onnx>=1.4" ] optional-dependencies.optimization = [ "keras-tuner==1.1.3", "ortools==9.4.1874",